diff --git a/dav/FriendicaACLPlugin.inc.php b/dav/FriendicaACLPlugin.inc.php new file mode 100644 index 000000000..6bb8fc990 --- /dev/null +++ b/dav/FriendicaACLPlugin.inc.php @@ -0,0 +1,31 @@ +getSize is only called for files, no longer for collections. + r303 + * Changed: Sabre_DAV_FilterTree is now Sabre_DAV_Tree_Filter + * Changed: Sabre_DAV_TemporaryFileFilter is now called + Sabre_DAV_Tree_TemporaryFileFilter. + * Changed: removed functions (get(/set)HTTPRequest(/Response)) from Server + class, and using a public property instead. + * Fixed: bug related to parsing proppatch and propfind requests. Didn't + show up in most clients, but it needed fixing regardless. (r255) + * Fixed: auth-int is now properly supported within HTTP Digest. + * Fixed: Using application/xml for a mimetype vs. text/xml as per RFC4918 + sec 8.2. + * Fixed: TemporaryFileFilter now lets through GET's if they actually + exist on the backend. (r274) + * FIxed: Some methods didn't get passed through in the FilterTree (r283). + * Fixed: LockManager is now slightly more complex, Tree classes slightly + less. (r287) + +0.6-alpha (2009-02-16) + * Added: Now uses streams for files, instead of strings. + This means it won't require to hold entire files in memory, which can be + an issue if you're dealing with big files. Note that this breaks + compatibility for put() and createFile methods. + * Added: HTTP Digest Authentication helper class. + * Added: Support for HTTP Range header + * Added: Support for ETags within If: headers + * Added: The API can now return ETags and override the default Content-Type + * Added: starting with basic framework for unittesting, using PHPUnit. + * Added: 49 unittests. + * Added: Abstraction for the HTTP request. + * Updated: Using Clark Notation for tags in properties. This means tags + are serialized as {namespace}tagName instead of namespace#tagName + * Fixed: HTTP_BasicAuth class now works as expected. + * Fixed: DAV_Server uses / for a default baseUrl. + * Fixed: Last modification date is no longer ignored in PROPFIND. + * Fixed: PROPFIND now sends back information about the requestUri even + when "Depth: 1" is specified. + +0.5-alpha (2009-01-14) + * Added: Added a very simple example for implementing a mapping to PHP + file streams. This should allow easy implementation of for example a + WebDAV to FTP proxy. + * Added: HTTP Basic Authentication helper class. + * Added: Sabre_HTTP_Response class. This centralizes HTTP operations and + will be a start towards the creating of a testing framework. + * Updated: Backwards compatibility break: all require_once() statements + are removed + from all the files. It is now recommended to use autoloading of + classes, or just including lib/Sabre.includes.php. This fix was made + to allow easier integration into applications not using this standard + inclusion model. + * Updated: Better in-file documentation. + * Updated: Sabre_DAV_Tree can now work with Sabre_DAV_LockManager. + * Updated: Fixes a shared-lock bug. + * Updated: Removed ?> from the bottom of each php file. + * Updated: Split up some operations from Sabre_DAV_Server to + Sabre_HTTP_Response. + * Fixed: examples are now actually included in the pear package. + +0.4-alpha (2008-11-05) + * Passes all litmus tests! + * Added: more examples + * Added: Custom property support + * Added: Shared lock support + * Added: Depth support to locks + * Added: Locking on unmapped urls (non-existent nodes) + * Fixed: Advertising as WebDAV class 3 support + +0.3-alpha (2008-06-29) + * Fully working in MS Windows clients. + * Added: temporary file filter: support for smultron files. + * Added: Phing build scripts + * Added: PEAR package + * Fixed: MOVE bug identified using finder. + * Fixed: Using gzuncompress instead of gzdecode in the temporary file + filter. This seems more common. + +0.2-alpha (2008-05-27) + * Somewhat working in Windows clients + * Added: Working PROPPATCH method (doesn't support custom properties yet) + * Added: Temporary filename handling system + * Added: Sabre_DAV_IQuota to return quota information + * Added: PROPFIND now reads the request body and only supplies the + requested properties + +0.1-alpha (2008-04-04) + * First release! + * Passes litmus: basic, http and copymove test. + * Fully working in Finder and DavFSv2 + +Project started: 2007-12-13 diff --git a/dav/SabreDAV/LICENSE b/dav/SabreDAV/LICENSE new file mode 100644 index 000000000..3a83c94a3 --- /dev/null +++ b/dav/SabreDAV/LICENSE @@ -0,0 +1,28 @@ +Copyright (C) 2007-2012 Rooftop Solutions. +Copyright (C) 2007-2009 FileMobile inc. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the SabreDAV nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/dav/SabreDAV/bin/googlecode_upload.py b/dav/SabreDAV/bin/googlecode_upload.py new file mode 100755 index 000000000..caafd5ded --- /dev/null +++ b/dav/SabreDAV/bin/googlecode_upload.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python +# +# Copyright 2006, 2007 Google Inc. All Rights Reserved. +# Author: danderson@google.com (David Anderson) +# +# Script for uploading files to a Google Code project. +# +# This is intended to be both a useful script for people who want to +# streamline project uploads and a reference implementation for +# uploading files to Google Code projects. +# +# To upload a file to Google Code, you need to provide a path to the +# file on your local machine, a small summary of what the file is, a +# project name, and a valid account that is a member or owner of that +# project. You can optionally provide a list of labels that apply to +# the file. The file will be uploaded under the same name that it has +# in your local filesystem (that is, the "basename" or last path +# component). Run the script with '--help' to get the exact syntax +# and available options. +# +# Note that the upload script requests that you enter your +# googlecode.com password. This is NOT your Gmail account password! +# This is the password you use on googlecode.com for committing to +# Subversion and uploading files. You can find your password by going +# to http://code.google.com/hosting/settings when logged in with your +# Gmail account. If you have already committed to your project's +# Subversion repository, the script will automatically retrieve your +# credentials from there (unless disabled, see the output of '--help' +# for details). +# +# If you are looking at this script as a reference for implementing +# your own Google Code file uploader, then you should take a look at +# the upload() function, which is the meat of the uploader. You +# basically need to build a multipart/form-data POST request with the +# right fields and send it to https://PROJECT.googlecode.com/files . +# Authenticate the request using HTTP Basic authentication, as is +# shown below. +# +# Licensed under the terms of the Apache Software License 2.0: +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Questions, comments, feature requests and patches are most welcome. +# Please direct all of these to the Google Code users group: +# http://groups.google.com/group/google-code-hosting + +"""Google Code file uploader script. +""" + +__author__ = 'danderson@google.com (David Anderson)' + +import httplib +import os.path +import optparse +import getpass +import base64 +import sys + + +def upload(file, project_name, user_name, password, summary, labels=None): + """Upload a file to a Google Code project's file server. + + Args: + file: The local path to the file. + project_name: The name of your project on Google Code. + user_name: Your Google account name. + password: The googlecode.com password for your account. + Note that this is NOT your global Google Account password! + summary: A small description for the file. + labels: an optional list of label strings with which to tag the file. + + Returns: a tuple: + http_status: 201 if the upload succeeded, something else if an + error occurred. + http_reason: The human-readable string associated with http_status + file_url: If the upload succeeded, the URL of the file on Google + Code, None otherwise. + """ + # The login is the user part of user@gmail.com. If the login provided + # is in the full user@domain form, strip it down. + if user_name.endswith('@gmail.com'): + user_name = user_name[:user_name.index('@gmail.com')] + + form_fields = [('summary', summary)] + if labels is not None: + form_fields.extend([('label', l.strip()) for l in labels]) + + content_type, body = encode_upload_request(form_fields, file) + + upload_host = '%s.googlecode.com' % project_name + upload_uri = '/files' + auth_token = base64.b64encode('%s:%s'% (user_name, password)) + headers = { + 'Authorization': 'Basic %s' % auth_token, + 'User-Agent': 'Googlecode.com uploader v0.9.4', + 'Content-Type': content_type, + } + + server = httplib.HTTPSConnection(upload_host) + server.request('POST', upload_uri, body, headers) + resp = server.getresponse() + server.close() + + if resp.status == 201: + location = resp.getheader('Location', None) + else: + location = None + return resp.status, resp.reason, location + + +def encode_upload_request(fields, file_path): + """Encode the given fields and file into a multipart form body. + + fields is a sequence of (name, value) pairs. file is the path of + the file to upload. The file will be uploaded to Google Code with + the same file name. + + Returns: (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla' + CRLF = '\r\n' + + body = [] + + # Add the metadata about the upload first + for key, value in fields: + body.extend( + ['--' + BOUNDARY, + 'Content-Disposition: form-data; name="%s"' % key, + '', + value, + ]) + + # Now add the file itself + file_name = os.path.basename(file_path) + f = open(file_path, 'rb') + file_content = f.read() + f.close() + + body.extend( + ['--' + BOUNDARY, + 'Content-Disposition: form-data; name="filename"; filename="%s"' + % file_name, + # The upload server determines the mime-type, no need to set it. + 'Content-Type: application/octet-stream', + '', + file_content, + ]) + + # Finalize the form body + body.extend(['--' + BOUNDARY + '--', '']) + + return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body) + + +def upload_find_auth(file_path, project_name, summary, labels=None, + user_name=None, password=None, tries=3): + """Find credentials and upload a file to a Google Code project's file server. + + file_path, project_name, summary, and labels are passed as-is to upload. + + Args: + file_path: The local path to the file. + project_name: The name of your project on Google Code. + summary: A small description for the file. + labels: an optional list of label strings with which to tag the file. + config_dir: Path to Subversion configuration directory, 'none', or None. + user_name: Your Google account name. + tries: How many attempts to make. + """ + + while tries > 0: + if user_name is None: + # Read username if not specified or loaded from svn config, or on + # subsequent tries. + sys.stdout.write('Please enter your googlecode.com username: ') + sys.stdout.flush() + user_name = sys.stdin.readline().rstrip() + if password is None: + # Read password if not loaded from svn config, or on subsequent tries. + print 'Please enter your googlecode.com password.' + print '** Note that this is NOT your Gmail account password! **' + print 'It is the password you use to access Subversion repositories,' + print 'and can be found here: http://code.google.com/hosting/settings' + password = getpass.getpass() + + status, reason, url = upload(file_path, project_name, user_name, password, + summary, labels) + # Returns 403 Forbidden instead of 401 Unauthorized for bad + # credentials as of 2007-07-17. + if status in [httplib.FORBIDDEN, httplib.UNAUTHORIZED]: + # Rest for another try. + user_name = password = None + tries = tries - 1 + else: + # We're done. + break + + return status, reason, url + + +def main(): + parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY ' + '-p PROJECT [options] FILE') + parser.add_option('-s', '--summary', dest='summary', + help='Short description of the file') + parser.add_option('-p', '--project', dest='project', + help='Google Code project name') + parser.add_option('-u', '--user', dest='user', + help='Your Google Code username') + parser.add_option('-w', '--password', dest='password', + help='Your Google Code password') + parser.add_option('-l', '--labels', dest='labels', + help='An optional list of comma-separated labels to attach ' + 'to the file') + + options, args = parser.parse_args() + + if not options.summary: + parser.error('File summary is missing.') + elif not options.project: + parser.error('Project name is missing.') + elif len(args) < 1: + parser.error('File to upload not provided.') + elif len(args) > 1: + parser.error('Only one file may be specified.') + + file_path = args[0] + + if options.labels: + labels = options.labels.split(',') + else: + labels = None + + status, reason, url = upload_find_auth(file_path, options.project, + options.summary, labels, + options.user, options.password) + if url: + print 'The file was uploaded successfully.' + print 'URL: %s' % url + return 0 + else: + print 'An error occurred. Your file was not uploaded.' + print 'Google Code upload server said: %s (%s)' % (reason, status) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/dav/SabreDAV/bin/gwdg.php b/dav/SabreDAV/bin/gwdg.php new file mode 100755 index 000000000..00a6f13d3 --- /dev/null +++ b/dav/SabreDAV/bin/gwdg.php @@ -0,0 +1,378 @@ +#!/usr/bin/env php +$fileInfo) { + + $tokens = token_get_all(file_get_contents($fileName)); + foreach($tokens as $tokenIndex=>$token) { + + if ($token[0]===T_CLASS || $token[0]===T_INTERFACE) { + $classNames[] = $tokens[$tokenIndex+2][1]; + } + + } + + } + + return $classNames; + +} + +function getClassTree($classNames) { + + $classTree = array(); + + foreach($classNames as $className) { + + if (!class_exists($className) && !interface_exists($className)) continue; + $rClass = new ReflectionClass($className); + + $parent = $rClass->getParentClass(); + if ($parent) $parent = $parent->name; + + if (!isset($classTree[$parent])) $classTree[$parent] = array(); + $classTree[$parent][] = $className; + + foreach($rClass->getInterfaceNames() as $interface) { + + if (!isset($classTree[$interface])) { + $classTree[$interface] = array(); + } + $classTree[$interface][] = $className; + + } + + } + return $classTree; + +} + +function createDoc($className, $extendedBy) { + + // ew + global $packageList; + + ob_start(); + $rClass = new ReflectionClass($className); + + echo "#summary API documentation for: ", $rClass->getName() , "\n"; + echo "#labels APIDoc\n"; + echo "#sidebar APIIndex\n"; + echo "=`" . $rClass->getName() . "`=\n"; + echo "\n"; + + $docs = parseDocs($rClass->getDocComment()); + echo $docs['description'] . "\n"; + echo "\n"; + + $parentClass = $rClass->getParentClass(); + + if($parentClass) { + echo " * Parent class: [" . $parentClass->getName() . "]\n"; + } + if ($interfaces = $rClass->getInterfaceNames()) { + $interfaces = array_map(function($int) { return '[' . $int . ']'; },$interfaces); + echo " * Implements: " . implode(", ", $interfaces) . "\n"; + } + $classType = $rClass->isInterface()?'interface':'class'; + if (isset($docs['deprecated'])) { + echo " * *Warning: This $classType is deprecated, and should not longer be used.*\n"; + } + if ($rClass->isInterface()) { + echo " * This is an interface.\n"; + } elseif ($rClass->isAbstract()) { + echo " * This is an abstract class.\n"; + } + if (isset($docs['package'])) { + $package = $docs['package']; + if (isset($docs['subpackage'])) { + $package.='_' . $docs['subpackage']; + } + if (!isset($packageList[$package])) { + $packageList[$package] = array(); + } + $packageList[$package][] = $rClass->getName(); + } + + if ($extendedBy) { + + echo "\n"; + if ($classType==='interface') { + echo "This interface is extended by the following interfaces:\n"; + foreach($extendedBy as $className) { + if (interface_exists($className)) { + echo " * [" . $className . "]\n"; + } + } + echo "\n"; + echo "This interface is implemented by the following classes:\n"; + } else { + echo "This class is extended by the following classes:\n"; + } + foreach($extendedBy as $className) { + if (class_exists($className)) { + echo " * [" . $className . "]\n"; + } + } + echo "\n"; + + } + echo "\n"; + + echo "==Properties==\n"; + + echo "\n"; + + $properties = $rClass->getProperties(ReflectionProperty::IS_STATIC | ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED); + + if (count($properties)>0) { + foreach($properties as $rProperty) { + + createPropertyDoc($rProperty); + + } + } else { + echo "This $classType does not define any public or protected properties.\n"; + } + + echo "\n"; + + echo "==Methods==\n"; + + echo "\n"; + + $methods = $rClass->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED); + + if (count($methods)>0) { + foreach($methods as $rMethod) { + + createMethodDoc($rMethod, $rClass); + + } + } else { + echo "\nThis $classType does not define any public or protected methods.\n"; + } + + return ob_get_clean(); + +} + +function createMethodDoc($rMethod, $rClass) { + + echo "===`" . $rMethod->getName() . "`===\n"; + echo "\n"; + + $docs = parseDocs($rMethod->getDocComment()); + + $return = isset($docs['return'])?$docs['return']:'void'; + + echo "{{{\n"; + echo $return . " " . $rMethod->class . "::" . $rMethod->getName() . "("; + foreach($rMethod->getParameters() as $parameter) { + if ($parameter->getPosition()>0) echo ", "; + if ($class = $parameter->getClass()) { + echo $class->name . " "; + } elseif (isset($docs['param'][$parameter->name])) { + echo $docs['param'][$parameter->name] . " "; + } + + echo '$' . $parameter->name; + + if ($parameter->isOptional() && $parameter->isDefaultValueAvailable()) { + $default = $parameter->getDefaultValue(); + $default = var_export($default,true); + $default = str_replace("\n","",$default); + echo " = " . $default; + + } + } + echo ")\n"; + echo "}}}\n"; + echo "\n"; + + echo $docs['description'] . "\n"; + + echo "\n"; + + $hasProp = false; + if (isset($docs['deprecated'])) { + echo " * *Warning: This method is deprecated, and should not longer be used.*\n"; + $hasProp = true; + } + if ($rMethod->isProtected()) { + echo " * This method is protected.\n"; + $hasProp = true; + } + if ($rMethod->isPrivate()) { + echo " * This method is private.\n"; + $hasProp = true; + } + if ($rMethod->isAbstract()) { + echo " * This is an abstract method\n"; + $hasProp = true; + } + + if ($rMethod->class != $rClass->name) { + echo " * Defined in [" . $rMethod->class . "]\n"; + $hasProp = true; + } + + if ($hasProp) echo "\n"; + +} + +function createPropertyDoc($rProperty) { + + echo "===`" . $rProperty->getName() . "`===\n"; + echo "\n"; + + $docs = parseDocs($rProperty->getDocComment()); + + $visibility = 'public'; + if ($rProperty->isProtected()) $visibility = 'protected'; + if ($rProperty->isPrivate()) $visibility = 'private'; + + echo "{{{\n"; + echo $visibility . " " . $rProperty->class . "::$" . $rProperty->getName(); + echo "\n}}}\n"; + echo "\n"; + + echo $docs['description'] . "\n"; + + echo "\n"; + + $hasProp = false; + if (isset($docs['deprecated'])) { + echo " * *Warning: This property is deprecated, and should not longer be used.*\n"; + $hasProp = true; + } + if ($rProperty->isProtected()) { + echo " * This property is protected.\n"; + $hasProp = true; + } + if ($rProperty->isPrivate()) { + echo " * This property is private.\n"; + $hasProp = true; + } + if ($rProperty->isStatic()) { + echo " * This property is static.\n"; + $hasProp = true; + } + + if ($hasProp) echo "\n"; + +} + +function parseDocs($docString) { + + $params = array(); + $description = array(); + + // Trimming all the comment characters + $docString = trim($docString,"\n*/ "); + $docString = explode("\n",$docString); + + foreach($docString as $str) { + + $str = ltrim($str,'* '); + $str = trim($str); + if ($str && $str[0]==='@') { + $r = explode(' ',substr($str,1),2); + $paramName = $r[0]; + $paramValue = (count($r)>1)?$r[1]:''; + + // 'param' paramName is special. Confusing, I know. + if ($paramName==='param') { + if (!isset($params['param'])) $params['param'] = array(); + $paramValue = explode(' ', $paramValue,3); + $params['param'][substr($paramValue[1],1)] = $paramValue[0]; + } else { + $params[$paramName] = trim($paramValue); + } + } else { + $description[]=$str; + } + + } + + $params['description'] = trim(implode("\n",$description),"\n "); + + return $params; + +} + +function createSidebarIndex($packageList) { + + ob_start(); + echo "#labels APIDocs\n"; + echo "#summary List of all classes, neatly organized\n"; + echo "=API Index=\n"; + + foreach($packageList as $package=>$classes) { + + echo " * $package\n"; + sort($classes); + foreach($classes as $class) { + + echo " * [$class $class]\n"; + + } + + } + + return ob_get_clean(); + +} diff --git a/dav/SabreDAV/bin/migrateto17.php b/dav/SabreDAV/bin/migrateto17.php new file mode 100644 index 000000000..4340013b5 --- /dev/null +++ b/dav/SabreDAV/bin/migrateto17.php @@ -0,0 +1,232 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + +echo "Validating existing table layout\n"; + +// The only cross-db way to do this, is to just fetch a single record. +$row = $pdo->query("SELECT * FROM calendarobjects LIMIT 1")->fetch(); + +if (!$row) { + echo "Error: This database did not have any records in the calendarobjects table, you should just recreate the table.\n"; + exit(-1); +} + +$requiredFields = array( + 'id', + 'calendardata', + 'uri', + 'calendarid', + 'lastmodified', +); + +foreach($requiredFields as $requiredField) { + if (!array_key_exists($requiredField,$row)) { + echo "Error: The current 'calendarobjects' table was missing a field we expected to exist.\n"; + echo "For safety reasons, this process is stopped.\n"; + exit(-1); + } +} + +$fields17 = array( + 'etag', + 'size', + 'componenttype', + 'firstoccurence', + 'lastoccurence', +); + +$found = 0; +foreach($fields17 as $field) { + if (array_key_exists($field, $row)) { + $found++; + } +} + +if ($found === 0) { + echo "The database had the 1.6 schema. Table will now be altered.\n"; + echo "This may take some time for large tables\n"; + $pdo->exec(<<query('SELECT id, calendardata FROM calendarobjects'); +$stmt = $pdo->prepare('UPDATE calendarobjects SET etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE id = ?'); + +echo "Total records found: " . $result->rowCount() . "\n"; +$done = 0; +$total = $result->rowCount(); +while($row = $result->fetch()) { + + try { + $newData = getDenormalizedData($row['calendardata']); + } catch (Exception $e) { + echo "===\nException caught will trying to parser calendarobject.\n"; + echo "Error message: " . $e->getMessage() . "\n"; + echo "Record id: " . $row['id'] . "\n"; + echo "This record is ignored, you should inspect it to see if there's anything wrong.\n===\n"; + continue; + } + $stmt->execute(array( + $newData['etag'], + $newData['size'], + $newData['componentType'], + $newData['firstOccurence'], + $newData['lastOccurence'], + $row['id'], + )); + $done++; + + if ($done % 500 === 0) { + echo "Completed: $done / $total\n"; + } +} + +echo "Process completed!\n"; + +/** + * Parses some information from calendar objects, used for optimized + * calendar-queries. + * + * Blantently copied from Sabre_CalDAV_Backend_PDO + * + * Returns an array with the following keys: + * * etag + * * size + * * componentType + * * firstOccurence + * * lastOccurence + * + * @param string $calendarData + * @return array + */ +function getDenormalizedData($calendarData) { + + $vObject = Sabre_VObject_Reader::read($calendarData); + $componentType = null; + $component = null; + $firstOccurence = null; + $lastOccurence = null; + foreach($vObject->getComponents() as $component) { + if ($component->name!=='VTIMEZONE') { + $componentType = $component->name; + break; + } + } + if (!$componentType) { + throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); + } + if ($componentType === 'VEVENT') { + $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); + // Finding the last occurence is a bit harder + if (!isset($component->RRULE)) { + if (isset($component->DTEND)) { + $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); + } elseif (isset($component->DURATION)) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value)); + $lastOccurence = $endDate->getTimeStamp(); + } elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate->modify('+1 day'); + $lastOccurence = $endDate->getTimeStamp(); + } else { + $lastOccurence = $firstOccurence; + } + } else { + $it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID); + $maxDate = new DateTime(self::MAX_DATE); + if ($it->isInfinite()) { + $lastOccurence = $maxDate->getTimeStamp(); + } else { + $end = $it->getDtEnd(); + while($it->valid() && $end < $maxDate) { + $end = $it->getDtEnd(); + $it->next(); + + } + $lastOccurence = $end->getTimeStamp(); + } + + } + } + + return array( + 'etag' => md5($calendarData), + 'size' => strlen($calendarData), + 'componentType' => $componentType, + 'firstOccurence' => $firstOccurence, + 'lastOccurence' => $lastOccurence, + ); + +} diff --git a/dav/SabreDAV/bin/naturalselection.py b/dav/SabreDAV/bin/naturalselection.py new file mode 100755 index 000000000..aa5554dd0 --- /dev/null +++ b/dav/SabreDAV/bin/naturalselection.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2009-2010 Evert Pot +# All rights reserved. +# http://www.rooftopsolutions.nl/ +# +# This utility is distributed along with SabreDAV +# license: http://code.google.com/p/sabredav/wiki/License Modified BSD License + +import os +from optparse import OptionParser +import time + +def getfreespace(path): + stat = os.statvfs(path) + return stat.f_frsize * stat.f_bavail + +def getbytesleft(path,treshold): + return getfreespace(path)-treshold + +def run(cacheDir, treshold, sleep=5, simulate=False, min_erase = 0): + + bytes = getbytesleft(cacheDir,treshold) + if (bytes>0): + print "Bytes to go before we hit treshhold:", bytes + else: + print "Treshold exceeded with:", -bytes, "bytes" + dir = os.listdir(cacheDir) + dir2 = [] + for file in dir: + path = cacheDir + '/' + file + dir2.append({ + "path" : path, + "atime": os.stat(path).st_atime, + "size" : os.stat(path).st_size + }) + + dir2.sort(lambda x,y: int(x["atime"]-y["atime"])) + + filesunlinked = 0 + gainedspace = 0 + + # Left is the amount of bytes that need to be freed up + # The default is the 'min_erase setting' + left = min_erase + + # If the min_erase setting is lower than the amount of bytes over + # the treshold, we use that number instead. + if left < -bytes : + left = -bytes + + print "Need to delete at least:", left; + + for file in dir2: + + # Only deleting files if we're not simulating + if not simulate: os.unlink(file["path"]) + left = int(left - file["size"]) + gainedspace = gainedspace + file["size"] + filesunlinked = filesunlinked + 1 + + if(left<0): + break + + print "%d files deleted (%d bytes)" % (filesunlinked, gainedspace) + + + time.sleep(sleep) + + + +def main(): + parser = OptionParser( + version="naturalselecton v0.3", + description="Cache directory manager. Deletes cache entries based on accesstime and free space tresholds.\n" + + "This utility is distributed alongside SabreDAV.", + usage="usage: %prog [options] cacheDirectory", + ) + parser.add_option( + '-s', + dest="simulate", + action="store_true", + help="Don't actually make changes, but just simulate the behaviour", + ) + parser.add_option( + '-r','--runs', + help="How many times to check before exiting. -1 is infinite, which is the default", + type="int", + dest="runs", + default=-1 + ) + parser.add_option( + '-n','--interval', + help="Sleep time in seconds (default = 5)", + type="int", + dest="sleep", + default=5 + ) + parser.add_option( + '-l','--treshold', + help="Treshhold in bytes (default = 10737418240, which is 10GB)", + type="int", + dest="treshold", + default=10737418240 + ) + parser.add_option( + '-m', '--min-erase', + help="Minimum number of bytes to erase when the treshold is reached. " + + "Setting this option higher will reduce the amount of times the cache directory will need to be scanned. " + + "(the default is 1073741824, which is 1GB.)", + type="int", + dest="min_erase", + default=1073741824 + ) + + options,args = parser.parse_args() + if len(args)<1: + parser.error("This utility requires at least 1 argument") + cacheDir = args[0] + + print "Natural Selection" + print "Cache directory:", cacheDir + free = getfreespace(cacheDir); + print "Current free disk space:", free + + runs = options.runs; + while runs!=0 : + run( + cacheDir, + sleep=options.sleep, + simulate=options.simulate, + treshold=options.treshold, + min_erase=options.min_erase + ) + if runs>0: + runs = runs - 1 + +if __name__ == '__main__' : + main() diff --git a/dav/SabreDAV/bin/pearpackage3.php b/dav/SabreDAV/bin/pearpackage3.php new file mode 100755 index 000000000..4af040720 --- /dev/null +++ b/dav/SabreDAV/bin/pearpackage3.php @@ -0,0 +1,321 @@ +#!/usr/bin/env php +$arg) { + if ($index==0) continue; + if ($arg=='make') { + $make = true; + continue; + } + + $packageName = $arg; +} + +if (is_null($packageName)) { + echo "A packagename is required\n"; + die(1); +} + +if (!is_dir('build/' . $packageName)) { + echo "Could not find package directory: build/$packageName\n"; + die(2); +} + +// We'll figure out something better for this one day + +$dependencies = array( + array( + 'type' => 'php', + 'min' => '5.3.1', + ), + array( + 'type' => 'pearinstaller', + 'min' => '1.9', + ), +); + + +switch($packageName) { + + case 'Sabre' : + $summary = 'Sabretooth base package.'; + $description = << 'package', + 'name' => 'Sabre', + 'channel' => 'pear.sabredav.org', + 'min' => '1.0.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_HTTP', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + + break; + + case 'Sabre_HTTP' : + $summary = 'Sabre_HTTP provides various HTTP helpers, for input and output and authentication'; + $description = << 'package', + 'name' => 'Sabre', + 'channel' => 'pear.sabredav.org', + 'min' => '1.0.0', + ); + break; + + case 'Sabre_DAVACL' : + $summary = 'Sabre_DAVACL provides rfc3744 support.'; + $description = << 'package', + 'name' => 'Sabre', + 'channel' => 'pear.sabredav.org', + 'min' => '1.0.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_DAV', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + break; + + case 'Sabre_CalDAV' : + $summary = 'Sabre_CalDAV provides CalDAV extensions to SabreDAV'; + $description = << 'package', + 'name' => 'Sabre', + 'channel' => 'pear.sabredav.org', + 'min' => '1.0.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_HTTP', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_DAV', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_DAVACL', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_VObject', + 'channel' => 'pear.sabredav.org', + 'min' => '1.3.0', + ); + break; + + case 'Sabre_CardDAV' : + $summary = 'Sabre_CardDAV provides CardDAV extensions to SabreDAV'; + $description = << 'package', + 'name' => 'Sabre', + 'channel' => 'pear.sabredav.org', + 'min' => '1.0.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_HTTP', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_DAV', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_DAVACL', + 'channel' => 'pear.sabredav.org', + 'min' => '1.6.0', + ); + $dependencies[] = array( + 'type' => 'package', + 'name' => 'Sabre_VObject', + 'channel' => 'pear.sabredav.org', + 'min' => '1.3.0', + ); + break; + + case 'Sabre_VObject' : + $summary = 'Sabre_VObject is a natural-interface iCalendar and vCard reader'; + $description = << 'package', + 'name' => 'Sabre', + 'channel' => 'pear.sabredav.org', + 'min' => '1.0.0', + ); + break; + +} + + +if (!isset($version)) { + include 'lib/' . str_replace('_','/',$packageName) . '/Version.php'; + $versionClassName = $packageName . '_Version'; + $version = $versionClassName::VERSION; + $stability = $versionClassName::STABILITY; +} + +$lead = 'Evert Pot'; +$lead_email = 'evert@rooftopsolutions.nl'; +$date = date('Y-m-d'); + +$license = 'Modified BSD'; +$licenseuri = 'http://code.google.com/p/sabredav/wiki/License'; +$notes = 'New release. Read the ChangeLog and announcement for more details'; +$channel = 'pear.sabredav.org'; + +/* This function is intended to generate the full file list */ +function parsePath($fullPath, $role, $padding = 4) { + + $fileList = ''; + $file = basename($fullPath); + if (is_dir($fullPath)) { + $fileList .= str_repeat(' ', $padding) . "\n"; + foreach(scandir($fullPath) as $subPath) {; + if ($subPath==='.' || $subPath==='..') continue; + $fileList .= parsePath($fullPath. '/' . $subPath,$role, $padding+2); + } + $fileList .= str_repeat(' ', $padding) . "\n"; + } elseif (is_file($fullPath)) { + $fileList .= str_repeat(' ', $padding) . "\n"; + } + + return $fileList; + +} + +$rootDir = realpath('build/' . $packageName); + +$fileList = parsePath($rootDir . '/Sabre', 'php'); +$fileList .= parsePath($rootDir . '/examples', 'doc'); +$fileList .= parsePath($rootDir . '/ChangeLog', 'doc'); +$fileList .= parsePath($rootDir . '/LICENSE', 'doc'); + +$dependenciesXML = "\n"; +foreach($dependencies as $dep) { + $pad = 8; + $dependenciesXML.=str_repeat(' ',$pad) . '<' . $dep['type'] . ">\n"; + foreach($dep as $key=>$value) { + if ($key=='type') continue; + $dependenciesXML.=str_repeat(' ',$pad+2) . "<$key>$value\n"; + } + $dependenciesXML.=str_repeat(' ',$pad) . '\n"; +} + +$package = << + + + {$packageName} + {$channel} + {$summary} + {$description} + + {$lead} + {$lead} + {$lead_email} + true + + {$date} + + {$version} + {$version} + + + {$stability} + {$stability} + + {$license} + {$notes} + + {$fileList} + + + + {$dependenciesXML} + + + + +XML; + +if (isset($argv) && in_array('make',$argv)) { + file_put_contents($rootDir . '/package.xml',$package); +} else { + echo $package; +} diff --git a/dav/SabreDAV/build.xml b/dav/SabreDAV/build.xml new file mode 100644 index 000000000..eca011991 --- /dev/null +++ b/dav/SabreDAV/build.xml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating combined SabreDAV build + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating Git release tag + + Uploading to Google Code + + + + + + + + + + + + + + + + + + Creating api documentation using PHP documentor + Writing to ${sabredav.apidocspath} + + + + + + + + + + + + + + + + + + getProject()->setNewProperty('sabredav.version',Sabre_DAV_Version::VERSION); + $this->getProject()->setNewProperty('sabredav.stability',Sabre_DAV_Version::STABILITY); + $this->getProject()->setNewProperty('sabredav.ucstability',ucwords(Sabre_DAV_Version::STABILITY)); + + } + + } + + ]]> + + SabreDAV version ${sabredav.version} + + + + diff --git a/dav/SabreDAV/composer.json b/dav/SabreDAV/composer.json new file mode 100644 index 000000000..8875829ec --- /dev/null +++ b/dav/SabreDAV/composer.json @@ -0,0 +1,21 @@ +{ + "name": "evert/sabredav", + "type": "library", + "description": "WebDAV Framework for PHP", + "keywords": ["Framework", "WebDAV", "CalDAV", "CardDAV", "iCalendar"], + "homepage": "http://code.google.com/p/sabredav/", + "license": "New BSD License", + "authors": [ + { + "name": "Evert Pot", + "email": "evert@rooftopsolutions.nl", + "homepage" : "http://www.rooftopsolutions.nl/" + } + ], + "require": { + "php": ">=5.3.1" + }, + "autoload": { + "psr-0": { "Sabre": "lib/" } + } +} diff --git a/dav/SabreDAV/docs/caldav-ctag.txt b/dav/SabreDAV/docs/caldav-ctag.txt new file mode 100644 index 000000000..4787ca260 --- /dev/null +++ b/dav/SabreDAV/docs/caldav-ctag.txt @@ -0,0 +1,336 @@ + + + +Calendar Server Extension C. Daboo + Apple + May 3, 2007 + + + Calendar Collection Entity Tag (CTag) in CalDAV + caldav-ctag-02 + +Abstract + + This specification defines an extension to CalDAV that provides a + fast way for a client to determine whether the contents of a calendar + collection may have changed. + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 2. Conventions Used in This Document . . . . . . . . . . . . . . . 2 + 3. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.1. Server . . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.2. Client . . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 4. New features in CalDAV . . . . . . . . . . . . . . . . . . . . 3 + 4.1. getctag WebDAV Property . . . . . . . . . . . . . . . . . . 4 + 5. Security Considerations . . . . . . . . . . . . . . . . . . . . 4 + 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 5 + 7. Normative References . . . . . . . . . . . . . . . . . . . . . 5 + Appendix A. Acknowledgments . . . . . . . . . . . . . . . . . . . 5 + Appendix B. Change History . . . . . . . . . . . . . . . . . . . . 5 + Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 6 + + + + + + + + + + + + + + + + + + + + + +Daboo [Page 1] + + CalDAV Proxy May 2007 + + +1. Introduction + + In CalDAV [RFC4791] calendar data is stored in calendar collection + resources. Clients need to "poll" calendar collections in order to + find out what has changed since the last time they examined it. + Currently that involves having to do a PROPFIND Depth:1 HTTP request, + or a CALDAV:calendar-query REPORT request. When a calendar + collection contains a large number of calendar resources those + operations become expensive on the server. + + Calendar users often configure their clients to poll at short time + intervals. So polling traffic to the server will be high, even + though the frequency at which changes actually occur to a calendar is + typically low. + + To improve on performance, this specification defines a new "calendar + collection entity tag" (CTag) WebDAV property that is defined on + calendar collections. When the calendar collection changes, the CTag + value changes. Thus a client can cache the CTag at some point in + time, then poll the collection only (i.e. PROPFIND Depth:0 HTTP + requests) and determine if a change has happened based on the + returned CTag value. If there is a change, it can then fall back to + doing the full (Depth:1) poll of the collection to actually determine + which resources in the collection changed. + + This extension also defines CTag's on CalDAV scheduling + [I-D.desruisseaux-caldav-sched] Inbox and Outbox collections. + + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:caldav" are referenced in this document + outside of the context of an XML fragment, the string "DAV:" and + "CALDAV:" will be prefixed to the element type names respectively. + + The namespace "http://calendarserver.org/ns/" is used for XML + elements defined in this specification. When XML element types in + this namespace are referenced in this document outside of the context + of an XML fragment, the string "CS:" will be prefixed to the element + type names respectively. + + + + + + +Daboo [Page 2] + + CalDAV Proxy May 2007 + + +3. Overview + +3.1. Server + + For each calendar or scheduling Inbox or Outbox collection on the + server, a new CS:getctag WebDAV property is present. + + The property value is an "opaque" token whose value is guaranteed to + be unique over the lifetime of any calendar or scheduling Inbox or + Outbox collection at a specific URI. + + Whenever a calendar resource is added to, modified or deleted from + the calendar collection, the value of the CS:getctag property MUST + change. Typically this change will occur when the DAV:getetag + property on a child resource changes due to some protocol action. It + could be the result of a change to the body or properties of the + resource. + +3.2. Client + + The client starts off with an empty string as the initial value for + the cached CTag of a calendar or scheduling Inbox or Outbox + collection that it intends to synchronize with. + + When polling a calendar or scheduling Inbox or Outbox collection, the + client issues a PROPFIND Depth:0 HTTP request, asking for the CS: + getctag property to be returned. + + If the returned value of CS:getctag property matches the one + currently cached for the calendar or scheduling Inbox or Outbox + collection, then the collection contents have not changed and no + further action is required until the next poll. + + If the returned value of CS:getctag property does not match the one + found previously, then the contents of the calendar or scheduling + Inbox or Outbox collection have changed. At that point the client + should re-issue the PROPFIND Depth:1 request to get the collection + changes in detail and the CS:getctag property value corresponding to + the new state. The new CSgetctag property value should replace the + one currently cached for that calendar or scheduling Inbox or Outbox + collection. + + +4. New features in CalDAV + + + + + + + +Daboo [Page 3] + + CalDAV Proxy May 2007 + + +4.1. getctag WebDAV Property + + Name: getctag + + Namespace: http://calendarserver.org/ns/ + + Purpose: Specifies a "synchronization" token used to indicate when + the contents of a calendar or scheduling Inbox or Outbox + collection have changed. + + Conformance: This property MUST be defined on a calendar or + scheduling Inbox or Outbox collection resource. It MUST be + protected and SHOULD be returned by a PROPFIND DAV:allprop request + (as defined in Section 12.14.1 of [RFC2518]). + + Description: The CS:getctag property allows clients to quickly + determine if the contents of a calendar or scheduling Inbox or + Outbox collection have changed since the last time a + "synchronization" operation was done. The CS:getctag property + value MUST change each time the contents of the calendar or + scheduling Inbox or Outbox collection change, and each change MUST + result in a value that is different from any other used with that + collection URI. + + Definition: + + + + Example: + + ABCD-GUID-IN-THIS-COLLECTION-20070228T122324010340 + + +5. Security Considerations + + The CS:getctag property value changes whenever any resource in the + collection or scheduling Inbox or Outbox changes. Thus a change to a + resource that a user does not have read access to will result in a + change in the CTag and the user will know that a change occurred. + However, that user will not able to get additional details about + exactly what changed as WebDAV ACLs [RFC3744] will prevent that. So + this does expose the fact that there are potentially "hidden" + resources in a calendar collection, but it does not expose any + details about them. + + + + + + +Daboo [Page 4] + + CalDAV Proxy May 2007 + + +6. IANA Considerations + + This document does not require any actions on the part of IANA. + + +7. Normative References + + [I-D.desruisseaux-caldav-sched] + Desruisseaux, B., "Scheduling Extensions to CalDAV", + draft-desruisseaux-caldav-sched-03 (work in progress), + January 2007. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S., and D. + Jensen, "HTTP Extensions for Distributed Authoring -- + WEBDAV", RFC 2518, February 1999. + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web + Distributed Authoring and Versioning (WebDAV) Access + Control Protocol", RFC 3744, May 2004. + + [RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault, + "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791, + March 2007. + + +Appendix A. Acknowledgments + + This specification is the result of discussions between the Apple + calendar server and client teams. + + +Appendix B. Change History + + Changes from -01: + + 1. Updated to RFC4791 reference. + + 2. Added text indicating that ctag applies to schedule Inbox and + Outbox as well. + + Changes from -00: + + 1. Relaxed requirement so that any type of change to a child + resource can trigger a CTag change (similar behavior to ETag). + + + + +Daboo [Page 5] + + CalDAV Proxy May 2007 + + +Author's Address + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + Email: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo [Page 6] + diff --git a/dav/SabreDAV/docs/caldav-proxy.txt b/dav/SabreDAV/docs/caldav-proxy.txt new file mode 100644 index 000000000..2d96bfc82 --- /dev/null +++ b/dav/SabreDAV/docs/caldav-proxy.txt @@ -0,0 +1,560 @@ + + + +Calendar Server Extension C. Daboo + Apple Computer + May 3, 2007 + + + Calendar User Proxy Functionality in CalDAV + caldav-cu-proxy-02 + +Abstract + + This specification defines an extension to CalDAV that makes it easy + for clients to setup and manage calendar user proxies, using the + WebDAV Access Control List extension as a basis. + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 2. Conventions Used in This Document . . . . . . . . . . . . . . 2 + 3. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.1. Server . . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3.2. Client . . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 4. Open Issues . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 5. New features in CalDAV . . . . . . . . . . . . . . . . . . . . 4 + 5.1. Proxy Principal Resource . . . . . . . . . . . . . . . . . 4 + 5.2. Privilege Provisioning . . . . . . . . . . . . . . . . . . 8 + 6. Security Considerations . . . . . . . . . . . . . . . . . . . 9 + 7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 9 + 8. Normative References . . . . . . . . . . . . . . . . . . . . . 9 + Appendix A. Acknowledgments . . . . . . . . . . . . . . . . . . . 9 + Appendix B. Change History . . . . . . . . . . . . . . . . . . . 10 + Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 10 + + + + + + + + + + + + + + + + + + + +Daboo [Page 1] + + CalDAV Proxy May 2007 + + +1. Introduction + + CalDAV [RFC4791] provides a way for calendar users to store calendar + data and exchange this data via scheduling operations. Based on the + WebDAV protocol [RFC2518], it also includes the ability to manage + access to calendar data via the WebDAV ACL extension [RFC3744]. + + It is often common for a calendar user to delegate some form of + responsibility for their calendar and schedules to another calendar + user (e.g., a boss allows an assistant to check a calendar or to send + and accept scheduling invites on his behalf). The user handling the + calendar data on behalf of someone else is often referred to as a + "calendar user proxy". + + Whilst CalDAV does have fine-grained access control features that can + be used to setup complex sharing and management of calendars, often + the proxy behavior required is an "all-or-nothing" approach - i.e. + the proxy has access to all the calendars or to no calendars (in + which case they are of course not a proxy). So a simple way to + manage access to an entire set of calendars and scheduling ability + would be handy. + + In addition, calendar user agents will often want to display to a + user who has proxy access to their calendars, or to whom they are + acting as a proxy. Again, CalDAV's access control discovery and + report features can be used to do that, but with fine-grained control + that exists, it can be hard to tell who is a "real" proxy as opposed + to someone just granted rights to some subset of calendars. Again, a + simple way to discover proxy information would be handy. + + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + When XML element types in the namespace "DAV:" are referenced in this + document outside of the context of an XML fragment, the string "DAV:" + will be prefixed to the element type names. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:caldav" are referenced in this document + outside of the context of an XML fragment, the string "DAV:" and + "CALDAV:" will be prefixed to the element type names respectively. + + The namespace "http://calendarserver.org/ns/" is used for XML + elements defined in this specification. When XML element types in + + + +Daboo [Page 2] + + CalDAV Proxy May 2007 + + + this namespace are referenced in this document outside of the context + of an XML fragment, the string "CS:" will be prefixed to the element + type names respectively. + + +3. Overview + +3.1. Server + + For each calendar user principal on the server, the server will + generate two group principals - "proxy groups". One is used to hold + the list of principals who have read-only proxy access to the main + principal's calendars, the other holds the list of principals who + have read-write and scheduling proxy access. NB these new group + principals would have no equivalent in Open Directory. + + Privileges on each "proxy group" principal will be set so that the + "owner" has the ability to change property values. + + The "proxy group" principals will be child resources of the user + principal resource with specific resource types and thus are easy to + discover. As a result the user principal resources will also be + collection resources. + + When provisioning the calendar user home collection, the server will: + + a. Add an ACE to the calendar home collection giving the read-only + "proxy group" inheritable read access. + + b. Add an ACE to the calendar home collection giving the read-write + "proxy group" inheritable read-write access. + + c. Add an ACE to each of the calendar Inbox and Outbox collections + giving the CALDAV:schedule privilege + [I-D.desruisseaux-caldav-sched] to the read-write "proxy group". + +3.2. Client + + A client can see who the proxies are for the current principal by + examining the principal resource for the two "proxy group" properties + and then looking at the DAV:group-member-set property of each. + + The client can edit the list of proxies for the current principal by + editing the DAV:group-member-set property on the relevant "proxy + group" principal resource. + + The client can find out who the current principal is a proxy for by + running a DAV:principal-match REPORT on the principal collection. + + + +Daboo [Page 3] + + CalDAV Proxy May 2007 + + + Alternatively, the client can find out who the current principal is a + proxy for by examining the DAV:group-membership property on the + current principal resource looking for membership in other users' + "proxy groups". + + +4. Open Issues + + 1. Do we want to separate read-write access to calendars vs the + ability to schedule as a proxy? + + 2. We may want to restrict changing properties on the proxy group + collections to just the DAV:group-member-set property? + + 3. There is no way for a proxy to be able to manage the list of + proxies. We could allow the main calendar user DAV:write-acl on + their "proxy group" principals, in which case they could grant + others the right to modify the group membership. + + 4. Should the "proxy group" principals also be collections given + that the regular principal resources will be? + + +5. New features in CalDAV + +5.1. Proxy Principal Resource + + Each "regular" principal resource that needs to allow calendar user + proxy support MUST be a collection resource. i.e. in addition to + including the DAV:principal XML element in the DAV:resourcetype + property on the resource, it MUST also include the DAV:collection XML + element. + + Each "regular" principal resource MUST contain two child resources + with names "calendar-proxy-read" and "calendar-proxy-write" (note + that these are only suggested names - the server could choose any + unique name for these). These resources are themselves principal + resources that are groups contain the list of principals for calendar + users who can act as a read-only or read-write proxy respectively. + + The server MUST include the CS:calendar-proxy-read or CS:calendar- + proxy-write XML elements in the DAV:resourcetype property of the + child resources, respectively. This allows clients to discover the + "proxy group" principals by using a PROPFIND, Depth:1 request on the + current user's principal resource and requesting the DAV:resourcetype + property be returned. The element type declarations are: + + + + + +Daboo [Page 4] + + CalDAV Proxy May 2007 + + + + + + + The server MUST allow the "parent" principal to change the DAV:group- + member-set property on each of the "child" "proxy group" principal + resources. When a principal is listed as a member of the "child" + resource, the server MUST include the "child" resource URI in the + DAV:group-membership property on the included principal resource. + Note that this is just "normal" behavior for a group principal. + + An example principal resource layout might be: + + + / + + principals/ + + users/ + + cyrus/ + calendar-proxy-read + calendar-proxy-write + + red/ + calendar-proxy-read + calendar-proxy-write + + wilfredo/ + calendar-proxy-read + calendar-proxy-write + + If the principal "cyrus" wishes to have the principal "red" act as a + calendar user proxy on his behalf and have the ability to change + items on his calendar or schedule meetings on his behalf, then he + would add the principal resource URI for "red" to the DAV:group- + member-set property of the principal resource /principals/users/ + cyrus/calendar-proxy-write, giving: + + + /principals/users/red/ + + + The DAV:group-membership property on the resource /principals/users/ + red/ would be: + + + /principals/users/cyrus/calendar-proxy-write + + + If the principal "red" was also a read-only proxy for the principal + "wilfredo", then the DA:group-membership property on the resource + /principals/users/red/ would be: + + + + +Daboo [Page 5] + + CalDAV Proxy May 2007 + + + + /principals/users/cyrus/calendar-proxy-write + /principals/users/wilfredo/calendar-proxy-read + + + Thus a client can discover to which principals a particular principal + is acting as a calendar user proxy for by examining the DAV:group- + membership property. + + An alternative to discovering which principals a user can proxy as is + to use the WebDAV ACL principal-match report, targeted at the + principal collections available on the server. + + Example: + + >> Request << + + REPORT /principals/ HTTP/1.1 + Host: cal.example.com + Depth: 0 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="red", + realm="cal.example.com", nonce="...", + uri="/principals/", response="...", opaque="..." + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo [Page 6] + + CalDAV Proxy May 2007 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Fri, 10 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + /principals/users/red/ + + + + + + + + HTTP/1.1 200 OK + + + + /principals/users/cyrus/calendar-proxy-write + + + + + + + + HTTP/1.1 200 OK + + + + /principals/users/wilfredo/calendar-proxy-read + + + + + + + + HTTP/1.1 200 OK + + + + + + + +Daboo [Page 7] + + CalDAV Proxy May 2007 + + +5.2. Privilege Provisioning + + In order for a calendar user proxy to be able to access the calendars + of the user they are proxying for the server MUST ensure that the + privileges on the relevant calendars are setup accordingly: + + The DAV:read privilege MUST be granted for read-only and read- + write calendar user proxy principals + + The DAV:write privilege MUST be granted for read-write calendar + user proxy principals. + + Additionally, the CalDAV scheduling Inbox and Outbox calendar + collections for the user allowing proxy access, MUST have the CALDAV: + schedule privilege [I-D.desruisseaux-caldav-sched] granted for read- + write calendar user proxy principals. + + Note that with a suitable repository layout, a server may be able to + grant the appropriate privileges on a parent collection and ensure + that all the contained collections and resources inherit that. For + example, given the following repository layout: + + + / + + calendars/ + + users/ + + cyrus/ + inbox + outbox + home + work + + red/ + inbox + outbox + work + soccer + + wilfredo/ + inbox + outbox + home + work + flying + + In order for the principal "red" to act as a read-write proxy for the + principal "cyrus", the following WebDAV ACE will need to be granted + on the resource /calendars/users/cyrus/ and all children of that + resource: + + + + + +Daboo [Page 8] + + CalDAV Proxy May 2007 + + + + + /principals/users/cyrus/calendar-proxy-write + + + + + + + +6. Security Considerations + + TBD + + +7. IANA Considerations + + This document does not require any actions on the part of IANA. + + +8. Normative References + + [I-D.desruisseaux-caldav-sched] + Desruisseaux, B., "Scheduling Extensions to CalDAV", + draft-desruisseaux-caldav-sched-03 (work in progress), + January 2007. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S., and D. + Jensen, "HTTP Extensions for Distributed Authoring -- + WEBDAV", RFC 2518, February 1999. + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web + Distributed Authoring and Versioning (WebDAV) Access + Control Protocol", RFC 3744, May 2004. + + [RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault, + "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791, + March 2007. + + +Appendix A. Acknowledgments + + This specification is the result of discussions between the Apple + calendar server and client teams. + + + + +Daboo [Page 9] + + CalDAV Proxy May 2007 + + +Appendix B. Change History + + Changes from -00: + + 1. Updated to RFC 4791 reference. + + Changes from -00: + + 1. Added more details on actual CalDAV protocol changes. + + 2. Changed namespace from http://apple.com/ns/calendarserver/ to + http://calendarserver.org/ns/. + + 3. Made "proxy group" principals child resources of their "owner" + principals. + + 4. The "proxy group" principals now have their own resourcetype. + + +Author's Address + + Cyrus Daboo + Apple Computer, Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + Email: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + +Daboo [Page 10] + diff --git a/dav/SabreDAV/docs/draft-daboo-carddav-directory-gateway-02.txt b/dav/SabreDAV/docs/draft-daboo-carddav-directory-gateway-02.txt new file mode 100644 index 000000000..63aa8b29c --- /dev/null +++ b/dav/SabreDAV/docs/draft-daboo-carddav-directory-gateway-02.txt @@ -0,0 +1,560 @@ + + + +Network Working Group C. Daboo +Internet-Draft Apple Inc. +Updates: XXXX-CardDAV August 24, 2010 +(if approved) +Intended status: Standards Track +Expires: February 25, 2011 + + + CardDAV Directory Gateway Extension + draft-daboo-carddav-directory-gateway-02 + +Abstract + + This document defines an extension to the vCard Extensions to WebDAV + (CardDAV) protocol that allows a server to expose a directory as a + read-only address book collection. + +Status of this Memo + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on February 25, 2011. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + +Daboo Expires February 25, 2011 [Page 1] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + +Table of Contents + + 1. Introduction and Overview . . . . . . . . . . . . . . . . . . 3 + 2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3. CARDDAV:directory-gateway Property . . . . . . . . . . . . . . 4 + 4. XML Element Definitions . . . . . . . . . . . . . . . . . . . 5 + 4.1. CARDDAV:directory . . . . . . . . . . . . . . . . . . . . 5 + 5. Client Guidelines . . . . . . . . . . . . . . . . . . . . . . 5 + 6. Server Guidelines . . . . . . . . . . . . . . . . . . . . . . 6 + 7. Security Considerations . . . . . . . . . . . . . . . . . . . 7 + 8. IANA Consideration . . . . . . . . . . . . . . . . . . . . . . 8 + 9. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 8 + 10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 8 + 10.1. Normative References . . . . . . . . . . . . . . . . . . . 8 + 10.2. Informative References . . . . . . . . . . . . . . . . . . 9 + Appendix A. Change History (to be removed prior to + publication as an RFC) . . . . . . . . . . . . . . . 9 + Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Expires February 25, 2011 [Page 2] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + +1. Introduction and Overview + + The CardDAV [I-D.ietf-vcarddav-carddav] protocol defines a standard + way of accessing, managing, and sharing contact information based on + the vCard [RFC2426] format. Often, in an enterprise or service + provider environment, a directory of all users hosted on the server + (or elsewhere) is available (for example via Lightweight Directory + Access Protocol (LDAP) [RFC4510] or some direct database access). It + would be convenient for CardDAV clients if this directory were + exposed as a "global" address book on the CardDAV server so it could + be searched in the same way as personal address books are. This + specification defines a "directory gateway" feature extension to + CardDAV to enable this. + + This specification adds one new WebDAV property to principal + resources that contains the URL to one or more directory gateway + address book collection resources. It is important for clients to be + able to distinguish this address book collection from others because + there are specific limitations involved in using it as described + below. To aid that, this specification defines an XML element that + can be included as a child element of the DAV:resourcetype property + of address book collections to identify them as directory gateways. + + Note that this feature is in no way intended to replace full + directory access - it is meant to simply provide a convenient way for + CardDAV clients to query contact-related attributes in directory + records. + + +2. Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The term "protected" is used in the Conformance field of property + definitions as defined in Section 15 of [RFC4918]. + + This document uses XML DTD fragments ([W3C.REC-xml-20081126], Section + 3.2) as a purely notational convention. WebDAV request and response + bodies cannot be validated by a DTD due to the specific extensibility + rules defined in Section 17 of [RFC4918] and due to the fact that all + XML elements defined by this specification use the XML namespace name + "DAV:". In particular: + + 1. element names use the "DAV:" namespace, + + + + + +Daboo Expires February 25, 2011 [Page 3] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + + 2. element ordering is irrelevant unless explicitly stated, + + 3. extension elements (elements not already defined as valid child + elements) may be added anywhere, except when explicitly stated + otherwise, + + 4. extension attributes (attributes not already defined as valid for + this element) may be added anywhere, except when explicitly + stated otherwise. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:carddav" are referenced in this document + outside of the context of an XML fragment, the strings "DAV:" and + "CARDDAV:" will be prefixed to the element types, respectively. + + +3. CARDDAV:directory-gateway Property + + Name: directory-gateway + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies URLs of CardDAV address book collections acting + as a directory gateway for the server. + + Protected: MUST be protected. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:directory-gateway identifies address book + collection resources that are directory gateway address books for + the server. + + Definition: + + + + Example: + + + /directory + + + + + + + + +Daboo Expires February 25, 2011 [Page 4] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + +4. XML Element Definitions + +4.1. CARDDAV:directory + + Name: directory + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Used to indicate that an address book collection is a + directory gateway. + + Description: This element appears in the DAV:resourcetype property + on a address book collection resources that are directory + gateways. Clients can use the presence of this element to + identify directory gateway collections when doing PROPFINDs to + list collection contents. + + Definition: + + + + Example: + + + + + + + + +5. Client Guidelines + + Clients wishing to make use of directory gateway address books can + request the CARDDAV:directory-gateway property (Section 3) when + examining other properties on the principal resource for the user. + If the property is not present, then the directory gateway feature is + not supported by the server at that time. + + Clients can also detect the presence of directory gateway address + book collections by retrieving the DAV:resourcetype property on + collections that it lists, and look for the presence of the CARDDAV: + directory element (Section 4.1). + + Since the directory being exposed via a directory gateway address + book collection could be large, clients SHOULD limit the number of + results returned in an CARDDAV:addressbook-query REPORT as defined in + Section 8.6.1 of [I-D.ietf-vcarddav-carddav]. + + + +Daboo Expires February 25, 2011 [Page 5] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + + Clients MUST treat the directory gateway address book collection as a + read-only collection, so HTTP methods that modify resource data or + properties in the address book collection MUST NOT be used. + + Clients SHOULD NOT attempt to cache the entire contents of the + directory gateway address book collection resource by retrieving all + resources, or trying to examine all the properties of all resources + (e.g., via a PROPFIND Depth:1 request). Instead, CARDDAV: + addressbook-query REPORTs are used to search for specific address + book object resources, and CARDDAV:multiget REPORTs and individual + GET requests can be made to retrieve the actual vCard data for + address book object resources found via a query. + + When presenting directory gateway collections to the user, clients + SHOULD use the DAV:displayname property on the corresponding address + book collections as the name of the directory gateway. This is + important in the case where more than one directory gateway is + available. Clients MAY also provide descriptive information about + each directory gateway by examining the CARDDAV:addressbook- + description property (see Section 6.2.1 of + [I-D.ietf-vcarddav-carddav]) on the resource. + + +6. Server Guidelines + + Servers wishing to expose a directory gateway as an address book + collection MUST include the CARDDAV:directory-gateway property on all + principal resources of users expected to use the feature. + + Since the directory being exposed via the directory gateway address + book collection could be large, servers SHOULD truncate the number of + results returned in an CARDDAV:addressbook-query REPORT as defined in + Section 8.6.2 of [I-D.ietf-vcarddav-carddav]. In addition, servers + SHOULD disallow requests that effectively enumerate the collection + contents (e.g., PROPFIND Depth:1, trivial CARDDAV:addressbook-query, + DAV:sync-collection REPORT). + + Servers need to expose the directory information as a set of address + book object resources in the directory gateway address book + collection resource. To do that, a mapping between the directory + record format and the vCard data has to be applied. In general, only + directory record attributes that have a direct equivalent in vCard + SHOULD be mapped. It is up to individual implementations to + determine which attributes to map. But in all cases servers MUST + generate valid vCard data as returned to the client. In addition, as + required by CardDAV, the UID vCard property MUST be present in the + vCard data, and this value MUST be persistent from query to query for + the same directory record. + + + +Daboo Expires February 25, 2011 [Page 6] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + + Multiple directory sources could be available to the server. The + server MAY use a single directory gateway resource to aggregate + results from each directory source. When doing so care is needed + when dealing with potential records that refer to the same entity. + Servers MAY suppress any duplicates that they are able to determine + themselves. Alternatively, multiple directory sources can be exposed + as separate directory gateway resources. + + For any directory source, a server MAY expose multiple directory + gateway resources where each represents a different query "scope" for + the directory. Different scopes MAY be offered to different + principals on the server. For example, the server might expose an + entire company directory for searching as the resource "/directory- + all" to all principals, but then provide "/directory-department-XYZ" + as another directory gateway that has a search scope that implicitly + limits the search results to just the "XYZ" department. Users in + that department would then have a CARDDAV:directory-gateway property + on their principal resource that included the "/directory-department- + XYZ" resource. Users in other departments would have corresponding + directory gateway resources available to them. + + Records in a directory can include data for more than just people, + e.g, resources such as rooms or projectors, groups, computer systems + etc. It is up to individual implementations to determine the most + appropriate "scope" for the data returned via the directory gateway + by filtering the appropriate record types. As above, servers could + choose to expose people and resources under different directory + gateway resources by implicitly limiting the search "scope" for each + of those. + + Servers MAY apply implementation defined access rules to determine, + on a per-user basis, what records are returned to a particularly user + and the content of those records exposed via vCard data. This per- + user behavior is in addition to the general security requirements + detailed below. + + When multiple directory gateway collections are present, servers + SHOULD provide a DAV:displayname property on each that disambiguates + them. Servers MAY include a CARDDAV:addressbook-description property + (see Section 6.2.1 of [I-D.ietf-vcarddav-carddav]) on each directory + gateway resource to provide a description of the directory and any + search "scope" that might be used, or any other useful information + for users. + + +7. Security Considerations + + Servers MUST ensure that client requests against the directory + + + +Daboo Expires February 25, 2011 [Page 7] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + + gateway address book collection cannot use excessive resources (CPU, + memory, network bandwidth etc), given that the directory could be + large. + + Servers MUST take care not to expose sensitive directory record + attributes in the vCard data via the directory gateway address book. + In general only those properties that have direct correspondence in + vCard SHOULD be exposed. + + Servers need to determine whether it is appropriate for the directory + information to be available via CardDAV to unauthenticated users. If + not, servers MUST ensure that unauthenticated users do not have + access to the directory gateway address book object resource and its + contents. If unauthenticated access is allowed, servers MAY choose + to limit the set of vCard properties that are searchable or returned + in the address book object resources when unauthenticated requests + are made. + + +8. IANA Consideration + + This document does not require any actions on the part of IANA. + + +9. Acknowledgments + + +10. References + +10.1. Normative References + + [I-D.ietf-vcarddav-carddav] + Daboo, C., "vCard Extensions to WebDAV (CardDAV)", + draft-ietf-vcarddav-carddav-10 (work in progress), + November 2009. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2426] Dawson, F. and T. Howes, "vCard MIME Directory Profile", + RFC 2426, September 1998. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + + [W3C.REC-xml-20081126] + Paoli, J., Yergeau, F., Bray, T., Sperberg-McQueen, C., + and E. Maler, "Extensible Markup Language (XML) 1.0 (Fifth + + + +Daboo Expires February 25, 2011 [Page 8] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + + Edition)", World Wide Web Consortium Recommendation REC- + xml-20081126, November 2008, + . + +10.2. Informative References + + [RFC4510] Zeilenga, K., "Lightweight Directory Access Protocol + (LDAP): Technical Specification Road Map", RFC 4510, + June 2006. + + +Appendix A. Change History (to be removed prior to publication as an + RFC) + + Changes in -02 + + 1. Added CARDDAV:directory element for use in DAV:resourcetype + + 2. Allow CARDDAV:directory-gateway to be multi-valued + + 3. Explain how a server could implicit "scope" queries on different + directory gateway resources + + Changes in -01 + + 1. Remove duplicated text in a couple of sections + + 2. Add example of LDAP/generic database as possible directory + "sources" + + 3. Add text to explain why the client needs to treat this as special + and thus the need for a property + + 4. Added text to server guidelines indicating requirements for + handling vCard UID properties + + 5. Added text to server guidelines explain that different record + "types" may exist in the directory and the server is free to + filter those as appropriate + + 6. Added text to server guidelines indicating that server are free + to aggregate directory records from multiple sources + + 7. Added text to server guidelines indicating that servers are free + to apply implementation defined access control to the returned + data on a per-user basis + + + + + +Daboo Expires February 25, 2011 [Page 9] + +Internet-Draft CardDAV Directory Gateway Extension August 2010 + + +Author's Address + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + Email: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Expires February 25, 2011 [Page 10] + diff --git a/dav/SabreDAV/docs/draft-desruisseaux-caldav-sched-10.txt b/dav/SabreDAV/docs/draft-desruisseaux-caldav-sched-10.txt new file mode 100644 index 000000000..bcb2520f0 --- /dev/null +++ b/dav/SabreDAV/docs/draft-desruisseaux-caldav-sched-10.txt @@ -0,0 +1,5544 @@ + + + +Network Working Group C. Daboo +Internet-Draft Apple Inc. +Updates: 4791 (if approved) B. Desruisseaux +Intended status: Standards Track Oracle +Expires: March 10, 2012 September 7, 2011 + + + CalDAV Scheduling Extensions to WebDAV + draft-desruisseaux-caldav-sched-10 + +Abstract + + This document defines extensions to the CalDAV "calendar-access" + feature to specify a standard way of performing scheduling + transactions with iCalendar-based calendar components. This document + defines the "calendar-auto-schedule" feature of CalDAV. + +Status of This Memo + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on March 10, 2012. + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 1] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 6 + 1.2. Approach . . . . . . . . . . . . . . . . . . . . . . . . . 7 + 1.3. Limitations . . . . . . . . . . . . . . . . . . . . . . . 7 + 1.4. Notational Conventions . . . . . . . . . . . . . . . . . . 8 + 1.5. XML Namespaces and Processing . . . . . . . . . . . . . . 8 + 2. Scheduling Process . . . . . . . . . . . . . . . . . . . . . . 10 + 3. Scheduling Support . . . . . . . . . . . . . . . . . . . . . . 11 + 3.1. Example OPTIONS Request . . . . . . . . . . . . . . . . . 11 + 4. Scheduling Collections . . . . . . . . . . . . . . . . . . . . 12 + 4.1. Scheduling Outbox Collection . . . . . . . . . . . . . . . 12 + 4.2. Scheduling Inbox Collection . . . . . . . . . . . . . . . 13 + 4.3. Calendaring Reports Extensions . . . . . . . . . . . . . . 15 + 5. Scheduling Transactions . . . . . . . . . . . . . . . . . . . 16 + 5.1. Identifying Scheduling Object Resources . . . . . . . . . 16 + 5.2. Handling Scheduling Object Resources . . . . . . . . . . . 16 + 5.2.1. Organizer Scheduling Object Resources . . . . . . . . 16 + 5.2.1.1. Create . . . . . . . . . . . . . . . . . . . . . . 17 + 5.2.1.2. Modify . . . . . . . . . . . . . . . . . . . . . . 18 + 5.2.1.3. Remove . . . . . . . . . . . . . . . . . . . . . . 20 + 5.2.2. Attendee Scheduling Object Resources . . . . . . . . . 20 + 5.2.2.1. Allowed Attendee Changes . . . . . . . . . . . . . 20 + 5.2.2.2. Create . . . . . . . . . . . . . . . . . . . . . . 21 + 5.2.2.3. Modify . . . . . . . . . . . . . . . . . . . . . . 22 + 5.2.2.4. Remove . . . . . . . . . . . . . . . . . . . . . . 23 + 5.2.3. HTTP Methods . . . . . . . . . . . . . . . . . . . . . 24 + 5.2.3.1. PUT . . . . . . . . . . . . . . . . . . . . . . . 24 + 5.2.3.2. COPY . . . . . . . . . . . . . . . . . . . . . . . 24 + 5.2.3.3. MOVE . . . . . . . . . . . . . . . . . . . . . . . 25 + 5.2.3.4. DELETE . . . . . . . . . . . . . . . . . . . . . . 26 + 5.2.4. Additional Method Preconditions . . . . . . . . . . . 26 + 5.2.4.1. CALDAV:unique-scheduling-object-resource + Precondition . . . . . . . . . . . . . . . . . . . 26 + 5.2.4.2. CALDAV:same-organizer-in-all-components + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 2] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Precondition . . . . . . . . . . . . . . . . . . . 26 + 5.2.4.3. CALDAV:allowed-organizer-scheduling-object-chan + Precondition . . . . . . . . . . . . . . . . . . . 27 + 5.2.4.4. CALDAV:allowed-attendee-scheduling-object-chang + Precondition . . . . . . . . . . . . . . . . . . . 28 + 5.2.5. DTSTAMP and SEQUENCE Properties . . . . . . . . . . . 28 + 5.2.6. Restrict Recurrence Instances Sent to Attendees . . . 28 + 5.2.7. Forcing the Server to Send a Scheduling Message . . . 29 + 6. Processing Incoming Scheduling Messages . . . . . . . . . . . 30 + 6.1. Processing Organizer Requests, Additions, and + Cancellations . . . . . . . . . . . . . . . . . . . . . . 30 + 6.2. Processing Attendee Replies . . . . . . . . . . . . . . . 31 + 6.3. Scheduling Messages as Notifications . . . . . . . . . . . 31 + 6.4. Default Calendar Collection . . . . . . . . . . . . . . . 31 + 6.4.1. Additional Method Preconditions . . . . . . . . . . . 32 + 6.4.1.1. CALDAV:default-calendar-needed Precondition . . . 32 + 6.4.1.2. CALDAV:valid-schedule-default-calendar-URL + Precondition . . . . . . . . . . . . . . . . . . . 33 + 7. Request for Busy Time Information . . . . . . . . . . . . . . 34 + 7.1. Status Codes . . . . . . . . . . . . . . . . . . . . . . . 34 + 7.2. Additional Method Preconditions . . . . . . . . . . . . . 34 + 7.2.1. DAV:need-privileges Precondition . . . . . . . . . . . 34 + 7.2.2. CALDAV:supported-collection Precondition . . . . . . . 35 + 7.2.3. CALDAV:supported-calendar-data Precondition . . . . . 36 + 7.2.4. CALDAV:valid-calendar-data Precondition . . . . . . . 36 + 7.2.5. CALDAV:valid-scheduling-message Precondition . . . . . 37 + 7.2.6. CALDAV:valid-organizer Precondition . . . . . . . . . 37 + 7.2.7. CALDAV:max-resource-size Precondition . . . . . . . . 38 + 7.3. Response to a POST request . . . . . . . . . . . . . . . . 38 + 8. Avoiding Conflicts when Updating Scheduling Object + Resources . . . . . . . . . . . . . . . . . . . . . . . . . . 40 + 8.1. PUT . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 + 8.2. DELETE, COPY or MOVE . . . . . . . . . . . . . . . . . . . 42 + 9. Other Scheduling Considerations . . . . . . . . . . . . . . . 44 + 9.1. Attendee Participation Status . . . . . . . . . . . . . . 44 + 9.2. Schedule Status Values . . . . . . . . . . . . . . . . . . 45 + 10. Additional iCalendar Property Parameters . . . . . . . . . . . 49 + 10.1. Schedule Agent Parameter . . . . . . . . . . . . . . . . . 49 + 10.2. Schedule Force Send Parameter . . . . . . . . . . . . . . 50 + 10.3. Schedule Status Parameter . . . . . . . . . . . . . . . . 51 + 11. Additional Message Header Fields . . . . . . . . . . . . . . . 53 + 11.1. Schedule-Reply Request Header . . . . . . . . . . . . . . 53 + 11.2. Schedule-Tag Response Header . . . . . . . . . . . . . . . 53 + 11.3. If-Schedule-Tag-Match Request Header . . . . . . . . . . . 54 + 12. Additional WebDAV Properties . . . . . . . . . . . . . . . . . 55 + 12.1. CALDAV:schedule-calendar-transp Property . . . . . . . . . 55 + 12.2. CALDAV:schedule-default-calendar-URL Property . . . . . . 56 + 12.3. CALDAV:schedule-tag Property . . . . . . . . . . . . . . . 57 + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 3] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + 13. Scheduling Access Control . . . . . . . . . . . . . . . . . . 58 + 13.1. Scheduling Privileges . . . . . . . . . . . . . . . . . . 58 + 13.1.1. Privileges on Scheduling Inbox Collections . . . . . . 58 + 13.1.1.1. CALDAV:schedule-deliver Privilege . . . . . . . . 58 + 13.1.1.2. CALDAV:schedule-deliver-invite Privilege . . . . . 59 + 13.1.1.3. CALDAV:schedule-deliver-reply Privilege . . . . . 59 + 13.1.1.4. CALDAV:schedule-query-freebusy Privilege . . . . . 59 + 13.1.2. Privileges on Scheduling Outbox Collections . . . . . 59 + 13.1.2.1. CALDAV:schedule-send Privilege . . . . . . . . . . 59 + 13.1.2.2. CALDAV:schedule-send-invite Privilege . . . . . . 60 + 13.1.2.3. CALDAV:schedule-send-reply Privilege . . . . . . . 60 + 13.1.2.4. CALDAV:schedule-send-freebusy Privilege . . . . . 60 + 13.1.3. Aggregation of Scheduling Privileges . . . . . . . . . 60 + 13.2. Additional Principal Properties . . . . . . . . . . . . . 61 + 13.2.1. CALDAV:schedule-inbox-URL Property . . . . . . . . . . 61 + 13.2.2. CALDAV:schedule-outbox-URL Property . . . . . . . . . 62 + 13.2.3. CALDAV:calendar-user-address-set Property . . . . . . 62 + 13.2.4. CALDAV:calendar-user-type Property . . . . . . . . . . 63 + 14. XML Element Definitions . . . . . . . . . . . . . . . . . . . 65 + 14.1. CALDAV:schedule-response XML Element . . . . . . . . . . . 65 + 14.2. CALDAV:response XML Element . . . . . . . . . . . . . . . 65 + 14.3. CALDAV:recipient XML Element . . . . . . . . . . . . . . . 65 + 14.4. CALDAV:request-status XML Element . . . . . . . . . . . . 66 + 15. Security Considerations . . . . . . . . . . . . . . . . . . . 67 + 15.1. Verifying Scheduling Transactions . . . . . . . . . . . . 67 + 15.2. Verifying Busy Time Information Requests . . . . . . . . . 67 + 15.3. Privacy Issues . . . . . . . . . . . . . . . . . . . . . . 68 + 16. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 69 + 16.1. Message Header Field Registrations . . . . . . . . . . . . 69 + 16.1.1. Schedule-Reply . . . . . . . . . . . . . . . . . . . . 69 + 16.1.2. Schedule-Tag . . . . . . . . . . . . . . . . . . . . . 69 + 16.1.3. If-Schedule-Tag-Match . . . . . . . . . . . . . . . . 69 + 16.2. iCalendar Property Parameter Registrations . . . . . . . . 70 + 16.3. iCalendar REQUEST-STATUS Value Registrations . . . . . . . 70 + 16.4. Additional iCalendar Elements Registries . . . . . . . . . 70 + 16.4.1. Schedule Agent Values Registry . . . . . . . . . . . . 70 + 16.4.2. Schedule Force Send Values Registry . . . . . . . . . 71 + 17. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 72 + 18. References . . . . . . . . . . . . . . . . . . . . . . . . . . 73 + 18.1. Normative References . . . . . . . . . . . . . . . . . . . 73 + 18.2. Informative References . . . . . . . . . . . . . . . . . . 74 + Appendix A. Scheduling Privileges Summary . . . . . . . . . . . . 75 + A.1. Scheduling Inbox Privileges . . . . . . . . . . . . . . . 75 + A.2. Scheduling Outbox Privileges . . . . . . . . . . . . . . . 75 + Appendix B. Example Scheduling Transactions . . . . . . . . . . . 77 + B.1. Example: Organizer Inviting Multiple Attendees . . . . . . 77 + B.2. Example: Attendee Receiving an Invitation . . . . . . . . 79 + B.3. Example: Attendee Replying to an Invitation . . . . . . . 81 + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 4] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + B.4. Example: Organizer Receiving a Reply to an Invitation . . 83 + B.5. Example: Organizer Requesting Busy Time Information . . . 85 + B.6. Example: User Attempting to Invite Attendee on behalf + of Organizer . . . . . . . . . . . . . . . . . . . . . . . 87 + B.7. Example: Attendee Declining an Instance of a Recurring + Event . . . . . . . . . . . . . . . . . . . . . . . . . . 88 + B.8. Example: Attendee Removing an Instance of a Recurring + Event . . . . . . . . . . . . . . . . . . . . . . . . . . 92 + Appendix C. Changes (to be removed by RFC Editor prior to + publication) . . . . . . . . . . . . . . . . . . . . 95 + C.1. Changes in -10 . . . . . . . . . . . . . . . . . . . . . . 95 + C.2. Changes in -09 . . . . . . . . . . . . . . . . . . . . . . 95 + C.3. Changes in -08 . . . . . . . . . . . . . . . . . . . . . . 96 + C.4. Changes in -07 . . . . . . . . . . . . . . . . . . . . . . 97 + C.5. Changes in -06 . . . . . . . . . . . . . . . . . . . . . . 97 + C.6. Changes in -05 . . . . . . . . . . . . . . . . . . . . . . 98 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 5] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +1. Introduction + + This document specifies extensions to the CalDAV "calendar-access" + [RFC4791] feature to enable scheduling of iCalendar-based [RFC5545] + calendar components between Calendar Users. This extension leverages + the scheduling methods defined in the iCalendar Transport-independent + Interoperability Protocol (iTIP) [RFC5546] to permit Calendar Users + to perform scheduling transactions such as schedule, reschedule, + respond to scheduling request or cancel calendar components, as well + as search for busy time information. + + Discussion of this Internet-Draft is taking place on the mailing list + . + +1.1. Terminology + + This specification uses much of the same terminology as iCalendar + [RFC5545], iTIP [RFC5546], WebDAV [RFC4918], and CalDAV [RFC4791]. + The following definitions are provided to aid the reader in + understanding this specification. + + Calendar User (CU): An entity (often a human) that accesses calendar + information [RFC3283]. + + Calendar collection: A resource that acts as a container of + references to child calendar object resources [RFC4791]. + + Calendar object resource: A resource representing a calendar object + (event, to-do, journal entry, or other calendar components) + [RFC4791]. + + Scheduling object resource: A calendar object resource contained in + a calendar collection for which the server will take care of + sending scheduling messages on behalf of the owner of the calendar + collection. + + Organizer scheduling object resource: A scheduling object resource + owned by an Organizer. + + Attendee scheduling object resource: A scheduling object resource + owned by an Attendee. + + Automatic scheduling transaction: Add, change or remove operations + on a scheduling object resource for which the server will deliver + scheduling messages to other Calendar Users. + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 6] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Scheduling message: A calendar object that describes a scheduling + transaction such as schedule, reschedule, reply, or cancel. + + Scheduling Outbox collection: A resource at which busy time + information requests are targeted. + + Scheduling Inbox collection: A collection in which incoming + scheduling messages are delivered. + +1.2. Approach + + iTIP [RFC5546] outlines a model where Calendar Users exchange + scheduling messages with one another. Often times, clients are made + responsible for generating and sending scheduling messages as well as + processing incoming scheduling messages. This approach yields a + number of problems, including: + + o For most updates to a calendar component, clients are responsible + for sending appropriate scheduling messages to the Organizer or + the Attendees. + + o The handling of incoming scheduling messages and the updates to + calendars impacted by those messages only occurs when clients are + active. + + o Due to the update latency, it is possible for calendars of + different Calendar Users to reflect different, inaccurate states. + + This specification uses an alternative approach where the server is + made responsible for sending scheduling messages and processing + incoming scheduling messages. This approach frees the clients from + the submission and processing of scheduling messages and ensures + better consistency of calendar data across users' calendars. The + operation of creating, modifying or deleting a calendar component in + a calendar is enough to trigger the server to deliver the necessary + scheduling messages to the appropriate Calendar Users. + +1.3. Limitations + + While the scheduling features described in this specification are + based on iTIP [RFC5546], some of its more advanced features have + deliberately been left out in order to keep this specification + simple. In particular, the following iTIP [RFC5546] features are not + covered: publishing, countering, delegating, refreshing and + forwarding calendar components, as well as replacing the Organizer of + a calendar component. + + The goal of this specification is to provide the essential scheduling + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 7] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + features needed. It is expected that future extensions will be + developed to address the more advanced features. + +1.4. Notational Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The Augmented BNF (ABNF) syntax used by this document to specify the + format definition of new iCalendar elements is defined in [RFC5234]. + + The Augmented BNF (ABNF) syntax used by this document to specify the + format definition of new message header fields to be used with the + HTTP/1.1 protocol is described in Section 2.1 of [RFC2616]. Since + this Augmented BNF uses the basic production rules provided in + Section 2.2 of [RFC2616], these rules apply to this document as well. + + The term "protected" is used in the Conformance field of WebDAV + property definitions as defined in Section 15 of [RFC4918]. + +1.5. XML Namespaces and Processing + + This document uses XML DTD fragments ([W3C.REC-xml-20081126], Section + 3.2) as a purely notational convention. WebDAV request and response + bodies cannot be validated by a DTD due to the specific extensibility + rules defined in Section 17 of [RFC4918] and due to the fact that all + XML elements defined by that specification use the XML namespace name + "DAV:". In particular: + + 1. element names use the "DAV:" namespace, + + 2. element ordering is irrelevant unless explicitly stated, + + 3. extension elements (elements not already defined as valid child + elements) may be added anywhere, except when explicitly stated + otherwise, + + 4. extension attributes (attributes not already defined as valid for + this element) may be added anywhere, except when explicitly + stated otherwise. + + The XML elements specified in this document are defined in the + "urn:ietf:params:xml:ns:caldav" XML namespace registered by CalDAV + [RFC4791]. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:caldav" are referenced in this document + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 8] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + outside of the context of an XML fragment, the strings "DAV:" and + "CALDAV:" will be prefixed to the element types, respectively. + + This document inherits, and sometimes extends, DTD productions from + Section 14 of [RFC4918]. + + Also note that some CalDAV XML element names are identical to WebDAV + XML element names, though their namespace differs. Care must be + taken not to confuse the two sets of names. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 9] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +2. Scheduling Process + + The process of scheduling an event between different parties often + involves a series of steps with different actors playing particular + roles during the whole process. Typically there is an event + "Organizer" whose role is to schedule an event between one or more + "Attendees", and this is done by sending out invitations and handling + responses from each Attendee. + + This process can typically be broken down into two phases. + + In the first phase, the Organizer will query the busy time + information of each Attendee to determine the most appropriate time + for the event. This request is sometimes called a "freebusy" lookup. + + In the second phase, the Organizer sends out invitations to each + Attendee using the time previously determined from the freebusy + lookup. There then follows exchanges between Organizer and Attendees + regarding the invitation. Some Attendees may choose to attend at the + time proposed by the Organizer, others may decline to attend. The + Organizer needs to process each of the replies from the Attendees and + take appropriate action to confirm the event, reschedule it or + perhaps cancel it. + + The user expectation as to how a calendaring and scheduling system + should respond in each of these two phases is somewhat different. In + the case of a freebusy lookup, users expect to get back results + immediately so that they can then move on to the invitation phase as + quickly as possible. In the case of invitations, it is expected that + each Attendee will reply with their participation status in their own + time, so delays in receiving replies are anticipated. Thus + calendaring and scheduling systems should treat these two operational + phases in different ways to accommodate the user expectations, which + is what this specification does. + + While the scenario described above only covers the case of scheduling + events between Calendar Users, and requesting busy time information, + this specification also provides support for the scheduling of to-dos + between Calendar Users. For the majority of the following + discussion, scheduling of events and freebusy lookups will be + discussed, as these are the more common operations. + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 10] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +3. Scheduling Support + + A server that supports the features described in this document MUST + include "calendar-auto-schedule" as a field in the DAV response + header from an OPTIONS request on any resource that supports any + scheduling actions, properties, privileges or methods. + + To advertise support for the CalDAV "calendar-auto-schedule" feature + a server is REQUIRED to support and advertise support for the CalDAV + "calendar-access" [RFC4791] feature. + +3.1. Example OPTIONS Request + + In this example, the OPTIONS response indicates that the server + supports the "calendar-access" and "calendar-auto-schedule" features + and that the resource "/home/cyrus/calendars/inbox/" supports the + scheduling actions, properties, privileges and methods defined in + this specification. + + >> Request << + + OPTIONS /home/cyrus/calendars/inbox/ HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 204 No Content + Date: Thu, 31 Mar 2011 09:00:00 GMT + Allow: OPTIONS, GET, HEAD, DELETE, TRACE, PROPFIND + Allow: PROPPATCH, LOCK, UNLOCK, REPORT, ACL + DAV: 1, 2, 3, access-control + DAV: calendar-access, calendar-auto-schedule + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 11] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +4. Scheduling Collections + + This specification introduces new collection resource types that are + used to manage scheduling object resources, and scheduling + privileges, as well as provide scheduling functionality. It is the + server's responsibility to create these collection resources, and + clients have no way to create or delete them. + +4.1. Scheduling Outbox Collection + + A scheduling Outbox collection is used as the target for busy time + information requests, and to manage privileges that apply to outgoing + scheduling requests. + + A scheduling Outbox collection MUST report the DAV:collection and + CALDAV:schedule-outbox XML elements in the value of the DAV: + resourcetype property. The element type declaration for CALDAV: + schedule-outbox is: + + + + Example: + + + + + + + New WebDAV ACL [RFC3744] privileges can be set on the scheduling + Outbox collection to control who is allowed to send scheduling + messages on behalf of the Calendar User associated with the + scheduling Outbox collection. See Section 13.1 for more details. + + A scheduling Outbox collection MUST NOT be a child (at any depth) of + a calendar collection resource. + + The following WebDAV properties specified in CalDAV "calendar-access" + [RFC4791] MAY also be defined on scheduling Outbox collections: + + CALDAV:supported-calendar-component-set - when present this + indicates the allowed calendar component types for scheduling + messages submitted to the scheduling Outbox collection with the + POST method. + + CALDAV:supported-calendar-data - when present this indicates the + allowed media types for scheduling messages submitted to the + scheduling Outbox collection with the POST method. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 12] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + CALDAV:max-resource-size - when present this indicates the maximum + size in octets of a resource that the server is willing to accept + for scheduling messages submitted to the scheduling Outbox + collection with the POST method. + + CALDAV:min-date-time - when present this indicates the earliest + date and time (in UTC) that the server is willing to accept for + any DATE or DATE-TIME value in scheduling messages submitted to + the scheduling Outbox collection with the POST method. + + CALDAV:max-date-time - when present this indicates the latest date + and time (in UTC) that the server is willing to accept for any + DATE or DATE-TIME value in scheduling messages submitted to the + scheduling Outbox collection with the POST method. + + CALDAV:max-attendees-per-instance - when present this indicates + the maximum number of ATTENDEE properties in any instance of + scheduling messages submitted to the scheduling Outbox collection + with the POST method. Specifically, this limits the total number + of Attendees whose freebusy information can be queried in a single + request. + + The use of child resources in a scheduling Outbox collection is + reserved for future revisions or extensions of this specification. + +4.2. Scheduling Inbox Collection + + A scheduling Inbox collection contains copies of incoming scheduling + messages. These may be requests sent by an Organizer, or replies + sent by an Attendee in response to a request. The scheduling Inbox + collection is also used to manage scheduling privileges. + + A scheduling Inbox collection MUST report the DAV:collection and + CALDAV:schedule-inbox XML elements in the value of the DAV: + resourcetype property. The element type declaration for CALDAV: + schedule-inbox is: + + + + Example: + + + + + + + Scheduling Inbox collections MUST only contain calendar object + resources that obey the restrictions specified in iTIP [RFC5546]. + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 13] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Consequently, scheduling Inbox collections MUST NOT contain any types + of collection resources. Restrictions defined in Section 4.1 of + CalDAV "calendar-access" [RFC4791] on calendar object resources + contained in calendar collections (e.g., "UID" uniqueness) do not + apply to calendar object resources contained in a scheduling Inbox + collection. Thus, multiple calendar object resources contained in a + scheduling Inbox collection can have the same "UID" property value + (i.e., multiple scheduling messages for the same calendar component). + + New WebDAV ACL [RFC3744] privileges can be set on the scheduling + Inbox collection to control from whom the Calendar User associated + with the scheduling Inbox collection will accept scheduling messages + from. See Section 13.1 for more details. + + A scheduling Inbox collection MUST NOT be a child (at any depth) of a + calendar collection resource. + + The following WebDAV properties specified in CalDAV "calendar-access" + [RFC4791] MAY also be defined on scheduling Inbox collections: + + CALDAV:calendar-timezone - when present this contains a time zone + that the server can use when calendar date-time operations are + carried out, for example when a time-range CALDAV:calendar-query + REPORT is targeted at a scheduling Inbox collection. + + CALDAV:supported-calendar-component-set - when present this + indicates the allowed calendar component types for scheduling + messages delivered to the scheduling Inbox collection. + + CALDAV:supported-calendar-data - when present this indicates the + allowed media types for scheduling messages delivered to the + scheduling Inbox collection. + + CALDAV:max-resource-size - when present this indicates the maximum + size in octets of a resource that the server is willing to accept + for scheduling messages delivered to the scheduling Inbox + collection. + + CALDAV:min-date-time - when present this indicates the earliest + date and time (in UTC) that the server is willing to accept for + any DATE or DATE-TIME value in scheduling messages delivered to + the scheduling Inbox collection. + + CALDAV:max-date-time - when present this indicates the latest date + and time (in UTC) that the server is willing to accept for any + DATE or DATE-TIME value in scheduling messages delivered to the + scheduling Inbox collection. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 14] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + CALDAV:max-instances - when present this indicates the maximum + number of recurrence instances in scheduling messages delivered to + the scheduling Inbox collection. + + CALDAV:max-attendees-per-instance - when present this indicates + the maximum number of ATTENDEE properties in any instance of + scheduling messages delivered to the scheduling Inbox collection. + +4.3. Calendaring Reports Extensions + + This specification extends the CALDAV:calendar-query and CALDAV: + calendar-multiget REPORTs to return results for calendar object + resources in scheduling Inbox collections. + + When a CALDAV:calendar-query REPORT includes a time-range query and + targets a scheduling Inbox collection, if any calendar object + resources contain "VEVENT" calendar components that do not include a + "DTSTART" iCalendar property (as allowed by iTIP [RFC5546]) then such + components MUST always match the time-range query test. + + Note that the CALDAV:free-busy-query REPORT is not supported on + scheduling Inbox collections. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 15] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +5. Scheduling Transactions + + When a calendar object resource is created, modified or removed from + a calendar collection, the server examines the calendar data and + checks to see whether the data represents a scheduling object + resource. If it does, the server will automatically attempt to + deliver a scheduling message to the appropriate Calendar Users. + Several types of scheduling operations can occur in this case, + equivalent to iTIP "REQUEST", "REPLY", "CANCEL", and "ADD" + operations. + +5.1. Identifying Scheduling Object Resources + + Calendar object resources on which the server performs automatic + scheduling transactions are referred to as scheduling object + resources. There are two types of scheduling object resources: + organizer scheduling object resources, and attendee scheduling object + resources. + + A calendar object resource is considered to be a valid organizer + scheduling object resource if the "ORGANIZER" iCalendar property is + present and set in all the calendar components to a value that + matches one of the calendar user addresses of the owner of the + calendar collection. + + A calendar object resource is considered to be a valid attendee + scheduling object resource if the "ORGANIZER" iCalendar property is + present and set in all the calendar components to the same value and + doesn't match one of the calendar user addresses of the owner of the + calendar collection, and at least one of the "ATTENDEE" iCalendar + property values matches one of the calendar user addresses of the + owner of the calendar collection. + + The creation of attendee scheduling object resources is typically + done by the server, with the resource being created in an appropriate + calendar collection (see Section 6.4). + +5.2. Handling Scheduling Object Resources + + The server's behavior when processing a scheduling object resource + depends on whether it is owned by the Organizer or an Attendee + specified in the calendar data. + +5.2.1. Organizer Scheduling Object Resources + + An Organizer can create, modify or remove a scheduling object + resource. The create, modify and remove behaviors for the server are + each described next, and the way these are invoked via HTTP requests + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 16] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + is described in Section 5.2.3. + + The Organizer of a calendar component may also be an Attendee of that + calendar component. In such cases the server MUST NOT send a + scheduling message to the Attendee that matches the Organizer. + +5.2.1.1. Create + + When a scheduling object resource is created by the Organizer, the + server will inspect each "ATTENDEE" property to determine if a + scheduling message should be delivered to this Attendee according to + the value of the "SCHEDULE-AGENT" property parameter (see + Section 10.1) as described in the table below: + + +------------------+-------------+ + | SCHEDULE-AGENT | iTIP METHOD | + +==================+=============+ + | SERVER (default) | REQUEST | + +------------------+-------------+ + | CLIENT | -- | + +------------------+-------------+ + | NONE | -- | + +------------------+-------------+ + + The attempt to deliver the scheduling message will either succeed or + fail. In all cases, the server MUST add a "SCHEDULE-STATUS" + iCalendar property parameter (see Section 10.3) to the "ATTENDEE" + iCalendar property in the scheduling object resource being created, + and set its value as described in Section 9.2. This will result in + the created calendar object resource differing from the calendar data + sent in the HTTP request. As a result clients MAY reload the + calendar data from the server in order to update to the new server + generated state information. Servers MUST NOT set the "SCHEDULE- + STATUS" property parameter on the "ATTENDEE" property of Attendees + for which it did not attempt to deliver a scheduling message. + + Restrictions: + + 1. The server SHOULD reject any attempt to set the "PARTSTAT" + iCalendar property parameter value of the "ATTENDEE" iCalendar + property of other users in the calendar object resource to a + value other than "NEEDS-ACTION" if the "SCHEDULE-AGENT" property + parameter value is not present or set to the value "SERVER". + + 2. The server MAY reject attempts to create a scheduling object + resource that specifies a "UID" property value already specified + in a scheduling object resource contained in another calendar + collection of the Organizer. + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 17] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + 3. The server MUST take into account scheduling privileges as + described in Section 13.1 when handling the creation of a + scheduling object resource. + + 4. Restrictions on calendar object resources defined in Section 4.1 + of [RFC4791] MUST also be enforced. + + The server MUST return an error with the CALDAV:allowed-organizer- + scheduling-object-change precondition code (Section 5.2.4.3) when the + Organizer attempts to change the iCalendar data in a manner that is + forbidden. + +5.2.1.2. Modify + + When a scheduling object resource is modified by the Organizer, the + server will inspect each "ATTENDEE" property in the new calendar data + to determine which ones have the "SCHEDULE-AGENT" iCalendar property + parameter. It will then need to compare this with the "ATTENDEE" + properties in the existing calendar object resource that is being + modified. + + For each Attendee in the old and new calendar data on a per-instance + basis, and taking into account the addition or removal of Attendees, + the server will determine whether to deliver a scheduling message to + the Attendee. The following table determines whether the server + needs to deliver a scheduling message, and if so which iTIP + scheduling method to use. The values "SERVER", "CLIENT", and "NONE" + in the top and left titles of the table refer to the "SCHEDULE-AGENT" + parameter value of the "ATTENDEE" property, and the values "" + and "" are used to cover the cases where the "ATTENDEE" + property is not present (Old) or is being removed (New). + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 18] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + +---------------+-----------------------------------------------+ + | | New | + | ATTENDEE +-----------+-----------+-----------+-----------+ + | | | SERVER | CLIENT | NONE | + | | | (default) | | | + +===+===========+===========+===========+===========+===========+ + | | | -- | REQUEST / | -- | -- | + | | | | ADD | | | + | +-----------+-----------+-----------+-----------+-----------+ + | | SERVER | CANCEL | REQUEST | CANCEL | CANCEL | + | O | (default) | | | | | + | l +-----------+-----------+-----------+-----------+-----------+ + | d | CLIENT | -- | REQUEST / | -- | -- | + | | | | ADD | | | + | +-----------+-----------+-----------+-----------+-----------+ + | | NONE | -- | REQUEST / | -- | -- | + | | | | ADD | | | + +---+-----------+-----------+-----------+-----------+-----------+ + + The attempt to deliver the scheduling message will either succeed or + fail. In all cases, the server MUST add a "SCHEDULE-STATUS" + iCalendar property parameter to the "ATTENDEE" iCalendar property in + the scheduling object resource being modified, and set its value as + described in Section 9.2. This will result in the created calendar + object resource differing from the calendar data sent in the HTTP + request. As a result clients MAY reload the calendar data from the + server in order to update to the new server generated state + information. + + Restrictions: + + 1. The server MAY reject any attempt to set the "PARTSTAT" iCalendar + property parameter value of the "ATTENDEE" iCalendar property of + other users in the calendar object resource to a value other than + "NEEDS-ACTION" if the "SCHEDULE-AGENT" property parameter value + is not present or set to the value "SERVER". + + 2. The server MUST take into account scheduling privileges as + described in Section 13.1 when handling the modification of a + scheduling object resource. + + 3. Restrictions on calendar object resources defined in Section 4.1 + of [RFC4791] MUST also be enforced. + + The server MUST return an error with the CALDAV:allowed-organizer- + scheduling-object-change precondition code (Section 5.2.4.3) when the + Organizer attempts to change the iCalendar data in a manner that is + forbidden. + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 19] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +5.2.1.3. Remove + + When a scheduling object resource is removed by the Organizer, the + server will inspect each "ATTENDEE" property in the scheduling object + resource being removed to determine which ones have the "SCHEDULE- + AGENT" iCalendar property parameter. + + For each Attendee the server will determine whether to attempt to + deliver a scheduling message into the Attendee's scheduling Inbox + collection, based on the table below: + + +------------------+-------------+ + | SCHEDULE-AGENT | iTIP METHOD | + +==================+=============+ + | SERVER (default) | CANCEL | + +------------------+-------------+ + | CLIENT | -- | + +------------------+-------------+ + | NONE | -- | + +------------------+-------------+ + + Restrictions: + + 1. The server MUST take into account scheduling privileges as + described in Section 13.1 when handling the deletion of a + scheduling object resource. + +5.2.2. Attendee Scheduling Object Resources + + An Attendee can create, modify or remove a scheduling object resource + by issuing HTTP requests with an appropriate method. The create, + modify and remove behaviors for the server are each described next, + and the way these are invoked via HTTP requests is described in + Section 5.2.3. + +5.2.2.1. Allowed Attendee Changes + + Attendees are allowed to make some changes to a scheduling object + resource, though key properties such as start time, end time, + location, and summary are typically under the control of the + Organizer. + + The server MUST allow Attendees to: + + 1. change their own "PARTSTAT" iCalendar property parameter value. + + 2. add, modify or remove any "TRANSP" iCalendar properties. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 20] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + 3. add, modify or remove any "PERCENT-COMPLETE" iCalendar + properties. + + 4. add, modify or remove any "COMPLETED" iCalendar properties. + + 5. add, modify or remove any "VALARM" iCalendar components. + + 6. add, modify or remove the "CALSCALE" iCalendar property within + the top-level "VCALENDAR" component. + + 7. modify the "PRODID" iCalendar property within the top-level + "VCALENDAR" component. + + 8. add "EXDATE" iCalendar properties and possibly remove components + for overridden recurrence instances. + + 9. add, modify or remove any "CREATED", "DTSTAMP" and "LAST- + MODIFIED" iCalendar properties. + + 10. add, modify or remove "SCHEDULE-STATUS" iCalendar property + parameters on "ATTENDEE" properties that have a "SCHEDULE-AGENT" + parameter set to "CLIENT". + + 11. add new components to represent overridden recurrence instances, + provided the only changes to the recurrence instance follow the + rules above. + + The server MUST return an error with the CALDAV:allowed-attendee- + scheduling-object-change precondition code (Section 5.2.4.4) when the + Attendee attempts to change the iCalendar data in a manner forbidden + by the server. + +5.2.2.2. Create + + Typically an Attendee does not create scheduling object resources, as + scheduling messages delivered to them on the server are automatically + processed by the server and placed on one of their calendars (see + Section 6). However, in some cases a scheduling message may get + delivered directly to the client, and the Attendee may wish to store + that on the server. In that case the client creates a scheduling + object resource in a suitable calendar belonging to the Attendee. It + can then set the "SCHEDULE-AGENT" iCalendar property parameter on all + "ORGANIZER" iCalendar properties in the resource to determine how the + server treats the resource. The value of the "SCHEDULE-AGENT" + iCalendar property parameter on all "ORGANIZER" iCalendar properties + MUST be the same. + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 21] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + +----------------+--------------------------------------------------+ + | SCHEDULE-AGENT | Action | + +----------------+--------------------------------------------------+ + | SERVER | The server will attempt to process changes to | + | (default) | the resource using the normal rules for attendee | + | | scheduling object resources. | + | | | + | CLIENT | The server does no special processing of the | + | | resource. The client is assumed to be handling | + | | Attendee replies etc. | + | | | + | NONE | The server does no special processing of the | + | | resource. | + +----------------+--------------------------------------------------+ + + In some cases a server may not be able to process an Attendee + scheduling object resource that originated from another system (i.e., + where the server is unable to deliver scheduling messages to the + Organizer). In such cases the server MUST add a "SCHEDULE-STATUS" + iCalendar property parameter to all "ORGANIZER" iCalendar properties + in the resource with a suitable value indicating a error. + +5.2.2.3. Modify + + When a scheduling object resource is modified by an Attendee, the + server behavior depends on the value of the "SCHEDULE-AGENT" + iCalendar property parameter on the "ORGANIZER" iCalendar properties: + + +----------------+--------------------------------------------------+ + | SCHEDULE-AGENT | Action | + +----------------+--------------------------------------------------+ + | SERVER | The server will attempt to process the removal | + | (default) | using the behavior listed below. | + | | | + | CLIENT | The server does no special processing of the | + | | resource. The client is assumed to be handling | + | | any Attendee replies etc. | + | | | + | NONE | The server does no special processing of the | + | | resource. | + +----------------+--------------------------------------------------+ + + The server will inspect the changes by comparing the new scheduling + object resource with the existing scheduling object resource. + + If the Attendee changes one or more "PARTSTAT" iCalendar property + values on any component, or adds an overridden component with a + changed "PARTSTAT" property, then the server MUST deliver an iTIP + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 22] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + "REPLY" scheduling message to the Organizer to indicate the new + participation status of the Attendee. + + If the Attendee adds an "EXDATE" property value to effectively remove + a recurrence instance, the server MUST deliver an iTIP "REPLY" + scheduling message to the Organizer to indicate that the Attendee has + declined the instance (i.e., the Attendee's "PARTSTAT" iCalendar + property parameter value is set to "DECLINED"). + + The attempt to deliver the scheduling message will either succeed or + fail. In all cases, the server MUST add a "SCHEDULE-STATUS" + iCalendar property parameter to the "ORGANIZER" iCalendar property in + the scheduling object resource being created, and set its value as + described in Section 9.2. This will result in the created calendar + object resource differing from the calendar data sent in the HTTP + request. As a result clients MAY reload the calendar data from the + server in order to update to the new server generated state + information. + +5.2.2.4. Remove + + When a scheduling object resource is removed by an Attendee, the + server behavior depends on the value of the "SCHEDULE-AGENT" + iCalendar property parameter on the "ORGANIZER" iCalendar properties: + + +----------------+--------------------------------------------------+ + | SCHEDULE-AGENT | Action | + +----------------+--------------------------------------------------+ + | SERVER | The server will attempt to process the removal | + | (default) | using either behaviors (1) or (2) listed below. | + | | | + | CLIENT | The server does no special processing of the | + | | resource. The client is assumed to be handling | + | | any Attendee replies etc. | + | | | + | NONE | The server does no special processing of the | + | | resource. | + +----------------+--------------------------------------------------+ + + 1. If the HTTP request contains a "Schedule-Reply" request header + set to the value "T" or there is no "Schedule-Reply" request + header, then the server MUST attempt to deliver a scheduling + message to the Organizer indicating that the Attendee has a + "PARTSTAT" iCalendar property parameter value set to "DECLINED". + That is, the Attendee has chosen not to attend any instances. If + the server is unable to deliver the scheduling message, the + remove action MUST fail, and an appropriate "SCHEDULE-STATUS" + iCalendar property parameter set on the "ORGANIZER" property in + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 23] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + the scheduling object resource stored by the server. + + 2. If the HTTP request contains a "Schedule-Reply" request header + set to the value "F", the server MUST NOT attempt to deliver a + scheduling message. The resource is simply removed. This + provides the client a way to silently remove unwanted scheduling + messages. + +5.2.3. HTTP Methods + + This section describes how use of various HTTP methods on a + scheduling object resource will cause a create, modify or remove + action on that resource as described above. The use of these methods + is subject to the restrictions in [RFC4791], in addition to what is + described below. + +5.2.3.1. PUT + + When a PUT method request is received, the server will execute the + following actions, provided all appropriate preconditions are met: + + +--------------------------+--------------------------+-------------+ + | Existing Destination | Resulting Destination | Server | + | Resource | Resource | Action | + +--------------------------+--------------------------+-------------+ + | None | Calendar object resource | None | + | | | | + | None | Scheduling object | Create | + | | resource | | + | | | | + | Calendar object resource | Calendar object resource | None | + | | | | + | Calendar object resource | Scheduling object | Create | + | | resource | | + | | | | + | Scheduling object | Calendar object resource | Remove | + | resource | | | + | | | | + | Scheduling object | Scheduling object | Modify | + | resource | resource | | + +--------------------------+--------------------------+-------------+ + +5.2.3.2. COPY + + When a COPY method request is received, the server will execute the + following actions based on the source and destination collections in + the request: + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 24] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + +-------------------------+-------------------------+---------------+ + | Source Collection | Destination Collection | Server Action | + +-------------------------+-------------------------+---------------+ + | Non-calendar collection | Non-calendar collection | None | + | | | | + | Non-calendar collection | Calendar collection | (1) | + | | | | + | Calendar collection | Non-calendar collection | None | + | | | | + | Calendar collection | Calendar collection | (2) | + +-------------------------+-------------------------+---------------+ + + Note 1. The same rules as used for PUT above are applied for the + destination of the COPY request. + + Note 2. The server MAY reject this as per Section 5.2.4.1, otherwise + None. + + The behavior of a COPY method request on a calendar collection is + undefined. + +5.2.3.3. MOVE + + When a MOVE method request is received, the server will execute the + following actions based on the source and destination collections in + the request: + + +-------------------------+-------------------------+---------------+ + | Source Collection | Destination Collection | Server Action | + +-------------------------+-------------------------+---------------+ + | Non-calendar collection | Non-calendar collection | None | + | | | | + | Non-calendar collection | Calendar collection | (1) | + | | | | + | Calendar collection | Non-calendar collection | (2) | + | | | | + | Calendar collection | Calendar collection | None | + +-------------------------+-------------------------+---------------+ + + Note 1. The same rules as used for PUT above are applied for the + destination of the MOVE request. + + Note 2. The same rules as used for DELETE below are applied for the + source of the MOVE request. + + The behavior of a MOVE method request on a calendar collection is + undefined. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 25] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +5.2.3.4. DELETE + + When a DELETE method is targeted at a scheduling object resource the + server will execute the Remove action. + + When a DELETE method is targeted at a calendar collection the server + will execute the Remove action on all scheduling object resources + contained in the calendar collection. + +5.2.4. Additional Method Preconditions + + This specification defines method preconditions (see Section 16 of + WebDAV [RFC4918]), in addition to the ones in [RFC4791], to provide + machine-parsable information in error responses. + +5.2.4.1. CALDAV:unique-scheduling-object-resource Precondition + + Name: unique-scheduling-object-resource + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: PUT, COPY, and MOVE + + Use with: 403 Forbidden + + Purpose: (precondition) -- Servers MAY reject requests to create a + scheduling object resource with an iCalendar "UID" property value + already in use by another scheduling object resource owned by the + same user in other calendar collections. Servers SHOULD report + the URL of the scheduling object resource that is already making + use of the same "UID" property value in the DAV:href element. + + Definition: + + + + Example: + + + /home/bernard/calendars/personal/abc123.ics + + +5.2.4.2. CALDAV:same-organizer-in-all-components Precondition + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 26] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Name: same-organizer-in-all-components + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: PUT, COPY, and MOVE + + Use with: 403 Forbidden + + Purpose: (precondition) -- All the calendar components in a + scheduling object resource MUST contain the same "ORGANIZER" + property value when present. + + Definition: + + + + Example: + + + +5.2.4.3. CALDAV:allowed-organizer-scheduling-object-change Precondition + + Name: allowed-organizer-scheduling-object-change + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: PUT, COPY, and MOVE + + Use with: 403 Forbidden + + Purpose: (precondition) -- Servers MAY impose restrictions on + modifications allowed by an Organizer. For instance, servers MAY + prevent the Organizer setting the "PARTSTAT" property parameter to + a value other than "NEEDS-ACTION" if the corresponding "ATTENDEE" + property has the "SCHEDULE-AGENT" property parameter set to + "SERVER", or has no "SCHEDULE-AGENT" property parameter. See + Section 5.2.1. + + Definition: + + + + Example: + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 27] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +5.2.4.4. CALDAV:allowed-attendee-scheduling-object-change Precondition + + Name: allowed-attendee-scheduling-object-change + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: PUT, COPY, and MOVE + + Use with: 403 Forbidden + + Purpose: (precondition) -- Servers MAY impose restrictions on + modifications allowed by an Attendee. Attendee modifications that + servers MUST allow are specified in Section 5.2.2.1. + + Definition: + + + + Example: + + + +5.2.5. DTSTAMP and SEQUENCE Properties + + Whenever the server generates a scheduling message for delivery to a + Calendar User, it MUST ensure that a "DTSTAMP" iCalendar property is + present and MUST set the value to the UTC time that the scheduling + message was generated (as required by iCalendar). + + iTIP [RFC5546] places certain requirements on how the "SEQUENCE" + iCalendar property value in scheduling messages changes. The server + MUST ensure that for each type of scheduling operation, the + "SEQUENCE" iCalendar property value is appropriately updated. If the + client does not update the "SEQUENCE" iCalendar property itself when + that is required, the server MUST update the property. + +5.2.6. Restrict Recurrence Instances Sent to Attendees + + When delivering scheduling messages for recurring calendar components + to Attendees, servers MUST ensure that Attendees only get information + about recurrence instances that explicitly include them as an + Attendee. + + For example, if an Attendee is invited to a single recurrence + instance of a recurring event, and no others, the scheduling object + resource contained in the Organizer's calendar collection will + contain an overridden instance in the form of a separate calendar + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 28] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + component. That separate calendar component will include the + "ATTENDEE" property referencing the "one-off" Attendee. That + Attendee will not be listed in any other calendar components in the + scheduling object resource. Any scheduling messages delivered to the + Attendee will only contain information about this overridden + instance. + + As another example, an Attendee could be excluded from one instance + of a recurring event. In that case the scheduling object resource + contained in the calendar collection of the Organizer will include an + overridden instance with an "ATTENDEE" list that does not include the + Attendee being excluded. The scheduling message that will be + delivered to the Attendee will not specify the overridden instance + but rather include an "EXDATE" property in the master recurring + component defining the recurrence set. + +5.2.7. Forcing the Server to Send a Scheduling Message + + The iCalendar property parameter "SCHEDULE-FORCE-SEND" defined in + Section 10.2 can be used by a Calendar User to force the server to + send a scheduling message to an Attendee or the Organizer in a + situation where the server would not normally send a scheduling + message. For instance, an Organizer could use this property + parameter to request an Attendee, that previously declined an + invitation, to reconsider their participation status without being + forced to modify the event. + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 29] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +6. Processing Incoming Scheduling Messages + + Scheduling operations can cause the delivery of a scheduling message + into an Organizer's or Attendee's scheduling Inbox collection. In + the former case the scheduling messages are replies from Attendees, + in the latter case the scheduling messages are requests, + cancellations or additions from the Organizer. + + Servers MUST automatically process incoming scheduling messages using + the rules defined by [RFC5546], by creating or updating the + corresponding scheduling object resources on calendars owned by the + owner of the scheduling Inbox collection. In addition, the + scheduling message is stored in the scheduling Inbox collection as an + indicator to the client that a scheduling operation has taken place. + + The server MUST take into account privileges on the scheduling Inbox + collection when processing incoming scheduling messages, to determine + whether delivery of the scheduling message is allowed. Privileges on + calendars containing any matching scheduling object resource are not + considered in this case (i.e., a schedule message from another user + can cause modifications to resources in calendar collections that the + other user would not normally have read or write access to). + Additionally, servers MUST take into account any scheduling Inbox + collection preconditions (see Section 4.2) when delivering the + scheduling message, and it MUST take into account the similar + preconditions on any calendar collection which contains, or would + contain, the corresponding scheduling object resource. + +6.1. Processing Organizer Requests, Additions, and Cancellations + + For a scheduling message sent by an Organizer, the server first tries + to locate a corresponding scheduling object resource belonging to the + Attendee. If no matching scheduling object resource exists, the + server treats the scheduling message as a new message, otherwise it + is treated as an update. + + In the case of a new message, the server MUST process the scheduling + message and create a new scheduling object resource in an appropriate + calendar collection for the Attendee. + + In the case of an update, the server MUST process the scheduling + message and update the matching scheduling object resource belonging + to the Attendee to reflect the changes sent by the Organizer. + + In each case, the scheduling message MUST only appear in the + Attendee's scheduling Inbox collection once all automatic processing + has been done. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 30] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +6.2. Processing Attendee Replies + + For a scheduling message reply sent by an Attendee, the server first + locates the corresponding scheduling object resource belonging to the + Organizer. + + The server MUST then update the "PARTSTAT" iCalendar property + parameter value of each "ATTENDEE" iCalendar property in the + scheduling object resource to match the changes indicated in the + reply (taking into account the fact that an Attendee could have + created a new overridden iCalendar component to indicate different + participation status on one or more recurrence instances of a + recurring event). + + The server MUST also update or add the "SCHEDULE-STATUS" property + parameter on each matching "ATTENDEE" iCalendar property and set its + value to that of the "REQUEST-STATUS" property in the reply, or to + "2.0" if "REQUEST-STATUS" is not present (also taking into account + recurrence instances). If there are multiple "REQUEST-STATUS" + properties in the reply, the "SCHEDULE-STATUS" property parameter + value is set to a comma-separated list of status codes, one from each + "REQUEST-STATUS" property. + + The server SHOULD send scheduling messages to all the other Attendees + indicating the change in participation status of the Attendee + replying, subject to the recurrence requirements of Section 5.2.6. + + The scheduling message MUST only appear in the Organizer's scheduling + Inbox collection once all automatic processing has been done. + +6.3. Scheduling Messages as Notifications + + Once the processing of an incoming scheduling message is completed by + the server, the message is made available as a child resource in the + scheduling Inbox collection of the Calendar User that received the + message, to serve as a notification that a change has been made to + the corresponding scheduling object resource. Scheduling messages + are typically removed from the scheduling Inbox collection by the + client once the calendar user has acknowledged the change. + +6.4. Default Calendar Collection + + The server is REQUIRED to process scheduling messages received for an + Attendee by creating a new scheduling object resource in a calendar + collection belonging to the Attendee, when one does not already + exist. A Calendar User that is an Attendee in a scheduling operation + MUST have at least one valid calendar collection available. If there + is no valid calendar collection, then the server MUST reject the + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 31] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + attempt to deliver the scheduling message to the Attendee. + + Servers MAY provide support for a default calendar collection, that + is, the calendar collection in which new scheduling object resources + will be created. The CALDAV:schedule-default-calendar-URL WebDAV + property, which can be present on the scheduling Inbox collection of + a Calendar User, specifies if this Calendar User has a default + calendar collection. See Section 12.2. + + Servers SHOULD create new scheduling object resources in the default + calendar collection, if the CALDAV:schedule-default-calendar-URL + WebDAV property is set. + + Servers MAY allow clients to change the default calendar collection + by changing the value of the CALDAV:schedule-default-calendar-URL + WebDAV property on the scheduling Inbox collection. However, the + servers MUST ensure that any new value for that property refers to a + valid calendar collection belonging to the owner of the scheduling + Inbox collection. + + Servers MUST reject any attempt to delete the default calendar + collection. + +6.4.1. Additional Method Preconditions + + This specification defines additional method preconditions (see + Section 16 of WebDAV [RFC4918]) to provide machine-parsable + information in error responses. + +6.4.1.1. CALDAV:default-calendar-needed Precondition + + Name: default-calendar-needed + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: DELETE + + Use with: 403 Forbidden + + Purpose: (precondition) -- The client attempted to delete the + calendar collection currently referenced by the CALDAV:schedule- + default-calendar-URL property, or attempted to remove the CALDAV: + schedule-default-calendar-URL property on the scheduling Inbox + collection on a server that doesn't allow such operations. + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 32] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Definition: + + + + Example: + + + +6.4.1.2. CALDAV:valid-schedule-default-calendar-URL Precondition + + Name: valid-schedule-default-calendar-URL + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: PROPPATCH + + Use with: 403 Forbidden + + Purpose: (precondition) -- The client attempted to set the CALDAV: + schedule-default-calendar-URL property to a DAV:href element that + doesn't reference a valid calendar collection. Note: Servers that + do not allow clients to change the CALDAV:schedule-default- + calendar-URL property would simply return the DAV:cannot-modify- + protected-property precondition defined in Section 16 of WebDAV + [RFC4918]. + + Definition: + + + + Example: + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 33] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +7. Request for Busy Time Information + + The POST method is used to request busy time information of one or + more Calendar Users by submitting a request at the scheduling Outbox + collection of the Calendar User requesting the information (the + Organizer). To accomplish this, the request body of a POST method + MUST contain a "VFREEBUSY" calendar component with the "METHOD" + iCalendar property set to the value "REQUEST" as specified in Section + 3.3.2 of iTIP [RFC5546]. The resource identified by the Request-URI + MUST be a resource collection of type CALDAV:schedule-outbox + (Section 4.1). The "ORGANIZER" property in the "VFREEBUSY" component + MUST match that of the Calendar User who "owns" the Outbox + collection. + +7.1. Status Codes + + The following are examples of response codes one would expect to be + used for this method. However, unless explicitly prohibited, any + 2/3/4/5xx series response code can be used in a response. + + 200 (OK) - The command succeeded. + + 204 (No Content) - The command succeeded. + + 400 (Bad Request) - The client has provided an invalid scheduling + message. + + 403 (Forbidden) - The client cannot submit a scheduling message to + the specified Request-URI. + + 404 (Not Found) - The URL in the Request-URI was not present. + + 423 (Locked) - The specified resource is locked and the client + either is not a lock owner or the lock type requires a lock token + to be submitted and the client did not submit it. + +7.2. Additional Method Preconditions + + This specification defines additional method preconditions for the + POST method. Preconditions defined in WebDAV ACL [RFC3744] and + CalDAV [RFC4791] that applies to the POST method are also listed here + for completeness. + +7.2.1. DAV:need-privileges Precondition + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 34] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Name: need-privileges + + Namespace: DAV: + + Apply to: POST + + Use with: 403 Forbidden + + Purpose: (precondition) -- The currently authenticated user MUST be + granted the CALDAV:schedule-send-freebusy privilege on the + scheduling Outbox collection being targeted by the request. + + Definition: + + + + + Example: + + + + /home/bernard/calendars/outbox/ + + + + +7.2.2. CALDAV:supported-collection Precondition + + Name: supported-collection + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: POST + + Use with: 400 Bad Request + + Purpose: (precondition) -- The Request-URI MUST identify the + location of a scheduling Outbox collection. + + Definition: + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 35] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Example: + + + +7.2.3. CALDAV:supported-calendar-data Precondition + + Name: supported-calendar-data + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: POST + + Use with: 400 Bad Request + + Purpose: (precondition) -- The resource body submitted in the POST + request MUST be a supported media type (e.g., text/calendar). + + Definition: + + + + Example: + + + +7.2.4. CALDAV:valid-calendar-data Precondition + + Name: valid-calendar-data + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: POST + + Use with: 400 Bad Request + + Purpose: (precondition) -- The resource submitted in the POST + request MUST be valid data for the media type being specified + (e.g., a valid iCalendar object). + + Definition: + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 36] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Example: + + + +7.2.5. CALDAV:valid-scheduling-message Precondition + + Name: valid-scheduling-message + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: POST + + Use with: 400 Bad Request + + Purpose: (precondition) -- The resource submitted in the POST + request MUST obey all restrictions specified for the POST request + (e.g., the scheduling message follow the restrictions of iTIP). + + Definition: + + + + Example: + + + +7.2.6. CALDAV:valid-organizer Precondition + + Name: valid-organizer + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: POST + + Use with: 403 Forbidden + + Purpose: (precondition) -- The Calendar User identified by the + "ORGANIZER" property in the POST request's scheduling message MUST + be the Calendar User (or one of the Calendar Users) associated + with the scheduling Outbox collection being targeted by the + request; + + Definition: + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 37] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Example: + + + +7.2.7. CALDAV:max-resource-size Precondition + + Name: max-resource-size + + Namespace: urn:ietf:params:xml:ns:caldav + + Apply to: POST + + Use with: 403 Forbidden + + Purpose: (precondition) -- The resource submitted in the POST + request MUST have a size in octets less than or equal to the value + of the CALDAV:max-resource-size property (defined in Section 5.2.5 + of [RFC4791]) specified on the scheduling Outbox collection + targeted by the request. + + Definition: + + + + Example: + + + +7.3. Response to a POST request + + A POST request can return freebusy information for one or more + Calendar Users. Thus the response needs to contain separate status + information for each recipient. This specification defines a new XML + response body to convey multiple recipient status. + + A response to a POST method that indicates status for one or more + recipients MUST be an XML document with a CALDAV:schedule-response + XML element as its root element. This element MUST contain one + CALDAV:response element for each recipient, with each of those + containing elements that indicate which recipient they correspond to, + the scheduling status for that recipient, any error codes and an + optional description. See Section 14.1 for the detail on the child + elements. + + In the case of a successful freebusy request, the CALDAV:response + elements can also contain CALDAV:calendar-data elements which contain + freebusy information (e.g., an iCalendar VFREEBUSY component) + indicating the busy state of the corresponding recipient. See + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 38] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Appendix B.5 for an example freebusy request and response. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 39] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +8. Avoiding Conflicts when Updating Scheduling Object Resources + + Because replies from Attendees and updates from Organizers are + automatically processed by the server, clients might be in a + situation where their copy of a calendar resource is different from + the one currently on the server. When an Attendee or Organizer makes + a change to the client's copy of the calendar resource, if the client + writes the data to the server it could overwrite the changes already + made there. Typically, clients use the ETag value and If-Match + request headers to avoid the "lost update problem". + + Clients can also use ETag and If-Match to avoid this problem. + However, when doing so the client will likely have to resolve the + differences between the new resource and the original one, and the + changes made by the Attendee or Organizer in the client. This can be + a complicated comparison particularly when recurring components are + present. + + Additionally, the data on the server may change frequently as + Attendees change their participation status, triggering updates to + the Organizer, and consequently other Attendees' copies of the + scheduling object resource. If the ETag/If-Match behavior were used, + clients would be forced to reconcile their cached copy of a + scheduling object resource with the updated one on the server in + order to attempt to write the user's changes back. This could lead + to a race condition that can effectively result in a temporary denial + of service when, for example, there is an event with a large Attendee + list. A "storm" of updates will occur if Attendees all start + responding at the same time, and this would prevent Attendees and the + Organizer from being able to update their own copies of the + scheduling object resource as the server copy is changing frequently. + + A solution is to have the server determine the best way to merge + changes made on the server with changes being made by the client. + For example, if an Attendee changes their participation status and + triggers an update to the Organizer's copy of the event, but the + Organizer also updates their cached copy of the event and attempts to + write it back, rather than failing on a conditional If-Match when the + Organizer writes their data, the server would instead take the + changes made by the Organizer and apply the Attendee changes and + store the result. Thus a form of "weak" ETag matching behavior is + needed such that scheduling changes made automatically on the server + do not invalidate the tag, so that when clients store data + conditionally based on the tag value, the server knows it can apply + the merge behavior. + + In order to do that, this specification introduces a new WebDAV + resource property CALDAV:schedule-tag with a corresponding response + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 40] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + header "Schedule-Tag", and a new "If-Schedule-Tag-Match" request + header to allow client changes to be appropriately merged with server + changes in the case where the changes on the server were the result + of an "inconsequential" scheduling message update. An + "inconsequential" scheduling message is one which simply updates the + status information of Attendees due to a reply from an Attendee. + + Servers MUST support requests targeted at scheduling object resources + using the "If-Schedule-Tag-Match" request header. Consequently, the + server MUST support the "Schedule-Tag" response header and CALDAV: + schedule-tag property for scheduling object resources. Servers MUST + automatically resolve conflicts with "inconsequential" changes done + to scheduling object resources when the "If-Schedule-Tag-Match" + request header is specified. + + The If-Schedule-Tag-Match request header applies only to the Request- + URI, and not to the Destination of a COPY or MOVE in the same way as + the If-Match request header. + + Clients SHOULD use the If-Schedule-Tag-Match header on requests that + update scheduling object resources. + + A response to any successful GET or PUT request targeting a + scheduling object resource MUST include a Schedule-Tag response + header with the value set to the same value as the CALDAV:schedule- + tag WebDAV property of the resource. + + A response to any successful COPY or MOVE request that specifies a + Destination request header targeting a scheduling object resource + MUST include a Schedule-Tag response header with the value set to the + same value as the CALDAV:schedule-tag WebDAV property of the resource + identified in the Request-URI. + + The Schedule-Tag feature is designed to be used to address the + problem of "inconsequential" changes on the server only. Normal ETag + operations are used in all other cases, e.g., for synchronization. + + The value of the CALDAV:schedule-tag property changes according to + these rules: + + o For an Organizer's copy of a scheduling object resource: + + 1. The server MUST NOT change the CALDAV:schedule-tag property + value when the scheduling object resource is updated as the + result of automatically processing a scheduling message reply + from an Attendee. For instance, when an Attendee replies to + the Organizer, the CALDAV:schedule-tag property is unchanged + after the Organizer's scheduling object resource has been + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 41] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + automatically updated by the server with the Attendee's new + participation status. + + 2. The server MUST change CALDAV:schedule-tag property value when + the scheduling object resource is changed directly via an HTTP + request (e.g., PUT, COPY or MOVE). + + o For an Attendee's copy of a scheduling object resource: + + 1. The server MUST change the CALDAV:schedule-tag property value + when the scheduling object resource is changed as the result + of processing a scheduling message update from an Organizer + that contains changes other than just the participation status + of Attendees. + + 2. The server MUST NOT change the CALDAV:schedule-tag property + value when the scheduling object resource is changed as the + result of processing a scheduling message update from an + Organizer that only specify changes in the participation + status of Attendees. For instance, when Attendee "A" replies + to Organizer "O", and Attendee "B" receives a scheduling + message update from Organizer "O" with the new participation + status of Attendee "A", the CALDAV:schedule-tag property of + Attendee "B"s scheduling object resource MUST NOT be changed. + + 3. The server MUST change the CALDAV:schedule-tag property value + when the scheduling object resource is changed directly via an + HTTP request (e.g., PUT, COPY or MOVE). + +8.1. PUT + + Clients can use the If-Schedule-Tag-Match request header to do a PUT + request that ensures that "inconsequential" changes on the server do + not result in a precondition error. The value of the request header + is set to the last Schedule-Tag value received for the resource being + modified. If the value of the If-Schedule-Tag-Match header matches + the current value of the CALDAV:schedule-tag property the server MUST + take any "ATTENDEE" property changes for all Attendees other than the + owner of the scheduling object resource and apply those to the new + resource being stored. Otherwise, the server MUST fail the request + with a 412 Precondition Failed status code. + +8.2. DELETE, COPY or MOVE + + Clients can use the If-Schedule-Tag-Match request header to do a + DELETE, COPY or MOVE request that ensures that "inconsequential" + changes on the server do not result in a precondition error. The + value of the request header is set to the last Schedule-Tag value + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 42] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + received for the resource being deleted. If the value of the If- + Schedule-Tag-Match header matches the current value of the CALDAV: + schedule-tag property the server performs the normal DELETE, COPY or + MOVE request processing for the resource. Otherwise, the server MUST + fail the request with a 412 Precondition Failed status code. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 43] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +9. Other Scheduling Considerations + +9.1. Attendee Participation Status + + This section specifies additional requirements on the handling of the + "PARTSTAT" property parameter when the "SCHEDULE-AGENT" property + parameter on the corresponding "ATTENDEE" property is set to the + value "SERVER" or is not present. + + Clients SHOULD, and servers MUST reset the "PARTSTAT" property + parameter value of all "ATTENDEE" properties, except the one that + corresponds to the Organizer, to "NEEDS-ACTION" when the Organizer + reschedules an event. + + A reschedule of an event occurs when any "DTSTART", "DTEND", + "DURATION", "DUE", "RRULE", "RDATE", or "EXDATE" property changes in + a calendar component such that existing recurrence instances are + impacted by the changes, as shown in the table below. + + +-----------+-------------------------------------------------------+ + | Property | Server Action | + +-----------+-------------------------------------------------------+ + | DTSTART, | Any change to these properties MUST result in | + | DTEND, | "PARTSTAT" being set to "NEEDS-ACTION" | + | DURATION, | | + | DUE | | + | | | + | | | + | | | + | RRULE | A change to or addition of this property that results | + | | in the addition of new recurring instances or a | + | | change in time for existing recurring instances MUST | + | | result in "PARTSTAT" being reset to "NEEDS-ACTION" on | + | | each affected component. | + | | | + | | | + | | | + | RDATE | A change to or addition of this property that results | + | | in the addition of new recurring instances or a | + | | change in time for existing recurring instances MUST | + | | result in "PARTSTAT" being reset to "NEEDS-ACTION" on | + | | each affected component. | + | | | + | | | + | | | + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 44] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + | EXDATE | A change to or removal of this property that results | + | | in the re-instatement of recurring instances MUST | + | | result in "PARTSTAT" being set to "NEEDS-ACTION" on | + | | each affected component. | + +-----------+-------------------------------------------------------+ + + The server MAY allow the Organizer's client to change an Attendee's + "PARTSTAT" property parameter value to "NEEDS-ACTION" at any other + time (e.g., when the "LOCATION" property value changes, an Organizer + might wish to re-invite Attendees who may be impacted by the change). + +9.2. Schedule Status Values + + When scheduling with an Attendee there are two types of status + information that can be returned during the transaction. The first + type of status information is a "delivery" status that indicates + whether the scheduling message from the Organizer to the Attendee was + delivered or not, or what the current status of delivery is. The + second type of status information is a "reply" status corresponding + to the Attendee's own "REQUEST-STATUS" information from the + scheduling message reply that is sent back to the Organizer. + + Similarly, when an Attendee sends a reply back to the Organizer, + there will be "delivery" status information for the scheduling + message sent to the Organizer. However, there is no "REQUEST-STATUS" + sent back by the Organizer, so there is no equivalent of the "reply" + status as per scheduling messages to Attendees. + + The "delivery" status information on an "ORGANIZER" or "ATTENDEE" + iCalendar property is conveyed in the "SCHEDULE-STATUS" property + parameter value (Section 10.3). The status code value for "delivery" + status can be one of the following: + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 45] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + +----------+--------------------------------------------------------+ + | Delivery | Description | + | Status | | + | Code | | + +----------+--------------------------------------------------------+ + | 1.0 | The scheduling message is pending. i.e. the server is | + | | still in the process of sending the message. The | + | | status code value can be expected to change once the | + | | server has completed its sending and delivery | + | | attempts. | + | | | + | | | + | | | + | 1.1 | The scheduling message has been successfully sent. | + | | However, the server does not have explicit information | + | | about whether the scheduling message was successfully | + | | delivered to the recipient. This state can occur with | + | | "store and forward" style scheduling protocols such as | + | | iMIP [RFC6047] (iTIP using email). | + | | | + | | | + | | | + | 1.2 | The scheduling message has been successfully | + | | delivered. | + | | | + | | | + | | | + | 3.7 | The scheduling message was not delivered because the | + | | server did not recognize the calendar user address as | + | | a valid calendar user. Note that this code applies to | + | | both Organizer and Attendee calendar user addresses. | + | | | + | | | + | | | + | 3.8 | The scheduling message was not delivered due to | + | | insufficient privileges. Note that this code applies | + | | to both privileges granted by both the Organizer and | + | | Attendee calendar users. | + | | | + | | | + | | | + | 5.1 | The scheduling message was not delivered because the | + | | server could not complete delivery of the message. | + | | This is likely due to a temporary failure, and the | + | | originator can try to send the message again at a | + | | later time. | + | | | + | | | + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 46] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + | 5.2 | The scheduling message was not delivered because the | + | | server was not able to find a suitable way to deliver | + | | the message. This is likely a permanent failure, and | + | | the originator should not try to send the message | + | | again, at least without verifying/correcting the | + | | calendar user address of the recipient. | + | | | + | | | + | | | + | 5.3 | The scheduling message was not delivered and was | + | | rejected because scheduling with that recipient is not | + | | allowed. This is likely a permanent failure, and the | + | | originator should not try to send the message again. | + +----------+--------------------------------------------------------+ + + The status code for "reply" status can be any of the valid iTIP + [RFC5546] "REQUEST-STATUS" values. + + The 1.xx "REQUEST-STATUS" codes are new. This specification modifies + item (2) of Section 3.6 of [RFC5546] by adding the following + restriction: + + For a 1.xx code, all components MUST have exactly the same code. + + Definition of the new 1.xx codes is as follows: + +9.2.1. Status Code 1.0 + + Status Code: 1.0 + + Status Description: Pending. + + Status Exception Data: None. + + Description: Delivery of the iTIP message is pending. + +9.2.2. Status Code 1.1 + + Status Code: 1.1 + + Status Description: Sent. + + Status Exception Data: None. + + Description: The iTIP message has been sent, though no information + about successful delivery is known. + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 47] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +9.2.3. Status Code 1.2 + + Status Code: 1.2 + + Status Description: Delivered. + + Status Exception Data: None. + + Description: The iTIP message has been sent and delivered. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 48] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +10. Additional iCalendar Property Parameters + + This specification defines additional iCalendar property parameters + to support the CalDAV scheduling extensions. + +10.1. Schedule Agent Parameter + + Parameter Name: SCHEDULE-AGENT + + Purpose: To specify the agent expected to deliver scheduling + messages to the corresponding Organizer or Attendee. + + Format Definition: This property parameter is defined by the + following notation: + + scheduleagentparam = "SCHEDULE-AGENT" "=" + ("SERVER" ; The server handles scheduling + / "CLIENT" ; The client handles scheduling + / "NONE" ; No scheduling + / x-name ; Experimental type + / iana-token) ; Other IANA registered type + ; + ; Default is SERVER + + Description: This property parameter MAY be specified on "ORGANIZER" + or "ATTENDEE" iCalendar properties. In the absence of this + parameter, the value "SERVER" MUST be used for the default + behavior. The value determines whether or not an automatic + scheduling transaction on a server will cause a scheduling message + to be sent to the corresponding Calendar User identified by the + "ORGANIZER" or "ATTENDEE" property value. When the value "SERVER" + is specified, or the parameter is absent, then it is the server's + responsibility to send a scheduling message as part of an + automatic scheduling transaction. When the value "CLIENT" is + specified, that indicates that the client is handling scheduling + messages with the Calendar User itself. When "NONE" is specified, + no scheduling messages are being sent to the Calendar User. + + Servers MUST NOT include this parameter in any scheduling messages + sent as the result of an automatic scheduling transaction. + + Clients MUST NOT include this parameter in any scheduling messages + that they themselves send. + + The parameter value MUST be the same on every "ORGANIZER" property + in a scheduling object resource. + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 49] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + The parameter value MUST be the same on each "ATTENDEE" property + whose values match in a scheduling object resource. + + Servers and clients MUST treat x-name and iana-token values they + do not recognize the same way as they would the "NONE" value. + + Example: + + ORGANIZER;SCHEDULE-AGENT=SERVER:mailto:bernard@example.com + + ATTENDEE;SCHEDULE-AGENT=NONE:mailto:cyrus@example.com + +10.2. Schedule Force Send Parameter + + Parameter Name: SCHEDULE-FORCE-SEND + + Purpose: To force a scheduling message to be sent to the Calendar + User specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + scheduleforcesendparam = "SCHEDULE-FORCE-SEND" "=" + ("REQUEST" ; Force a "REQUEST" + / "REPLY" ; Force a "REPLY" + / iana-token) ; IANA registered method + + Description: This property parameter MAY be specified on "ATTENDEE" + and "ORGANIZER" properties on which the "SCHEDULE-AGENT" property + parameter is set to the value "SERVER" or is not specified. This + property parameter is used to force a server to send a scheduling + message to a specific Calendar User in situations where the server + would not send a scheduling message otherwise (e.g., when no + change that warrants the delivery of a new scheduling message was + performed on the scheduling object resource). An Organizer MAY + specify this parameter on an "ATTENDEE" property with the value + "REQUEST" to force a "REQUEST" scheduling message to be sent to + this Attendee. An Attendee MAY specify this parameter on the + "ORGANIZER" with the value "REPLY" to force a "REPLY" scheduling + message to be sent to the Organizer. + + Servers MUST NOT preserve this property parameter in scheduling + object resources, nor include it in any scheduling messages sent + as the result of an automatic scheduling transaction. + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 50] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Clients MUST NOT include this parameter in any scheduling messages + that they themselves send. + + Servers MUST set the "SCHEDULE-STATUS" parameter of the "ATTENDEE" + or "ORGANIZER" to 2.3 (i.e., "Success, invalid property parameter + ignored", see Section 3.6 of [RFC5546]) when the "SCHEDULE-FORCE- + SEND" parameter is set to a x-name or iana-token value they do not + recognize. + + Example: + + ATTENDEE;SCHEDULE-FORCE-SEND=REQUEST:mailto:bernard@example.com + + ORGANIZER;SCHEDULE-FORCE-SEND=REPLY:mailto:cyrus@example.com + +10.3. Schedule Status Parameter + + Parameter Name: SCHEDULE-STATUS + + Purpose: To specify the status codes returned from processing of the + most recent scheduling message sent to the corresponding Attendee, + or received from the corresponding Organizer. + + Format Definition: This property parameter is defined by the + following notation: + + schedulestatusparam = "SCHEDULE-STATUS" "=" + ( statcode + / DQUOTE statcode *("," statcode) DQUOTE) + ; "statcode" is defined in Section 3.8.8.3 of + ; [RFC5545]. Value is a single + ; "statcode" or a comma-separated list of "statcode" values. + + Description: This property parameter MAY be specified on the + "ATTENDEE" and "ORGANIZER" properties. + + Servers MUST add this property parameter to any "ATTENDEE" + properties corresponding to Calendar Users who were sent a + scheduling message via an automatic scheduling transaction. + Clients SHOULD NOT change or remove this parameter if it was + provided by the server. In the case where the client is handling + the scheduling, the client MAY add, change or remove this + parameter to indicate the last scheduling message status it + received. + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 51] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Servers MUST add this parameter to any "ORGANIZER" properties + corresponding to Calendar Users who were sent a scheduling message + reply by an Attendee via an automatic scheduling transaction. + Clients SHOULD NOT change or remove this parameter if it was + provided by the server. In the case where the client is handling + the scheduling, the client MAY add, change or remove this + parameter to indicate the last scheduling message status it + received. + + Servers MUST NOT include this parameter in any scheduling messages + sent as the result of an automatic scheduling transaction. + + Clients MUST NOT include this parameter in any scheduling messages + that they themselves send. + + Suitable values for this property parameter are described in + Section 9.2. + + Example: + + ATTENDEE;SCHEDULE-STATUS="2.0":mailto:bernard@example.com + + ATTENDEE;SCHEDULE-STATUS="2.0,2.4":mailto:cyrus@example.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 52] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +11. Additional Message Header Fields + + This specification defines additional HTTP request and response + headers for use with CalDAV. + +11.1. Schedule-Reply Request Header + + + Schedule-Reply = "Schedule-Reply" ":" ("T" | "F") + + Example: + + Schedule-Reply: F + + When an Attendee removes a scheduling object resource, and the + Schedule-Reply header is not present, or present and set to the value + "T", the server MUST send an appropriate reply scheduling message + with the Attendee's "PARTSTAT" iCalendar property parameter value set + to "DECLINED" as part of its normal automatic scheduling transaction + processing. + + When the Schedule-Reply header is set to the value "F", the server + MUST NOT send a scheduling message as part of its normal automatic + scheduling transaction processing. + + The Schedule-Reply request header is used by a client to indicate to + a server whether or not an automatic scheduling transaction should + occur when an Attendee deletes a scheduling object resource. In + particular it controls whether a reply scheduling message is sent to + the Organizer as a result of the removal. There are situations in + which unsolicited scheduling messages need to be silently removed (or + ignored) for security or privacy reasons. This request header allows + the scheduling object resource to be removed if such a need arises. + + All scheduling object resources MUST support the Schedule-Reply + request header. + +11.2. Schedule-Tag Response Header + + The Schedule-Tag response header provides the current value of the + CALDAV:schedule-tag property value. The behavior of this response + header is described in Section 8. + + All scheduling object resources MUST support the Schedule-Tag header. + + Schedule-Tag = "Schedule-Tag" ":" opaque-tag + ; "opaque-tag" is defined in Section 3.11 of [RFC2616] + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 53] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Example: + + Schedule-Tag: "12ab34-cd56ef" + +11.3. If-Schedule-Tag-Match Request Header + + The If-Schedule-Tag-Match request header field is used with a method + to make it conditional. Clients can set this header to the value + returned in the Schedule-Tag response header, or the CALDAV:schedule- + tag property, of a scheduling object resource previously retrieved + from the server to avoid overwriting "consequential" changes to the + scheduling object resource. + + All scheduling object resources MUST support the If-Schedule-Tag- + Match header. + + If-Schedule-Tag-Match = "If-Schedule-Tag-Match" ":" opaque-tag + ; "opaque-tag" is defined in Section 3.11 of [RFC2616] + + Example: + + If-Schedule-Tag-Match: "12ab34-cd56ef" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 54] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +12. Additional WebDAV Properties + + This specification defines the following new WebDAV properties for + use with CalDAV. + +12.1. CALDAV:schedule-calendar-transp Property + + Name: schedule-calendar-transp + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Determines whether the calendar object resources in a + calendar collection will affect the owner's freebusy. + + Protected: This property MAY be protected and SHOULD NOT be returned + by a PROPFIND allprop request (as defined in Section 14.2 of + [RFC4918]). + + COPY/MOVE behavior: This property value SHOULD be kept during a MOVE + operation, and SHOULD be copied and preserved in a COPY. + + Description: This property SHOULD be defined on all calendar + collections. If present, it contains one of two XML elements that + indicate whether the calendar object resources in the calendar + collection should contribute to the owner's freebusy or not. When + the CALDAV:opaque element is used, all calendar object resources + in the corresponding calendar collection MUST contribute to + freebusy, assuming access privileges and other iCalendar + properties allow it to. When the CALDAV:transparent XML element + is used, the calendar object resources in the corresponding + calendar collection MUST NOT contribute to freebusy. + + If this property is not present on a calendar collection, then the + default value CALDAV:opaque MUST be assumed. + + Definition: + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 55] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Example: + + + + + +12.2. CALDAV:schedule-default-calendar-URL Property + + Name: schedule-default-calendar-URL + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a default calendar for an Attendee where new + scheduling object resources are created. + + Protected: This property MAY be protected in the case where a server + does not support changing the default calendar, or does not + support a default calendar. + + COPY/MOVE behavior: This property is only defined on a scheduling + Inbox collection which cannot be moved or copied. + + Description: This property MAY be defined on a scheduling Inbox + collection. If present, it contains zero or one DAV:href XML + elements. When a DAV:href element is present, its value indicates + a URL to a calendar collection that is used as the default + calendar. When no DAV:href element is present, it indicates that + there is no default calendar. In the absence of this property + there is no default calendar. When there is no default calendar + the server is free to choose the calendar in which a new + scheduling object resource is created. See Section 6.4. + + Definition: + + + + Example: + + + /home/cyrus/calendars/work/ + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 56] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +12.3. CALDAV:schedule-tag Property + + Name: schedule-tag + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Indicates whether a scheduling object resource has had a + "consequential" change made to it. + + Value: opaque-tag (defined in Section 3.11 of [RFC2616]) + + Protected: This property MUST be protected as only the server can + update the value. + + COPY/MOVE behavior: This property is only defined on scheduling + object resources. It MUST be preserved when a scheduling object + resource is copied or moved and the resulting resource is also a + scheduling object resource. If the source resource is not a + scheduling object resource but the destination resource is, this + property MUST be added to the destination resource. + + Description: The CALDAV:schedule-tag property MUST be defined on all + scheduling object resources. This property is described in + Section 8. + + Definition: + + + + Example: + + "12345-67890" + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 57] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +13. Scheduling Access Control + +13.1. Scheduling Privileges + + CalDAV servers MUST support and adhere to the requirements of WebDAV + ACL [RFC3744]. Furthermore, CalDAV servers that advertise support + for the "calendar-auto-schedule" feature MUST also support the + scheduling privileges defined in this section. + + All the scheduling privileges MUST be non-abstract and MUST appear in + the DAV:supported-privilege-set property of scheduling Outbox and + Inbox collections on which they are defined. + + The tables specified in Appendix A clarify which scheduling methods + (e.g., "REQUEST", "REPLY", etc.) are controlled by each scheduling + privilege defined in this section. + +13.1.1. Privileges on Scheduling Inbox Collections + + This section defines new WebDAV ACL privileges that are for use on + scheduling Inbox collections. These privileges determine whether + delivery of scheduling messages from a calendar user is allowed by + the calendar user who "owns" the scheduling Inbox collection. This + allows calendar users to choose which other calendar users can + schedule with them. + + Note that when a scheduling message is delivered to a calendar user, + in addition to a scheduling object resource being created in the + calendar user's scheduling Inbox collection, a new scheduling object + resource might be created or an existing one updated in a calendar + belonging to the calendar user. In that case, the ability to create + or update the scheduling object resource in the calendar is + controlled by the privileges assigned to the scheduling Inbox + collection. + + The privileges defined in this section are ignored if applied to a + resource other than a scheduling Inbox collection. + +13.1.1.1. CALDAV:schedule-deliver Privilege + + CALDAV:schedule-deliver is an aggregate privilege that contains all + the scheduling privileges that control the processing and delivery of + incoming scheduling messages, that is, CALDAV:schedule-deliver-invite + and CALDAV:schedule-deliver-reply, as well as freebusy requests + targeted at the owner of the scheduling Inbox collection, that is, + CALDAV:schedule-query-freebusy. + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 58] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +13.1.1.2. CALDAV:schedule-deliver-invite Privilege + + The CALDAV:schedule-deliver-invite privilege controls the processing + and delivery of scheduling messages coming from an Organizer. + + + +13.1.1.3. CALDAV:schedule-deliver-reply Privilege + + The CALDAV:schedule-deliver-reply privilege controls the processing + and delivery of scheduling messages coming from an Attendee. + + + +13.1.1.4. CALDAV:schedule-query-freebusy Privilege + + The CALDAV:schedule-query-freebusy privilege controls freebusy + requests targeted at the owner of the scheduling Inbox collection. + + + +13.1.2. Privileges on Scheduling Outbox Collections + + This section defines new WebDAV ACL privileges that are defined for + use on scheduling Outbox collections. These privileges determine + which calendar users are allowed to send scheduling messages on + behalf of the calendar user who "owns" the scheduling Outbox + collection. This allows calendar users to choose other calendar + users who can act on their behalf to send schedule messages to other + calendar users (e.g. assistants working on behalf of their boss). + + The privileges defined in this section are ignored if applied to a + resource other than a scheduling Outbox collection. + +13.1.2.1. CALDAV:schedule-send Privilege + + CALDAV:schedule-send is an aggregate privilege that contains all the + scheduling privileges that control the use of methods that will cause + scheduling messages to be delivered to other users, that is, CALDAV: + schedule-send-invite and CALDAV:schedule-send-reply, as well as + freebusy requests to be targeted at other users, that is, CALDAV: + schedule-send-freebusy. + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 59] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +13.1.2.2. CALDAV:schedule-send-invite Privilege + + The CALDAV:schedule-send-invite privilege controls the sending of + scheduling messages by Organizers. + + Users granted the DAV:bind privilege on a calendar collection, or + DAV:write privilege on scheduling object resources, will also need + the CALDAV:schedule-send-invite privilege granted on the scheduling + Outbox collection of the owner of the calendar collection or + scheduling object resource in order to be allowed to create, modify + or delete scheduling object resources in a way that will trigger the + CalDAV server to deliver organizer scheduling messages to other + calendar users. + + + +13.1.2.3. CALDAV:schedule-send-reply Privilege + + The CALDAV:schedule-send-reply privilege controls the sending of + scheduling messages by Attendees. + + Users granted the DAV:bind privilege on a calendar collection, or + DAV:write privilege on scheduling object resources, will also need + the CALDAV:schedule-send-reply privilege granted on the scheduling + Outbox collection of the owner of the calendar collection or + scheduling object resource in order to be allowed to create, modify + or delete scheduling object resources in a way that will trigger the + CalDAV server to deliver attendee scheduling messages to other + calendar users. + + + +13.1.2.4. CALDAV:schedule-send-freebusy Privilege + + The CALDAV:schedule-send-freebusy privilege controls the use of the + POST method to submit scheduling messages that specify the scheduling + method "REQUEST" with a "VFREEBUSY" calendar component. + + + +13.1.3. Aggregation of Scheduling Privileges + + Server implementations MUST aggregate the scheduling privileges as + follows: + + DAV:all MUST contain CALDAV:schedule-send and CALDAV:schedule- + deliver; + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 60] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + CALDAV:schedule-send MUST contain CALDAV:schedule-send-invite, + CALDAV:schedule-send-reply, and CALDAV:schedule-send-freebusy; + + CALDAV:schedule-deliver MUST contain CALDAV:schedule-deliver- + invite, CALDAV:schedule-deliver-reply, and CALDAV:schedule-query- + freebusy. + + The following diagram illustrates how scheduling privileges are + aggregated according to the above requirements. + + [DAV:all] (aggregate) + | + +-- [CALDAV:schedule-deliver] (aggregate) + | | + | +-- [CALDAV:schedule-deliver-invite] + | +-- [CALDAV:schedule-deliver-reply] + | +-- [CALDAV:schedule-query-freebusy] + | + +-- [CALDAV:schedule-send] (aggregate) + | + +-- [CALDAV:schedule-send-invite] + +-- [CALDAV:schedule-send-reply] + +-- [CALDAV:schedule-send-freebusy] + +13.2. Additional Principal Properties + + This section defines new properties for WebDAV principal resources as + defined in [RFC3744]. These properties are likely to be protected + but the server MAY allow them to be written by appropriate users. + +13.2.1. CALDAV:schedule-inbox-URL Property + + Name: schedule-inbox-URL + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identify the URL of the scheduling Inbox collection owned + by the associated principal resource. + + Protected: This property MAY be protected. + + PROPFIND behavior: This property SHOULD NOT be returned by a + PROPFIND allprop request (as defined in Section 14.2 of + [RFC4918]). + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 61] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + Description: This property is needed for a client to determine where + the scheduling Inbox collection of the current user is located so + that processing of scheduling messages can occur. If not present, + then the associated calendar user is not enabled for reception of + scheduling messages on the server. + + Definition: + + + +13.2.2. CALDAV:schedule-outbox-URL Property + + Name: schedule-outbox-URL + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identify the URL of the scheduling Outbox collection owned + by the associated principal resource. + + Protected: This property MAY be protected. + + PROPFIND behavior: This property SHOULD NOT be returned by a + PROPFIND allprop request (as defined in Section 14.2 of + [RFC4918]). + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + Description: This property is needed for a client to determine where + the scheduling Outbox collection of the current user is located so + that sending of scheduling messages can occur. If not present, + then the associated calendar user is not enabled for the sending + of scheduling messages on the server. + + Definition: + + + +13.2.3. CALDAV:calendar-user-address-set Property + + Name: calendar-user-address-set + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 62] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identify the calendar addresses of the associated principal + resource. + + Protected: This property MAY be protected. + + PROPFIND behavior: This property SHOULD NOT be returned by a + PROPFIND allprop request (as defined in Section 14.2 of + [RFC4918]). + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + Description: Support for this property is REQUIRED. This property + is needed to map calendar user addresses in iCalendar data to + principal resources and their associated scheduling Inbox and + Outbox collections. In the event that a user has no well defined + identifier for their calendar user address, the URI of their + principal resource can be used. This property SHOULD be + searchable using the DAV:principal-property-search REPORT. The + DAV:principal-search-property-set REPORT SHOULD identify this + property as such. If not present, then the associated calendar + user is not enabled for scheduling on the server. + + Definition: + + + + Example: + + + mailto:bernard@example.com + mailto:bernard.desruisseaux@example.com + + +13.2.4. CALDAV:calendar-user-type Property + + Name: calendar-user-type + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identifies the calendar user type of the associated + principal resource. + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 63] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Value: Same values allowed for the iCalendar "CUTYPE" property + parameter defined in Section 3.2.3 of [RFC5545]. + + Protected: This property MAY be protected. + + PROPFIND behavior: This property SHOULD NOT be returned by a + PROPFIND allprop request (as defined in Section 14.2 of + [RFC4918]). + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + Description: Clients can query principal resources in order to + lookup attendees available on the server. When doing this, it is + useful to know, or restrict the query to, certain types of + calendar user (e.g., only search for "people", or only search for + "rooms"). This property MAY be defined on principal resources to + indicate the type of calendar user associated with the principal + resource. Its value is the same as the iCalendar "CUTYPE" + property parameter that can be used on "ATTENDEE" properties. + This property SHOULD be searchable using the DAV:principal- + property-search REPORT. The DAV:principal-search-property-set + REPORT SHOULD identify this property as such. + + Definition: + + + + Example: + + INDIVIDUAL< + /C:calendar-user-type> + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 64] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +14. XML Element Definitions + +14.1. CALDAV:schedule-response XML Element + + Name: schedule-response + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Contains the set of responses for a POST method request. + + Description: See Section 7.3. + + Definition: + + + +14.2. CALDAV:response XML Element + + Name: response + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Contains a single response for a POST method request. + + Description: See Section 7.3. + + Definition: + + + + + +14.3. CALDAV:recipient XML Element + + Name: recipient + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: The calendar user address that the enclosing response for a + POST method request is for. + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 65] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Description: See Section 7.3. + + Definition: + + + +14.4. CALDAV:request-status XML Element + + Name: request-status + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: The iTIP "REQUEST-STATUS" property value for this response. + + Description: See Section 7.3. + + Definition: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 66] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +15. Security Considerations + + The process of scheduling involves the sending and receiving of + scheduling messages. As a result, the security problems related to + messaging in general are relevant here. In particular the + authenticity of the scheduling messages needs to be verified. + Servers and clients MUST use an HTTP connection protected with TLS as + defined in [RFC2818] for all scheduling transactions. + +15.1. Verifying Scheduling Transactions + + When handling a scheduling transaction: + + Servers MUST verify that the principal associated with the DAV: + owner of the calendar collection in which a scheduling object + resource is being manipulated contains a CALDAV:schedule-outbox- + URL property value. + + Servers MUST verify that the currently authenticated user has the + CALDAV:schedule-send privilege, or a suitable sub-privilege + aggregated under this privilege, on the scheduling Outbox + collection of the DAV:owner of the calendar collection in which a + scheduling object resource is being manipulated. + + Servers MUST only deliver scheduling messages to recipients when + the CALDAV:schedule-deliver privilege, or a suitable sub-privilege + aggregated under this privilege, is granted on the recipient's + scheduling Inbox collection for the principal associated with the + DAV:owner of the calendar collection in which a scheduling object + resource is being manipulated. + + To prevent impersonation of calendar users, the server MUST verify + that the "ORGANIZER" property in an organizer scheduling object + resource matches one of the calendar user addresses of the DAV: + owner of the calendar collection in which the resource is stored. + + To prevent spoofing of an existing scheduling object resource, + servers MUST verify that the "UID" iCalendar property value in a + new scheduling object resource does not match that of an existing + scheduling object resource with a different "ORGANIZER" property + value. + +15.2. Verifying Busy Time Information Requests + + When handling a POST request on a scheduling Outbox collection: + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 67] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Servers MUST verify that the principal associated with the + calendar user address specified in the "ORGANIZER" property of the + scheduling message data in the request contains a CALDAV:schedule- + outbox-URL property value that matches the scheduling Outbox + collection targeted by the request. + + Servers MUST verify that the currently authenticated user has the + CALDAV:schedule-send privilege, or a sub-privilege aggregated + under this privilege, on the scheduling Outbox collection targeted + by the request. + + Servers MUST only return valid freebusy information for recipients + when the CALDAV:schedule-deliver privilege, or a sub-privilege + aggregated under this privilege, is granted on the recipient's + scheduling Inbox collection for the principal associated with the + DAV:owner of the scheduling Outbox collection targeted by the + request. + +15.3. Privacy Issues + + As noted in Section 11.1, Attendees can use the Schedule-Reply + request header with the value set to "F" to prevent notification to + an Organizer that a scheduling object resource was deleted. This + allows Attendees to remove unwanted scheduling messages without any + response to the Organizer. + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 68] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +16. IANA Considerations + +16.1. Message Header Field Registrations + + The message header fields below should be added to the Permanent + Message Header Field Registry (see [RFC3864]). + +16.1.1. Schedule-Reply + + Header field name: Schedule-Reply + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document(s): this specification (Section 11.1) + + Related information: none + +16.1.2. Schedule-Tag + + Header field name: Schedule-Tag + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document(s): this specification (Section 11.2) + + Related information: none + +16.1.3. If-Schedule-Tag-Match + + Header field name: If-Schedule-Tag-Match + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document(s): this specification (Section 11.3) + + Related information: none + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 69] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +16.2. iCalendar Property Parameter Registrations + + The following iCalendar property parameters should be added to the + iCalendar Property Parameter Registry defined in Section 8.3.3 of + [RFC5545]. + + +---------------------+---------+-----------------------+ + | Parameter | Status | Reference | + +---------------------+---------+-----------------------+ + | SCHEDULE-AGENT | Current | RFCXXXX, Section 10.1 | + | | | | + | SCHEDULE-STATUS | Current | RFCXXXX, Section 10.3 | + | | | | + | SCHEDULE-FORCE-SEND | Current | RFCXXXX, Section 10.2 | + +---------------------+---------+-----------------------+ + +16.3. iCalendar REQUEST-STATUS Value Registrations + + The following iCalendar "REQUEST-STATUS" values should be added to + the iCalendar REQUEST-STATUS Value Registry defined in Section 7.3 of + [RFC5546]. + + +-------------+---------+-------------------------+ + | Status Code | Status | Reference | + +-------------+---------+-------------------------+ + | 1.0 | Current | RFC XXXX, Section 9.2.1 | + | | | | + | 1.1 | Current | RFC XXXX, Section 9.2.2 | + | | | | + | 1.2 | Current | RFC XXXX, Section 9.2.3 | + +-------------+---------+-------------------------+ + +16.4. Additional iCalendar Elements Registries + + This specification adds two new IANA registries for iCalendar + elements. Additional codes MAY be used, provided the process + described in Section 8.2.1 of [RFC5545] is used to register them. + +16.4.1. Schedule Agent Values Registry + + The following table has been used to initialize the schedule agent + values registry. + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 70] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + +----------------+---------+------------------------+ + | Schedule Agent | Status | Reference | + +----------------+---------+------------------------+ + | SERVER | Current | RFC XXXX, Section 10.1 | + | | | | + | CLIENT | Current | RFC XXXX, Section 10.1 | + | | | | + | NONE | Current | RFC XXXX, Section 10.1 | + +----------------+---------+------------------------+ + +16.4.2. Schedule Force Send Values Registry + + The following table has been used to initialize the schedule send + values registry. + + +---------------------+---------+------------------------+ + | Schedule Force Send | Status | Reference | + +---------------------+---------+------------------------+ + | REQUEST | Current | RFC XXXX, Section 10.2 | + | | | | + | REPLY | Current | RFC XXXX, Section 10.2 | + +---------------------+---------+------------------------+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 71] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +17. Acknowledgements + + The authors would like to thank the following individuals for + contributing their ideas and support for writing this specification: + Mike Douglass, Lisa Dusseault, Helge Hess, Arnaud Quillaud, Julian F. + Reschke, Wilfredo Sanchez Vega, Simon Vaillancourt, and Jim + Whitehead. + + The authors would also like to thank the Calendaring and Scheduling + Consortium for advice with this specification, and for organizing + interoperability testing events to help refine it. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 72] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +18. References + +18.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, + RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, + H., Masinter, L., Leach, P., and T. Berners- + Lee, "Hypertext Transfer Protocol -- + HTTP/1.1", RFC 2616, June 1999. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, + May 2000. + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. + Whitehead, "Web Distributed Authoring and + Versioning (WebDAV) Access Control Protocol", + RFC 3744, May 2004. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, + "Registration Procedures for Message Header + Fields", BCP 90, RFC 3864, September 2004. + + [RFC4791] Daboo, C., Desruisseaux, B., and L. + Dusseault, "Calendaring Extensions to WebDAV + (CalDAV)", RFC 4791, March 2007. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web + Distributed Authoring and Versioning + (WebDAV)", RFC 4918, June 2007. + + [RFC5234] Crocker, D. and P. Overell, "Augmented BNF + for Syntax Specifications: ABNF", STD 68, + RFC 5234, January 2008. + + [RFC5545] Desruisseaux, B., "Internet Calendaring and + Scheduling Core Object Specification + (iCalendar)", RFC 5545, September 2009. + + [RFC5546] Daboo, C., "iCalendar Transport-Independent + Interoperability Protocol (iTIP)", RFC 5546, + December 2009. + + [W3C.REC-xml-20081126] Paoli, J., Yergeau, F., Bray, T., Sperberg- + McQueen, C., and E. Maler, "Extensible Markup + Language (XML) 1.0 (Fifth Edition)", World + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 73] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + Wide Web Consortium Recommendation REC-xml- + 20081126, November 2008, + . + +18.2. Informative References + + [RFC3283] Mahoney, B., Babics, G., and A. Taler, "Guide + to Internet Calendaring", RFC 3283, + June 2002. + + [RFC6047] Melnikov, A., "iCalendar Message-Based + Interoperability Protocol (iMIP)", RFC 6047, + December 2010. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 74] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +Appendix A. Scheduling Privileges Summary + +A.1. Scheduling Inbox Privileges + + The following tables specify which scheduling privileges grant the + right to a calendar user to deliver a scheduling message to the + scheduling Inbox collection of another calendar user. The + appropriate behavior depends on the calendar component type as well + as the scheduling "METHOD" specified in the scheduling message. + + +--------------------------------+ + | METHOD for VEVENT and VTODO | + +-----------------------------+---------+-------+-----+--------+ + | Scheduling Inbox Privilege | REQUEST | REPLY | ADD | CANCEL | + +-----------------------------+---------+-------+-----+--------+ + | schedule-deliver | * | * | * | * | + | schedule-deliver-invite | * | | * | * | + | schedule-deliver-reply | | * | | | + | schedule-query-freebusy | | | | | + +-----------------------------+---------+-------+-----+--------+ + + + +----------------------+ + | METHOD for VFREEBUSY | + +-----------------------------+----------------------+ + | Scheduling Inbox Privilege | REQUEST | + +-----------------------------+----------------------+ + | schedule-deliver | * | + | schedule-deliver-invite | | + | schedule-deliver-reply | | + | schedule-query-freebusy | * | + +-----------------------------+----------------------+ + +A.2. Scheduling Outbox Privileges + + The following tables specify which scheduling privileges grant the + right to a Calendar User to perform busy time information requests + and to submit scheduling messages to other Calendar Users as the + result of a scheduling transaction. The appropriate behavior depends + on the calendar component type as well as the scheduling "METHOD" + specified in the scheduling message. + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 75] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + +--------------------------------+ + | METHOD for VEVENT and VTODO | + +-----------------------------+---------+-------+-----+--------+ + | Scheduling Outbox Privilege | REQUEST | REPLY | ADD | CANCEL | + +-----------------------------+---------+-------+-----+--------+ + | schedule-send | * | * | * | * | + | schedule-send-invite | * | | * | * | + | schedule-send-reply | | * | | | + | schedule-send-freebusy | | | | | + +-----------------------------+---------+-------+-----+--------+ + + + +----------------------+ + | METHOD for VFREEBUSY | + +-----------------------------+----------------------+ + | Scheduling Outbox Privilege | REQUEST | + +-----------------------------+----------------------+ + | schedule-send | * | + | schedule-send-invite | | + | schedule-send-reply | | + | schedule-send-freebusy | * | + +-----------------------------+----------------------+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 76] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +Appendix B. Example Scheduling Transactions + + This section describes some example scheduling transactions that give + a general idea of how scheduling is carried out between CalDAV + clients and servers from the perspective of meeting Organizers and + Attendees. + + In the following examples the requests and responses are incomplete + and are only for illustrative purposes. In particular, HTTP + authentication headers and behaviors are not shown, even though they + are required in normal operation. + +B.1. Example: Organizer Inviting Multiple Attendees + + In the following example, Cyrus invites Wilfredo, Bernard and Mike to + a single instance event by simply creating a new scheduling object + resource in one of his calendar collection by using the PUT method. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 77] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Request << + + PUT /home/cyrus/calendars/work/9263504FD3AD.ics HTTP/1.1 + Host: cal.example.com + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + If-None-Match: * + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T185254Z + DTSTART:20090602T160000Z + DTEND:20090602T170000Z + TRANSP:OPAQUE + SUMMARY:Lunch + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT + =NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:wilfredo@ + example.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@ex + ample.net + ATTENDEE;CN="Mike Douglass";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-A + CTION;RSVP=TRUE:mailto:mike@example.org + END:VEVENT + END:VCALENDAR + + >> Response << + + HTTP/1.1 201 Created + Content-Length: 0 + Date: Tue, 02 Jun 2009 18:52:54 GMT + Last-Modified: Tue, 02 Jun 2009 18:52:54 GMT + ETag: "d85561cfe74a4e785eb4639451b434fb" + Schedule-Tag: "488177c8-2ea7-4176-a6cb-fab8cfccdea2" + + Once the event creation has been completed, Cyrus's client will + retrieve the event back from the server to get the schedule status of + each Attendee. In this example, the server reports that a scheduling + message was delivered to Wilfredo, a scheduling message is still + pending for Bernard, and the server was unable to deliver a + scheduling message to Mike. + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 78] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Request << + + GET /home/cyrus/calendars/work/9263504FD3AD.ics HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 18:52:58 GMT + Last-Modified: Tue, 02 Jun 2009 18:52:58 GMT + ETag: "eb897deabc8939589da116714bc99265" + Schedule-Tag: "488177c8-2ea7-4176-a6cb-fab8cfccdea2" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T185300Z + DTSTART:20090602T160000Z + DTEND:20090602T170000Z + TRANSP:OPAQUE + SUMMARY:Lunch + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT + =NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;SCHEDULE-STATUS= + 1.2:mailto:wilfredo@e + xample.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;SCHEDULE-STATUS= + 1.0:mailto:bernard@example.net + ATTENDEE;CN="Mike Douglass";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-A + CTION;RSVP=TRUE;SCHEDULE-STATUS=3.7:mailto:mike@example.org + END:VEVENT + END:VCALENDAR + +B.2. Example: Attendee Receiving an Invitation + + In the following example, Wilfredo's client retrieves and deletes the + new scheduling message that appeared in his scheduling Inbox + collection after the server automatically processed it and created a + new scheduling object resource in his default calendar collection. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 79] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Request << + + GET /home/wilfredo/calendars/inbox/27d93fc0a58c.ics HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 18:59:58 GMT + Last-Modified: Tue, 02 Jun 2009 18:59:58 GMT + ETag: "da116714bc9926c89395895eb897deab" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + METHOD:REQUEST + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T185254Z + DTSTART:20090602T160000Z + DTEND:20090602T170000Z + TRANSP:OPAQUE + SUMMARY:Lunch + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT + =NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:wilfredo@ + example.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@ex + ample.net + ATTENDEE;CN="Mike Douglass";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-A + CTION;RSVP=TRUE:mailto:mike@example.org + END:VEVENT + END:VCALENDAR + + >> Request << + + DELETE /home/wilfredo/calendars/inbox/27d93fc0a58c.ics HTTP/1.1 + Host: cal.example.com + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 80] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Response << + + HTTP/1.1 204 No Content + Date: Tue, 02 Jun 2009 20:40:36 GMT + +B.3. Example: Attendee Replying to an Invitation + + In the following example, Wilfredo's accepts Cyrus's invitation and + sets a reminder on the event. + + >> Request << + + PUT /home/wilfredo/calendars/work/BB64861C2228.ics HTTP/1.1 + Host: cal.example.com + If-Schedule-Tag-Match: "e78f23ed-0188-4bab-938d-2aeb3324c7e8" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T185254Z + DTSTART:20090602T160000Z + DTEND:20090602T170000Z + TRANSP:OPAQUE + SUMMARY:Lunch + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT + =ACCEPTED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:wilfredo@exam + ple.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@ex + ample.net + ATTENDEE;CN="Mike Douglass";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-A + CTION;RSVP=TRUE:mailto:mike@example.org + BEGIN:VALARM + TRIGGER:-PT15M + ACTION:DISPLAY + DESCRIPTION:Reminder + END:VALARM + END:VEVENT + END:VCALENDAR + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 81] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Response << + + HTTP/1.1 200 OK + Content-Length: 0 + Date: Tue, 02 Jun 2009 18:57:54 GMT + Last-Modified: Tue, 02 Jun 2009 18:57:54 GMT + ETag: "eb4639451b434fbd85561cfe74a4e785" + Schedule-Tag: "8893ee45-eb9d-428f-b53c-c777daf19e41" + + Once the event modification has been completed, Wilfredo's client + will retrieve the event back from the server to get the schedule + status of the Organizer. + + >> Request << + + GET /home/wilfredo/calendars/work/BB64861C2228.ics HTTP/1.1 + Host: cal.example.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 82] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 19:03:03 GMT + Last-Modified: Tue, 02 Jun 2009 19:02:21 GMT + ETag: "5eb897deabda116714bc9926c8939589" + Schedule-Tag: "8893ee45-eb9d-428f-b53c-c777daf19e41" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T190221Z + DTSTART:20090602T160000Z + DTEND:20090602T170000Z + TRANSP:OPAQUE + SUMMARY:Lunch + ORGANIZER;CN="Cyrus Daboo";SCHEDULE-STATUS=1.2:mailto:cyrus@ex + ample.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT + =ACCEPTED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:wilfredo@exam + ple.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@ex + ample.net + ATTENDEE;CN="Mike Douglass";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-A + CTION;RSVP=TRUE:mailto:mike@example.org + BEGIN:VALARM + TRIGGER:-PT15M + ACTION:DISPLAY + DESCRIPTION:Reminder + END:VALARM + END:VEVENT + END:VCALENDAR + +B.4. Example: Organizer Receiving a Reply to an Invitation + + On reception of Wilfredo's reply, Cyrus's server will automatically + update Cyrus's scheduling object resource, make Wilfredo's scheduling + message available in Cyrus's scheduling Inbox collection, and deliver + an updated scheduling message to Bernard to share Wilfredo's updated + participation status. In this example, Cyrus's client retrieves and + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 83] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + deletes this scheduling message in his scheduling Inbox collection. + + >> Request << + + GET /home/cyrus/calendars/inbox/c0a58c27d93f.ics HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 19:05:02 GMT + Last-Modified: Tue, 02 Jun 2009 19:04:20 GMT + ETag: "9265eb897deabc8939589da116714bc9" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + METHOD:REPLY + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T185754Z + DTSTART:20090602T160000Z + DTEND:20090602T170000Z + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";PARTSTAT=ACCEPTED:mailto:w + ilfredo@example.com + REQUEST-STATUS:2.0;Success + END:VEVENT + END:VCALENDAR + + >> Request << + + DELETE /home/cyrus/calendars/inbox/c0a58c27d93f.ics HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 204 No Content + Date: Tue, 02 Jun 2009 19:05:05 GMT + + Cyrus's client then retrieves the event back from the server with + Wilfredo's updated participation status. + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 84] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Request << + + GET /home/cyrus/calendars/work/9263504FD3AD.ics HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 19:05:02 GMT + Last-Modified: Tue, 02 Jun 2009 19:04:20 GMT + ETag: "eb897deabc8939589da116714bc99265" + Schedule-Tag: "132cab27-1fe3-67ab-de13-abd348d1dee3" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T190420Z + DTSTART:20090602T160000Z + DTEND:20090602T170000Z + TRANSP:OPAQUE + SUMMARY:Lunch + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT + =ACCEPTED;ROLE=REQ-PARTICIPANT;RSVP=TRUE;SCHEDULE-STATUS=2.0: + mailto:wilfredo@example.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;SCHEDULE-STATUS=1 + .0:mailto:bernard@example.net + ATTENDEE;CN="Mike Douglass";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-A + CTION;RSVP=TRUE;SCHEDULE-STATUS=3.7:mailto:mike@example.org + END:VEVENT + END:VCALENDAR + +B.5. Example: Organizer Requesting Busy Time Information + + In this example, Cyrus requests the busy time information of + Wilfredo, Bernard and Mike. + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 85] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Request << + + POST /home/cyrus/calendars/outbox/ HTTP/1.1 + Host: cal.example.com + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + METHOD:REQUEST + BEGIN:VFREEBUSY + UID:4FD3AD926350 + DTSTAMP:20090602T190420Z + DTSTART:20090602T000000Z + DTEND:20090604T000000Z + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega":mailto:wilfredo@example.com + ATTENDEE;CN="Bernard Desruisseaux":mailto:bernard@example.net + ATTENDEE;CN="Mike Douglass":mailto:mike@example.org + END:VFREEBUSY + END:VCALENDAR + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 20:07:34 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + mailto:wilfredo@example.com + + 2.0;Success + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + METHOD:REPLY + BEGIN:VFREEBUSY + UID:4FD3AD926350 + DTSTAMP:20090602T200733Z + DTSTART:20090602T000000Z + DTEND:20090604T000000Z + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 86] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + ATTENDEE;CN="Wilfredo Sanchez Vega":mailto:wilfredo@example.com + FREEBUSY;FBTYPE=BUSY:20090602T110000Z/20090602T120000Z + FREEBUSY;FBTYPE=BUSY:20090603T170000Z/20090603T180000Z + END:VFREEBUSY + END:VCALENDAR + + + + + mailto:bernard@example.net + + 2.0;Success + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + METHOD:REPLY + BEGIN:VFREEBUSY + UID:4FD3AD926350 + DTSTAMP:20090602T200733Z + DTSTART:20090602T000000Z + DTEND:20090604T000000Z + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Bernard Desruisseaux":mailto:bernard@example.net + FREEBUSY;FBTYPE=BUSY:20090602T150000Z/20090602T160000Z + FREEBUSY;FBTYPE=BUSY:20090603T090000Z/20090603T100000Z + FREEBUSY;FBTYPE=BUSY:20090603T180000Z/20090603T190000Z + END:VFREEBUSY + END:VCALENDAR + + + + + mailto:mike@example.org + + 3.7;Invalid calendar user + + + +B.6. Example: User Attempting to Invite Attendee on behalf of Organizer + + In the following example, Cyrus attempts to create, on behalf of + Wilfredo, an event with Bernard specified as an Attendee. The + request fails since Wilfredo didn't grant Cyrus the right to invite + other Calendar Users on his behalf. + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 87] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Request << + + PUT /home/wilfredo/calendars/work/def456.ics HTTP/1.1 + Host: cal.example.com + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + If-None-Match: * + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + UID:3504F926D3AD + SEQUENCE:0 + DTSTAMP:20090602T190221Z + DTSTART:20090602T230000Z + DTEND:20090603T000000Z + TRANSP:OPAQUE + SUMMARY:Dinner + ORGANIZER;CN="Wilfredo Sanchez Vega":mailto:wilfredo@example.com + ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT=A + CCEPTED:mailto:wilfredo@example.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT=NE + EDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@exampl + e.net + END:VEVENT + END:VCALENDAR + + >> Response << + + HTTP/1.1 403 Forbidden + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/wilfredo/calendars/outbox/ + + + + + +B.7. Example: Attendee Declining an Instance of a Recurring Event + + In the following example, Bernard declines the second recurrence + instance of a daily recurring event he's been invited to by Cyrus. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 88] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Request << + + PUT /home/bernard/calendars/work/4FD3AD926350.ics HTTP/1.1 + Host: cal.example.com + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + If-Schedule-Tag-Match: "7775FB30-7534-489E-A79A-0EA147B933EB" + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + TZID:America/Montreal + BEGIN:STANDARD + DTSTART:20071104T020000 + RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:20070311T020000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + END:VTIMEZONE + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T185254Z + DTSTART;TZID=America/Montreal:20090601T150000 + DTEND;TZID=America/Montreal:20090601T160000 + RRULE:FREQ=DAILY;INTERVAL=1;COUNT=5 + TRANSP:OPAQUE + SUMMARY:Review Internet-Draft + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + ACCEPTED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@exampl + e.net + END:VEVENT + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090603T183823Z + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 89] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + RECURRENCE-ID;TZID=America/Montreal:20090602T150000 + DTSTART;TZID=America/Montreal:20090602T150000 + DTEND;TZID=America/Montreal:20090602T160000 + TRANSP:TRANSPARENT + SUMMARY:Review Internet-Draft + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + DECLINED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@exampl + e.net + END:VEVENT + END:VCALENDAR + + >> Response << + + HTTP/1.1 200 OK + Content-Length: 0 + Date: Tue, 02 Jun 2009 18:52:54 GMT + Last-Modified: Tue, 02 Jun 2009 18:52:54 GMT + ETag: "d85561cfe74a4e785eb4639451b434fb" + Schedule-Tag: "488177c8-2ea7-4176-a6cb-fab8cfccdea2" + + Bernard's participation status update will cause his server to + deliver a scheduling message to Cyrus. Cyrus's client will find the + following reply message from Bernard in Cyrus's scheduling Inbox + collection: + + >> Request << + + GET /home/cyrus/calendars/inbox/9263504FD3AD.ics HTTP/1.1 + Host: cal.example.com + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 90] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 18:52:58 GMT + Last-Modified: Tue, 02 Jun 2009 18:52:58 GMT + ETag: "eb897deabc8939589da116714bc99265" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + METHOD:REPLY + BEGIN:VTIMEZONE + TZID:America/Montreal + BEGIN:STANDARD + DTSTART:20071104T020000 + RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:20070311T020000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + END:VTIMEZONE + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090603T183823Z + RECURRENCE-ID;TZID=America/Montreal:20090602T150000 + DTSTART;TZID=America/Montreal:20090602T150000 + DTEND;TZID=America/Montreal:20090602T160000 + SUMMARY:Review Internet-Draft + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Bernard Desruisseaux";PARTSTAT=DECLINED: + mailto:bernard@example.net + REQUEST-STATUS:2.0;Success + END:VEVENT + END:VCALENDAR + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 91] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +B.8. Example: Attendee Removing an Instance of a Recurring Event + + In the following example, Bernard removes from his calendar the third + recurrence instance of a daily recurring event he's been invited to + by Cyrus. This is accomplished by the addition of an "EXDATE" + property to the scheduling object resource stored by Bernard. + + >> Request << + + PUT /home/bernard/calendars/work/4FD3AD926350.ics HTTP/1.1 + Host: cal.example.com + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + If-Schedule-Tag-Match: "488177c8-2ea7-4176-a6cb-fab8cfccdea2" + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + TZID:America/Montreal + BEGIN:STANDARD + DTSTART:20071104T020000 + RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:20070311T020000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + END:VTIMEZONE + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090602T185254Z + DTSTART;TZID=America/Montreal:20090601T150000 + DTEND;TZID=America/Montreal:20090601T160000 + RRULE:FREQ=DAILY;INTERVAL=1;COUNT=5 + EXDATE;TZID=America/Montreal:20090603T150000 + TRANSP:OPAQUE + SUMMARY:Review Internet-Draft + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 92] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + ACCEPTED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@exampl + e.net + END:VEVENT + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090603T183823Z + RECURRENCE-ID;TZID=America/Montreal:20090602T150000 + DTSTART;TZID=America/Montreal:20090602T150000 + DTEND;TZID=America/Montreal:20090602T160000 + TRANSP:TRANSPARENT + SUMMARY:Review Internet-Draft + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED: + mailto:cyrus@example.com + ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT= + DECLINED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@exampl + e.net + END:VEVENT + END:VCALENDAR + + Bernard's deletion of a recurrence instance will cause his server to + deliver a scheduling message to Cyrus. Cyrus's client will find the + following reply message from Bernard in Cyrus's scheduling Inbox + collection: + + >> Request << + + GET /home/cyrus/calendars/inbox/6504923FD3AD.ics HTTP/1.1 + Host: cal.example.com + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 93] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + >> Response << + + HTTP/1.1 200 OK + Date: Tue, 02 Jun 2009 18:52:58 GMT + Last-Modified: Tue, 02 Jun 2009 18:52:58 GMT + ETag: "eb897deabc8939589da116714bc99265" + Content-Type: text/calendar; charset="utf-8" + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + METHOD:REPLY + BEGIN:VTIMEZONE + TZID:America/Montreal + BEGIN:STANDARD + DTSTART:20071104T020000 + RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:20070311T020000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + END:VTIMEZONE + BEGIN:VEVENT + UID:9263504FD3AD + SEQUENCE:0 + DTSTAMP:20090603T183823Z + RECURRENCE-ID;TZID=America/Montreal:20090603T150000 + DTSTART;TZID=America/Montreal:20090603T150000 + DTEND;TZID=America/Montreal:20090603T160000 + SUMMARY:Review Internet-Draft + ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com + ATTENDEE;CN="Bernard Desruisseaux";PARTSTAT=DECLINED: + mailto:bernard@example.net + REQUEST-STATUS:2.0;Success + END:VEVENT + END:VCALENDAR + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 94] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +Appendix C. Changes (to be removed by RFC Editor prior to publication) + +C.1. Changes in -10 + + a. Updated to RFC 6047 reference. + + b. Various minor clarifications to behavior and terminology done. + + c. Clarified that Inbox/Outbox are the server's responsibility to + create. + + d. Changed MAY to SHOULD for server rejecting organizer PARTSTAT + changes of attendees. + + e. Allow COMPLETED as a valid attendee change. + + f. Allow SCHEDULE-STATUS as a valid attendee change on SCHEDULE- + AGENT=CLIENT attendee properties. + + g. COPY or MOVE on a calendar collection now declared to be + undefined. + + h. Changed pre-condition error codes from 409 to 403. + + i. Clarified that rules 5546 must be used when server processes + incoming scheduling messages. + + j. default-calendar-delete-allowed -> default-calendar-needed. + + k. Clarified that SCHEDULE-AGENT must be the same on all matching + properties. + + l. Added more text justifying the need for calendar-user-type + property. + +C.2. Changes in -09 + + a. Fixed some examples. + + b. Tweaked XML conventions. + + c. Removed description in SCHEDULE-STATUS example values. + + d. Tweaked 3.7 and 3.8 SCHEDULE-STATUS description to indicate it + applies to the Organizer as well as Attendee. + + e. Updated to RFC 5545 reference. + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 95] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + f. AD Review: clarified text about inbox resource deletion being + acknowledgment of change. + + g. AD Review: clarified description of freebusy Outbox POST. + + h. AD Review: registered new 1.xx request-status codes and added new + restriction on usage as per iTIP. + + i. AD Review: changes SHOULD NOT to MUST NOT for new property + parameters when clients send scheduling messages. + + j. AD Review: CALDAV:schedule-calendar-transp now preserved during + COPY. + + k. AD Review: changed CALDAV- to CALDAV: in acl descriptions. + + l. AD Review: fixed various minor typos. + + m. AD Review: Added text to new principal properties to indicate + that if they are not present, then the user is not enabled for + the various scheduling operations. + + n. AD Review: clarified use of CALDAV:calendar-data element in + CALDAV:response element. + + o. AD Review: made reference to 5545 IANA registry procedures for + the two new element registries. + + p. AD Review: Fixed description of B5. example. + + q. Fixed SCHEDULE-AGENT/SCHEDULE-STATUS behavior for Attendee + replies. + +C.3. Changes in -08 + + a. Added "Updates 4791". + + b. XML conventions changed to match that in CardDAV spec. + + c. Reworded child response behavior for Outbox. + + d. Reworded "octet size". + + e. If-Schedule-Match descriptions changed to remove implication that + it is purely a conditional operation. + + f. Schedule-Reply header descriptions generalized to resource + removal rather than just HTTP DELETE. + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 96] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + g. Fixed various examples. + +C.4. Changes in -07 + + a. Restructured document. + + b. Clarified that CALDAV:schedule-calendar-transp only applies to + calendar collection. + + c. Removed CALDAV:schedule-state property on scheduling messages in + the scheduling Inbox collection. + + d. Added conditional requests on scheduling object resources. + + e. Added section on handling of PARTSTAT. + + f. Added SCHEDULE-FORCE-SEND iCalendar property parameter. + + g. Added clarification on child resources in scheduling Outbox + collections. + + h. Clarified Attendee changes that server MUST allow, and removed + restrictions on changes that Attendee MUST NOT do. + + i. Added Example Scheduling Transactions appendix. + + j. Scheduling privileges are no longer required to be non-abstract. + + k. Removed handling of REFRESH requests. + + l. Removed handling of VJOURNAL components. + + m. Completed IANA Considerations section. + + n. Added references to RFC3283 and RFC5234. + + o. Updated references to iCalendar, iTIP and iMIP. + +C.5. Changes in -06 + + a. Removed distinction between scheduling calendar collections and + basic calendar collections - now just have calendar collections. + + b. Clients now "MAY" reload data rather than "SHOULD" reload data. + + c. Fixed in examples. + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 97] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + + d. Removed CALDAV:attachments-allowed precondition on POST to Outbox + as that is no longer relevant. + + e. Added CALDAV:default-calendar-delete-allowed precondition for + DELETE. + + f. Relaxed MUST->MAY for Organizer setting PARTSTAT value. + + g. Tweaked restrictions on Create/Modify to emphasize that 4791 + restrictions also apply. + + h. Added comment that 'opaque' is the default when the CALDAV: + schedule-calendar-transp property is not present. + + i. Description of Schedule-Reply header changed to reflect that it + is only relevant for Attendees. + + j. Minor typos fixed. + +C.6. Changes in -05 + + This draft has changed substantially since the -04 version. The + primary reason for this change was implementation experience from a + number of vendors who implemented products based on the earlier + drafts. Experience showed that the client/server interaction was not + reliable in keeping scheduling messages synchronized between + organizer and attendees. In addition the latency in updates due to + clients being offline proved unacceptable to users. These issues led + to the redesign of this specification to support a server-based + processing model that eliminates all the problems seen previously. + Whilst this adds significant complexity to the server in that it + needs to be a full blown iTIP processing agent, it does remove a lot + of the same complexity from clients, opening up the possibility of + supporting complex scheduling behaviors even with "thin" clients. + + In the judgement of the authors, we consider this new specification + to be a substantial improvement over the old one and believe it + represents a stronger protocol that will lead to better + interoperability. + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 98] + +Internet-Draft CalDAV Scheduling Extensions September 2011 + + +Authors' Addresses + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + Bernard Desruisseaux + Oracle Corporation + 600 Blvd. de Maisonneuve West + Suite 1900 + Montreal, QC H3A 3J2 + CANADA + + EMail: bernard.desruisseaux@oracle.com + URI: http://www.oracle.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo & Desruisseaux Expires March 10, 2012 [Page 99] + diff --git a/dav/SabreDAV/docs/draft-ietf-httpbis-p1-messaging-11.txt b/dav/SabreDAV/docs/draft-ietf-httpbis-p1-messaging-11.txt new file mode 100644 index 000000000..c0d449877 --- /dev/null +++ b/dav/SabreDAV/docs/draft-ietf-httpbis-p1-messaging-11.txt @@ -0,0 +1,5152 @@ + + + +HTTPbis Working Group R. Fielding, Ed. +Internet-Draft Day Software +Obsoletes: 2616 (if approved) J. Gettys +Updates: 2817 (if approved) Alcatel-Lucent +Intended status: Standards Track J. Mogul +Expires: February 5, 2011 HP + H. Frystyk + Microsoft + L. Masinter + Adobe Systems + P. Leach + Microsoft + T. Berners-Lee + W3C/MIT + Y. Lafon, Ed. + W3C + J. Reschke, Ed. + greenbytes + August 4, 2010 + + + HTTP/1.1, part 1: URIs, Connections, and Message Parsing + draft-ietf-httpbis-p1-messaging-11 + +Abstract + + The Hypertext Transfer Protocol (HTTP) is an application-level + protocol for distributed, collaborative, hypertext information + systems. HTTP has been in use by the World Wide Web global + information initiative since 1990. This document is Part 1 of the + seven-part specification that defines the protocol referred to as + "HTTP/1.1" and, taken together, obsoletes RFC 2616. Part 1 provides + an overview of HTTP and its associated terminology, defines the + "http" and "https" Uniform Resource Identifier (URI) schemes, defines + the generic message syntax and parsing requirements for HTTP message + frames, and describes general security concerns for implementations. + +Editorial Note (To be removed by RFC Editor) + + Discussion of this draft should take place on the HTTPBIS working + group mailing list (ietf-http-wg@w3.org). The current issues list is + at and related + documents (including fancy diffs) can be found at + . + + The changes in this draft are summarized in Appendix D.12. + +Status of This Memo + + + +Fielding, et al. Expires February 5, 2011 [Page 1] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on February 5, 2011. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 1.1. Requirements . . . . . . . . . . . . . . . . . . . . . . . 7 + 1.2. Syntax Notation . . . . . . . . . . . . . . . . . . . . . 7 + 1.2.1. ABNF Extension: #rule . . . . . . . . . . . . . . . . 7 + + + +Fielding, et al. Expires February 5, 2011 [Page 2] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + 1.2.2. Basic Rules . . . . . . . . . . . . . . . . . . . . . 8 + 1.2.3. ABNF Rules defined in other Parts of the + Specification . . . . . . . . . . . . . . . . . . . . 10 + 2. HTTP-related architecture . . . . . . . . . . . . . . . . . . 10 + 2.1. Client/Server Messaging . . . . . . . . . . . . . . . . . 10 + 2.2. Intermediaries . . . . . . . . . . . . . . . . . . . . . . 12 + 2.3. Caches . . . . . . . . . . . . . . . . . . . . . . . . . . 13 + 2.4. Transport Independence . . . . . . . . . . . . . . . . . . 14 + 2.5. HTTP Version . . . . . . . . . . . . . . . . . . . . . . . 14 + 2.6. Uniform Resource Identifiers . . . . . . . . . . . . . . . 16 + 2.6.1. http URI scheme . . . . . . . . . . . . . . . . . . . 16 + 2.6.2. https URI scheme . . . . . . . . . . . . . . . . . . . 18 + 2.6.3. http and https URI Normalization and Comparison . . . 18 + 3. HTTP Message . . . . . . . . . . . . . . . . . . . . . . . . . 19 + 3.1. Message Parsing Robustness . . . . . . . . . . . . . . . . 20 + 3.2. Header Fields . . . . . . . . . . . . . . . . . . . . . . 20 + 3.3. Message Body . . . . . . . . . . . . . . . . . . . . . . . 22 + 3.4. General Header Fields . . . . . . . . . . . . . . . . . . 25 + 4. Request . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 + 4.1. Request-Line . . . . . . . . . . . . . . . . . . . . . . . 26 + 4.1.1. Method . . . . . . . . . . . . . . . . . . . . . . . . 26 + 4.1.2. request-target . . . . . . . . . . . . . . . . . . . . 27 + 4.2. The Resource Identified by a Request . . . . . . . . . . . 29 + 4.3. Effective Request URI . . . . . . . . . . . . . . . . . . 29 + 5. Response . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 + 5.1. Status-Line . . . . . . . . . . . . . . . . . . . . . . . 31 + 5.1.1. Status Code and Reason Phrase . . . . . . . . . . . . 31 + 6. Protocol Parameters . . . . . . . . . . . . . . . . . . . . . 32 + 6.1. Date/Time Formats: Full Date . . . . . . . . . . . . . . . 32 + 6.2. Transfer Codings . . . . . . . . . . . . . . . . . . . . . 34 + 6.2.1. Chunked Transfer Coding . . . . . . . . . . . . . . . 35 + 6.2.2. Compression Codings . . . . . . . . . . . . . . . . . 37 + 6.2.3. Transfer Coding Registry . . . . . . . . . . . . . . . 38 + 6.3. Product Tokens . . . . . . . . . . . . . . . . . . . . . . 39 + 6.4. Quality Values . . . . . . . . . . . . . . . . . . . . . . 39 + 7. Connections . . . . . . . . . . . . . . . . . . . . . . . . . 39 + 7.1. Persistent Connections . . . . . . . . . . . . . . . . . . 39 + 7.1.1. Purpose . . . . . . . . . . . . . . . . . . . . . . . 40 + 7.1.2. Overall Operation . . . . . . . . . . . . . . . . . . 40 + 7.1.3. Proxy Servers . . . . . . . . . . . . . . . . . . . . 42 + 7.1.4. Practical Considerations . . . . . . . . . . . . . . . 44 + 7.2. Message Transmission Requirements . . . . . . . . . . . . 45 + 7.2.1. Persistent Connections and Flow Control . . . . . . . 45 + 7.2.2. Monitoring Connections for Error Status Messages . . . 45 + 7.2.3. Use of the 100 (Continue) Status . . . . . . . . . . . 46 + 7.2.4. Client Behavior if Server Prematurely Closes + Connection . . . . . . . . . . . . . . . . . . . . . . 48 + 8. Miscellaneous notes that might disappear . . . . . . . . . . . 49 + + + +Fielding, et al. Expires February 5, 2011 [Page 3] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + 8.1. Scheme aliases considered harmful . . . . . . . . . . . . 49 + 8.2. Use of HTTP for proxy communication . . . . . . . . . . . 49 + 8.3. Interception of HTTP for access control . . . . . . . . . 49 + 8.4. Use of HTTP by other protocols . . . . . . . . . . . . . . 49 + 8.5. Use of HTTP by media type specification . . . . . . . . . 49 + 9. Header Field Definitions . . . . . . . . . . . . . . . . . . . 49 + 9.1. Connection . . . . . . . . . . . . . . . . . . . . . . . . 49 + 9.2. Content-Length . . . . . . . . . . . . . . . . . . . . . . 50 + 9.3. Date . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 + 9.3.1. Clockless Origin Server Operation . . . . . . . . . . 52 + 9.4. Host . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 + 9.5. TE . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 + 9.6. Trailer . . . . . . . . . . . . . . . . . . . . . . . . . 54 + 9.7. Transfer-Encoding . . . . . . . . . . . . . . . . . . . . 55 + 9.8. Upgrade . . . . . . . . . . . . . . . . . . . . . . . . . 55 + 9.8.1. Upgrade Token Registry . . . . . . . . . . . . . . . . 56 + 9.9. Via . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 + 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 59 + 10.1. Header Field Registration . . . . . . . . . . . . . . . . 59 + 10.2. URI Scheme Registration . . . . . . . . . . . . . . . . . 59 + 10.3. Internet Media Type Registrations . . . . . . . . . . . . 59 + 10.3.1. Internet Media Type message/http . . . . . . . . . . . 59 + 10.3.2. Internet Media Type application/http . . . . . . . . . 61 + 10.4. Transfer Coding Registry . . . . . . . . . . . . . . . . . 62 + 10.5. Upgrade Token Registration . . . . . . . . . . . . . . . . 62 + 11. Security Considerations . . . . . . . . . . . . . . . . . . . 62 + 11.1. Personal Information . . . . . . . . . . . . . . . . . . . 63 + 11.2. Abuse of Server Log Information . . . . . . . . . . . . . 63 + 11.3. Attacks Based On File and Path Names . . . . . . . . . . . 63 + 11.4. DNS Spoofing . . . . . . . . . . . . . . . . . . . . . . . 63 + 11.5. Proxies and Caching . . . . . . . . . . . . . . . . . . . 64 + 11.6. Denial of Service Attacks on Proxies . . . . . . . . . . . 65 + 12. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 65 + 13. References . . . . . . . . . . . . . . . . . . . . . . . . . . 66 + 13.1. Normative References . . . . . . . . . . . . . . . . . . . 66 + 13.2. Informative References . . . . . . . . . . . . . . . . . . 68 + Appendix A. Tolerant Applications . . . . . . . . . . . . . . . . 70 + Appendix B. Compatibility with Previous Versions . . . . . . . . 71 + B.1. Changes from HTTP/1.0 . . . . . . . . . . . . . . . . . . 71 + B.1.1. Changes to Simplify Multi-homed Web Servers and + Conserve IP Addresses . . . . . . . . . . . . . . . . 72 + B.2. Compatibility with HTTP/1.0 Persistent Connections . . . . 72 + B.3. Changes from RFC 2616 . . . . . . . . . . . . . . . . . . 73 + Appendix C. Collected ABNF . . . . . . . . . . . . . . . . . . . 74 + Appendix D. Change Log (to be removed by RFC Editor before + publication) . . . . . . . . . . . . . . . . . . . . 78 + D.1. Since RFC2616 . . . . . . . . . . . . . . . . . . . . . . 78 + D.2. Since draft-ietf-httpbis-p1-messaging-00 . . . . . . . . . 78 + + + +Fielding, et al. Expires February 5, 2011 [Page 4] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + D.3. Since draft-ietf-httpbis-p1-messaging-01 . . . . . . . . . 80 + D.4. Since draft-ietf-httpbis-p1-messaging-02 . . . . . . . . . 81 + D.5. Since draft-ietf-httpbis-p1-messaging-03 . . . . . . . . . 81 + D.6. Since draft-ietf-httpbis-p1-messaging-04 . . . . . . . . . 82 + D.7. Since draft-ietf-httpbis-p1-messaging-05 . . . . . . . . . 82 + D.8. Since draft-ietf-httpbis-p1-messaging-06 . . . . . . . . . 83 + D.9. Since draft-ietf-httpbis-p1-messaging-07 . . . . . . . . . 84 + D.10. Since draft-ietf-httpbis-p1-messaging-08 . . . . . . . . . 84 + D.11. Since draft-ietf-httpbis-p1-messaging-09 . . . . . . . . . 85 + D.12. Since draft-ietf-httpbis-p1-messaging-10 . . . . . . . . . 85 + Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 5] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +1. Introduction + + The Hypertext Transfer Protocol (HTTP) is an application-level + request/response protocol that uses extensible semantics and MIME- + like message payloads for flexible interaction with network-based + hypertext information systems. HTTP relies upon the Uniform Resource + Identifier (URI) standard [RFC3986] to indicate request targets and + relationships between resources. Messages are passed in a format + similar to that used by Internet mail [RFC5322] and the Multipurpose + Internet Mail Extensions (MIME) [RFC2045] (see Appendix A of [Part3] + for the differences between HTTP and MIME messages). + + HTTP is a generic interface protocol for information systems. It is + designed to hide the details of how a service is implemented by + presenting a uniform interface to clients that is independent of the + types of resources provided. Likewise, servers do not need to be + aware of each client's purpose: an HTTP request can be considered in + isolation rather than being associated with a specific type of client + or a predetermined sequence of application steps. The result is a + protocol that can be used effectively in many different contexts and + for which implementations can evolve independently over time. + + HTTP is also designed for use as an intermediation protocol for + translating communication to and from non-HTTP information systems. + HTTP proxies and gateways can provide access to alternative + information services by translating their diverse protocols into a + hypertext format that can be viewed and manipulated by clients in the + same way as HTTP services. + + One consequence of HTTP flexibility is that the protocol cannot be + defined in terms of what occurs behind the interface. Instead, we + are limited to defining the syntax of communication, the intent of + received communication, and the expected behavior of recipients. If + the communication is considered in isolation, then successful actions + ought to be reflected in corresponding changes to the observable + interface provided by servers. However, since multiple clients might + act in parallel and perhaps at cross-purposes, we cannot require that + such changes be observable beyond the scope of a single response. + + This document is Part 1 of the seven-part specification of HTTP, + defining the protocol referred to as "HTTP/1.1" and obsoleting + [RFC2616]. Part 1 describes the architectural elements that are used + or referred to in HTTP, defines the "http" and "https" URI schemes, + describes overall network operation and connection management, and + defines HTTP message framing and forwarding requirements. Our goal + is to define all of the mechanisms necessary for HTTP message + handling that are independent of message semantics, thereby defining + the complete set of requirements for message parsers and message- + + + +Fielding, et al. Expires February 5, 2011 [Page 6] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + forwarding intermediaries. + +1.1. Requirements + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + An implementation is not compliant if it fails to satisfy one or more + of the "MUST" or "REQUIRED" level requirements for the protocols it + implements. An implementation that satisfies all the "MUST" or + "REQUIRED" level and all the "SHOULD" level requirements for its + protocols is said to be "unconditionally compliant"; one that + satisfies all the "MUST" level requirements but not all the "SHOULD" + level requirements for its protocols is said to be "conditionally + compliant". + +1.2. Syntax Notation + + This specification uses the Augmented Backus-Naur Form (ABNF) + notation of [RFC5234]. + + The following core rules are included by reference, as defined in + [RFC5234], Appendix B.1: ALPHA (letters), CR (carriage return), CRLF + (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), + HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed), OCTET (any 8-bit + sequence of data), SP (space), VCHAR (any visible [USASCII] + character), and WSP (whitespace). + + As a syntactic convention, ABNF rule names prefixed with "obs-" + denote "obsolete" grammar rules that appear for historical reasons. + +1.2.1. ABNF Extension: #rule + + The #rule extension to the ABNF rules of [RFC5234] is used to improve + readability. + + A construct "#" is defined, similar to "*", for defining comma- + delimited lists of elements. The full form is "#element" + indicating at least and at most elements, each separated by a + single comma (",") and optional whitespace (OWS, Section 1.2.2). + + Thus, + + 1#element => element *( OWS "," OWS element ) + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 7] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + and: + + #element => [ 1#element ] + + and for n >= 1 and m > 1: + + #element => element *( OWS "," OWS element ) + + For compatibility with legacy list rules, recipients SHOULD accept + empty list elements. In other words, consumers would follow the list + productions: + + #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ] + + 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] ) + + Note that empty elements do not contribute to the count of elements + present, though. + + For example, given these ABNF productions: + + example-list = 1#example-list-elmt + example-list-elmt = token ; see Section 1.2.2 + + Then these are valid values for example-list (not including the + double quotes, which are present for delimitation only): + + "foo,bar" + " foo ,bar," + " foo , ,bar,charlie " + "foo ,bar, charlie " + + But these values would be invalid, as at least one non-empty element + is required: + + "" + "," + ", ," + + Appendix C shows the collected ABNF, with the list rules expanded as + explained above. + +1.2.2. Basic Rules + + HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all + protocol elements other than the message-body (see Appendix A for + tolerant applications). + + + + +Fielding, et al. Expires February 5, 2011 [Page 8] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + This specification uses three rules to denote the use of linear + whitespace: OWS (optional whitespace), RWS (required whitespace), and + BWS ("bad" whitespace). + + The OWS rule is used where zero or more linear whitespace characters + might appear. OWS SHOULD either not be produced or be produced as a + single SP character. Multiple OWS characters that occur within + field-content SHOULD be replaced with a single SP before interpreting + the field value or forwarding the message downstream. + + RWS is used when at least one linear whitespace character is required + to separate field tokens. RWS SHOULD be produced as a single SP + character. Multiple RWS characters that occur within field-content + SHOULD be replaced with a single SP before interpreting the field + value or forwarding the message downstream. + + BWS is used where the grammar allows optional whitespace for + historical reasons but senders SHOULD NOT produce it in messages. + HTTP/1.1 recipients MUST accept such bad optional whitespace and + remove it before interpreting the field value or forwarding the + message downstream. + + + OWS = *( [ obs-fold ] WSP ) + ; "optional" whitespace + RWS = 1*( [ obs-fold ] WSP ) + ; "required" whitespace + BWS = OWS + ; "bad" whitespace + obs-fold = CRLF + ; see Section 3.2 + + Many HTTP/1.1 header field values consist of words (token or quoted- + string) separated by whitespace or special characters. These special + characters MUST be in a quoted string to be used within a parameter + value (as defined in Section 6.2). + + word = token / quoted-string + + token = 1*tchar + + tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" + / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" + / DIGIT / ALPHA + ; any VCHAR, except special + + special = "(" / ")" / "<" / ">" / "@" / "," + / ";" / ":" / "\" / DQUOTE / "/" / "[" + + + +Fielding, et al. Expires February 5, 2011 [Page 9] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + / "]" / "?" / "=" / "{" / "}" + + A string of text is parsed as a single word if it is quoted using + double-quote marks. + + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + qdtext = OWS / %x21 / %x23-5B / %x5D-7E / obs-text + ; OWS / / obs-text + obs-text = %x80-FF + + The backslash character ("\") can be used as a single-character + quoting mechanism within quoted-string constructs: + + quoted-pair = "\" ( WSP / VCHAR / obs-text ) + + Producers SHOULD NOT escape characters that do not require escaping + (i.e., other than DQUOTE and the backslash character). + +1.2.3. ABNF Rules defined in other Parts of the Specification + + The ABNF rules below are defined in other parts: + + request-header = + response-header = + + + MIME-Version = + + + Cache-Control = + Pragma = + Warning = + +2. HTTP-related architecture + + HTTP was created for the World Wide Web architecture and has evolved + over time to support the scalability needs of a worldwide hypertext + system. Much of that architecture is reflected in the terminology + and syntax productions used to define HTTP. + +2.1. Client/Server Messaging + + HTTP is a stateless request/response protocol that operates by + exchanging messages across a reliable transport or session-layer + connection. An HTTP "client" is a program that establishes a + connection to a server for the purpose of sending one or more HTTP + requests. An HTTP "server" is a program that accepts connections in + order to service HTTP requests by sending HTTP responses. + + + +Fielding, et al. Expires February 5, 2011 [Page 10] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Note that the terms client and server refer only to the roles that + these programs perform for a particular connection. The same program + might act as a client on some connections and a server on others. We + use the term "user agent" to refer to the program that initiates a + request, such as a WWW browser, editor, or spider (web-traversing + robot), and the term "origin server" to refer to the program that can + originate authoritative responses to a request. For general + requirements, we use the term "sender" to refer to whichever + component sent a given message and the term "recipient" to refer to + any component that receives the message. + + Most HTTP communication consists of a retrieval request (GET) for a + representation of some resource identified by a URI. In the simplest + case, this might be accomplished via a single bidirectional + connection (===) between the user agent (UA) and the origin server + (O). + + request > + UA ======================================= O + < response + + A client sends an HTTP request to the server in the form of a request + message (Section 4), beginning with a method, URI, and protocol + version, followed by MIME-like header fields containing request + modifiers, client information, and payload metadata, an empty line to + indicate the end of the header section, and finally the payload body + (if any). + + A server responds to the client's request by sending an HTTP response + message (Section 5), beginning with a status line that includes the + protocol version, a success or error code, and textual reason phrase, + followed by MIME-like header fields containing server information, + resource metadata, and payload metadata, an empty line to indicate + the end of the header section, and finally the payload body (if any). + + The following example illustrates a typical message exchange for a + GET request on the URI "http://www.example.com/hello.txt": + + client request: + + GET /hello.txt HTTP/1.1 + User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3 + Host: www.example.com + Accept: */* + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 11] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + server response: + + HTTP/1.1 200 OK + Date: Mon, 27 Jul 2009 12:28:53 GMT + Server: Apache + Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT + ETag: "34aa387-d-1568eb00" + Accept-Ranges: bytes + Content-Length: 14 + Vary: Accept-Encoding + Content-Type: text/plain + + Hello World! + +2.2. Intermediaries + + A more complicated situation occurs when one or more intermediaries + are present in the request/response chain. There are three common + forms of intermediary: proxy, gateway, and tunnel. In some cases, a + single intermediary might act as an origin server, proxy, gateway, or + tunnel, switching behavior based on the nature of each request. + + > > > > + UA =========== A =========== B =========== C =========== O + < < < < + + The figure above shows three intermediaries (A, B, and C) between the + user agent and origin server. A request or response message that + travels the whole chain will pass through four separate connections. + Some HTTP communication options might apply only to the connection + with the nearest, non-tunnel neighbor, only to the end-points of the + chain, or to all connections along the chain. Although the diagram + is linear, each participant might be engaged in multiple, + simultaneous communications. For example, B might be receiving + requests from many clients other than A, and/or forwarding requests + to servers other than C, at the same time that it is handling A's + request. + + We use the terms "upstream" and "downstream" to describe various + requirements in relation to the directional flow of a message: all + messages flow from upstream to downstream. Likewise, we use the + terms "inbound" and "outbound" to refer to directions in relation to + the request path: "inbound" means toward the origin server and + "outbound" means toward the user agent. + + A "proxy" is a message forwarding agent that is selected by the + client, usually via local configuration rules, to receive requests + for some type(s) of absolute URI and attempt to satisfy those + + + +Fielding, et al. Expires February 5, 2011 [Page 12] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + requests via translation through the HTTP interface. Some + translations are minimal, such as for proxy requests for "http" URIs, + whereas other requests might require translation to and from entirely + different application-layer protocols. Proxies are often used to + group an organization's HTTP requests through a common intermediary + for the sake of security, annotation services, or shared caching. + + A "gateway" (a.k.a., "reverse proxy") is a receiving agent that acts + as a layer above some other server(s) and translates the received + requests to the underlying server's protocol. Gateways are often + used for load balancing or partitioning HTTP services across multiple + machines. Unlike a proxy, a gateway receives requests as if it were + the origin server for the target resource; the requesting client will + not be aware that it is communicating with a gateway. A gateway + communicates with the client as if the gateway is the origin server + and thus is subject to all of the requirements on origin servers for + that connection. A gateway communicates with inbound servers using + any protocol it desires, including private extensions to HTTP that + are outside the scope of this specification. + + A "tunnel" acts as a blind relay between two connections without + changing the messages. Once active, a tunnel is not considered a + party to the HTTP communication, though the tunnel might have been + initiated by an HTTP request. A tunnel ceases to exist when both + ends of the relayed connection are closed. Tunnels are used to + extend a virtual connection through an intermediary, such as when + transport-layer security is used to establish private communication + through a shared firewall proxy. + +2.3. Caches + + A "cache" is a local store of previous response messages and the + subsystem that controls its message storage, retrieval, and deletion. + A cache stores cacheable responses in order to reduce the response + time and network bandwidth consumption on future, equivalent + requests. Any client or server MAY employ a cache, though a cache + cannot be used by a server while it is acting as a tunnel. + + The effect of a cache is that the request/response chain is shortened + if one of the participants along the chain has a cached response + applicable to that request. The following illustrates the resulting + chain if B has a cached copy of an earlier response from O (via C) + for a request which has not been cached by UA or A. + + > > + UA =========== A =========== B - - - - - - C - - - - - - O + < < + + + + +Fielding, et al. Expires February 5, 2011 [Page 13] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + A response is "cacheable" if a cache is allowed to store a copy of + the response message for use in answering subsequent requests. Even + when a response is cacheable, there might be additional constraints + placed by the client or by the origin server on when that cached + response can be used for a particular request. HTTP requirements for + cache behavior and cacheable responses are defined in Section 2 of + [Part6]. + + There are a wide variety of architectures and configurations of + caches and proxies deployed across the World Wide Web and inside + large organizations. These systems include national hierarchies of + proxy caches to save transoceanic bandwidth, systems that broadcast + or multicast cache entries, organizations that distribute subsets of + cached data via optical media, and so on. + +2.4. Transport Independence + + HTTP systems are used in a wide variety of environments, from + corporate intranets with high-bandwidth links to long-distance + communication over low-power radio links and intermittent + connectivity. + + HTTP communication usually takes place over TCP/IP connections. The + default port is TCP 80 + (), but other ports can + be used. This does not preclude HTTP from being implemented on top + of any other protocol on the Internet, or on other networks. HTTP + only presumes a reliable transport; any protocol that provides such + guarantees can be used; the mapping of the HTTP/1.1 request and + response structures onto the transport data units of the protocol in + question is outside the scope of this specification. + + In HTTP/1.0, most implementations used a new connection for each + request/response exchange. In HTTP/1.1, a connection might be used + for one or more request/response exchanges, although connections + might be closed for a variety of reasons (see Section 7.1). + +2.5. HTTP Version + + HTTP uses a "." numbering scheme to indicate versions + of the protocol. The protocol versioning policy is intended to allow + the sender to indicate the format of a message and its capacity for + understanding further HTTP communication, rather than the features + obtained via that communication. No change is made to the version + number for the addition of message components which do not affect + communication behavior or which only add to extensible field values. + The number is incremented when the changes made to the + protocol add features which do not change the general message parsing + + + +Fielding, et al. Expires February 5, 2011 [Page 14] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + algorithm, but which might add to the message semantics and imply + additional capabilities of the sender. The number is + incremented when the format of a message within the protocol is + changed. See [RFC2145] for a fuller explanation. + + The version of an HTTP message is indicated by an HTTP-Version field + in the first line of the message. HTTP-Version is case-sensitive. + + HTTP-Version = HTTP-Prot-Name "/" 1*DIGIT "." 1*DIGIT + HTTP-Prot-Name = %x48.54.54.50 ; "HTTP", case-sensitive + + Note that the major and minor numbers MUST be treated as separate + integers and that each MAY be incremented higher than a single digit. + Thus, HTTP/2.4 is a lower version than HTTP/2.13, which in turn is + lower than HTTP/12.3. Leading zeros MUST be ignored by recipients + and MUST NOT be sent. + + An application that sends a request or response message that includes + HTTP-Version of "HTTP/1.1" MUST be at least conditionally compliant + with this specification. Applications that are at least + conditionally compliant with this specification SHOULD use an HTTP- + Version of "HTTP/1.1" in their messages, and MUST do so for any + message that is not compatible with HTTP/1.0. For more details on + when to send specific HTTP-Version values, see [RFC2145]. + + The HTTP version of an application is the highest HTTP version for + which the application is at least conditionally compliant. + + Proxy and gateway applications need to be careful when forwarding + messages in protocol versions different from that of the application. + Since the protocol version indicates the protocol capability of the + sender, a proxy/gateway MUST NOT send a message with a version + indicator which is greater than its actual version. If a higher + version request is received, the proxy/gateway MUST either downgrade + the request version, or respond with an error, or switch to tunnel + behavior. + + Due to interoperability problems with HTTP/1.0 proxies discovered + since the publication of [RFC2068], caching proxies MUST, gateways + MAY, and tunnels MUST NOT upgrade the request to the highest version + they support. The proxy/gateway's response to that request MUST be + in the same major version as the request. + + Note: Converting between versions of HTTP might involve + modification of header fields required or forbidden by the + versions involved. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 15] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +2.6. Uniform Resource Identifiers + + Uniform Resource Identifiers (URIs) [RFC3986] are used throughout + HTTP as the means for identifying resources. URI references are used + to target requests, indicate redirects, and define relationships. + HTTP does not limit what a resource might be; it merely defines an + interface that can be used to interact with a resource via HTTP. + More information on the scope of URIs and resources can be found in + [RFC3986]. + + This specification adopts the definitions of "URI-reference", + "absolute-URI", "relative-part", "port", "host", "path-abempty", + "path-absolute", "query", and "authority" from [RFC3986]. In + addition, we define a partial-URI rule for protocol elements that + allow a relative URI without a fragment. + + URI-reference = + absolute-URI = + relative-part = + authority = + path-abempty = + path-absolute = + port = + query = + uri-host = + + partial-URI = relative-part [ "?" query ] + + Each protocol element in HTTP that allows a URI reference will + indicate in its ABNF production whether the element allows only a URI + in absolute form (absolute-URI), any relative reference (relative- + ref), or some other subset of the URI-reference grammar. Unless + otherwise indicated, URI references are parsed relative to the + request target (the default base URI for both the request and its + corresponding response). + +2.6.1. http URI scheme + + The "http" URI scheme is hereby defined for the purpose of minting + identifiers according to their association with the hierarchical + namespace governed by a potential HTTP origin server listening for + TCP connections on a given port. The HTTP server is identified via + the generic syntax's authority component, which includes a host + identifier and optional TCP port, and the remainder of the URI is + considered to be identifying data corresponding to a resource for + which that server might provide an HTTP interface. + + http-URI = "http:" "//" authority path-abempty [ "?" query ] + + + +Fielding, et al. Expires February 5, 2011 [Page 16] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + The host identifier within an authority component is defined in + [RFC3986], Section 3.2.2. If host is provided as an IP literal or + IPv4 address, then the HTTP server is any listener on the indicated + TCP port at that IP address. If host is a registered name, then that + name is considered an indirect identifier and the recipient might use + a name resolution service, such as DNS, to find the address of a + listener for that host. The host MUST NOT be empty; if an "http" URI + is received with an empty host, then it MUST be rejected as invalid. + If the port subcomponent is empty or not given, then TCP port 80 is + assumed (the default reserved port for WWW services). + + Regardless of the form of host identifier, access to that host is not + implied by the mere presence of its name or address. The host might + or might not exist and, even when it does exist, might or might not + be running an HTTP server or listening to the indicated port. The + "http" URI scheme makes use of the delegated nature of Internet names + and addresses to establish a naming authority (whatever entity has + the ability to place an HTTP server at that Internet name or address) + and allows that authority to determine which names are valid and how + they might be used. + + When an "http" URI is used within a context that calls for access to + the indicated resource, a client MAY attempt access by resolving the + host to an IP address, establishing a TCP connection to that address + on the indicated port, and sending an HTTP request message to the + server containing the URI's identifying data as described in + Section 4. If the server responds to that request with a non-interim + HTTP response message, as described in Section 5, then that response + is considered an authoritative answer to the client's request. + + Although HTTP is independent of the transport protocol, the "http" + scheme is specific to TCP-based services because the name delegation + process depends on TCP for establishing authority. An HTTP service + based on some other underlying connection protocol would presumably + be identified using a different URI scheme, just as the "https" + scheme (below) is used for servers that require an SSL/TLS transport + layer on a connection. Other protocols might also be used to provide + access to "http" identified resources --- it is only the + authoritative interface used for mapping the namespace that is + specific to TCP. + + The URI generic syntax for authority also includes a deprecated + userinfo subcomponent ([RFC3986], Section 3.2.1) for including user + authentication information in the URI. The userinfo subcomponent + (and its "@" delimiter) MUST NOT be used in an "http" URI. URI + reference recipients SHOULD parse for the existence of userinfo and + treat its presence as an error, likely indicating that the deprecated + subcomponent is being used to obscure the authority for the sake of + + + +Fielding, et al. Expires February 5, 2011 [Page 17] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + phishing attacks. + +2.6.2. https URI scheme + + The "https" URI scheme is hereby defined for the purpose of minting + identifiers according to their association with the hierarchical + namespace governed by a potential HTTP origin server listening for + SSL/TLS-secured connections on a given TCP port. + + All of the requirements listed above for the "http" scheme are also + requirements for the "https" scheme, except that a default TCP port + of 443 is assumed if the port subcomponent is empty or not given, and + the TCP connection MUST be secured for privacy through the use of + strong encryption prior to sending the first HTTP request. + + https-URI = "https:" "//" authority path-abempty [ "?" query ] + + Unlike the "http" scheme, responses to "https" identified requests + are never "public" and thus are ineligible for shared caching. Their + default is "private" and might be further constrained via use of the + Cache-Control header field. + + Resources made available via the "https" scheme have no shared + identity with the "http" scheme even if their resource identifiers + only differ by the single "s" in the scheme name. They are different + services governed by different authorities. However, some extensions + to HTTP that apply to entire host domains, such as the Cookie + protocol, do allow one service to effect communication with the other + services based on host domain matching. + + The process for authoritative access to an "https" identified + resource is defined in [RFC2818]. + +2.6.3. http and https URI Normalization and Comparison + + Since the "http" and "https" schemes conform to the URI generic + syntax, such URIs are normalized and compared according to the + algorithm defined in [RFC3986], Section 6, using the defaults + described above for each scheme. + + If the port is equal to the default port for a scheme, the normal + form is to elide the port subcomponent. Likewise, an empty path + component is equivalent to an absolute path of "/", so the normal + form is to provide a path of "/" instead. The scheme and host are + case-insensitive and normally provided in lowercase; all other + components are compared in a case-sensitive manner. Characters other + than those in the "reserved" set are equivalent to their percent- + encoded octets (see [RFC3986], Section 2.1): the normal form is to + + + +Fielding, et al. Expires February 5, 2011 [Page 18] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + not encode them. + + For example, the following three URIs are equivalent: + + http://example.com:80/~smith/home.html + http://EXAMPLE.com/%7Esmith/home.html + http://EXAMPLE.com:/%7esmith/home.html + + [[TODO-not-here: This paragraph does not belong here. --roy]] If + path-abempty is the empty string (i.e., there is no slash "/" path + separator following the authority), then the "http" URI MUST be given + as "/" when used as a request-target (Section 4.1.2). If a proxy + receives a host name which is not a fully qualified domain name, it + MAY add its domain to the host name it received. If a proxy receives + a fully qualified domain name, the proxy MUST NOT change the host + name. + +3. HTTP Message + + All HTTP/1.1 messages consist of a start-line followed by a sequence + of characters in a format similar to the Internet Message Format + [RFC5322]: zero or more header fields (collectively referred to as + the "headers" or the "header section"), an empty line indicating the + end of the header section, and an optional message-body. + + An HTTP message can either be a request from client to server or a + response from server to client. Syntactically, the two types of + message differ only in the start-line, which is either a Request-Line + (for requests) or a Status-Line (for responses), and in the algorithm + for determining the length of the message-body (Section 3.3). In + theory, a client could receive requests and a server could receive + responses, distinguishing them by their different start-line formats, + but in practice servers are implemented to only expect a request (a + response is interpreted as an unknown or invalid request method) and + clients are implemented to only expect a response. + + HTTP-message = start-line + *( header-field CRLF ) + CRLF + [ message-body ] + start-line = Request-Line / Status-Line + + Whitespace (WSP) MUST NOT be sent between the start-line and the + first header field. The presence of whitespace might be an attempt + to trick a noncompliant implementation of HTTP into ignoring that + field or processing the next line as a new request, either of which + might result in security issues when implementations within the + request chain interpret the same message differently. HTTP/1.1 + + + +Fielding, et al. Expires February 5, 2011 [Page 19] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + servers MUST reject such a message with a 400 (Bad Request) response. + +3.1. Message Parsing Robustness + + In the interest of robustness, servers SHOULD ignore at least one + empty line received where a Request-Line is expected. In other + words, if the server is reading the protocol stream at the beginning + of a message and receives a CRLF first, it SHOULD ignore the CRLF. + + Some old HTTP/1.0 client implementations generate an extra CRLF after + a POST request as a lame workaround for some early server + applications that failed to read message-body content that was not + terminated by a line-ending. An HTTP/1.1 client MUST NOT preface or + follow a request with an extra CRLF. If terminating the request + message-body with a line-ending is desired, then the client MUST + include the terminating CRLF octets as part of the message-body + length. + + The normal procedure for parsing an HTTP message is to read the + start-line into a structure, read each header field into a hash table + by field name until the empty line, and then use the parsed data to + determine if a message-body is expected. If a message-body has been + indicated, then it is read as a stream until an amount of octets + equal to the message-body length is read or the connection is closed. + Care must be taken to parse an HTTP message as a sequence of octets + in an encoding that is a superset of US-ASCII. Attempting to parse + HTTP as a stream of Unicode characters in a character encoding like + UTF-16 might introduce security flaws due to the differing ways that + such parsers interpret invalid characters. + + HTTP allows the set of defined header fields to be extended without + changing the protocol version (see Section 10.1). However, such + fields might not be recognized by a downstream recipient and might be + stripped by non-transparent intermediaries. Unrecognized header + fields MUST be forwarded by transparent proxies and SHOULD be ignored + by a recipient. + +3.2. Header Fields + + Each HTTP header field consists of a case-insensitive field name + followed by a colon (":"), optional whitespace, and the field value. + + header-field = field-name ":" OWS [ field-value ] OWS + field-name = token + field-value = *( field-content / OWS ) + field-content = *( WSP / VCHAR / obs-text ) + + No whitespace is allowed between the header field name and colon. + + + +Fielding, et al. Expires February 5, 2011 [Page 20] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + For security reasons, any request message received containing such + whitespace MUST be rejected with a response code of 400 (Bad + Request). A proxy MUST remove any such whitespace from a response + message before forwarding the message downstream. + + A field value MAY be preceded by optional whitespace (OWS); a single + SP is preferred. The field value does not include any leading or + trailing white space: OWS occurring before the first non-whitespace + character of the field value or after the last non-whitespace + character of the field value is ignored and SHOULD be removed before + further processing (as this does not change the meaning of the header + field). + + The order in which header fields with differing field names are + received is not significant. However, it is "good practice" to send + header fields that contain control data first, such as Host on + requests and Date on responses, so that implementations can decide + when not to handle a message as early as possible. A server MUST + wait until the entire header section is received before interpreting + a request message, since later header fields might include + conditionals, authentication credentials, or deliberately misleading + duplicate header fields that would impact request processing. + + Multiple header fields with the same field name MUST NOT be sent in a + message unless the entire field value for that header field is + defined as a comma-separated list [i.e., #(values)]. Multiple header + fields with the same field name can be combined into one "field-name: + field-value" pair, without changing the semantics of the message, by + appending each subsequent field value to the combined field value in + order, separated by a comma. The order in which header fields with + the same field name are received is therefore significant to the + interpretation of the combined field value; a proxy MUST NOT change + the order of these field values when forwarding a message. + + Note: The "Set-Cookie" header as implemented in practice (as + opposed to how it is specified in [RFC2109]) can occur multiple + times, but does not use the list syntax, and thus cannot be + combined into a single line. (See Appendix A.2.3 of [Kri2001] for + details.) Also note that the Set-Cookie2 header specified in + [RFC2965] does not share this problem. + + Historically, HTTP header field values could be extended over + multiple lines by preceding each extra line with at least one space + or horizontal tab character (line folding). This specification + deprecates such line folding except within the message/http media + type (Section 10.3.1). HTTP/1.1 senders MUST NOT produce messages + that include line folding (i.e., that contain any field-content that + matches the obs-fold rule) unless the message is intended for + + + +Fielding, et al. Expires February 5, 2011 [Page 21] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + packaging within the message/http media type. HTTP/1.1 recipients + SHOULD accept line folding and replace any embedded obs-fold + whitespace with a single SP prior to interpreting the field value or + forwarding the message downstream. + + Historically, HTTP has allowed field content with text in the ISO- + 8859-1 [ISO-8859-1] character encoding and supported other character + sets only through use of [RFC2047] encoding. In practice, most HTTP + header field values use only a subset of the US-ASCII character + encoding [USASCII]. Newly defined header fields SHOULD limit their + field values to US-ASCII characters. Recipients SHOULD treat other + (obs-text) octets in field content as opaque data. + + Comments can be included in some HTTP header fields by surrounding + the comment text with parentheses. Comments are only allowed in + fields containing "comment" as part of their field value definition. + + comment = "(" *( ctext / quoted-cpair / comment ) ")" + ctext = OWS / %x21-27 / %x2A-5B / %x5D-7E / obs-text + ; OWS / / obs-text + + The backslash character ("\") can be used as a single-character + quoting mechanism within comment constructs: + + quoted-cpair = "\" ( WSP / VCHAR / obs-text ) + + Producers SHOULD NOT escape characters that do not require escaping + (i.e., other than the backslash character "\" and the parentheses "(" + and ")"). + +3.3. Message Body + + The message-body (if any) of an HTTP message is used to carry the + payload body associated with the request or response. + + message-body = *OCTET + + The message-body differs from the payload body only when a transfer- + coding has been applied, as indicated by the Transfer-Encoding header + field (Section 9.7). When one or more transfer-codings are applied + to a payload in order to form the message-body, the Transfer-Encoding + header field MUST contain the list of transfer-codings applied. + Transfer-Encoding is a property of the message, not of the payload, + and thus MAY be added or removed by any implementation along the + request/response chain under the constraints found in Section 6.2. + + The rules for when a message-body is allowed in a message differ for + requests and responses. + + + +Fielding, et al. Expires February 5, 2011 [Page 22] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + The presence of a message-body in a request is signaled by the + inclusion of a Content-Length or Transfer-Encoding header field in + the request's header fields, even if the request method does not + define any use for a message-body. This allows the request message + framing algorithm to be independent of method semantics. + + For response messages, whether or not a message-body is included with + a message is dependent on both the request method and the response + status code (Section 5.1.1). Responses to the HEAD request method + never include a message-body because the associated response header + fields (e.g., Transfer-Encoding, Content-Length, etc.) only indicate + what their values would have been if the method had been GET. All + 1xx (Informational), 204 (No Content), and 304 (Not Modified) + responses MUST NOT include a message-body. All other responses do + include a message-body, although the body MAY be of zero length. + + The length of the message-body is determined by one of the following + (in order of precedence): + + 1. Any response to a HEAD request and any response with a status + code of 100-199, 204, or 304 is always terminated by the first + empty line after the header fields, regardless of the header + fields present in the message, and thus cannot contain a message- + body. + + 2. If a Transfer-Encoding header field (Section 9.7) is present and + the "chunked" transfer-coding (Section 6.2) is the final + encoding, the message-body length is determined by reading and + decoding the chunked data until the transfer-coding indicates the + data is complete. + + If a Transfer-Encoding header field is present in a response and + the "chunked" transfer-coding is not the final encoding, the + message-body length is determined by reading the connection until + it is closed by the server. If a Transfer-Encoding header field + is present in a request and the "chunked" transfer-coding is not + the final encoding, the message-body length cannot be determined + reliably; the server MUST respond with the 400 (Bad Request) + status code and then close the connection. + + If a message is received with both a Transfer-Encoding header + field and a Content-Length header field, the Transfer-Encoding + overrides the Content-Length. Such a message might indicate an + attempt to perform request or response smuggling (bypass of + security-related checks on message routing or content) and thus + ought to be handled as an error. The provided Content-Length + MUST be removed, prior to forwarding the message downstream, or + replaced with the real message-body length after the transfer- + + + +Fielding, et al. Expires February 5, 2011 [Page 23] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + coding is decoded. + + 3. If a message is received without Transfer-Encoding and with + either multiple Content-Length header fields or a single Content- + Length header field with an invalid value, then the message + framing is invalid and MUST be treated as an error to prevent + request or response smuggling. If this is a request message, the + server MUST respond with a 400 (Bad Request) status code and then + close the connection. If this is a response message received by + a proxy or gateway, the proxy or gateway MUST discard the + received response, send a 502 (Bad Gateway) status code as its + downstream response, and then close the connection. If this is a + response message received by a user-agent, the message-body + length is determined by reading the connection until it is + closed; an error SHOULD be indicated to the user. + + 4. If a valid Content-Length header field (Section 9.2) is present + without Transfer-Encoding, its decimal value defines the message- + body length in octets. If the actual number of octets sent in + the message is less than the indicated Content-Length, the + recipient MUST consider the message to be incomplete and treat + the connection as no longer usable. If the actual number of + octets sent in the message is more than the indicated Content- + Length, the recipient MUST only process the message-body up to + the field value's number of octets; the remainder of the message + MUST either be discarded or treated as the next message in a + pipeline. For the sake of robustness, a user-agent MAY attempt + to detect and correct such an error in message framing if it is + parsing the response to the last request on on a connection and + the connection has been closed by the server. + + 5. If this is a request message and none of the above are true, then + the message-body length is zero (no message-body is present). + + 6. Otherwise, this is a response message without a declared message- + body length, so the message-body length is determined by the + number of octets received prior to the server closing the + connection. + + Since there is no way to distinguish a successfully completed, close- + delimited message from a partially-received message interrupted by + network failure, implementations SHOULD use encoding or length- + delimited messages whenever possible. The close-delimiting feature + exists primarily for backwards compatibility with HTTP/1.0. + + A server MAY reject a request that contains a message-body but not a + Content-Length by responding with 411 (Length Required). + + + + +Fielding, et al. Expires February 5, 2011 [Page 24] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Unless a transfer-coding other than "chunked" has been applied, a + client that sends a request containing a message-body SHOULD use a + valid Content-Length header field if the message-body length is known + in advance, rather than the "chunked" encoding, since some existing + services respond to "chunked" with a 411 (Length Required) status + code even though they understand the chunked encoding. This is + typically because such services are implemented via a gateway that + requires a content-length in advance of being called and the server + is unable or unwilling to buffer the entire request before + processing. + + A client that sends a request containing a message-body MUST include + a valid Content-Length header field if it does not know the server + will handle HTTP/1.1 (or later) requests; such knowledge can be in + the form of specific user configuration or by remembering the version + of a prior received response. + + Request messages that are prematurely terminated, possibly due to a + cancelled connection or a server-imposed time-out exception, MUST + result in closure of the connection; sending an HTTP/1.1 error + response prior to closing the connection is OPTIONAL. Response + messages that are prematurely terminated, usually by closure of the + connection prior to receiving the expected number of octets or by + failure to decode a transfer-encoded message-body, MUST be recorded + as incomplete. A user agent MUST NOT render an incomplete response + message-body as if it were complete (i.e., some indication must be + given to the user that an error occurred). Cache requirements for + incomplete responses are defined in Section 2.1.1 of [Part6]. + + A server MUST read the entire request message-body or close the + connection after sending its response, since otherwise the remaining + data on a persistent connection would be misinterpreted as the next + request. Likewise, a client MUST read the entire response message- + body if it intends to reuse the same connection for a subsequent + request. Pipelining multiple requests on a connection is described + in Section 7.1.2.2. + +3.4. General Header Fields + + There are a few header fields which have general applicability for + both request and response messages, but which do not apply to the + payload being transferred. These header fields apply only to the + message being transmitted. + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 25] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + general-header = Cache-Control ; [Part6], Section 3.2 + / Connection ; Section 9.1 + / Date ; Section 9.3 + / Pragma ; [Part6], Section 3.4 + / Trailer ; Section 9.6 + / Transfer-Encoding ; Section 9.7 + / Upgrade ; Section 9.8 + / Via ; Section 9.9 + / Warning ; [Part6], Section 3.6 + / MIME-Version ; [Part3], Appendix A.1 + + General-header field names can be extended reliably only in + combination with a change in the protocol version. However, new or + experimental header fields might be given the semantics of general + header fields if all parties in the communication recognize them to + be general-header fields. + +4. Request + + A request message from a client to a server includes, within the + first line of that message, the method to be applied to the resource, + the identifier of the resource, and the protocol version in use. + + Request = Request-Line ; Section 4.1 + *( header-field CRLF ) ; Section 3.2 + CRLF + [ message-body ] ; Section 3.3 + +4.1. Request-Line + + The Request-Line begins with a method token, followed by the request- + target and the protocol version, and ending with CRLF. The elements + are separated by SP characters. No CR or LF is allowed except in the + final CRLF sequence. + + Request-Line = Method SP request-target SP HTTP-Version CRLF + +4.1.1. Method + + The Method token indicates the method to be performed on the resource + identified by the request-target. The method is case-sensitive. + + Method = token + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 26] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +4.1.2. request-target + + The request-target identifies the resource upon which to apply the + request. + + request-target = "*" + / absolute-URI + / ( path-absolute [ "?" query ] ) + / authority + + The four options for request-target are dependent on the nature of + the request. + + The asterisk "*" means that the request does not apply to a + particular resource, but to the server itself, and is only allowed + when the method used does not necessarily apply to a resource. One + example would be + + OPTIONS * HTTP/1.1 + + The absolute-URI form is REQUIRED when the request is being made to a + proxy. The proxy is requested to forward the request or service it + from a valid cache, and return the response. Note that the proxy MAY + forward the request on to another proxy or directly to the server + specified by the absolute-URI. In order to avoid request loops, a + proxy MUST be able to recognize all of its server names, including + any aliases, local variations, and the numeric IP address. An + example Request-Line would be: + + GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1 + + To allow for transition to absolute-URIs in all requests in future + versions of HTTP, all HTTP/1.1 servers MUST accept the absolute-URI + form in requests, even though HTTP/1.1 clients will only generate + them in requests to proxies. + + The authority form is only used by the CONNECT method (Section 7.9 of + [Part2]). + + The most common form of request-target is that used to identify a + resource on an origin server or gateway. In this case the absolute + path of the URI MUST be transmitted (see Section 2.6.1, path- + absolute) as the request-target, and the network location of the URI + (authority) MUST be transmitted in a Host header field. For example, + a client wishing to retrieve the resource above directly from the + origin server would create a TCP connection to port 80 of the host + "www.example.org" and send the lines: + + + + +Fielding, et al. Expires February 5, 2011 [Page 27] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + GET /pub/WWW/TheProject.html HTTP/1.1 + Host: www.example.org + + followed by the remainder of the Request. Note that the absolute + path cannot be empty; if none is present in the original URI, it MUST + be given as "/" (the server root). + + If a proxy receives a request without any path in the request-target + and the method specified is capable of supporting the asterisk form + of request-target, then the last proxy on the request chain MUST + forward the request with "*" as the final request-target. + + For example, the request + + OPTIONS http://www.example.org:8001 HTTP/1.1 + + would be forwarded by the proxy as + + OPTIONS * HTTP/1.1 + Host: www.example.org:8001 + + after connecting to port 8001 of host "www.example.org". + + The request-target is transmitted in the format specified in + Section 2.6.1. If the request-target is percent-encoded ([RFC3986], + Section 2.1), the origin server MUST decode the request-target in + order to properly interpret the request. Servers SHOULD respond to + invalid request-targets with an appropriate status code. + + A transparent proxy MUST NOT rewrite the "path-absolute" part of the + received request-target when forwarding it to the next inbound + server, except as noted above to replace a null path-absolute with + "/" or "*". + + Note: The "no rewrite" rule prevents the proxy from changing the + meaning of the request when the origin server is improperly using + a non-reserved URI character for a reserved purpose. Implementors + need to be aware that some pre-HTTP/1.1 proxies have been known to + rewrite the request-target. + + HTTP does not place a pre-defined limit on the length of a request- + target. A server MUST be prepared to receive URIs of unbounded + length and respond with the 414 (URI Too Long) status code if the + received request-target would be longer than the server wishes to + handle (see Section 8.4.15 of [Part2]). + + Various ad-hoc limitations on request-target length are found in + practice. It is RECOMMENDED that all HTTP senders and recipients + + + +Fielding, et al. Expires February 5, 2011 [Page 28] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + support request-target lengths of 8000 or more octets. + + Note: Fragments ([RFC3986], Section 3.5) are not part of the + request-target and thus will not be transmitted in an HTTP + request. + +4.2. The Resource Identified by a Request + + The exact resource identified by an Internet request is determined by + examining both the request-target and the Host header field. + + An origin server that does not allow resources to differ by the + requested host MAY ignore the Host header field value when + determining the resource identified by an HTTP/1.1 request. (But see + Appendix B.1.1 for other requirements on Host support in HTTP/1.1.) + + An origin server that does differentiate resources based on the host + requested (sometimes referred to as virtual hosts or vanity host + names) MUST use the following rules for determining the requested + resource on an HTTP/1.1 request: + + 1. If request-target is an absolute-URI, the host is part of the + request-target. Any Host header field value in the request MUST + be ignored. + + 2. If the request-target is not an absolute-URI, and the request + includes a Host header field, the host is determined by the Host + header field value. + + 3. If the host as determined by rule 1 or 2 is not a valid host on + the server, the response MUST be a 400 (Bad Request) error + message. + + Recipients of an HTTP/1.0 request that lacks a Host header field MAY + attempt to use heuristics (e.g., examination of the URI path for + something unique to a particular host) in order to determine what + exact resource is being requested. + +4.3. Effective Request URI + + HTTP requests often do not carry the absolute URI ([RFC3986], Section + 4.3) for the target resource; instead, the URI needs to be inferred + from the request-target, Host header field, and connection context. + The result of this process is called the "effective request URI". + The "target resource" is the resource identified by the effective + request URI. + + If the request-target is an absolute-URI, then the effective request + + + +Fielding, et al. Expires February 5, 2011 [Page 29] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + URI is the request-target. + + If the request-target uses the path-absolute (plus optional query) + syntax or if it is just the asterisk "*", then the effective request + URI is constructed by concatenating + + o the scheme name: "http" if the request was received over an + insecure TCP connection, or "https" when received over a SSL/ + TLS-secured TCP connection, + + o the character sequence "://", + + o the authority component, as specified in the Host header + (Section 9.4) and determined by the rules in Section 4.2, + [[effrequri-nohost: Do we need to include the handling of missing + hosts in HTTP/1.0 messages, as described in Section 4.2? -- See + --jre]] and + + o the request-target obtained from the Request-Line, unless the + request-target is just the asterisk "*". + + Otherwise, when request-target uses the authority form, the effective + Request URI is undefined. + + Example 1: the effective request URI for the message + + GET /pub/WWW/TheProject.html HTTP/1.1 + Host: www.example.org:8080 + + (received over an insecure TCP connection) is "http", plus "://", + plus the authority component "www.example.org:8080", plus the + request-target "/pub/WWW/TheProject.html", thus + "http://www.example.org:8080/pub/WWW/TheProject.html". + + Example 2: the effective request URI for the message + + GET * HTTP/1.1 + Host: www.example.org + + (received over an SSL/TLS secured TCP connection) is "https", plus + "://", plus the authority component "www.example.org", thus + "https://www.example.org". + + Effective request URIs are compared using the rules described in + Section 2.6.3, except that empty path components MUST NOT be treated + as equivalent to an absolute path of "/". + + + + + +Fielding, et al. Expires February 5, 2011 [Page 30] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +5. Response + + After receiving and interpreting a request message, a server responds + with an HTTP response message. + + Response = Status-Line ; Section 5.1 + *( header-field CRLF ) ; Section 3.2 + CRLF + [ message-body ] ; Section 3.3 + +5.1. Status-Line + + The first line of a Response message is the Status-Line, consisting + of the protocol version followed by a numeric status code and its + associated textual phrase, with each element separated by SP + characters. No CR or LF is allowed except in the final CRLF + sequence. + + Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF + +5.1.1. Status Code and Reason Phrase + + The Status-Code element is a 3-digit integer result code of the + attempt to understand and satisfy the request. These codes are fully + defined in Section 8 of [Part2]. The Reason Phrase exists for the + sole purpose of providing a textual description associated with the + numeric status code, out of deference to earlier Internet application + protocols that were more frequently used with interactive text + clients. A client SHOULD ignore the content of the Reason Phrase. + + The first digit of the Status-Code defines the class of response. + The last two digits do not have any categorization role. There are 5 + values for the first digit: + + o 1xx: Informational - Request received, continuing process + + o 2xx: Success - The action was successfully received, understood, + and accepted + + o 3xx: Redirection - Further action must be taken in order to + complete the request + + o 4xx: Client Error - The request contains bad syntax or cannot be + fulfilled + + o 5xx: Server Error - The server failed to fulfill an apparently + valid request + + + + +Fielding, et al. Expires February 5, 2011 [Page 31] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Status-Code = 3DIGIT + Reason-Phrase = *( WSP / VCHAR / obs-text ) + +6. Protocol Parameters + +6.1. Date/Time Formats: Full Date + + HTTP applications have historically allowed three different formats + for date/time stamps. However, the preferred format is a fixed- + length subset of that defined by [RFC1123]: + + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 1123 + + The other formats are described here only for compatibility with + obsolete implementations. + + Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + + HTTP/1.1 clients and servers that parse a date value MUST accept all + three formats (for compatibility with HTTP/1.0), though they MUST + only generate the RFC 1123 format for representing HTTP-date values + in header fields. See Appendix A for further information. + + All HTTP date/time stamps MUST be represented in Greenwich Mean Time + (GMT), without exception. For the purposes of HTTP, GMT is exactly + equal to UTC (Coordinated Universal Time). This is indicated in the + first two formats by the inclusion of "GMT" as the three-letter + abbreviation for time zone, and MUST be assumed when reading the + asctime format. HTTP-date is case sensitive and MUST NOT include + additional whitespace beyond that specifically included as SP in the + grammar. + + HTTP-date = rfc1123-date / obs-date + + Preferred format: + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 32] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT + + day-name = %x4D.6F.6E ; "Mon", case-sensitive + / %x54.75.65 ; "Tue", case-sensitive + / %x57.65.64 ; "Wed", case-sensitive + / %x54.68.75 ; "Thu", case-sensitive + / %x46.72.69 ; "Fri", case-sensitive + / %x53.61.74 ; "Sat", case-sensitive + / %x53.75.6E ; "Sun", case-sensitive + + date1 = day SP month SP year + ; e.g., 02 Jun 1982 + + day = 2DIGIT + month = %x4A.61.6E ; "Jan", case-sensitive + / %x46.65.62 ; "Feb", case-sensitive + / %x4D.61.72 ; "Mar", case-sensitive + / %x41.70.72 ; "Apr", case-sensitive + / %x4D.61.79 ; "May", case-sensitive + / %x4A.75.6E ; "Jun", case-sensitive + / %x4A.75.6C ; "Jul", case-sensitive + / %x41.75.67 ; "Aug", case-sensitive + / %x53.65.70 ; "Sep", case-sensitive + / %x4F.63.74 ; "Oct", case-sensitive + / %x4E.6F.76 ; "Nov", case-sensitive + / %x44.65.63 ; "Dec", case-sensitive + year = 4DIGIT + + GMT = %x47.4D.54 ; "GMT", case-sensitive + + time-of-day = hour ":" minute ":" second + ; 00:00:00 - 23:59:59 + + hour = 2DIGIT + minute = 2DIGIT + second = 2DIGIT + + The semantics of day-name, day, month, year, and time-of-day are the + same as those defined for the RFC 5322 constructs with the + corresponding name ([RFC5322], Section 3.3). + + Obsolete formats: + + obs-date = rfc850-date / asctime-date + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 33] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT + date2 = day "-" month "-" 2DIGIT + ; day-month-year (e.g., 02-Jun-82) + + day-name-l = %x4D.6F.6E.64.61.79 ; "Monday", case-sensitive + / %x54.75.65.73.64.61.79 ; "Tuesday", case-sensitive + / %x57.65.64.6E.65.73.64.61.79 ; "Wednesday", case-sensitive + / %x54.68.75.72.73.64.61.79 ; "Thursday", case-sensitive + / %x46.72.69.64.61.79 ; "Friday", case-sensitive + / %x53.61.74.75.72.64.61.79 ; "Saturday", case-sensitive + / %x53.75.6E.64.61.79 ; "Sunday", case-sensitive + + + asctime-date = day-name SP date3 SP time-of-day SP year + date3 = month SP ( 2DIGIT / ( SP 1DIGIT )) + ; month day (e.g., Jun 2) + + Note: Recipients of date values are encouraged to be robust in + accepting date values that might have been sent by non-HTTP + applications, as is sometimes the case when retrieving or posting + messages via proxies/gateways to SMTP or NNTP. + + Note: HTTP requirements for the date/time stamp format apply only + to their usage within the protocol stream. Clients and servers + are not required to use these formats for user presentation, + request logging, etc. + +6.2. Transfer Codings + + Transfer-coding values are used to indicate an encoding + transformation that has been, can be, or might need to be applied to + a payload body in order to ensure "safe transport" through the + network. This differs from a content coding in that the transfer- + coding is a property of the message rather than a property of the + representation that is being transferred. + + transfer-coding = "chunked" ; Section 6.2.1 + / "compress" ; Section 6.2.2.1 + / "deflate" ; Section 6.2.2.2 + / "gzip" ; Section 6.2.2.3 + / transfer-extension + transfer-extension = token *( OWS ";" OWS transfer-parameter ) + + Parameters are in the form of attribute/value pairs. + + transfer-parameter = attribute BWS "=" BWS value + attribute = token + value = word + + + +Fielding, et al. Expires February 5, 2011 [Page 34] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + All transfer-coding values are case-insensitive. HTTP/1.1 uses + transfer-coding values in the TE header field (Section 9.5) and in + the Transfer-Encoding header field (Section 9.7). + + Transfer-codings are analogous to the Content-Transfer-Encoding + values of MIME, which were designed to enable safe transport of + binary data over a 7-bit transport service ([RFC2045], Section 6). + However, safe transport has a different focus for an 8bit-clean + transfer protocol. In HTTP, the only unsafe characteristic of + message-bodies is the difficulty in determining the exact message + body length (Section 3.3), or the desire to encrypt data over a + shared transport. + + A server that receives a request message with a transfer-coding it + does not understand SHOULD respond with 501 (Not Implemented) and + then close the connection. A server MUST NOT send transfer-codings + to an HTTP/1.0 client. + +6.2.1. Chunked Transfer Coding + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + Chunked-Body = *chunk + last-chunk + trailer-part + CRLF + + chunk = chunk-size *WSP [ chunk-ext ] CRLF + chunk-data CRLF + chunk-size = 1*HEXDIG + last-chunk = 1*("0") *WSP [ chunk-ext ] CRLF + + chunk-ext = *( ";" *WSP chunk-ext-name + [ "=" chunk-ext-val ] *WSP ) + chunk-ext-name = token + chunk-ext-val = token / quoted-str-nf + chunk-data = 1*OCTET ; a sequence of chunk-size octets + trailer-part = *( header-field CRLF ) + + quoted-str-nf = DQUOTE *( qdtext-nf / quoted-pair ) DQUOTE + ; like quoted-string, but disallowing line folding + qdtext-nf = WSP / %x21 / %x23-5B / %x5D-7E / obs-text + ; WSP / / obs-text + + + +Fielding, et al. Expires February 5, 2011 [Page 35] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + The chunk-size field is a string of hex digits indicating the size of + the chunk-data in octets. The chunked encoding is ended by any chunk + whose size is zero, followed by the trailer, which is terminated by + an empty line. + + The trailer allows the sender to include additional HTTP header + fields at the end of the message. The Trailer header field can be + used to indicate which header fields are included in a trailer (see + Section 9.6). + + A server using chunked transfer-coding in a response MUST NOT use the + trailer for any header fields unless at least one of the following is + true: + + 1. the request included a TE header field that indicates "trailers" + is acceptable in the transfer-coding of the response, as + described in Section 9.5; or, + + 2. the server is the origin server for the response, the trailer + fields consist entirely of optional metadata, and the recipient + could use the message (in a manner acceptable to the origin + server) without receiving this metadata. In other words, the + origin server is willing to accept the possibility that the + trailer fields might be silently discarded along the path to the + client. + + This requirement prevents an interoperability failure when the + message is being received by an HTTP/1.1 (or later) proxy and + forwarded to an HTTP/1.0 recipient. It avoids a situation where + compliance with the protocol would have necessitated a possibly + infinite buffer on the proxy. + + A process for decoding the "chunked" transfer-coding can be + represented in pseudo-code as: + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 36] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + length := 0 + read chunk-size, chunk-ext (if any) and CRLF + while (chunk-size > 0) { + read chunk-data and CRLF + append chunk-data to decoded-body + length := length + chunk-size + read chunk-size and CRLF + } + read header-field + while (header-field not empty) { + append header-field to existing header fields + read header-field + } + Content-Length := length + Remove "chunked" from Transfer-Encoding + + All HTTP/1.1 applications MUST be able to receive and decode the + "chunked" transfer-coding and MUST ignore chunk-ext extensions they + do not understand. + + Since "chunked" is the only transfer-coding required to be understood + by HTTP/1.1 recipients, it plays a crucial role in delimiting + messages on a persistent connection. Whenever a transfer-coding is + applied to a payload body in a request, the final transfer-coding + applied MUST be "chunked". If a transfer-coding is applied to a + response payload body, then either the final transfer-coding applied + MUST be "chunked" or the message MUST be terminated by closing the + connection. When the "chunked" transfer-coding is used, it MUST be + the last transfer-coding applied to form the message-body. The + "chunked" transfer-coding MUST NOT be applied more than once in a + message-body. + +6.2.2. Compression Codings + + The codings defined below can be used to compress the payload of a + message. + + Note: Use of program names for the identification of encoding + formats is not desirable and is discouraged for future encodings. + Their use here is representative of historical practice, not good + design. + + Note: For compatibility with previous implementations of HTTP, + applications SHOULD consider "x-gzip" and "x-compress" to be + equivalent to "gzip" and "compress" respectively. + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 37] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +6.2.2.1. Compress Coding + + The "compress" format is produced by the common UNIX file compression + program "compress". This format is an adaptive Lempel-Ziv-Welch + coding (LZW). + +6.2.2.2. Deflate Coding + + The "deflate" format is defined as the "deflate" compression + mechanism (described in [RFC1951]) used inside the "zlib" data format + ([RFC1950]). + + Note: Some incorrect implementations send the "deflate" compressed + data without the zlib wrapper. + +6.2.2.3. Gzip Coding + + The "gzip" format is produced by the file compression program "gzip" + (GNU zip), as described in [RFC1952]. This format is a Lempel-Ziv + coding (LZ77) with a 32 bit CRC. + +6.2.3. Transfer Coding Registry + + The HTTP Transfer Coding Registry defines the name space for the + transfer coding names. + + Registrations MUST include the following fields: + + o Name + + o Description + + o Pointer to specification text + + Names of transfer codings MUST NOT overlap with names of content + codings (Section 2.2 of [Part3]), unless the encoding transformation + is identical (as it is the case for the compression codings defined + in Section 6.2.2). + + Values to be added to this name space require a specification (see + "Specification Required" in Section 4.1 of [RFC5226]), and MUST + conform to the purpose of transfer coding defined in this section. + + The registry itself is maintained at + . + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 38] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +6.3. Product Tokens + + Product tokens are used to allow communicating applications to + identify themselves by software name and version. Most fields using + product tokens also allow sub-products which form a significant part + of the application to be listed, separated by whitespace. By + convention, the products are listed in order of their significance + for identifying the application. + + product = token ["/" product-version] + product-version = token + + Examples: + + User-Agent: CERN-LineMode/2.15 libwww/2.17b3 + Server: Apache/0.8.4 + + Product tokens SHOULD be short and to the point. They MUST NOT be + used for advertising or other non-essential information. Although + any token character MAY appear in a product-version, this token + SHOULD only be used for a version identifier (i.e., successive + versions of the same product SHOULD only differ in the product- + version portion of the product value). + +6.4. Quality Values + + Both transfer codings (TE request header, Section 9.5) and content + negotiation (Section 5 of [Part3]) use short "floating point" numbers + to indicate the relative importance ("weight") of various negotiable + parameters. A weight is normalized to a real number in the range 0 + through 1, where 0 is the minimum and 1 the maximum value. If a + parameter has a quality value of 0, then content with this parameter + is "not acceptable" for the client. HTTP/1.1 applications MUST NOT + generate more than three digits after the decimal point. User + configuration of these values SHOULD also be limited in this fashion. + + qvalue = ( "0" [ "." 0*3DIGIT ] ) + / ( "1" [ "." 0*3("0") ] ) + + Note: "Quality values" is a misnomer, since these values merely + represent relative degradation in desired quality. + +7. Connections + +7.1. Persistent Connections + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 39] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +7.1.1. Purpose + + Prior to persistent connections, a separate TCP connection was + established to fetch each URL, increasing the load on HTTP servers + and causing congestion on the Internet. The use of inline images and + other associated data often requires a client to make multiple + requests of the same server in a short amount of time. Analysis of + these performance problems and results from a prototype + implementation are available [Pad1995] [Spe]. Implementation + experience and measurements of actual HTTP/1.1 implementations show + good results [Nie1997]. Alternatives have also been explored, for + example, T/TCP [Tou1998]. + + Persistent HTTP connections have a number of advantages: + + o By opening and closing fewer TCP connections, CPU time is saved in + routers and hosts (clients, servers, proxies, gateways, tunnels, + or caches), and memory used for TCP protocol control blocks can be + saved in hosts. + + o HTTP requests and responses can be pipelined on a connection. + Pipelining allows a client to make multiple requests without + waiting for each response, allowing a single TCP connection to be + used much more efficiently, with much lower elapsed time. + + o Network congestion is reduced by reducing the number of packets + caused by TCP opens, and by allowing TCP sufficient time to + determine the congestion state of the network. + + o Latency on subsequent requests is reduced since there is no time + spent in TCP's connection opening handshake. + + o HTTP can evolve more gracefully, since errors can be reported + without the penalty of closing the TCP connection. Clients using + future versions of HTTP might optimistically try a new feature, + but if communicating with an older server, retry with old + semantics after an error is reported. + + HTTP implementations SHOULD implement persistent connections. + +7.1.2. Overall Operation + + A significant difference between HTTP/1.1 and earlier versions of + HTTP is that persistent connections are the default behavior of any + HTTP connection. That is, unless otherwise indicated, the client + SHOULD assume that the server will maintain a persistent connection, + even after error responses from the server. + + + + +Fielding, et al. Expires February 5, 2011 [Page 40] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Persistent connections provide a mechanism by which a client and a + server can signal the close of a TCP connection. This signaling + takes place using the Connection header field (Section 9.1). Once a + close has been signaled, the client MUST NOT send any more requests + on that connection. + +7.1.2.1. Negotiation + + An HTTP/1.1 server MAY assume that a HTTP/1.1 client intends to + maintain a persistent connection unless a Connection header including + the connection-token "close" was sent in the request. If the server + chooses to close the connection immediately after sending the + response, it SHOULD send a Connection header including the + connection-token "close". + + An HTTP/1.1 client MAY expect a connection to remain open, but would + decide to keep it open based on whether the response from a server + contains a Connection header with the connection-token close. In + case the client does not want to maintain a connection for more than + that request, it SHOULD send a Connection header including the + connection-token close. + + If either the client or the server sends the close token in the + Connection header, that request becomes the last one for the + connection. + + Clients and servers SHOULD NOT assume that a persistent connection is + maintained for HTTP versions less than 1.1 unless it is explicitly + signaled. See Appendix B.2 for more information on backward + compatibility with HTTP/1.0 clients. + + In order to remain persistent, all messages on the connection MUST + have a self-defined message length (i.e., one not defined by closure + of the connection), as described in Section 3.3. + +7.1.2.2. Pipelining + + A client that supports persistent connections MAY "pipeline" its + requests (i.e., send multiple requests without waiting for each + response). A server MUST send its responses to those requests in the + same order that the requests were received. + + Clients which assume persistent connections and pipeline immediately + after connection establishment SHOULD be prepared to retry their + connection if the first pipelined attempt fails. If a client does + such a retry, it MUST NOT pipeline before it knows the connection is + persistent. Clients MUST also be prepared to resend their requests + if the server closes the connection before sending all of the + + + +Fielding, et al. Expires February 5, 2011 [Page 41] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + corresponding responses. + + Clients SHOULD NOT pipeline requests using non-idempotent methods or + non-idempotent sequences of methods (see Section 7.1.2 of [Part2]). + Otherwise, a premature termination of the transport connection could + lead to indeterminate results. A client wishing to send a non- + idempotent request SHOULD wait to send that request until it has + received the response status line for the previous request. + +7.1.3. Proxy Servers + + It is especially important that proxies correctly implement the + properties of the Connection header field as specified in + Section 9.1. + + The proxy server MUST signal persistent connections separately with + its clients and the origin servers (or other proxy servers) that it + connects to. Each persistent connection applies to only one + transport link. + + A proxy server MUST NOT establish a HTTP/1.1 persistent connection + with an HTTP/1.0 client (but see Section 19.7.1 of [RFC2068] for + information and discussion of the problems with the Keep-Alive header + implemented by many HTTP/1.0 clients). + +7.1.3.1. End-to-end and Hop-by-hop Headers + + For the purpose of defining the behavior of caches and non-caching + proxies, we divide HTTP headers into two categories: + + o End-to-end headers, which are transmitted to the ultimate + recipient of a request or response. End-to-end headers in + responses MUST be stored as part of a cache entry and MUST be + transmitted in any response formed from a cache entry. + + o Hop-by-hop headers, which are meaningful only for a single + transport-level connection, and are not stored by caches or + forwarded by proxies. + + The following HTTP/1.1 headers are hop-by-hop headers: + + o Connection + + o Keep-Alive + + o Proxy-Authenticate + + + + + +Fielding, et al. Expires February 5, 2011 [Page 42] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + o Proxy-Authorization + + o TE + + o Trailer + + o Transfer-Encoding + + o Upgrade + + All other headers defined by HTTP/1.1 are end-to-end headers. + + Other hop-by-hop headers MUST be listed in a Connection header + (Section 9.1). + +7.1.3.2. Non-modifiable Headers + + Some features of HTTP/1.1, such as Digest Authentication, depend on + the value of certain end-to-end headers. A transparent proxy SHOULD + NOT modify an end-to-end header unless the definition of that header + requires or specifically allows that. + + A transparent proxy MUST NOT modify any of the following fields in a + request or response, and it MUST NOT add any of these fields if not + already present: + + o Content-Location + + o Content-MD5 + + o ETag + + o Last-Modified + + A transparent proxy MUST NOT modify any of the following fields in a + response: + + o Expires + + but it MAY add any of these fields if not already present. If an + Expires header is added, it MUST be given a field-value identical to + that of the Date header in that response. + + A proxy MUST NOT modify or add any of the following fields in a + message that contains the no-transform cache-control directive, or in + any request: + + + + + +Fielding, et al. Expires February 5, 2011 [Page 43] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + o Content-Encoding + + o Content-Range + + o Content-Type + + A non-transparent proxy MAY modify or add these fields to a message + that does not include no-transform, but if it does so, it MUST add a + Warning 214 (Transformation applied) if one does not already appear + in the message (see Section 3.6 of [Part6]). + + Warning: Unnecessary modification of end-to-end headers might + cause authentication failures if stronger authentication + mechanisms are introduced in later versions of HTTP. Such + authentication mechanisms MAY rely on the values of header fields + not listed here. + + A transparent proxy MUST preserve the message payload ([Part3]), + though it MAY change the message-body through application or removal + of a transfer-coding (Section 6.2). + +7.1.4. Practical Considerations + + Servers will usually have some time-out value beyond which they will + no longer maintain an inactive connection. Proxy servers might make + this a higher value since it is likely that the client will be making + more connections through the same server. The use of persistent + connections places no requirements on the length (or existence) of + this time-out for either the client or the server. + + When a client or server wishes to time-out it SHOULD issue a graceful + close on the transport connection. Clients and servers SHOULD both + constantly watch for the other side of the transport close, and + respond to it as appropriate. If a client or server does not detect + the other side's close promptly it could cause unnecessary resource + drain on the network. + + A client, server, or proxy MAY close the transport connection at any + time. For example, a client might have started to send a new request + at the same time that the server has decided to close the "idle" + connection. From the server's point of view, the connection is being + closed while it was idle, but from the client's point of view, a + request is in progress. + + This means that clients, servers, and proxies MUST be able to recover + from asynchronous close events. Client software SHOULD reopen the + transport connection and retransmit the aborted sequence of requests + without user interaction so long as the request sequence is + + + +Fielding, et al. Expires February 5, 2011 [Page 44] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + idempotent (see Section 7.1.2 of [Part2]). Non-idempotent methods or + sequences MUST NOT be automatically retried, although user agents MAY + offer a human operator the choice of retrying the request(s). + Confirmation by user-agent software with semantic understanding of + the application MAY substitute for user confirmation. The automatic + retry SHOULD NOT be repeated if the second sequence of requests + fails. + + Servers SHOULD always respond to at least one request per connection, + if at all possible. Servers SHOULD NOT close a connection in the + middle of transmitting a response, unless a network or client failure + is suspected. + + Clients (including proxies) SHOULD limit the number of simultaneous + connections that they maintain to a given server (including proxies). + + Previous revisions of HTTP gave a specific number of connections as a + ceiling, but this was found to be impractical for many applications. + As a result, this specification does not mandate a particular maximum + number of connections, but instead encourages clients to be + conservative when opening multiple connections. + + In particular, while using multiple connections avoids the "head-of- + line blocking" problem (whereby a request that takes significant + server-side processing and/or has a large payload can block + subsequent requests on the same connection), each connection used + consumes server resources (sometimes significantly), and furthermore + using multiple connections can cause undesirable side effects in + congested networks. + + Note that servers might reject traffic that they deem abusive, + including an excessive number of connections from a client. + +7.2. Message Transmission Requirements + +7.2.1. Persistent Connections and Flow Control + + HTTP/1.1 servers SHOULD maintain persistent connections and use TCP's + flow control mechanisms to resolve temporary overloads, rather than + terminating connections with the expectation that clients will retry. + The latter technique can exacerbate network congestion. + +7.2.2. Monitoring Connections for Error Status Messages + + An HTTP/1.1 (or later) client sending a message-body SHOULD monitor + the network connection for an error status code while it is + transmitting the request. If the client sees an error status code, + it SHOULD immediately cease transmitting the body. If the body is + + + +Fielding, et al. Expires February 5, 2011 [Page 45] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + being sent using a "chunked" encoding (Section 6.2), a zero length + chunk and empty trailer MAY be used to prematurely mark the end of + the message. If the body was preceded by a Content-Length header, + the client MUST close the connection. + +7.2.3. Use of the 100 (Continue) Status + + The purpose of the 100 (Continue) status code (see Section 8.1.1 of + [Part2]) is to allow a client that is sending a request message with + a request body to determine if the origin server is willing to accept + the request (based on the request headers) before the client sends + the request body. In some cases, it might either be inappropriate or + highly inefficient for the client to send the body if the server will + reject the message without looking at the body. + + Requirements for HTTP/1.1 clients: + + o If a client will wait for a 100 (Continue) response before sending + the request body, it MUST send an Expect request-header field + (Section 9.2 of [Part2]) with the "100-continue" expectation. + + o A client MUST NOT send an Expect request-header field (Section 9.2 + of [Part2]) with the "100-continue" expectation if it does not + intend to send a request body. + + Because of the presence of older implementations, the protocol allows + ambiguous situations in which a client might send "Expect: 100- + continue" without receiving either a 417 (Expectation Failed) or a + 100 (Continue) status code. Therefore, when a client sends this + header field to an origin server (possibly via a proxy) from which it + has never seen a 100 (Continue) status code, the client SHOULD NOT + wait for an indefinite period before sending the request body. + + Requirements for HTTP/1.1 origin servers: + + o Upon receiving a request which includes an Expect request-header + field with the "100-continue" expectation, an origin server MUST + either respond with 100 (Continue) status code and continue to + read from the input stream, or respond with a final status code. + The origin server MUST NOT wait for the request body before + sending the 100 (Continue) response. If it responds with a final + status code, it MAY close the transport connection or it MAY + continue to read and discard the rest of the request. It MUST NOT + perform the requested method if it returns a final status code. + + o An origin server SHOULD NOT send a 100 (Continue) response if the + request message does not include an Expect request-header field + with the "100-continue" expectation, and MUST NOT send a 100 + + + +Fielding, et al. Expires February 5, 2011 [Page 46] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + (Continue) response if such a request comes from an HTTP/1.0 (or + earlier) client. There is an exception to this rule: for + compatibility with [RFC2068], a server MAY send a 100 (Continue) + status code in response to an HTTP/1.1 PUT or POST request that + does not include an Expect request-header field with the "100- + continue" expectation. This exception, the purpose of which is to + minimize any client processing delays associated with an + undeclared wait for 100 (Continue) status code, applies only to + HTTP/1.1 requests, and not to requests with any other HTTP-version + value. + + o An origin server MAY omit a 100 (Continue) response if it has + already received some or all of the request body for the + corresponding request. + + o An origin server that sends a 100 (Continue) response MUST + ultimately send a final status code, once the request body is + received and processed, unless it terminates the transport + connection prematurely. + + o If an origin server receives a request that does not include an + Expect request-header field with the "100-continue" expectation, + the request includes a request body, and the server responds with + a final status code before reading the entire request body from + the transport connection, then the server SHOULD NOT close the + transport connection until it has read the entire request, or + until the client closes the connection. Otherwise, the client + might not reliably receive the response message. However, this + requirement is not be construed as preventing a server from + defending itself against denial-of-service attacks, or from badly + broken client implementations. + + Requirements for HTTP/1.1 proxies: + + o If a proxy receives a request that includes an Expect request- + header field with the "100-continue" expectation, and the proxy + either knows that the next-hop server complies with HTTP/1.1 or + higher, or does not know the HTTP version of the next-hop server, + it MUST forward the request, including the Expect header field. + + o If the proxy knows that the version of the next-hop server is + HTTP/1.0 or lower, it MUST NOT forward the request, and it MUST + respond with a 417 (Expectation Failed) status code. + + o Proxies SHOULD maintain a cache recording the HTTP version numbers + received from recently-referenced next-hop servers. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 47] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + o A proxy MUST NOT forward a 100 (Continue) response if the request + message was received from an HTTP/1.0 (or earlier) client and did + not include an Expect request-header field with the "100-continue" + expectation. This requirement overrides the general rule for + forwarding of 1xx responses (see Section 8.1 of [Part2]). + +7.2.4. Client Behavior if Server Prematurely Closes Connection + + If an HTTP/1.1 client sends a request which includes a request body, + but which does not include an Expect request-header field with the + "100-continue" expectation, and if the client is not directly + connected to an HTTP/1.1 origin server, and if the client sees the + connection close before receiving a status line from the server, the + client SHOULD retry the request. If the client does retry this + request, it MAY use the following "binary exponential backoff" + algorithm to be assured of obtaining a reliable response: + + 1. Initiate a new connection to the server + + 2. Transmit the request-headers + + 3. Initialize a variable R to the estimated round-trip time to the + server (e.g., based on the time it took to establish the + connection), or to a constant value of 5 seconds if the round- + trip time is not available. + + 4. Compute T = R * (2**N), where N is the number of previous retries + of this request. + + 5. Wait either for an error response from the server, or for T + seconds (whichever comes first) + + 6. If no error response is received, after T seconds transmit the + body of the request. + + 7. If client sees that the connection is closed prematurely, repeat + from step 1 until the request is accepted, an error response is + received, or the user becomes impatient and terminates the retry + process. + + If at any point an error status code is received, the client + + o SHOULD NOT continue and + + o SHOULD close the connection if it has not completed sending the + request message. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 48] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +8. Miscellaneous notes that might disappear + +8.1. Scheme aliases considered harmful + + [[TBD-aliases-harmful: describe why aliases like webcal are + harmful.]] + +8.2. Use of HTTP for proxy communication + + [[TBD-proxy-other: Configured to use HTTP to proxy HTTP or other + protocols.]] + +8.3. Interception of HTTP for access control + + [[TBD-intercept: Interception of HTTP traffic for initiating access + control.]] + +8.4. Use of HTTP by other protocols + + [[TBD-profiles: Profiles of HTTP defined by other protocol. + Extensions of HTTP like WebDAV.]] + +8.5. Use of HTTP by media type specification + + [[TBD-hypertext: Instructions on composing HTTP requests via + hypertext formats.]] + +9. Header Field Definitions + + This section defines the syntax and semantics of HTTP/1.1 header + fields related to message framing and transport protocols. + +9.1. Connection + + The "Connection" general-header field allows the sender to specify + options that are desired for that particular connection and MUST NOT + be communicated by proxies over further connections. + + The Connection header's value has the following grammar: + + Connection = "Connection" ":" OWS Connection-v + Connection-v = 1#connection-token + connection-token = token + + HTTP/1.1 proxies MUST parse the Connection header field before a + message is forwarded and, for each connection-token in this field, + remove any header field(s) from the message with the same name as the + connection-token. Connection options are signaled by the presence of + + + +Fielding, et al. Expires February 5, 2011 [Page 49] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + a connection-token in the Connection header field, not by any + corresponding additional header field(s), since the additional header + field might not be sent if there are no parameters associated with + that connection option. + + Message headers listed in the Connection header MUST NOT include end- + to-end headers, such as Cache-Control. + + HTTP/1.1 defines the "close" connection option for the sender to + signal that the connection will be closed after completion of the + response. For example, + + Connection: close + + in either the request or the response header fields indicates that + the connection SHOULD NOT be considered "persistent" (Section 7.1) + after the current request/response is complete. + + An HTTP/1.1 client that does not support persistent connections MUST + include the "close" connection option in every request message. + + An HTTP/1.1 server that does not support persistent connections MUST + include the "close" connection option in every response message that + does not have a 1xx (Informational) status code. + + A system receiving an HTTP/1.0 (or lower-version) message that + includes a Connection header MUST, for each connection-token in this + field, remove and ignore any header field(s) from the message with + the same name as the connection-token. This protects against + mistaken forwarding of such header fields by pre-HTTP/1.1 proxies. + See Appendix B.2. + +9.2. Content-Length + + The "Content-Length" header field indicates the size of the message- + body, in decimal number of octets, for any message other than a + response to the HEAD method or a response with a status code of 304. + In the case of responses to the HEAD method, it indicates the size of + the payload body (not including any potential transfer-coding) that + would have been sent had the request been a GET. In the case of the + 304 (Not Modified) response, it indicates the size of the payload + body (not including any potential transfer-coding) that would have + been sent in a 200 (OK) response. + + Content-Length = "Content-Length" ":" OWS 1*Content-Length-v + Content-Length-v = 1*DIGIT + + An example is + + + +Fielding, et al. Expires February 5, 2011 [Page 50] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Content-Length: 3495 + + Implementations SHOULD use this field to indicate the message-body + length when no transfer-coding is being applied and the payload's + body length can be determined prior to being transferred. + Section 3.3 describes how recipients determine the length of a + message-body. + + Any Content-Length greater than or equal to zero is a valid value. + + Note that the use of this field in HTTP is significantly different + from the corresponding definition in MIME, where it is an optional + field used within the "message/external-body" content-type. + +9.3. Date + + The "Date" general-header field represents the date and time at which + the message was originated, having the same semantics as the + Origination Date Field (orig-date) defined in Section 3.6.1 of + [RFC5322]. The field value is an HTTP-date, as described in + Section 6.1; it MUST be sent in rfc1123-date format. + + Date = "Date" ":" OWS Date-v + Date-v = HTTP-date + + An example is + + Date: Tue, 15 Nov 1994 08:12:31 GMT + + Origin servers MUST include a Date header field in all responses, + except in these cases: + + 1. If the response status code is 100 (Continue) or 101 (Switching + Protocols), the response MAY include a Date header field, at the + server's option. + + 2. If the response status code conveys a server error, e.g., 500 + (Internal Server Error) or 503 (Service Unavailable), and it is + inconvenient or impossible to generate a valid Date. + + 3. If the server does not have a clock that can provide a reasonable + approximation of the current time, its responses MUST NOT include + a Date header field. In this case, the rules in Section 9.3.1 + MUST be followed. + + A received message that does not have a Date header field MUST be + assigned one by the recipient if the message will be cached by that + recipient or gatewayed via a protocol which requires a Date. An HTTP + + + +Fielding, et al. Expires February 5, 2011 [Page 51] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + implementation without a clock MUST NOT cache responses without + revalidating them on every use. An HTTP cache, especially a shared + cache, SHOULD use a mechanism, such as NTP [RFC1305], to synchronize + its clock with a reliable external standard. + + Clients SHOULD only send a Date header field in messages that include + a payload, as is usually the case for PUT and POST requests, and even + then it is optional. A client without a clock MUST NOT send a Date + header field in a request. + + The HTTP-date sent in a Date header SHOULD NOT represent a date and + time subsequent to the generation of the message. It SHOULD + represent the best available approximation of the date and time of + message generation, unless the implementation has no means of + generating a reasonably accurate date and time. In theory, the date + ought to represent the moment just before the payload is generated. + In practice, the date can be generated at any time during the message + origination without affecting its semantic value. + +9.3.1. Clockless Origin Server Operation + + Some origin server implementations might not have a clock available. + An origin server without a clock MUST NOT assign Expires or Last- + Modified values to a response, unless these values were associated + with the resource by a system or user with a reliable clock. It MAY + assign an Expires value that is known, at or before server + configuration time, to be in the past (this allows "pre-expiration" + of responses without storing separate Expires values for each + resource). + +9.4. Host + + The "Host" request-header field specifies the Internet host and port + number of the resource being requested, allowing the origin server or + gateway to differentiate between internally-ambiguous URLs, such as + the root "/" URL of a server for multiple host names on a single IP + address. + + The Host field value MUST represent the naming authority of the + origin server or gateway given by the original URL obtained from the + user or referring resource (generally an http URI, as described in + Section 2.6.1). + + Host = "Host" ":" OWS Host-v + Host-v = uri-host [ ":" port ] ; Section 2.6.1 + + A "host" without any trailing port information implies the default + port for the service requested (e.g., "80" for an HTTP URL). For + + + +Fielding, et al. Expires February 5, 2011 [Page 52] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + example, a request on the origin server for + would properly include: + + GET /pub/WWW/ HTTP/1.1 + Host: www.example.org + + A client MUST include a Host header field in all HTTP/1.1 request + messages. If the requested URI does not include an Internet host + name for the service being requested, then the Host header field MUST + be given with an empty value. An HTTP/1.1 proxy MUST ensure that any + request message it forwards does contain an appropriate Host header + field that identifies the service being requested by the proxy. All + Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request) + status code to any HTTP/1.1 request message which lacks a Host header + field. + + See Sections 4.2 and B.1.1 for other requirements relating to Host. + +9.5. TE + + The "TE" request-header field indicates what extension transfer- + codings it is willing to accept in the response, and whether or not + it is willing to accept trailer fields in a chunked transfer-coding. + + Its value consists of the keyword "trailers" and/or a comma-separated + list of extension transfer-coding names with optional accept + parameters (as described in Section 6.2). + + TE = "TE" ":" OWS TE-v + TE-v = #t-codings + t-codings = "trailers" / ( transfer-extension [ te-params ] ) + te-params = OWS ";" OWS "q=" qvalue *( te-ext ) + te-ext = OWS ";" OWS token [ "=" word ] + + The presence of the keyword "trailers" indicates that the client is + willing to accept trailer fields in a chunked transfer-coding, as + defined in Section 6.2.1. This keyword is reserved for use with + transfer-coding values even though it does not itself represent a + transfer-coding. + + Examples of its use are: + + TE: deflate + TE: + TE: trailers, deflate;q=0.5 + + The TE header field only applies to the immediate connection. + Therefore, the keyword MUST be supplied within a Connection header + + + +Fielding, et al. Expires February 5, 2011 [Page 53] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + field (Section 9.1) whenever TE is present in an HTTP/1.1 message. + + A server tests whether a transfer-coding is acceptable, according to + a TE field, using these rules: + + 1. The "chunked" transfer-coding is always acceptable. If the + keyword "trailers" is listed, the client indicates that it is + willing to accept trailer fields in the chunked response on + behalf of itself and any downstream clients. The implication is + that, if given, the client is stating that either all downstream + clients are willing to accept trailer fields in the forwarded + response, or that it will attempt to buffer the response on + behalf of downstream recipients. + + Note: HTTP/1.1 does not define any means to limit the size of a + chunked response such that a client can be assured of buffering + the entire response. + + 2. If the transfer-coding being tested is one of the transfer- + codings listed in the TE field, then it is acceptable unless it + is accompanied by a qvalue of 0. (As defined in Section 6.4, a + qvalue of 0 means "not acceptable".) + + 3. If multiple transfer-codings are acceptable, then the acceptable + transfer-coding with the highest non-zero qvalue is preferred. + The "chunked" transfer-coding always has a qvalue of 1. + + If the TE field-value is empty or if no TE field is present, the only + transfer-coding is "chunked". A message with no transfer-coding is + always acceptable. + +9.6. Trailer + + The "Trailer" general-header field indicates that the given set of + header fields is present in the trailer of a message encoded with + chunked transfer-coding. + + Trailer = "Trailer" ":" OWS Trailer-v + Trailer-v = 1#field-name + + An HTTP/1.1 message SHOULD include a Trailer header field in a + message using chunked transfer-coding with a non-empty trailer. + Doing so allows the recipient to know which header fields to expect + in the trailer. + + If no Trailer header field is present, the trailer SHOULD NOT include + any header fields. See Section 6.2.1 for restrictions on the use of + trailer fields in a "chunked" transfer-coding. + + + +Fielding, et al. Expires February 5, 2011 [Page 54] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Message header fields listed in the Trailer header field MUST NOT + include the following header fields: + + o Transfer-Encoding + + o Content-Length + + o Trailer + +9.7. Transfer-Encoding + + The "Transfer-Encoding" general-header field indicates what transfer- + codings (if any) have been applied to the message body. It differs + from Content-Encoding (Section 2.2 of [Part3]) in that transfer- + codings are a property of the message (and therefore are removed by + intermediaries), whereas content-codings are not. + + Transfer-Encoding = "Transfer-Encoding" ":" OWS + Transfer-Encoding-v + Transfer-Encoding-v = 1#transfer-coding + + Transfer-codings are defined in Section 6.2. An example is: + + Transfer-Encoding: chunked + + If multiple encodings have been applied to a representation, the + transfer-codings MUST be listed in the order in which they were + applied. Additional information about the encoding parameters MAY be + provided by other header fields not defined by this specification. + + Many older HTTP/1.0 applications do not understand the Transfer- + Encoding header. + +9.8. Upgrade + + The "Upgrade" general-header field allows the client to specify what + additional communication protocols it would like to use, if the + server chooses to switch protocols. Additionally, the server MUST + use the Upgrade header field within a 101 (Switching Protocols) + response to indicate which protocol(s) are being switched to. + + Upgrade = "Upgrade" ":" OWS Upgrade-v + Upgrade-v = 1#product + + For example, + + Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 + + + + +Fielding, et al. Expires February 5, 2011 [Page 55] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + The Upgrade header field is intended to provide a simple mechanism + for transition from HTTP/1.1 to some other, incompatible protocol. + It does so by allowing the client to advertise its desire to use + another protocol, such as a later version of HTTP with a higher major + version number, even though the current request has been made using + HTTP/1.1. This eases the difficult transition between incompatible + protocols by allowing the client to initiate a request in the more + commonly supported protocol while indicating to the server that it + would like to use a "better" protocol if available (where "better" is + determined by the server, possibly according to the nature of the + method and/or resource being requested). + + The Upgrade header field only applies to switching application-layer + protocols upon the existing transport-layer connection. Upgrade + cannot be used to insist on a protocol change; its acceptance and use + by the server is optional. The capabilities and nature of the + application-layer communication after the protocol change is entirely + dependent upon the new protocol chosen, although the first action + after changing the protocol MUST be a response to the initial HTTP + request containing the Upgrade header field. + + The Upgrade header field only applies to the immediate connection. + Therefore, the upgrade keyword MUST be supplied within a Connection + header field (Section 9.1) whenever Upgrade is present in an HTTP/1.1 + message. + + The Upgrade header field cannot be used to indicate a switch to a + protocol on a different connection. For that purpose, it is more + appropriate to use a 301, 302, 303, or 305 redirection response. + + This specification only defines the protocol name "HTTP" for use by + the family of Hypertext Transfer Protocols, as defined by the HTTP + version rules of Section 2.5 and future updates to this + specification. Additional tokens can be registered with IANA using + the registration procedure defined below. + +9.8.1. Upgrade Token Registry + + The HTTP Upgrade Token Registry defines the name space for product + tokens used to identify protocols in the Upgrade header field. Each + registered token is associated with contact information and an + optional set of specifications that details how the connection will + be processed after it has been upgraded. + + Registrations are allowed on a First Come First Served basis as + described in Section 4.1 of [RFC5226]. The specifications need not + be IETF documents or be subject to IESG review. Registrations are + subject to the following rules: + + + +Fielding, et al. Expires February 5, 2011 [Page 56] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + 1. A token, once registered, stays registered forever. + + 2. The registration MUST name a responsible party for the + registration. + + 3. The registration MUST name a point of contact. + + 4. The registration MAY name a set of specifications associated with + that token. Such specifications need not be publicly available. + + 5. The responsible party MAY change the registration at any time. + The IANA will keep a record of all such changes, and make them + available upon request. + + 6. The responsible party for the first registration of a "product" + token MUST approve later registrations of a "version" token + together with that "product" token before they can be registered. + + 7. If absolutely required, the IESG MAY reassign the responsibility + for a token. This will normally only be used in the case when a + responsible party cannot be contacted. + +9.9. Via + + The "Via" general-header field MUST be used by gateways and proxies + to indicate the intermediate protocols and recipients between the + user agent and the server on requests, and between the origin server + and the client on responses. It is analogous to the "Received" field + defined in Section 3.6.7 of [RFC5322] and is intended to be used for + tracking message forwards, avoiding request loops, and identifying + the protocol capabilities of all senders along the request/response + chain. + + Via = "Via" ":" OWS Via-v + Via-v = 1#( received-protocol RWS received-by + [ RWS comment ] ) + received-protocol = [ protocol-name "/" ] protocol-version + protocol-name = token + protocol-version = token + received-by = ( uri-host [ ":" port ] ) / pseudonym + pseudonym = token + + The received-protocol indicates the protocol version of the message + received by the server or client along each segment of the request/ + response chain. The received-protocol version is appended to the Via + field value when the message is forwarded so that information about + the protocol capabilities of upstream applications remains visible to + all recipients. + + + +Fielding, et al. Expires February 5, 2011 [Page 57] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + The protocol-name is optional if and only if it would be "HTTP". The + received-by field is normally the host and optional port number of a + recipient server or client that subsequently forwarded the message. + However, if the real host is considered to be sensitive information, + it MAY be replaced by a pseudonym. If the port is not given, it MAY + be assumed to be the default port of the received-protocol. + + Multiple Via field values represent each proxy or gateway that has + forwarded the message. Each recipient MUST append its information + such that the end result is ordered according to the sequence of + forwarding applications. + + Comments MAY be used in the Via header field to identify the software + of the recipient proxy or gateway, analogous to the User-Agent and + Server header fields. However, all comments in the Via field are + optional and MAY be removed by any recipient prior to forwarding the + message. + + For example, a request message could be sent from an HTTP/1.0 user + agent to an internal proxy code-named "fred", which uses HTTP/1.1 to + forward the request to a public proxy at p.example.net, which + completes the request by forwarding it to the origin server at + www.example.com. The request received by www.example.com would then + have the following Via header field: + + Via: 1.0 fred, 1.1 p.example.net (Apache/1.1) + + Proxies and gateways used as a portal through a network firewall + SHOULD NOT, by default, forward the names and ports of hosts within + the firewall region. This information SHOULD only be propagated if + explicitly enabled. If not enabled, the received-by host of any host + behind the firewall SHOULD be replaced by an appropriate pseudonym + for that host. + + For organizations that have strong privacy requirements for hiding + internal structures, a proxy MAY combine an ordered subsequence of + Via header field entries with identical received-protocol values into + a single such entry. For example, + + Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy + + could be collapsed to + + Via: 1.0 ricky, 1.1 mertz, 1.0 lucy + + Applications SHOULD NOT combine multiple entries unless they are all + under the same organizational control and the hosts have already been + replaced by pseudonyms. Applications MUST NOT combine entries which + + + +Fielding, et al. Expires February 5, 2011 [Page 58] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + have different received-protocol values. + +10. IANA Considerations + +10.1. Header Field Registration + + The Message Header Field Registry located at shall be + updated with the permanent registrations below (see [RFC3864]): + + +-------------------+----------+----------+-------------+ + | Header Field Name | Protocol | Status | Reference | + +-------------------+----------+----------+-------------+ + | Connection | http | standard | Section 9.1 | + | Content-Length | http | standard | Section 9.2 | + | Date | http | standard | Section 9.3 | + | Host | http | standard | Section 9.4 | + | TE | http | standard | Section 9.5 | + | Trailer | http | standard | Section 9.6 | + | Transfer-Encoding | http | standard | Section 9.7 | + | Upgrade | http | standard | Section 9.8 | + | Via | http | standard | Section 9.9 | + +-------------------+----------+----------+-------------+ + + The change controller is: "IETF (iesg@ietf.org) - Internet + Engineering Task Force". + +10.2. URI Scheme Registration + + The entries for the "http" and "https" URI Schemes in the registry + located at shall + be updated to point to Sections 2.6.1 and 2.6.2 of this document (see + [RFC4395]). + +10.3. Internet Media Type Registrations + + This document serves as the specification for the Internet media + types "message/http" and "application/http". The following is to be + registered with IANA (see [RFC4288]). + +10.3.1. Internet Media Type message/http + + The message/http type can be used to enclose a single HTTP request or + response message, provided that it obeys the MIME restrictions for + all "message" types regarding line length and encodings. + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 59] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Type name: message + + Subtype name: http + + Required parameters: none + + Optional parameters: version, msgtype + + version: The HTTP-Version number of the enclosed message (e.g., + "1.1"). If not present, the version can be determined from the + first line of the body. + + msgtype: The message type -- "request" or "response". If not + present, the type can be determined from the first line of the + body. + + Encoding considerations: only "7bit", "8bit", or "binary" are + permitted + + Security considerations: none + + Interoperability considerations: none + + Published specification: This specification (see Section 10.3.1). + + Applications that use this media type: + + Additional information: + + Magic number(s): none + + File extension(s): none + + Macintosh file type code(s): none + + Person and email address to contact for further information: See + Authors Section. + + Intended usage: COMMON + + Restrictions on usage: none + + Author/Change controller: IESG + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 60] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +10.3.2. Internet Media Type application/http + + The application/http type can be used to enclose a pipeline of one or + more HTTP request or response messages (not intermixed). + + Type name: application + + Subtype name: http + + Required parameters: none + + Optional parameters: version, msgtype + + version: The HTTP-Version number of the enclosed messages (e.g., + "1.1"). If not present, the version can be determined from the + first line of the body. + + msgtype: The message type -- "request" or "response". If not + present, the type can be determined from the first line of the + body. + + Encoding considerations: HTTP messages enclosed by this type are in + "binary" format; use of an appropriate Content-Transfer-Encoding + is required when transmitted via E-mail. + + Security considerations: none + + Interoperability considerations: none + + Published specification: This specification (see Section 10.3.2). + + Applications that use this media type: + + Additional information: + + Magic number(s): none + + File extension(s): none + + Macintosh file type code(s): none + + Person and email address to contact for further information: See + Authors Section. + + Intended usage: COMMON + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 61] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Restrictions on usage: none + + Author/Change controller: IESG + +10.4. Transfer Coding Registry + + The registration procedure for HTTP Transfer Codings is now defined + by Section 6.2.3 of this document. + + The HTTP Transfer Codings Registry located at + shall be updated + with the registrations below: + + +----------+--------------------------------------+-----------------+ + | Name | Description | Reference | + +----------+--------------------------------------+-----------------+ + | chunked | Transfer in a series of chunks | Section 6.2.1 | + | compress | UNIX "compress" program method | Section 6.2.2.1 | + | deflate | "deflate" compression mechanism | Section 6.2.2.2 | + | | ([RFC1951]) used inside the "zlib" | | + | | data format ([RFC1950]) | | + | gzip | Same as GNU zip [RFC1952] | Section 6.2.2.3 | + +----------+--------------------------------------+-----------------+ + +10.5. Upgrade Token Registration + + The registration procedure for HTTP Upgrade Tokens -- previously + defined in Section 7.2 of [RFC2817] -- is now defined by + Section 9.8.1 of this document. + + The HTTP Status Code Registry located at + shall be + updated with the registration below: + + +-------+---------------------------+-------------------------------+ + | Value | Description | Reference | + +-------+---------------------------+-------------------------------+ + | HTTP | Hypertext Transfer | Section 2.5 of this | + | | Protocol | specification | + +-------+---------------------------+-------------------------------+ + +11. Security Considerations + + This section is meant to inform application developers, information + providers, and users of the security limitations in HTTP/1.1 as + described by this document. The discussion does not include + definitive solutions to the problems revealed, though it does make + some suggestions for reducing security risks. + + + +Fielding, et al. Expires February 5, 2011 [Page 62] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +11.1. Personal Information + + HTTP clients are often privy to large amounts of personal information + (e.g., the user's name, location, mail address, passwords, encryption + keys, etc.), and SHOULD be very careful to prevent unintentional + leakage of this information. We very strongly recommend that a + convenient interface be provided for the user to control + dissemination of such information, and that designers and + implementors be particularly careful in this area. History shows + that errors in this area often create serious security and/or privacy + problems and generate highly adverse publicity for the implementor's + company. + +11.2. Abuse of Server Log Information + + A server is in the position to save personal data about a user's + requests which might identify their reading patterns or subjects of + interest. This information is clearly confidential in nature and its + handling can be constrained by law in certain countries. People + using HTTP to provide data are responsible for ensuring that such + material is not distributed without the permission of any individuals + that are identifiable by the published results. + +11.3. Attacks Based On File and Path Names + + Implementations of HTTP origin servers SHOULD be careful to restrict + the documents returned by HTTP requests to be only those that were + intended by the server administrators. If an HTTP server translates + HTTP URIs directly into file system calls, the server MUST take + special care not to serve files that were not intended to be + delivered to HTTP clients. For example, UNIX, Microsoft Windows, and + other operating systems use ".." as a path component to indicate a + directory level above the current one. On such a system, an HTTP + server MUST disallow any such construct in the request-target if it + would otherwise allow access to a resource outside those intended to + be accessible via the HTTP server. Similarly, files intended for + reference only internally to the server (such as access control + files, configuration files, and script code) MUST be protected from + inappropriate retrieval, since they might contain sensitive + information. Experience has shown that minor bugs in such HTTP + server implementations have turned into security risks. + +11.4. DNS Spoofing + + Clients using HTTP rely heavily on the Domain Name Service, and are + thus generally prone to security attacks based on the deliberate mis- + association of IP addresses and DNS names. Clients need to be + cautious in assuming the continuing validity of an IP number/DNS name + + + +Fielding, et al. Expires February 5, 2011 [Page 63] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + association. + + In particular, HTTP clients SHOULD rely on their name resolver for + confirmation of an IP number/DNS name association, rather than + caching the result of previous host name lookups. Many platforms + already can cache host name lookups locally when appropriate, and + they SHOULD be configured to do so. It is proper for these lookups + to be cached, however, only when the TTL (Time To Live) information + reported by the name server makes it likely that the cached + information will remain useful. + + If HTTP clients cache the results of host name lookups in order to + achieve a performance improvement, they MUST observe the TTL + information reported by DNS. + + If HTTP clients do not observe this rule, they could be spoofed when + a previously-accessed server's IP address changes. As network + renumbering is expected to become increasingly common [RFC1900], the + possibility of this form of attack will grow. Observing this + requirement thus reduces this potential security vulnerability. + + This requirement also improves the load-balancing behavior of clients + for replicated servers using the same DNS name and reduces the + likelihood of a user's experiencing failure in accessing sites which + use that strategy. + +11.5. Proxies and Caching + + By their very nature, HTTP proxies are men-in-the-middle, and + represent an opportunity for man-in-the-middle attacks. Compromise + of the systems on which the proxies run can result in serious + security and privacy problems. Proxies have access to security- + related information, personal information about individual users and + organizations, and proprietary information belonging to users and + content providers. A compromised proxy, or a proxy implemented or + configured without regard to security and privacy considerations, + might be used in the commission of a wide range of potential attacks. + + Proxy operators need to protect the systems on which proxies run as + they would protect any system that contains or transports sensitive + information. In particular, log information gathered at proxies + often contains highly sensitive personal information, and/or + information about organizations. Log information needs to be + carefully guarded, and appropriate guidelines for use need to be + developed and followed. (Section 11.2). + + Proxy implementors need to consider the privacy and security + implications of their design and coding decisions, and of the + + + +Fielding, et al. Expires February 5, 2011 [Page 64] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + configuration options they provide to proxy operators (especially the + default configuration). + + Users of a proxy need to be aware that proxies are no trustworthier + than the people who run them; HTTP itself cannot solve this problem. + + The judicious use of cryptography, when appropriate, might suffice to + protect against a broad range of security and privacy attacks. Such + cryptography is beyond the scope of the HTTP/1.1 specification. + +11.6. Denial of Service Attacks on Proxies + + They exist. They are hard to defend against. Research continues. + Beware. + +12. Acknowledgments + + HTTP has evolved considerably over the years. It has benefited from + a large and active developer community--the many people who have + participated on the www-talk mailing list--and it is that community + which has been most responsible for the success of HTTP and of the + World-Wide Web in general. Marc Andreessen, Robert Cailliau, Daniel + W. Connolly, Bob Denny, John Franks, Jean-Francois Groff, Phillip M. + Hallam-Baker, Hakon W. Lie, Ari Luotonen, Rob McCool, Lou Montulli, + Dave Raggett, Tony Sanders, and Marc VanHeyningen deserve special + recognition for their efforts in defining early aspects of the + protocol. + + This document has benefited greatly from the comments of all those + participating in the HTTP-WG. In addition to those already + mentioned, the following individuals have contributed to this + specification: + + Gary Adams, Harald Tveit Alvestrand, Keith Ball, Brian Behlendorf, + Paul Burchard, Maurizio Codogno, Josh Cohen, Mike Cowlishaw, Roman + Czyborra, Michael A. Dolan, Daniel DuBois, David J. Fiander, Alan + Freier, Marc Hedlund, Greg Herlihy, Koen Holtman, Alex Hopmann, Bob + Jernigan, Shel Kaphan, Rohit Khare, John Klensin, Martijn Koster, + Alexei Kosut, David M. Kristol, Daniel LaLiberte, Ben Laurie, Paul J. + Leach, Albert Lunde, John C. Mallery, Jean-Philippe Martin-Flatin, + Mitra, David Morris, Gavin Nicol, Ross Patterson, Bill Perry, Jeffrey + Perry, Scott Powers, Owen Rees, Luigi Rizzo, David Robinson, Marc + Salomon, Rich Salz, Allan M. Schiffman, Jim Seidman, Chuck Shotton, + Eric W. Sink, Simon E. Spero, Richard N. Taylor, Robert S. Thau, Bill + (BearHeart) Weinman, Francois Yergeau, Mary Ellen Zurko. + + Thanks to the "cave men" of Palo Alto. You know who you are. + + + + +Fielding, et al. Expires February 5, 2011 [Page 65] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Jim Gettys (the editor of [RFC2616]) wishes particularly to thank Roy + Fielding, the editor of [RFC2068], along with John Klensin, Jeff + Mogul, Paul Leach, Dave Kristol, Koen Holtman, John Franks, Josh + Cohen, Alex Hopmann, Scott Lawrence, and Larry Masinter for their + help. And thanks go particularly to Jeff Mogul and Scott Lawrence + for performing the "MUST/MAY/SHOULD" audit. + + The Apache Group, Anselm Baird-Smith, author of Jigsaw, and Henrik + Frystyk implemented RFC 2068 early, and we wish to thank them for the + discovery of many of the problems that this document attempts to + rectify. + + This specification makes heavy use of the augmented BNF and generic + constructs defined by David H. Crocker for [RFC5234]. Similarly, it + reuses many of the definitions provided by Nathaniel Borenstein and + Ned Freed for MIME [RFC2045]. We hope that their inclusion in this + specification will help reduce past confusion over the relationship + between HTTP and Internet mail message formats. + +13. References + +13.1. Normative References + + [ISO-8859-1] International Organization for Standardization, + "Information technology -- 8-bit single-byte coded + graphic character sets -- Part 1: Latin alphabet No. + 1", ISO/IEC 8859-1:1998, 1998. + + [Part2] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., + Ed., and J. Reschke, Ed., "HTTP/1.1, part 2: Message + Semantics", draft-ietf-httpbis-p2-semantics-11 (work in + progress), August 2010. + + [Part3] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., + Ed., and J. Reschke, Ed., "HTTP/1.1, part 3: Message + Payload and Content Negotiation", + draft-ietf-httpbis-p3-payload-11 (work in progress), + August 2010. + + [Part6] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., + Ed., Nottingham, M., Ed., and J. Reschke, Ed., + "HTTP/1.1, part 6: Caching", + draft-ietf-httpbis-p6-cache-11 (work in progress), + August 2010. + + + + +Fielding, et al. Expires February 5, 2011 [Page 66] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + [RFC1950] Deutsch, L. and J-L. Gailly, "ZLIB Compressed Data + Format Specification version 3.3", RFC 1950, May 1996. + + RFC 1950 is an Informational RFC, thus it might be less + stable than this specification. On the other hand, + this downward reference was present since the + publication of RFC 2068 in 1997 ([RFC2068]), therefore + it is unlikely to cause problems in practice. See also + [BCP97]. + + [RFC1951] Deutsch, P., "DEFLATE Compressed Data Format + Specification version 1.3", RFC 1951, May 1996. + + RFC 1951 is an Informational RFC, thus it might be less + stable than this specification. On the other hand, + this downward reference was present since the + publication of RFC 2068 in 1997 ([RFC2068]), therefore + it is unlikely to cause problems in practice. See also + [BCP97]. + + [RFC1952] Deutsch, P., Gailly, J-L., Adler, M., Deutsch, L., and + G. Randers-Pehrson, "GZIP file format specification + version 4.3", RFC 1952, May 1996. + + RFC 1952 is an Informational RFC, thus it might be less + stable than this specification. On the other hand, + this downward reference was present since the + publication of RFC 2068 in 1997 ([RFC2068]), therefore + it is unlikely to cause problems in practice. See also + [BCP97]. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, + "Uniform Resource Identifier (URI): Generic Syntax", + RFC 3986, STD 66, January 2005. + + [RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for + Syntax Specifications: ABNF", STD 68, RFC 5234, + January 2008. + + [USASCII] American National Standards Institute, "Coded Character + Set -- 7-bit American Standard Code for Information + Interchange", ANSI X3.4, 1986. + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 67] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +13.2. Informative References + + [BCP97] Klensin, J. and S. Hartman, "Handling Normative + References to Standards-Track Documents", BCP 97, + RFC 4897, June 2007. + + [Kri2001] Kristol, D., "HTTP Cookies: Standards, Privacy, and + Politics", ACM Transactions on Internet Technology Vol. + 1, #2, November 2001, + . + + [Nie1997] Frystyk, H., Gettys, J., Prud'hommeaux, E., Lie, H., + and C. Lilley, "Network Performance Effects of + HTTP/1.1, CSS1, and PNG", ACM Proceedings of the ACM + SIGCOMM '97 conference on Applications, technologies, + architectures, and protocols for computer communication + SIGCOMM '97, September 1997, + . + + [Pad1995] Padmanabhan, V. and J. Mogul, "Improving HTTP Latency", + Computer Networks and ISDN Systems v. 28, pp. 25-35, + December 1995, + . + + [RFC1123] Braden, R., "Requirements for Internet Hosts - + Application and Support", STD 3, RFC 1123, + October 1989. + + [RFC1305] Mills, D., "Network Time Protocol (Version 3) + Specification, Implementation", RFC 1305, March 1992. + + [RFC1900] Carpenter, B. and Y. Rekhter, "Renumbering Needs Work", + RFC 1900, February 1996. + + [RFC1945] Berners-Lee, T., Fielding, R., and H. Nielsen, + "Hypertext Transfer Protocol -- HTTP/1.0", RFC 1945, + May 1996. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet + Mail Extensions (MIME) Part One: Format of Internet + Message Bodies", RFC 2045, November 1996. + + [RFC2047] Moore, K., "MIME (Multipurpose Internet Mail + Extensions) Part Three: Message Header Extensions for + Non-ASCII Text", RFC 2047, November 1996. + + [RFC2068] Fielding, R., Gettys, J., Mogul, J., Nielsen, H., and + T. Berners-Lee, "Hypertext Transfer Protocol -- + + + +Fielding, et al. Expires February 5, 2011 [Page 68] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + HTTP/1.1", RFC 2068, January 1997. + + [RFC2109] Kristol, D. and L. Montulli, "HTTP State Management + Mechanism", RFC 2109, February 1997. + + [RFC2145] Mogul, J., Fielding, R., Gettys, J., and H. Nielsen, + "Use and Interpretation of HTTP Version Numbers", + RFC 2145, May 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC2817] Khare, R. and S. Lawrence, "Upgrading to TLS Within + HTTP/1.1", RFC 2817, May 2000. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000. + + [RFC2965] Kristol, D. and L. Montulli, "HTTP State Management + Mechanism", RFC 2965, October 2000. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration + Procedures for Message Header Fields", BCP 90, + RFC 3864, September 2004. + + [RFC4288] Freed, N. and J. Klensin, "Media Type Specifications + and Registration Procedures", BCP 13, RFC 4288, + December 2005. + + [RFC4395] Hansen, T., Hardie, T., and L. Masinter, "Guidelines + and Registration Procedures for New URI Schemes", + BCP 115, RFC 4395, February 2006. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing + an IANA Considerations Section in RFCs", BCP 26, + RFC 5226, May 2008. + + [RFC5322] Resnick, P., "Internet Message Format", RFC 5322, + October 2008. + + [Spe] Spero, S., "Analysis of HTTP Performance Problems", + . + + [Tou1998] Touch, J., Heidemann, J., and K. Obraczka, "Analysis of + HTTP Performance", ISI Research Report ISI/RR-98-463, + Aug 1998, . + + (original report dated Aug. 1996) + + + +Fielding, et al. Expires February 5, 2011 [Page 69] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +Appendix A. Tolerant Applications + + Although this document specifies the requirements for the generation + of HTTP/1.1 messages, not all applications will be correct in their + implementation. We therefore recommend that operational applications + be tolerant of deviations whenever those deviations can be + interpreted unambiguously. + + Clients SHOULD be tolerant in parsing the Status-Line and servers + SHOULD be tolerant when parsing the Request-Line. In particular, + they SHOULD accept any amount of WSP characters between fields, even + though only a single SP is required. + + The line terminator for header fields is the sequence CRLF. However, + we recommend that applications, when parsing such headers, recognize + a single LF as a line terminator and ignore the leading CR. + + The character set of a representation SHOULD be labeled as the lowest + common denominator of the character codes used within that + representation, with the exception that not labeling the + representation is preferred over labeling the representation with the + labels US-ASCII or ISO-8859-1. See [Part3]. + + Additional rules for requirements on parsing and encoding of dates + and other potential problems with date encodings include: + + o HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date + which appears to be more than 50 years in the future is in fact in + the past (this helps solve the "year 2000" problem). + + o Although all date formats are specified to be case-sensitive, + recipients SHOULD match day, week and timezone names case- + insensitively. + + o An HTTP/1.1 implementation MAY internally represent a parsed + Expires date as earlier than the proper value, but MUST NOT + internally represent a parsed Expires date as later than the + proper value. + + o All expiration-related calculations MUST be done in GMT. The + local time zone MUST NOT influence the calculation or comparison + of an age or expiration time. + + o If an HTTP header incorrectly carries a date value with a time + zone other than GMT, it MUST be converted into GMT using the most + conservative possible conversion. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 70] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +Appendix B. Compatibility with Previous Versions + + HTTP has been in use by the World-Wide Web global information + initiative since 1990. The first version of HTTP, later referred to + as HTTP/0.9, was a simple protocol for hypertext data transfer across + the Internet with only a single method and no metadata. HTTP/1.0, as + defined by [RFC1945], added a range of request methods and MIME-like + messaging that could include metadata about the data transferred and + modifiers on the request/response semantics. However, HTTP/1.0 did + not sufficiently take into consideration the effects of hierarchical + proxies, caching, the need for persistent connections, or name-based + virtual hosts. The proliferation of incompletely-implemented + applications calling themselves "HTTP/1.0" further necessitated a + protocol version change in order for two communicating applications + to determine each other's true capabilities. + + HTTP/1.1 remains compatible with HTTP/1.0 by including more stringent + requirements that enable reliable implementations, adding only those + new features that will either be safely ignored by an HTTP/1.0 + recipient or only sent when communicating with a party advertising + compliance with HTTP/1.1. + + It is beyond the scope of a protocol specification to mandate + compliance with previous versions. HTTP/1.1 was deliberately + designed, however, to make supporting previous versions easy. It is + worth noting that, at the time of composing this specification, we + would expect general-purpose HTTP/1.1 servers to: + + o understand any valid request in the format of HTTP/1.0 and 1.1; + + o respond appropriately with a message in the same major version + used by the client. + + And we would expect HTTP/1.1 clients to: + + o understand any valid response in the format of HTTP/1.0 or 1.1. + + For most implementations of HTTP/1.0, each connection is established + by the client prior to the request and closed by the server after + sending the response. Some implementations implement the Keep-Alive + version of persistent connections described in Section 19.7.1 of + [RFC2068]. + +B.1. Changes from HTTP/1.0 + + This section summarizes major differences between versions HTTP/1.0 + and HTTP/1.1. + + + + +Fielding, et al. Expires February 5, 2011 [Page 71] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +B.1.1. Changes to Simplify Multi-homed Web Servers and Conserve IP + Addresses + + The requirements that clients and servers support the Host request- + header, report an error if the Host request-header (Section 9.4) is + missing from an HTTP/1.1 request, and accept absolute URIs + (Section 4.1.2) are among the most important changes defined by this + specification. + + Older HTTP/1.0 clients assumed a one-to-one relationship of IP + addresses and servers; there was no other established mechanism for + distinguishing the intended server of a request than the IP address + to which that request was directed. The changes outlined above will + allow the Internet, once older HTTP clients are no longer common, to + support multiple Web sites from a single IP address, greatly + simplifying large operational Web servers, where allocation of many + IP addresses to a single host has created serious problems. The + Internet will also be able to recover the IP addresses that have been + allocated for the sole purpose of allowing special-purpose domain + names to be used in root-level HTTP URLs. Given the rate of growth + of the Web, and the number of servers already deployed, it is + extremely important that all implementations of HTTP (including + updates to existing HTTP/1.0 applications) correctly implement these + requirements: + + o Both clients and servers MUST support the Host request-header. + + o A client that sends an HTTP/1.1 request MUST send a Host header. + + o Servers MUST report a 400 (Bad Request) error if an HTTP/1.1 + request does not include a Host request-header. + + o Servers MUST accept absolute URIs. + +B.2. Compatibility with HTTP/1.0 Persistent Connections + + Some clients and servers might wish to be compatible with some + previous implementations of persistent connections in HTTP/1.0 + clients and servers. Persistent connections in HTTP/1.0 are + explicitly negotiated as they are not the default behavior. HTTP/1.0 + experimental implementations of persistent connections are faulty, + and the new facilities in HTTP/1.1 are designed to rectify these + problems. The problem was that some existing HTTP/1.0 clients might + send Keep-Alive to a proxy server that doesn't understand Connection, + which would then erroneously forward it to the next inbound server, + which would establish the Keep-Alive connection and result in a hung + HTTP/1.0 proxy waiting for the close on the response. The result is + that HTTP/1.0 clients must be prevented from using Keep-Alive when + + + +Fielding, et al. Expires February 5, 2011 [Page 72] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + talking to proxies. + + However, talking to proxies is the most important use of persistent + connections, so that prohibition is clearly unacceptable. Therefore, + we need some other mechanism for indicating a persistent connection + is desired, which is safe to use even when talking to an old proxy + that ignores Connection. Persistent connections are the default for + HTTP/1.1 messages; we introduce a new keyword (Connection: close) for + declaring non-persistence. See Section 9.1. + + The original HTTP/1.0 form of persistent connections (the Connection: + Keep-Alive and Keep-Alive header) is documented in Section 19.7.1 of + [RFC2068]. + +B.3. Changes from RFC 2616 + + Empty list elements in list productions have been deprecated. + (Section 1.2.1) + + Rules about implicit linear whitespace between certain grammar + productions have been removed; now it's only allowed when + specifically pointed out in the ABNF. The NUL character is no longer + allowed in comment and quoted-string text. The quoted-pair rule no + longer allows escaping control characters other than HTAB. Non-ASCII + content in header fields and reason phrase has been obsoleted and + made opaque (the TEXT rule was removed) (Section 1.2.2) + + Clarify that HTTP-Version is case sensitive. (Section 2.5) + + Remove reference to non-existent identity transfer-coding value + tokens. (Sections 6.2 and 3.3) + + Require that invalid whitespace around field-names be rejected. + (Section 3.2) + + Update use of abs_path production from RFC1808 to the path-absolute + + query components of RFC3986. (Section 4.1.2) + + Clarification that the chunk length does not include the count of the + octets in the chunk header and trailer. Furthermore disallowed line + folding in chunk extensions. (Section 6.2.1) + + Remove hard limit of two connections per server. (Section 7.1.4) + + Clarify exactly when close connection options must be sent. + (Section 9.1) + +Appendix C. Collected ABNF + + + +Fielding, et al. Expires February 5, 2011 [Page 73] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + BWS = OWS + + Cache-Control = + Chunked-Body = *chunk last-chunk trailer-part CRLF + Connection = "Connection:" OWS Connection-v + Connection-v = *( "," OWS ) connection-token *( OWS "," [ OWS + connection-token ] ) + Content-Length = "Content-Length:" OWS 1*Content-Length-v + Content-Length-v = 1*DIGIT + + Date = "Date:" OWS Date-v + Date-v = HTTP-date + + GMT = %x47.4D.54 ; GMT + + HTTP-Prot-Name = %x48.54.54.50 ; HTTP + HTTP-Version = HTTP-Prot-Name "/" 1*DIGIT "." 1*DIGIT + HTTP-date = rfc1123-date / obs-date + HTTP-message = start-line *( header-field CRLF ) CRLF [ message-body + ] + Host = "Host:" OWS Host-v + Host-v = uri-host [ ":" port ] + + MIME-Version = + Method = token + + OWS = *( [ obs-fold ] WSP ) + + Pragma = + + RWS = 1*( [ obs-fold ] WSP ) + Reason-Phrase = *( WSP / VCHAR / obs-text ) + Request = Request-Line *( header-field CRLF ) CRLF [ message-body ] + Request-Line = Method SP request-target SP HTTP-Version CRLF + Response = Status-Line *( header-field CRLF ) CRLF [ message-body ] + + Status-Code = 3DIGIT + Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF + + TE = "TE:" OWS TE-v + TE-v = [ ( "," / t-codings ) *( OWS "," [ OWS t-codings ] ) ] + Trailer = "Trailer:" OWS Trailer-v + Trailer-v = *( "," OWS ) field-name *( OWS "," [ OWS field-name ] ) + Transfer-Encoding = "Transfer-Encoding:" OWS Transfer-Encoding-v + Transfer-Encoding-v = *( "," OWS ) transfer-coding *( OWS "," [ OWS + transfer-coding ] ) + + URI-reference = + + + +Fielding, et al. Expires February 5, 2011 [Page 74] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Upgrade = "Upgrade:" OWS Upgrade-v + Upgrade-v = *( "," OWS ) product *( OWS "," [ OWS product ] ) + + Via = "Via:" OWS Via-v + Via-v = *( "," OWS ) received-protocol RWS received-by [ RWS comment + ] *( OWS "," [ OWS received-protocol RWS received-by [ RWS comment ] + ] ) + + Warning = + + absolute-URI = + asctime-date = day-name SP date3 SP time-of-day SP year + attribute = token + authority = + + chunk = chunk-size *WSP [ chunk-ext ] CRLF chunk-data CRLF + chunk-data = 1*OCTET + chunk-ext = *( ";" *WSP chunk-ext-name [ "=" chunk-ext-val ] *WSP ) + chunk-ext-name = token + chunk-ext-val = token / quoted-str-nf + chunk-size = 1*HEXDIG + comment = "(" *( ctext / quoted-cpair / comment ) ")" + connection-token = token + ctext = OWS / %x21-27 ; '!'-''' + / %x2A-5B ; '*'-'[' + / %x5D-7E ; ']'-'~' + / obs-text + + date1 = day SP month SP year + date2 = day "-" month "-" 2DIGIT + date3 = month SP ( 2DIGIT / ( SP DIGIT ) ) + day = 2DIGIT + day-name = %x4D.6F.6E ; Mon + / %x54.75.65 ; Tue + / %x57.65.64 ; Wed + / %x54.68.75 ; Thu + / %x46.72.69 ; Fri + / %x53.61.74 ; Sat + / %x53.75.6E ; Sun + day-name-l = %x4D.6F.6E.64.61.79 ; Monday + / %x54.75.65.73.64.61.79 ; Tuesday + / %x57.65.64.6E.65.73.64.61.79 ; Wednesday + / %x54.68.75.72.73.64.61.79 ; Thursday + / %x46.72.69.64.61.79 ; Friday + / %x53.61.74.75.72.64.61.79 ; Saturday + / %x53.75.6E.64.61.79 ; Sunday + + field-content = *( WSP / VCHAR / obs-text ) + + + +Fielding, et al. Expires February 5, 2011 [Page 75] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + field-name = token + field-value = *( field-content / OWS ) + + general-header = Cache-Control / Connection / Date / Pragma / Trailer + / Transfer-Encoding / Upgrade / Via / Warning / MIME-Version + + header-field = field-name ":" OWS [ field-value ] OWS + hour = 2DIGIT + http-URI = "http://" authority path-abempty [ "?" query ] + https-URI = "https://" authority path-abempty [ "?" query ] + + last-chunk = 1*"0" *WSP [ chunk-ext ] CRLF + + message-body = *OCTET + minute = 2DIGIT + month = %x4A.61.6E ; Jan + / %x46.65.62 ; Feb + / %x4D.61.72 ; Mar + / %x41.70.72 ; Apr + / %x4D.61.79 ; May + / %x4A.75.6E ; Jun + / %x4A.75.6C ; Jul + / %x41.75.67 ; Aug + / %x53.65.70 ; Sep + / %x4F.63.74 ; Oct + / %x4E.6F.76 ; Nov + / %x44.65.63 ; Dec + + obs-date = rfc850-date / asctime-date + obs-fold = CRLF + obs-text = %x80-FF + + partial-URI = relative-part [ "?" query ] + path-abempty = + path-absolute = + port = + product = token [ "/" product-version ] + product-version = token + protocol-name = token + protocol-version = token + pseudonym = token + + qdtext = OWS / "!" / %x23-5B ; '#'-'[' + / %x5D-7E ; ']'-'~' + / obs-text + qdtext-nf = WSP / "!" / %x23-5B ; '#'-'[' + / %x5D-7E ; ']'-'~' + / obs-text + + + +Fielding, et al. Expires February 5, 2011 [Page 76] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + query = + quoted-cpair = "\" ( WSP / VCHAR / obs-text ) + quoted-pair = "\" ( WSP / VCHAR / obs-text ) + quoted-str-nf = DQUOTE *( qdtext-nf / quoted-pair ) DQUOTE + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + qvalue = ( "0" [ "." *3DIGIT ] ) / ( "1" [ "." *3"0" ] ) + + received-by = ( uri-host [ ":" port ] ) / pseudonym + received-protocol = [ protocol-name "/" ] protocol-version + relative-part = + request-header = + request-target = "*" / absolute-URI / ( path-absolute [ "?" query ] ) + / authority + response-header = + rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT + rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT + + second = 2DIGIT + special = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / + DQUOTE / "/" / "[" / "]" / "?" / "=" / "{" / "}" + start-line = Request-Line / Status-Line + + t-codings = "trailers" / ( transfer-extension [ te-params ] ) + tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / + "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA + te-ext = OWS ";" OWS token [ "=" word ] + te-params = OWS ";" OWS "q=" qvalue *te-ext + time-of-day = hour ":" minute ":" second + token = 1*tchar + trailer-part = *( header-field CRLF ) + transfer-coding = "chunked" / "compress" / "deflate" / "gzip" / + transfer-extension + transfer-extension = token *( OWS ";" OWS transfer-parameter ) + transfer-parameter = attribute BWS "=" BWS value + + uri-host = + + value = word + + word = token / quoted-string + + year = 4DIGIT + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 77] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + ABNF diagnostics: + + ; Chunked-Body defined but not used + ; Content-Length defined but not used + ; HTTP-message defined but not used + ; Host defined but not used + ; Request defined but not used + ; Response defined but not used + ; TE defined but not used + ; URI-reference defined but not used + ; general-header defined but not used + ; http-URI defined but not used + ; https-URI defined but not used + ; partial-URI defined but not used + ; request-header defined but not used + ; response-header defined but not used + ; special defined but not used + +Appendix D. Change Log (to be removed by RFC Editor before publication) + +D.1. Since RFC2616 + + Extracted relevant partitions from [RFC2616]. + +D.2. Since draft-ietf-httpbis-p1-messaging-00 + + Closed issues: + + o : "HTTP Version + should be case sensitive" + () + + o : "'unsafe' + characters" () + + o : "Chunk Size + Definition" () + + o : "Message Length" + () + + o : "Media Type + Registrations" () + + o : "URI includes + query" () + + + + + +Fielding, et al. Expires February 5, 2011 [Page 78] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + o : "No close on + 1xx responses" () + + o : "Remove + 'identity' token references" + () + + o : "Import query + BNF" + + o : "qdtext BNF" + + o : "Normative and + Informative references" + + o : "RFC2606 + Compliance" + + o : "RFC977 + reference" + + o : "RFC1700 + references" + + o : "inconsistency + in date format explanation" + + o : "Date reference + typo" + + o : "Informative + references" + + o : "ISO-8859-1 + Reference" + + o : "Normative up- + to-date references" + + Other changes: + + o Update media type registrations to use RFC4288 template. + + o Use names of RFC4234 core rules DQUOTE and WSP, fix broken ABNF + for chunk-data (work in progress on + ) + + + + + +Fielding, et al. Expires February 5, 2011 [Page 79] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +D.3. Since draft-ietf-httpbis-p1-messaging-01 + + Closed issues: + + o : "Bodies on GET + (and other) requests" + + o : "Updating to + RFC4288" + + o : "Status Code + and Reason Phrase" + + o : "rel_path not + used" + + Ongoing work on ABNF conversion + (): + + o Get rid of duplicate BNF rule names ("host" -> "uri-host", + "trailer" -> "trailer-part"). + + o Avoid underscore character in rule names ("http_URL" -> "http- + URL", "abs_path" -> "path-absolute"). + + o Add rules for terms imported from URI spec ("absoluteURI", + "authority", "path-absolute", "port", "query", "relativeURI", + "host) -- these will have to be updated when switching over to + RFC3986. + + o Synchronize core rules with RFC5234. + + o Get rid of prose rules that span multiple lines. + + o Get rid of unused rules LOALPHA and UPALPHA. + + o Move "Product Tokens" section (back) into Part 1, as "token" is + used in the definition of the Upgrade header. + + o Add explicit references to BNF syntax and rules imported from + other parts of the specification. + + o Rewrite prose rule "token" in terms of "tchar", rewrite prose rule + "TEXT". + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 80] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +D.4. Since draft-ietf-httpbis-p1-messaging-02 + + Closed issues: + + o : "HTTP-date vs. + rfc1123-date" + + o : "WS in quoted- + pair" + + Ongoing work on IANA Message Header Registration + (): + + o Reference RFC 3984, and update header registrations for headers + defined in this document. + + Ongoing work on ABNF conversion + (): + + o Replace string literals when the string really is case-sensitive + (HTTP-Version). + +D.5. Since draft-ietf-httpbis-p1-messaging-03 + + Closed issues: + + o : "Connection + closing" + + o : "Move + registrations and registry information to IANA Considerations" + + o : "need new URL + for PAD1995 reference" + + o : "IANA + Considerations: update HTTP URI scheme registration" + + o : "Cite HTTPS + URI scheme definition" + + o : "List-type + headers vs Set-Cookie" + + Ongoing work on ABNF conversion + (): + + + + + +Fielding, et al. Expires February 5, 2011 [Page 81] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + o Replace string literals when the string really is case-sensitive + (HTTP-Date). + + o Replace HEX by HEXDIG for future consistence with RFC 5234's core + rules. + +D.6. Since draft-ietf-httpbis-p1-messaging-04 + + Closed issues: + + o : "Out-of-date + reference for URIs" + + o : "RFC 2822 is + updated by RFC 5322" + + Ongoing work on ABNF conversion + (): + + o Use "/" instead of "|" for alternatives. + + o Get rid of RFC822 dependency; use RFC5234 plus extensions instead. + + o Only reference RFC 5234's core rules. + + o Introduce new ABNF rules for "bad" whitespace ("BWS"), optional + whitespace ("OWS") and required whitespace ("RWS"). + + o Rewrite ABNFs to spell out whitespace rules, factor out header + value format definitions. + +D.7. Since draft-ietf-httpbis-p1-messaging-05 + + Closed issues: + + o : "Header LWS" + + o : "Sort 1.3 + Terminology" + + o : "RFC2047 + encoded words" + + o : "Character + Encodings in TEXT" + + o : "Line Folding" + + + + +Fielding, et al. Expires February 5, 2011 [Page 82] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + o : "OPTIONS * and + proxies" + + o : "Reason-Phrase + BNF" + + o : "Use of TEXT" + + o : "Join + "Differences Between HTTP Entities and RFC 2045 Entities"?" + + o : "RFC822 + reference left in discussion of date formats" + + Final work on ABNF conversion + (): + + o Rewrite definition of list rules, deprecate empty list elements. + + o Add appendix containing collected and expanded ABNF. + + Other changes: + + o Rewrite introduction; add mostly new Architecture Section. + + o Move definition of quality values from Part 3 into Part 1; make TE + request header grammar independent of accept-params (defined in + Part 3). + +D.8. Since draft-ietf-httpbis-p1-messaging-06 + + Closed issues: + + o : "base for + numeric protocol elements" + + o : "comment ABNF" + + Partly resolved issues: + + o : "205 Bodies" + (took out language that implied that there might be methods for + which a request body MUST NOT be included) + + o : "editorial + improvements around HTTP-date" + + + + + +Fielding, et al. Expires February 5, 2011 [Page 83] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + +D.9. Since draft-ietf-httpbis-p1-messaging-07 + + Closed issues: + + o : "Repeating + single-value headers" + + o : "increase + connection limit" + + o : "IP addresses + in URLs" + + o : "take over + HTTP Upgrade Token Registry" + + o : "CR and LF in + chunk extension values" + + o : "HTTP/0.9 + support" + + o : "pick IANA + policy (RFC5226) for Transfer Coding / Content Coding" + + o : "move + definitions of gzip/deflate/compress to part 1" + + o : "disallow + control characters in quoted-pair" + + Partly resolved issues: + + o : "update IANA + requirements wrt Transfer-Coding values" (add the IANA + Considerations subsection) + +D.10. Since draft-ietf-httpbis-p1-messaging-08 + + Closed issues: + + o : "header + parsing, treatment of leading and trailing OWS" + + Partly resolved issues: + + o : "Placement of + 13.5.1 and 13.5.2" + + + +Fielding, et al. Expires February 5, 2011 [Page 84] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + o : "use of term + "word" when talking about header structure" + +D.11. Since draft-ietf-httpbis-p1-messaging-09 + + Closed issues: + + o : "Clarification + of the term 'deflate'" + + o : "OPTIONS * and + proxies" + + o : "MIME-Version + not listed in P1, general header fields" + + o : "IANA registry + for content/transfer encodings" + + o : "Case- + sensitivity of HTTP-date" + + o : "use of term + "word" when talking about header structure" + + Partly resolved issues: + + o : "Term for the + requested resource's URI" + +D.12. Since draft-ietf-httpbis-p1-messaging-10 + + Closed issues: + + o : "Connection + Closing" + + o : "Delimiting + messages with multipart/byteranges" + + o : "Handling + multiple Content-Length headers" + + o : "Clarify + entity / representation / variant terminology" + + o : "consider + removing the 'changes from 2068' sections" + + + +Fielding, et al. Expires February 5, 2011 [Page 85] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Partly resolved issues: + + o : "HTTP(s) URI + scheme definitions" + +Index + + A + application/http Media Type 61 + + B + browser 10 + + C + cache 13 + cacheable 14 + chunked (Coding Format) 35 + client 10 + Coding Format + chunked 35 + compress 38 + deflate 38 + gzip 38 + compress (Coding Format) 38 + connection 10 + Connection header 49 + Content-Length header 50 + + D + Date header 51 + deflate (Coding Format) 38 + downstream 12 + + E + effective request URI 29 + + G + gateway 13 + Grammar + absolute-URI 16 + ALPHA 7 + asctime-date 34 + attribute 34 + authority 16 + BWS 9 + chunk 35 + chunk-data 35 + chunk-ext 35 + + + +Fielding, et al. Expires February 5, 2011 [Page 86] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + chunk-ext-name 35 + chunk-ext-val 35 + chunk-size 35 + Chunked-Body 35 + comment 22 + Connection 49 + connection-token 49 + Connection-v 49 + Content-Length 50 + Content-Length-v 50 + CR 7 + CRLF 7 + ctext 22 + CTL 7 + Date 51 + Date-v 51 + date1 33 + date2 34 + date3 34 + day 33 + day-name 33 + day-name-l 33 + DIGIT 7 + DQUOTE 7 + extension-code 32 + extension-method 26 + field-content 20 + field-name 20 + field-value 20 + general-header 26 + GMT 33 + header-field 20 + HEXDIG 7 + Host 52 + Host-v 52 + hour 33 + HTTP-date 32 + HTTP-message 19 + HTTP-Prot-Name 15 + http-URI 16 + HTTP-Version 15 + https-URI 18 + last-chunk 35 + LF 7 + message-body 22 + Method 26 + minute 33 + month 33 + + + +Fielding, et al. Expires February 5, 2011 [Page 87] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + obs-date 33 + obs-text 10 + OCTET 7 + OWS 9 + path-absolute 16 + port 16 + product 39 + product-version 39 + protocol-name 57 + protocol-version 57 + pseudonym 57 + qdtext 10 + qdtext-nf 35 + query 16 + quoted-cpair 22 + quoted-pair 10 + quoted-str-nf 35 + quoted-string 10 + qvalue 39 + Reason-Phrase 32 + received-by 57 + received-protocol 57 + Request 26 + Request-Line 26 + request-target 27 + Response 31 + rfc850-date 34 + rfc1123-date 33 + RWS 9 + second 33 + SP 7 + special 9 + Status-Code 32 + Status-Line 31 + t-codings 53 + tchar 9 + TE 53 + te-ext 53 + te-params 53 + TE-v 53 + time-of-day 33 + token 9 + Trailer 54 + trailer-part 35 + Trailer-v 54 + transfer-coding 34 + Transfer-Encoding 55 + Transfer-Encoding-v 55 + + + +Fielding, et al. Expires February 5, 2011 [Page 88] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + transfer-extension 34 + transfer-parameter 34 + Upgrade 55 + Upgrade-v 55 + uri-host 16 + URI-reference 16 + value 34 + VCHAR 7 + Via 57 + Via-v 57 + word 9 + WSP 7 + year 33 + gzip (Coding Format) 38 + + H + header field 19 + header section 19 + Headers + Connection 49 + Content-Length 50 + Date 51 + Host 52 + TE 53 + Trailer 54 + Transfer-Encoding 55 + Upgrade 55 + Via 57 + headers 19 + Host header 52 + http URI scheme 16 + https URI scheme 18 + + I + inbound 12 + intermediary 12 + + M + Media Type + application/http 61 + message/http 59 + message 11 + message/http Media Type 59 + + O + origin server 10 + outbound 12 + + + + +Fielding, et al. Expires February 5, 2011 [Page 89] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + P + proxy 12 + + R + request 11 + resource 16 + response 11 + reverse proxy 13 + + S + server 10 + spider 10 + + T + target resource 29 + TE header 53 + Trailer header 54 + Transfer-Encoding header 55 + tunnel 13 + + U + Upgrade header 55 + upstream 12 + URI scheme + http 16 + https 18 + user agent 10 + + V + Via header 57 + +Authors' Addresses + + Roy T. Fielding (editor) + Day Software + 23 Corporate Plaza DR, Suite 280 + Newport Beach, CA 92660 + USA + + Phone: +1-949-706-5300 + Fax: +1-949-706-5305 + EMail: fielding@gbiv.com + URI: http://roy.gbiv.com/ + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 90] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Jim Gettys + Alcatel-Lucent Bell Labs + 21 Oak Knoll Road + Carlisle, MA 01741 + USA + + EMail: jg@freedesktop.org + URI: http://gettys.wordpress.com/ + + + Jeffrey C. Mogul + Hewlett-Packard Company + HP Labs, Large Scale Systems Group + 1501 Page Mill Road, MS 1177 + Palo Alto, CA 94304 + USA + + EMail: JeffMogul@acm.org + + + Henrik Frystyk Nielsen + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + USA + + EMail: henrikn@microsoft.com + + + Larry Masinter + Adobe Systems, Incorporated + 345 Park Ave + San Jose, CA 95110 + USA + + EMail: LMM@acm.org + URI: http://larry.masinter.net/ + + + Paul J. Leach + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + + EMail: paulle@microsoft.com + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 91] + +Internet-Draft HTTP/1.1, Part 1 August 2010 + + + Tim Berners-Lee + World Wide Web Consortium + MIT Computer Science and Artificial Intelligence Laboratory + The Stata Center, Building 32 + 32 Vassar Street + Cambridge, MA 02139 + USA + + EMail: timbl@w3.org + URI: http://www.w3.org/People/Berners-Lee/ + + + Yves Lafon (editor) + World Wide Web Consortium + W3C / ERCIM + 2004, rte des Lucioles + Sophia-Antipolis, AM 06902 + France + + EMail: ylafon@w3.org + URI: http://www.raubacapeu.net/people/yves/ + + + Julian F. Reschke (editor) + greenbytes GmbH + Hafenweg 16 + Muenster, NW 48155 + Germany + + Phone: +49 251 2807760 + Fax: +49 251 2807761 + EMail: julian.reschke@greenbytes.de + URI: http://greenbytes.de/tech/webdav/ + + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 92] + diff --git a/dav/SabreDAV/docs/draft-ietf-httpbis-p4-conditional-11.txt b/dav/SabreDAV/docs/draft-ietf-httpbis-p4-conditional-11.txt new file mode 100644 index 000000000..2dd1b7fc2 --- /dev/null +++ b/dav/SabreDAV/docs/draft-ietf-httpbis-p4-conditional-11.txt @@ -0,0 +1,1512 @@ + + + +HTTPbis Working Group R. Fielding, Ed. +Internet-Draft Day Software +Obsoletes: 2616 (if approved) J. Gettys +Intended status: Standards Track Alcatel-Lucent +Expires: February 5, 2011 J. Mogul + HP + H. Frystyk + Microsoft + L. Masinter + Adobe Systems + P. Leach + Microsoft + T. Berners-Lee + W3C/MIT + Y. Lafon, Ed. + W3C + J. Reschke, Ed. + greenbytes + August 4, 2010 + + + HTTP/1.1, part 4: Conditional Requests + draft-ietf-httpbis-p4-conditional-11 + +Abstract + + The Hypertext Transfer Protocol (HTTP) is an application-level + protocol for distributed, collaborative, hypermedia information + systems. HTTP has been in use by the World Wide Web global + information initiative since 1990. This document is Part 4 of the + seven-part specification that defines the protocol referred to as + "HTTP/1.1" and, taken together, obsoletes RFC 2616. Part 4 defines + request header fields for indicating conditional requests and the + rules for constructing responses to those requests. + +Editorial Note (To be removed by RFC Editor) + + Discussion of this draft should take place on the HTTPBIS working + group mailing list (ietf-http-wg@w3.org). The current issues list is + at and related + documents (including fancy diffs) can be found at + . + + The changes in this draft are summarized in Appendix C.12. + +Status of This Memo + + This Internet-Draft is submitted in full conformance with the + + + +Fielding, et al. Expires February 5, 2011 [Page 1] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on February 5, 2011. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 2] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 1.1. Requirements . . . . . . . . . . . . . . . . . . . . . . . 4 + 1.2. Syntax Notation . . . . . . . . . . . . . . . . . . . . . 4 + 1.2.1. Core Rules . . . . . . . . . . . . . . . . . . . . . . 5 + 1.2.2. ABNF Rules defined in other Parts of the + Specification . . . . . . . . . . . . . . . . . . . . 5 + 2. Entity-Tags . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 2.1. Example: Entity-tags varying on Content-Negotiated + Resources . . . . . . . . . . . . . . . . . . . . . . . . 6 + 3. Status Code Definitions . . . . . . . . . . . . . . . . . . . 7 + 3.1. 304 Not Modified . . . . . . . . . . . . . . . . . . . . . 7 + 3.2. 412 Precondition Failed . . . . . . . . . . . . . . . . . 7 + 4. Weak and Strong Validators . . . . . . . . . . . . . . . . . . 8 + 5. Rules for When to Use Entity-tags and Last-Modified Dates . . 10 + 6. Header Field Definitions . . . . . . . . . . . . . . . . . . . 12 + 6.1. ETag . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 6.2. If-Match . . . . . . . . . . . . . . . . . . . . . . . . . 13 + 6.3. If-Modified-Since . . . . . . . . . . . . . . . . . . . . 14 + 6.4. If-None-Match . . . . . . . . . . . . . . . . . . . . . . 16 + 6.5. If-Unmodified-Since . . . . . . . . . . . . . . . . . . . 17 + 6.6. Last-Modified . . . . . . . . . . . . . . . . . . . . . . 18 + 7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 18 + 7.1. Status Code Registration . . . . . . . . . . . . . . . . . 19 + 7.2. Header Field Registration . . . . . . . . . . . . . . . . 19 + 8. Security Considerations . . . . . . . . . . . . . . . . . . . 19 + 9. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 19 + 10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 19 + 10.1. Normative References . . . . . . . . . . . . . . . . . . . 19 + 10.2. Informative References . . . . . . . . . . . . . . . . . . 20 + Appendix A. Changes from RFC 2616 . . . . . . . . . . . . . . . . 20 + Appendix B. Collected ABNF . . . . . . . . . . . . . . . . . . . 21 + Appendix C. Change Log (to be removed by RFC Editor before + publication) . . . . . . . . . . . . . . . . . . . . 21 + C.1. Since RFC2616 . . . . . . . . . . . . . . . . . . . . . . 21 + C.2. Since draft-ietf-httpbis-p4-conditional-00 . . . . . . . . 22 + C.3. Since draft-ietf-httpbis-p4-conditional-01 . . . . . . . . 22 + C.4. Since draft-ietf-httpbis-p4-conditional-02 . . . . . . . . 22 + C.5. Since draft-ietf-httpbis-p4-conditional-03 . . . . . . . . 22 + C.6. Since draft-ietf-httpbis-p4-conditional-04 . . . . . . . . 23 + C.7. Since draft-ietf-httpbis-p4-conditional-05 . . . . . . . . 23 + C.8. Since draft-ietf-httpbis-p4-conditional-06 . . . . . . . . 23 + C.9. Since draft-ietf-httpbis-p4-conditional-07 . . . . . . . . 23 + C.10. Since draft-ietf-httpbis-p4-conditional-08 . . . . . . . . 23 + C.11. Since draft-ietf-httpbis-p4-conditional-09 . . . . . . . . 23 + C.12. Since draft-ietf-httpbis-p4-conditional-10 . . . . . . . . 24 + Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 + + + +Fielding, et al. Expires February 5, 2011 [Page 3] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +1. Introduction + + This document defines HTTP/1.1 response metadata for indicating + potential changes to payload content, including modification time + stamps and opaque entity-tags, and the HTTP conditional request + mechanisms that allow preconditions to be placed on a request method. + Conditional GET requests allow for efficient cache updates. Other + conditional request methods are used to protect against overwriting + or misunderstanding the state of a resource that has been changed + unbeknownst to the requesting client. + + This document is currently disorganized in order to minimize the + changes between drafts and enable reviewers to see the smaller errata + changes. The next draft will reorganize the sections to better + reflect the content. In particular, the sections on resource + metadata will be discussed first and then followed by each + conditional request-header, concluding with a definition of + precedence and the expectation of ordering strong validator checks + before weak validator checks. It is likely that more content from + [Part6] will migrate to this part, where appropriate. The current + mess reflects how widely dispersed these topics and associated + requirements had become in [RFC2616]. + +1.1. Requirements + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + An implementation is not compliant if it fails to satisfy one or more + of the "MUST" or "REQUIRED" level requirements for the protocols it + implements. An implementation that satisfies all the "MUST" or + "REQUIRED" level and all the "SHOULD" level requirements for its + protocols is said to be "unconditionally compliant"; one that + satisfies all the "MUST" level requirements but not all the "SHOULD" + level requirements for its protocols is said to be "conditionally + compliant". + +1.2. Syntax Notation + + This specification uses the ABNF syntax defined in Section 1.2 of + [Part1] (which extends the syntax defined in [RFC5234] with a list + rule). Appendix B shows the collected ABNF, with the list rule + expanded. + + The following core rules are included by reference, as defined in + [RFC5234], Appendix B.1: ALPHA (letters), CR (carriage return), CRLF + (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), + + + +Fielding, et al. Expires February 5, 2011 [Page 4] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed), OCTET (any 8-bit + sequence of data), SP (space), VCHAR (any visible USASCII character), + and WSP (whitespace). + +1.2.1. Core Rules + + The core rules below are defined in Section 1.2.2 of [Part1]: + + quoted-string = + OWS = + +1.2.2. ABNF Rules defined in other Parts of the Specification + + The ABNF rules below are defined in other parts: + + HTTP-date = + +2. Entity-Tags + + Entity-tags are used for comparing two or more representations of the + same resource. HTTP/1.1 uses entity-tags in the ETag (Section 6.1), + If-Match (Section 6.2), If-None-Match (Section 6.4), and If-Range + (Section 5.3 of [Part5]) header fields. The definition of how they + are used and compared as cache validators is in Section 4. An + entity-tag consists of an opaque quoted string, possibly prefixed by + a weakness indicator. + + entity-tag = [ weak ] opaque-tag + weak = %x57.2F ; "W/", case-sensitive + opaque-tag = quoted-string + + A "strong entity-tag" MAY be shared by two representations of a + resource only if they are equivalent by octet equality. + + A "weak entity-tag", indicated by the "W/" prefix, MAY be shared by + two representations of a resource only if the representations are + equivalent and could be substituted for each other with no + significant change in semantics. A weak entity-tag can only be used + for weak comparison. + + An entity-tag MUST be unique across all versions of all + representations associated with a particular resource. A given + entity-tag value MAY be used for representations obtained by requests + on different URIs. The use of the same entity-tag value in + conjunction with representations obtained by requests on different + URIs does not imply the equivalence of those representations. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 5] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +2.1. Example: Entity-tags varying on Content-Negotiated Resources + + Consider a resource that is subject to content negotiation (Section 5 + of [Part3]), and where the representations returned upon a GET + request vary based on the Accept-Encoding request header field + (Section 6.3 of [Part3]): + + >> Request: + + GET /index HTTP/1.1 + Host: www.example.com + Accept-Encoding: gzip + + + In this case, the response might or might not use the gzip content + coding. If it does not, the response might look like: + + >> Response: + + HTTP/1.1 200 OK + Date: Thu, 26 Mar 2010 00:05:00 GMT + ETag: "123-a" + Content-Length: 70 + Vary: Accept-Encoding + Content-Type: text/plain + + Hello World! + Hello World! + Hello World! + Hello World! + Hello World! + + An alternative representation that does use gzip content coding would + be: + + >> Response: + + HTTP/1.1 200 OK + Date: Thu, 26 Mar 2010 00:05:00 GMT + ETag: "123-b" + Content-Length: 43 + Vary: Accept-Encoding + Content-Type: text/plain + Content-Encoding: gzip + + ...binary data... + + + + + +Fielding, et al. Expires February 5, 2011 [Page 6] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + Note: Content codings are a property of the representation, so + therefore an entity-tag of an encoded representation must be + distinct from an unencoded representation to prevent conflicts + during cache updates and range requests. In contrast, transfer + codings (Section 6.2 of [Part1]) apply only during message + transfer and do not require distinct entity-tags. + +3. Status Code Definitions + +3.1. 304 Not Modified + + If the client has performed a conditional GET request and access is + allowed, but the document has not been modified, the server SHOULD + respond with this status code. The 304 response MUST NOT contain a + message-body, and thus is always terminated by the first empty line + after the header fields. + + A 304 response MUST include a Date header field (Section 9.3 of + [Part1]) unless its omission is required by Section 9.3.1 of [Part1]. + If a 200 response to the same request would have included any of the + header fields Cache-Control, Content-Location, ETag, Expires, Last- + Modified, or Vary, then those same header fields MUST be sent in a + 304 response. + + Since the goal of a 304 response is to minimize information transfer + when the recipient already has one or more cached representations, + the response SHOULD NOT include representation metadata other than + the above listed fields unless said metadata exists for the purpose + of guiding cache updates (e.g., future HTTP extensions). + + If a 304 response includes an entity-tag that indicates a + representation not currently cached, then the recipient MUST NOT use + the 304 to update its own cache. If that conditional request + originated with an outbound client, such as a user agent with its own + cache sending a conditional GET to a shared proxy, then the 304 + response MAY be forwarded to the outbound client. Otherwise, + disregard the response and repeat the request without the + conditional. + + If a cache uses a received 304 response to update a cache entry, the + cache MUST update the entry to reflect any new field values given in + the response. + +3.2. 412 Precondition Failed + + The precondition given in one or more of the request-header fields + evaluated to false when it was tested on the server. This response + code allows the client to place preconditions on the current resource + + + +Fielding, et al. Expires February 5, 2011 [Page 7] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + metadata (header field data) and thus prevent the requested method + from being applied to a resource other than the one intended. + +4. Weak and Strong Validators + + Since both origin servers and caches will compare two validators to + decide if they represent the same or different representations, one + normally would expect that if the representation (including both + representation header fields and representation body) changes in any + way, then the associated validator would change as well. If this is + true, then we call this validator a "strong validator". + + However, there might be cases when a server prefers to change the + validator only on semantically significant changes, and not when + insignificant aspects of the representation change. A validator that + does not always change when the representation changes is a "weak + validator". + + An entity-tag is normally a strong validator, but the protocol + provides a mechanism to tag an entity-tag as "weak". One can think + of a strong validator as one that changes whenever the sequence of + bits in a representation changes, while a weak value changes whenever + the meaning of a representation changes. Alternatively, one can + think of a strong validator as part of an identifier for a specific + representation, whereas a weak validator is part of an identifier for + a set of semantically equivalent representations. + + Note: One example of a strong validator is an integer that is + incremented in stable storage every time a representation is + changed. + + A representation's modification time, if defined with only one- + second resolution, could be a weak validator, since it is possible + that the representation might be modified twice during a single + second. + + Support for weak validators is optional. However, weak validators + allow for more efficient caching of equivalent objects; for + example, a hit counter on a site is probably good enough if it is + updated every few days or weeks, and any value during that period + is likely "good enough" to be equivalent. + + A "use" of a validator is either when a client generates a request + and includes the validator in a validating header field, or when a + server compares two validators. + + Strong validators are usable in any context. Weak validators are + only usable in contexts that do not depend on exact equality of a + + + +Fielding, et al. Expires February 5, 2011 [Page 8] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + representation. For example, either kind is usable for a normal + conditional GET. However, only a strong validator is usable for a + sub-range retrieval, since otherwise the client might end up with an + internally inconsistent representation. + + Clients MUST NOT use weak validators in range requests ([Part5]). + + The only function that HTTP/1.1 defines on validators is comparison. + There are two validator comparison functions, depending on whether + the comparison context allows the use of weak validators or not: + + o The strong comparison function: in order to be considered equal, + both opaque-tags MUST be identical character-by-character, and + both MUST NOT be weak. + + o The weak comparison function: in order to be considered equal, + both opaque-tags MUST be identical character-by-character, but + either or both of them MAY be tagged as "weak" without affecting + the result. + + The example below shows the results for a set of entity-tag pairs, + and both the weak and strong comparison function results: + + +--------+--------+-------------------+-----------------+ + | ETag 1 | ETag 2 | Strong Comparison | Weak Comparison | + +--------+--------+-------------------+-----------------+ + | W/"1" | W/"1" | no match | match | + | W/"1" | W/"2" | no match | no match | + | W/"1" | "1" | no match | match | + | "1" | "1" | match | match | + +--------+--------+-------------------+-----------------+ + + An entity-tag is strong unless it is explicitly tagged as weak. + Section 2 gives the syntax for entity-tags. + + A Last-Modified time, when used as a validator in a request, is + implicitly weak unless it is possible to deduce that it is strong, + using the following rules: + + o The validator is being compared by an origin server to the actual + current validator for the representation and, + + o That origin server reliably knows that the associated + representation did not change twice during the second covered by + the presented validator. + + or + + + + +Fielding, et al. Expires February 5, 2011 [Page 9] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + o The validator is about to be used by a client in an If-Modified- + Since or If-Unmodified-Since header, because the client has a + cache entry for the associated representation, and + + o That cache entry includes a Date value, which gives the time when + the origin server sent the original response, and + + o The presented Last-Modified time is at least 60 seconds before the + Date value. + + or + + o The validator is being compared by an intermediate cache to the + validator stored in its cache entry for the representation, and + + o That cache entry includes a Date value, which gives the time when + the origin server sent the original response, and + + o The presented Last-Modified time is at least 60 seconds before the + Date value. + + This method relies on the fact that if two different responses were + sent by the origin server during the same second, but both had the + same Last-Modified time, then at least one of those responses would + have a Date value equal to its Last-Modified time. The arbitrary 60- + second limit guards against the possibility that the Date and Last- + Modified values are generated from different clocks, or at somewhat + different times during the preparation of the response. An + implementation MAY use a value larger than 60 seconds, if it is + believed that 60 seconds is too short. + + If a client wishes to perform a sub-range retrieval on a value for + which it has only a Last-Modified time and no opaque validator, it + MAY do this only if the Last-Modified time is strong in the sense + described here. + + A cache or origin server receiving a conditional range request + ([Part5]) MUST use the strong comparison function to evaluate the + condition. + + These rules allow HTTP/1.1 caches and clients to safely perform sub- + range retrievals on values that have been obtained from HTTP/1.0 + servers. + +5. Rules for When to Use Entity-tags and Last-Modified Dates + + We adopt a set of rules and recommendations for origin servers, + clients, and caches regarding when various validator types ought to + + + +Fielding, et al. Expires February 5, 2011 [Page 10] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + be used, and for what purposes. + + HTTP/1.1 origin servers: + + o SHOULD send an entity-tag validator unless it is not feasible to + generate one. + + o MAY send a weak entity-tag instead of a strong entity-tag, if + performance considerations support the use of weak entity-tags, or + if it is unfeasible to send a strong entity-tag. + + o SHOULD send a Last-Modified value if it is feasible to send one, + unless the risk of a breakdown in semantic transparency that could + result from using this date in an If-Modified-Since header would + lead to serious problems. + + In other words, the preferred behavior for an HTTP/1.1 origin server + is to send both a strong entity-tag and a Last-Modified value. + + In order to be legal, a strong entity-tag MUST change whenever the + associated representation changes in any way. A weak entity-tag + SHOULD change whenever the associated representation changes in a + semantically significant way. + + Note: In order to provide semantically transparent caching, an + origin server must avoid reusing a specific strong entity-tag + value for two different representations, or reusing a specific + weak entity-tag value for two semantically different + representations. Cache entries might persist for arbitrarily long + periods, regardless of expiration times, so it might be + inappropriate to expect that a cache will never again attempt to + validate an entry using a validator that it obtained at some point + in the past. + + HTTP/1.1 clients: + + o MUST use that entity-tag in any cache-conditional request (using + If-Match or If-None-Match) if an entity-tag has been provided by + the origin server. + + o SHOULD use the Last-Modified value in non-subrange cache- + conditional requests (using If-Modified-Since) if only a Last- + Modified value has been provided by the origin server. + + o MAY use the Last-Modified value in subrange cache-conditional + requests (using If-Unmodified-Since) if only a Last-Modified value + has been provided by an HTTP/1.0 origin server. The user agent + SHOULD provide a way to disable this, in case of difficulty. + + + +Fielding, et al. Expires February 5, 2011 [Page 11] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + o SHOULD use both validators in cache-conditional requests if both + an entity-tag and a Last-Modified value have been provided by the + origin server. This allows both HTTP/1.0 and HTTP/1.1 caches to + respond appropriately. + + An HTTP/1.1 origin server, upon receiving a conditional request that + includes both a Last-Modified date (e.g., in an If-Modified-Since or + If-Unmodified-Since header field) and one or more entity-tags (e.g., + in an If-Match, If-None-Match, or If-Range header field) as cache + validators, MUST NOT return a response status code of 304 (Not + Modified) unless doing so is consistent with all of the conditional + header fields in the request. + + An HTTP/1.1 caching proxy, upon receiving a conditional request that + includes both a Last-Modified date and one or more entity-tags as + cache validators, MUST NOT return a locally cached response to the + client unless that cached response is consistent with all of the + conditional header fields in the request. + + Note: The general principle behind these rules is that HTTP/1.1 + servers and clients ought to transmit as much non-redundant + information as is available in their responses and requests. + HTTP/1.1 systems receiving this information will make the most + conservative assumptions about the validators they receive. + + HTTP/1.0 clients and caches will ignore entity-tags. Generally, + last-modified values received or used by these systems will + support transparent and efficient caching, and so HTTP/1.1 origin + servers should provide Last-Modified values. In those rare cases + where the use of a Last-Modified value as a validator by an + HTTP/1.0 system could result in a serious problem, then HTTP/1.1 + origin servers should not provide one. + +6. Header Field Definitions + + This section defines the syntax and semantics of HTTP/1.1 header + fields related to conditional requests. + +6.1. ETag + + The "ETag" response-header field provides the current value of the + entity-tag (see Section 2) for one representation of the target + resource. An entity-tag is intended for use as a resource-local + identifier for differentiating between representations of the same + resource that vary over time or via content negotiation (see + Section 4). + + ETag = "ETag" ":" OWS ETag-v + + + +Fielding, et al. Expires February 5, 2011 [Page 12] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + ETag-v = entity-tag + + Examples: + + ETag: "xyzzy" + ETag: W/"xyzzy" + ETag: "" + + An entity-tag provides an "opaque" cache validator that allows for + more reliable validation than modification dates in situations where + it is inconvenient to store modification dates, where the one-second + resolution of HTTP date values is not sufficient, or where the origin + server wishes to avoid certain paradoxes that might arise from the + use of modification dates. + + The principle behind entity-tags is that only the service author + knows the semantics of a resource well enough to select an + appropriate cache validation mechanism, and the specification of any + validator comparison function more complex than byte-equality would + open up a can of worms. Thus, comparisons of any other headers + (except Last-Modified, for compatibility with HTTP/1.0) are never + used for purposes of validating a cache entry. + +6.2. If-Match + + The "If-Match" request-header field is used to make a request method + conditional. A client that has one or more representations + previously obtained from the resource can verify that one of those + representations is current by including a list of their associated + entity-tags in the If-Match header field. + + This allows efficient updates of cached information with a minimum + amount of transaction overhead. It is also used when updating + resources, to prevent inadvertent modification of the wrong version + of a resource. As a special case, the value "*" matches any current + representation of the resource. + + If-Match = "If-Match" ":" OWS If-Match-v + If-Match-v = "*" / 1#entity-tag + + If any of the entity-tags match the entity-tag of the representation + that would have been returned in the response to a similar GET + request (without the If-Match header) on that resource, or if "*" is + given and any current representation exists for that resource, then + the server MAY perform the requested method as if the If-Match header + field did not exist. + + If none of the entity-tags match, or if "*" is given and no current + + + +Fielding, et al. Expires February 5, 2011 [Page 13] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + representation exists, the server MUST NOT perform the requested + method, and MUST return a 412 (Precondition Failed) response. This + behavior is most useful when the client wants to prevent an updating + method, such as PUT, from modifying a resource that has changed since + the client last retrieved it. + + If the request would, without the If-Match header field, result in + anything other than a 2xx or 412 status code, then the If-Match + header MUST be ignored. + + The meaning of "If-Match: *" is that the method SHOULD be performed + if the representation selected by the origin server (or by a cache, + possibly using the Vary mechanism, see Section 3.5 of [Part6]) + exists, and MUST NOT be performed if the representation does not + exist. + + A request intended to update a resource (e.g., a PUT) MAY include an + If-Match header field to signal that the request method MUST NOT be + applied if the representation corresponding to the If-Match value (a + single entity-tag) is no longer a representation of that resource. + This allows the user to indicate that they do not wish the request to + be successful if the resource has been changed without their + knowledge. Examples: + + If-Match: "xyzzy" + If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz" + If-Match: * + + The result of a request having both an If-Match header field and + either an If-None-Match or an If-Modified-Since header fields is + undefined by this specification. + +6.3. If-Modified-Since + + The "If-Modified-Since" request-header field is used to make a + request method conditional by date: if the representation that would + have been transferred in a 200 response to a GET request has not been + modified since the time specified in this field, then do not perform + the method; instead, respond as detailed below. + + If-Modified-Since = "If-Modified-Since" ":" OWS + If-Modified-Since-v + If-Modified-Since-v = HTTP-date + + An example of the field is: + + If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT + + + + +Fielding, et al. Expires February 5, 2011 [Page 14] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + A GET method with an If-Modified-Since header and no Range header + requests that the representation be transferred only if it has been + modified since the date given by the If-Modified-Since header. The + algorithm for determining this includes the following cases: + + 1. If the request would normally result in anything other than a 200 + (OK) status code, or if the passed If-Modified-Since date is + invalid, the response is exactly the same as for a normal GET. A + date which is later than the server's current time is invalid. + + 2. If the representation has been modified since the If-Modified- + Since date, the response is exactly the same as for a normal GET. + + 3. If the representation has not been modified since a valid If- + Modified-Since date, the server SHOULD return a 304 (Not + Modified) response. + + The purpose of this feature is to allow efficient updates of cached + information with a minimum amount of transaction overhead. + + Note: The Range request-header field modifies the meaning of If- + Modified-Since; see Section 5.4 of [Part5] for full details. + + Note: If-Modified-Since times are interpreted by the server, whose + clock might not be synchronized with the client. + + Note: When handling an If-Modified-Since header field, some + servers will use an exact date comparison function, rather than a + less-than function, for deciding whether to send a 304 (Not + Modified) response. To get best results when sending an If- + Modified-Since header field for cache validation, clients are + advised to use the exact date string received in a previous Last- + Modified header field whenever possible. + + Note: If a client uses an arbitrary date in the If-Modified-Since + header instead of a date taken from the Last-Modified header for + the same request, the client needs to be aware that this date is + interpreted in the server's understanding of time. Unsynchronized + clocks and rounding problems, due to the different encodings of + time between the client and server, are concerns. This includes + the possibility of race conditions if the document has changed + between the time it was first requested and the If-Modified-Since + date of a subsequent request, and the possibility of clock-skew- + related problems if the If-Modified-Since date is derived from the + client's clock without correction to the server's clock. + Corrections for different time bases between client and server are + at best approximate due to network latency. + + + + +Fielding, et al. Expires February 5, 2011 [Page 15] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + The result of a request having both an If-Modified-Since header field + and either an If-Match or an If-Unmodified-Since header fields is + undefined by this specification. + +6.4. If-None-Match + + The "If-None-Match" request-header field is used to make a request + method conditional. A client that has one or more representations + previously obtained from the resource can verify that none of those + representations is current by including a list of their associated + entity-tags in the If-None-Match header field. + + This allows efficient updates of cached information with a minimum + amount of transaction overhead. It is also used to prevent a method + (e.g., PUT) from inadvertently modifying an existing resource when + the client believes that the resource does not exist. + + As a special case, the value "*" matches any current representation + of the resource. + + If-None-Match = "If-None-Match" ":" OWS If-None-Match-v + If-None-Match-v = "*" / 1#entity-tag + + If any of the entity-tags match the entity-tag of the representation + that would have been returned in the response to a similar GET + request (without the If-None-Match header) on that resource, or if + "*" is given and any current representation exists for that resource, + then the server MUST NOT perform the requested method, unless + required to do so because the resource's modification date fails to + match that supplied in an If-Modified-Since header field in the + request. Instead, if the request method was GET or HEAD, the server + SHOULD respond with a 304 (Not Modified) response, including the + cache-related header fields (particularly ETag) of one of the + representations that matched. For all other request methods, the + server MUST respond with a 412 (Precondition Failed) status code. + + If none of the entity-tags match, then the server MAY perform the + requested method as if the If-None-Match header field did not exist, + but MUST also ignore any If-Modified-Since header field(s) in the + request. That is, if no entity-tags match, then the server MUST NOT + return a 304 (Not Modified) response. + + If the request would, without the If-None-Match header field, result + in anything other than a 2xx or 304 status code, then the If-None- + Match header MUST be ignored. (See Section 5 for a discussion of + server behavior when both If-Modified-Since and If-None-Match appear + in the same request.) + + + + +Fielding, et al. Expires February 5, 2011 [Page 16] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + The meaning of "If-None-Match: *" is that the method MUST NOT be + performed if the representation selected by the origin server (or by + a cache, possibly using the Vary mechanism, see Section 3.5 of + [Part6]) exists, and SHOULD be performed if the representation does + not exist. This feature is intended to be useful in preventing races + between PUT operations. + + Examples: + + If-None-Match: "xyzzy" + If-None-Match: W/"xyzzy" + If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz" + If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz" + If-None-Match: * + + The result of a request having both an If-None-Match header field and + either an If-Match or an If-Unmodified-Since header fields is + undefined by this specification. + +6.5. If-Unmodified-Since + + The "If-Unmodified-Since" request-header field is used to make a + request method conditional. If the representation that would have + been transferred in a 200 response to a GET request on the same + resource has not been modified since the time specified in this + field, the server SHOULD perform the requested operation as if the + If-Unmodified-Since header were not present. + + If the representation has been modified since the specified time, the + server MUST NOT perform the requested operation, and MUST return a + 412 (Precondition Failed). + + If-Unmodified-Since = "If-Unmodified-Since" ":" OWS + If-Unmodified-Since-v + If-Unmodified-Since-v = HTTP-date + + An example of the field is: + + If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT + + If the request normally (i.e., without the If-Unmodified-Since + header) would result in anything other than a 2xx or 412 status code, + the If-Unmodified-Since header SHOULD be ignored. + + If the specified date is invalid, the header is ignored. + + The result of a request having both an If-Unmodified-Since header + field and either an If-None-Match or an If-Modified-Since header + + + +Fielding, et al. Expires February 5, 2011 [Page 17] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + fields is undefined by this specification. + +6.6. Last-Modified + + The "Last-Modified" header field indicates the date and time at which + the origin server believes the representation was last modified. + + Last-Modified = "Last-Modified" ":" OWS Last-Modified-v + Last-Modified-v = HTTP-date + + An example of its use is + + Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT + + The exact meaning of this header field depends on the implementation + of the origin server and the nature of the original resource. For + files, it might be just the file system last-modified time. For + representations with dynamically included parts, it might be the most + recent of the set of last-modify times for its component parts. For + database gateways, it might be the last-update time stamp of the + record. For virtual objects, it might be the last time the internal + state changed. + + An origin server MUST NOT send a Last-Modified date which is later + than the server's time of message origination. In such cases, where + the resource's last modification would indicate some time in the + future, the server MUST replace that date with the message + origination date. + + An origin server SHOULD obtain the Last-Modified value of the + representation as close as possible to the time that it generates the + Date value of its response. This allows a recipient to make an + accurate assessment of the representation's modification time, + especially if the representation changes near the time that the + response is generated. + + HTTP/1.1 servers SHOULD send Last-Modified whenever feasible. + + The Last-Modified header field value is often used as a cache + validator. In simple terms, a cache entry is considered to be valid + if the representation has not been modified since the Last-Modified + value. + +7. IANA Considerations + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 18] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +7.1. Status Code Registration + + The HTTP Status Code Registry located at + shall be updated + with the registrations below: + + +-------+---------------------+-------------+ + | Value | Description | Reference | + +-------+---------------------+-------------+ + | 304 | Not Modified | Section 3.1 | + | 412 | Precondition Failed | Section 3.2 | + +-------+---------------------+-------------+ + +7.2. Header Field Registration + + The Message Header Field Registry located at shall be + updated with the permanent registrations below (see [RFC3864]): + + +---------------------+----------+----------+-------------+ + | Header Field Name | Protocol | Status | Reference | + +---------------------+----------+----------+-------------+ + | ETag | http | standard | Section 6.1 | + | If-Match | http | standard | Section 6.2 | + | If-Modified-Since | http | standard | Section 6.3 | + | If-None-Match | http | standard | Section 6.4 | + | If-Unmodified-Since | http | standard | Section 6.5 | + | Last-Modified | http | standard | Section 6.6 | + +---------------------+----------+----------+-------------+ + + The change controller is: "IETF (iesg@ietf.org) - Internet + Engineering Task Force". + +8. Security Considerations + + No additional security considerations have been identified beyond + those applicable to HTTP in general [Part1]. + +9. Acknowledgments + +10. References + +10.1. Normative References + + [Part1] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 1: URIs, Connections, + and Message Parsing", draft-ietf-httpbis-p1-messaging-11 + + + +Fielding, et al. Expires February 5, 2011 [Page 19] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + (work in progress), August 2010. + + [Part3] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 3: Message Payload + and Content Negotiation", draft-ietf-httpbis-p3-payload-11 + (work in progress), August 2010. + + [Part5] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 5: Range Requests and + Partial Responses", draft-ietf-httpbis-p5-range-11 (work + in progress), August 2010. + + [Part6] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + Nottingham, M., Ed., and J. Reschke, Ed., "HTTP/1.1, part + 6: Caching", draft-ietf-httpbis-p6-cache-11 (work in + progress), August 2010. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", STD 68, RFC 5234, January 2008. + +10.2. Informative References + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration + Procedures for Message Header Fields", BCP 90, RFC 3864, + September 2004. + +Appendix A. Changes from RFC 2616 + + Allow weak entity-tags in all requests except range requests + (Sections 4 and 6.4). + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 20] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +Appendix B. Collected ABNF + + ETag = "ETag:" OWS ETag-v + ETag-v = entity-tag + + HTTP-date = + + If-Match = "If-Match:" OWS If-Match-v + If-Match-v = "*" / ( *( "," OWS ) entity-tag *( OWS "," [ OWS + entity-tag ] ) ) + If-Modified-Since = "If-Modified-Since:" OWS If-Modified-Since-v + If-Modified-Since-v = HTTP-date + If-None-Match = "If-None-Match:" OWS If-None-Match-v + If-None-Match-v = "*" / ( *( "," OWS ) entity-tag *( OWS "," [ OWS + entity-tag ] ) ) + If-Unmodified-Since = "If-Unmodified-Since:" OWS + If-Unmodified-Since-v + If-Unmodified-Since-v = HTTP-date + + Last-Modified = "Last-Modified:" OWS Last-Modified-v + Last-Modified-v = HTTP-date + + OWS = + + entity-tag = [ weak ] opaque-tag + + opaque-tag = quoted-string + + quoted-string = + + weak = %x57.2F ; W/ + + ABNF diagnostics: + + ; ETag defined but not used + ; If-Match defined but not used + ; If-Modified-Since defined but not used + ; If-None-Match defined but not used + ; If-Unmodified-Since defined but not used + ; Last-Modified defined but not used + +Appendix C. Change Log (to be removed by RFC Editor before publication) + +C.1. Since RFC2616 + + Extracted relevant partitions from [RFC2616]. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 21] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +C.2. Since draft-ietf-httpbis-p4-conditional-00 + + Closed issues: + + o : "Normative and + Informative references" + + Other changes: + + o Move definitions of 304 and 412 condition codes from Part2. + +C.3. Since draft-ietf-httpbis-p4-conditional-01 + + Ongoing work on ABNF conversion + (): + + o Add explicit references to BNF syntax and rules imported from + other parts of the specification. + +C.4. Since draft-ietf-httpbis-p4-conditional-02 + + Closed issues: + + o : "Weak ETags on + non-GET requests" + + Ongoing work on IANA Message Header Registration + (): + + o Reference RFC 3984, and update header registrations for headers + defined in this document. + +C.5. Since draft-ietf-httpbis-p4-conditional-03 + + Closed issues: + + o : "Examples for + ETag matching" + + o : "'entity + value' undefined" + + o : "bogus 2068 + Date header reference" + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 22] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +C.6. Since draft-ietf-httpbis-p4-conditional-04 + + Ongoing work on ABNF conversion + (): + + o Use "/" instead of "|" for alternatives. + + o Introduce new ABNF rules for "bad" whitespace ("BWS"), optional + whitespace ("OWS") and required whitespace ("RWS"). + + o Rewrite ABNFs to spell out whitespace rules, factor out header + value format definitions. + +C.7. Since draft-ietf-httpbis-p4-conditional-05 + + Final work on ABNF conversion + (): + + o Add appendix containing collected and expanded ABNF, reorganize + ABNF introduction. + +C.8. Since draft-ietf-httpbis-p4-conditional-06 + + Closed issues: + + o : "case- + sensitivity of etag weakness indicator" + +C.9. Since draft-ietf-httpbis-p4-conditional-07 + + Closed issues: + + o : "Weak ETags on + non-GET requests" (If-Match still was defined to require strong + matching) + + o : "move IANA + registrations for optional status codes" + +C.10. Since draft-ietf-httpbis-p4-conditional-08 + + No significant changes. + +C.11. Since draft-ietf-httpbis-p4-conditional-09 + + No significant changes. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 23] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + +C.12. Since draft-ietf-httpbis-p4-conditional-10 + + Closed issues: + + o : "Clarify + 'Requested Variant'" + + o : "Clarify + entity / representation / variant terminology" + + o : "consider + removing the 'changes from 2068' sections" + +Index + + 3 + 304 Not Modified (status code) 7 + + 4 + 412 Precondition Failed (status code) 7 + + E + ETag header 12 + + G + Grammar + entity-tag 5 + ETag 12 + ETag-v 12 + If-Match 13 + If-Match-v 13 + If-Modified-Since 14 + If-Modified-Since-v 14 + If-None-Match 16 + If-None-Match-v 16 + If-Unmodified-Since 17 + If-Unmodified-Since-v 17 + Last-Modified 18 + Last-Modified-v 18 + opaque-tag 5 + weak 5 + + H + Headers + ETag 12 + If-Match 13 + If-Modified-Since 14 + If-None-Match 16 + + + +Fielding, et al. Expires February 5, 2011 [Page 24] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + If-Unmodified-Since 17 + Last-Modified 18 + + I + If-Match header 13 + If-Modified-Since header 14 + If-None-Match header 16 + If-Unmodified-Since header 17 + + L + Last-Modified header 18 + + S + Status Codes + 304 Not Modified 7 + 412 Precondition Failed 7 + +Authors' Addresses + + Roy T. Fielding (editor) + Day Software + 23 Corporate Plaza DR, Suite 280 + Newport Beach, CA 92660 + USA + + Phone: +1-949-706-5300 + Fax: +1-949-706-5305 + EMail: fielding@gbiv.com + URI: http://roy.gbiv.com/ + + + Jim Gettys + Alcatel-Lucent Bell Labs + 21 Oak Knoll Road + Carlisle, MA 01741 + USA + + EMail: jg@freedesktop.org + URI: http://gettys.wordpress.com/ + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 25] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + Jeffrey C. Mogul + Hewlett-Packard Company + HP Labs, Large Scale Systems Group + 1501 Page Mill Road, MS 1177 + Palo Alto, CA 94304 + USA + + EMail: JeffMogul@acm.org + + + Henrik Frystyk Nielsen + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + USA + + EMail: henrikn@microsoft.com + + + Larry Masinter + Adobe Systems, Incorporated + 345 Park Ave + San Jose, CA 95110 + USA + + EMail: LMM@acm.org + URI: http://larry.masinter.net/ + + + Paul J. Leach + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + + EMail: paulle@microsoft.com + + + Tim Berners-Lee + World Wide Web Consortium + MIT Computer Science and Artificial Intelligence Laboratory + The Stata Center, Building 32 + 32 Vassar Street + Cambridge, MA 02139 + USA + + EMail: timbl@w3.org + URI: http://www.w3.org/People/Berners-Lee/ + + + + +Fielding, et al. Expires February 5, 2011 [Page 26] + +Internet-Draft HTTP/1.1, Part 4 August 2010 + + + Yves Lafon (editor) + World Wide Web Consortium + W3C / ERCIM + 2004, rte des Lucioles + Sophia-Antipolis, AM 06902 + France + + EMail: ylafon@w3.org + URI: http://www.raubacapeu.net/people/yves/ + + + Julian F. Reschke (editor) + greenbytes GmbH + Hafenweg 16 + Muenster, NW 48155 + Germany + + Phone: +49 251 2807760 + Fax: +49 251 2807761 + EMail: julian.reschke@greenbytes.de + URI: http://greenbytes.de/tech/webdav/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 27] + diff --git a/dav/SabreDAV/docs/draft-ietf-httpbis-p5-range-11.txt b/dav/SabreDAV/docs/draft-ietf-httpbis-p5-range-11.txt new file mode 100644 index 000000000..1ef7b0946 --- /dev/null +++ b/dav/SabreDAV/docs/draft-ietf-httpbis-p5-range-11.txt @@ -0,0 +1,1512 @@ + + + +HTTPbis Working Group R. Fielding, Ed. +Internet-Draft Day Software +Obsoletes: 2616 (if approved) J. Gettys +Intended status: Standards Track Alcatel-Lucent +Expires: February 5, 2011 J. Mogul + HP + H. Frystyk + Microsoft + L. Masinter + Adobe Systems + P. Leach + Microsoft + T. Berners-Lee + W3C/MIT + Y. Lafon, Ed. + W3C + J. Reschke, Ed. + greenbytes + August 4, 2010 + + + HTTP/1.1, part 5: Range Requests and Partial Responses + draft-ietf-httpbis-p5-range-11 + +Abstract + + The Hypertext Transfer Protocol (HTTP) is an application-level + protocol for distributed, collaborative, hypermedia information + systems. HTTP has been in use by the World Wide Web global + information initiative since 1990. This document is Part 5 of the + seven-part specification that defines the protocol referred to as + "HTTP/1.1" and, taken together, obsoletes RFC 2616. Part 5 defines + range-specific requests and the rules for constructing and combining + responses to those requests. + +Editorial Note (To be removed by RFC Editor) + + Discussion of this draft should take place on the HTTPBIS working + group mailing list (ietf-http-wg@w3.org). The current issues list is + at and related + documents (including fancy diffs) can be found at + . + + The changes in this draft are summarized in Appendix D.12. + +Status of This Memo + + This Internet-Draft is submitted in full conformance with the + + + +Fielding, et al. Expires February 5, 2011 [Page 1] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on February 5, 2011. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 2] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 1.1. Requirements . . . . . . . . . . . . . . . . . . . . . . . 5 + 1.2. Syntax Notation . . . . . . . . . . . . . . . . . . . . . 5 + 1.2.1. Core Rules . . . . . . . . . . . . . . . . . . . . . . 6 + 1.2.2. ABNF Rules defined in other Parts of the + Specification . . . . . . . . . . . . . . . . . . . . 6 + 2. Range Units . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 2.1. Range Specifier Registry . . . . . . . . . . . . . . . . . 6 + 3. Status Code Definitions . . . . . . . . . . . . . . . . . . . 7 + 3.1. 206 Partial Content . . . . . . . . . . . . . . . . . . . 7 + 3.2. 416 Requested Range Not Satisfiable . . . . . . . . . . . 8 + 4. Combining Ranges . . . . . . . . . . . . . . . . . . . . . . . 8 + 5. Header Field Definitions . . . . . . . . . . . . . . . . . . . 8 + 5.1. Accept-Ranges . . . . . . . . . . . . . . . . . . . . . . 9 + 5.2. Content-Range . . . . . . . . . . . . . . . . . . . . . . 9 + 5.3. If-Range . . . . . . . . . . . . . . . . . . . . . . . . . 11 + 5.4. Range . . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 5.4.1. Byte Ranges . . . . . . . . . . . . . . . . . . . . . 12 + 5.4.2. Range Retrieval Requests . . . . . . . . . . . . . . . 14 + 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 15 + 6.1. Status Code Registration . . . . . . . . . . . . . . . . . 15 + 6.2. Header Field Registration . . . . . . . . . . . . . . . . 15 + 6.3. Range Specifier Registration . . . . . . . . . . . . . . . 16 + 7. Security Considerations . . . . . . . . . . . . . . . . . . . 16 + 8. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 16 + 9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 16 + 9.1. Normative References . . . . . . . . . . . . . . . . . . . 16 + 9.2. Informative References . . . . . . . . . . . . . . . . . . 17 + Appendix A. Internet Media Type multipart/byteranges . . . . . . 17 + Appendix B. Compatibility with Previous Versions . . . . . . . . 20 + B.1. Changes from RFC 2616 . . . . . . . . . . . . . . . . . . 20 + Appendix C. Collected ABNF . . . . . . . . . . . . . . . . . . . 20 + Appendix D. Change Log (to be removed by RFC Editor before + publication) . . . . . . . . . . . . . . . . . . . . 21 + D.1. Since RFC2616 . . . . . . . . . . . . . . . . . . . . . . 21 + D.2. Since draft-ietf-httpbis-p5-range-00 . . . . . . . . . . . 21 + D.3. Since draft-ietf-httpbis-p5-range-01 . . . . . . . . . . . 22 + D.4. Since draft-ietf-httpbis-p5-range-02 . . . . . . . . . . . 22 + D.5. Since draft-ietf-httpbis-p5-range-03 . . . . . . . . . . . 22 + D.6. Since draft-ietf-httpbis-p5-range-04 . . . . . . . . . . . 22 + D.7. Since draft-ietf-httpbis-p5-range-05 . . . . . . . . . . . 22 + D.8. Since draft-ietf-httpbis-p5-range-06 . . . . . . . . . . . 23 + D.9. Since draft-ietf-httpbis-p5-range-07 . . . . . . . . . . . 23 + D.10. Since draft-ietf-httpbis-p5-range-08 . . . . . . . . . . . 23 + D.11. Since draft-ietf-httpbis-p5-range-09 . . . . . . . . . . . 23 + D.12. Since draft-ietf-httpbis-p5-range-10 . . . . . . . . . . . 23 + + + +Fielding, et al. Expires February 5, 2011 [Page 3] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 4] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + +1. Introduction + + HTTP clients often encounter interrupted data transfers as a result + of cancelled requests or dropped connections. When a cache has + stored a partial representation, it is desirable to request the + remainder of that representation in a subsequent request rather than + transfer the entire representation. There are also a number of Web + applications that benefit from being able to request only a subset of + a larger representation, such as a single page of a very large + document or only part of an image to be rendered by a device with + limited local storage. + + This document defines HTTP/1.1 range requests, partial responses, and + the multipart/byteranges media type. The protocol for range requests + is an OPTIONAL feature of HTTP, designed so resources or recipients + that do not implement this feature can respond as if it is a normal + GET request without impacting interoperability. Partial responses + are indicated by a distinct status code to not be mistaken for full + responses by intermediate caches that might not implement the + feature. + + Although the HTTP range request mechanism is designed to allow for + extensible range types, this specification only defines requests for + byte ranges. + +1.1. Requirements + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + An implementation is not compliant if it fails to satisfy one or more + of the "MUST" or "REQUIRED" level requirements for the protocols it + implements. An implementation that satisfies all the "MUST" or + "REQUIRED" level and all the "SHOULD" level requirements for its + protocols is said to be "unconditionally compliant"; one that + satisfies all the "MUST" level requirements but not all the "SHOULD" + level requirements for its protocols is said to be "conditionally + compliant". + +1.2. Syntax Notation + + This specification uses the ABNF syntax defined in Section 1.2 of + [Part1] (which extends the syntax defined in [RFC5234] with a list + rule). Appendix C shows the collected ABNF, with the list rule + expanded. + + The following core rules are included by reference, as defined in + + + +Fielding, et al. Expires February 5, 2011 [Page 5] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + [RFC5234], Appendix B.1: ALPHA (letters), CR (carriage return), CRLF + (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), + HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed), OCTET (any 8-bit + sequence of data), SP (space), VCHAR (any visible USASCII character), + and WSP (whitespace). + +1.2.1. Core Rules + + The core rules below are defined in Section 1.2.2 of [Part1]: + + token = + OWS = + +1.2.2. ABNF Rules defined in other Parts of the Specification + + The ABNF rules below are defined in other parts: + + HTTP-date = + + + entity-tag = + +2. Range Units + + HTTP/1.1 allows a client to request that only part (a range of) the + representation be included within the response. HTTP/1.1 uses range + units in the Range (Section 5.4) and Content-Range (Section 5.2) + header fields. A representation can be broken down into subranges + according to various structural units. + + range-unit = bytes-unit / other-range-unit + bytes-unit = "bytes" + other-range-unit = token + + HTTP/1.1 has been designed to allow implementations of applications + that do not depend on knowledge of ranges. The only range unit + defined by HTTP/1.1 is "bytes". Additional specifiers can be defined + as described in Section 2.1. + + If a range unit is not understood in a request, a server MUST ignore + the whole Range header (Section 5.4). If a range unit is not + understood in a response, an intermediary SHOULD pass the response to + the client; a client MUST fail. + +2.1. Range Specifier Registry + + The HTTP Ranger Specifier Registry defines the name space for the + range specifier names. + + + +Fielding, et al. Expires February 5, 2011 [Page 6] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + Registrations MUST include the following fields: + + o Name + + o Description + + o Pointer to specification text + + Values to be added to this name space are subject to IETF review + ([RFC5226], Section 4.1). + + The registry itself is maintained at + . + +3. Status Code Definitions + +3.1. 206 Partial Content + + The server has fulfilled the partial GET request for the resource. + The request MUST have included a Range header field (Section 5.4) + indicating the desired range, and MAY have included an If-Range + header field (Section 5.3) to make the request conditional. + + The response MUST include the following header fields: + + o Either a Content-Range header field (Section 5.2) indicating the + range included with this response, or a multipart/byteranges + Content-Type including Content-Range fields for each part. If a + Content-Length header field is present in the response, its value + MUST match the actual number of octets transmitted in the message- + body. + + o Date + + o Cache-Control, ETag, Expires, Content-Location, Last-Modified, + and/or Vary, if the header field would have been sent in a 200 + response to the same request + + If the 206 response is the result of an If-Range request, the + response SHOULD NOT include other representation header fields. + Otherwise, the response MUST include all of the representation header + fields that would have been returned with a 200 (OK) response to the + same request. + + A cache MUST NOT combine a 206 response with other previously cached + content if the ETag or Last-Modified headers do not match exactly, + see Section 4. + + + + +Fielding, et al. Expires February 5, 2011 [Page 7] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + A cache that does not support the Range and Content-Range headers + MUST NOT cache 206 (Partial Content) responses. Furthermore, if a + response uses a range unit that is not understood by the cache, then + it MUST NOT be cached either. + +3.2. 416 Requested Range Not Satisfiable + + A server SHOULD return a response with this status code if a request + included a Range request-header field (Section 5.4), and none of the + ranges-specifier values in this field overlap the current extent of + the selected resource, and the request did not include an If-Range + request-header field (Section 5.3). (For byte-ranges, this means + that the first-byte-pos of all of the byte-range-spec values were + greater than the current length of the selected resource.) + + When this status code is returned for a byte-range request, the + response SHOULD include a Content-Range header field specifying the + current length of the representation (see Section 5.2). This + response MUST NOT use the multipart/byteranges content-type. + +4. Combining Ranges + + A response might transfer only a subrange of a representation, either + because the request included one or more Range specifications, or + because a connection closed prematurely. After several such + transfers, a cache might have received several ranges of the same + representation. + + If a cache has a stored non-empty set of subranges for a + representation, and an incoming response transfers another subrange, + the cache MAY combine the new subrange with the existing set if both + the following conditions are met: + + o Both the incoming response and the cache entry have a cache + validator. + + o The two cache validators match using the strong comparison + function (see Section 4 of [Part4]). + + If either requirement is not met, the cache MUST use only the most + recent partial response (based on the Date values transmitted with + every response, and using the incoming response if these values are + equal or missing), and MUST discard the other partial information. + +5. Header Field Definitions + + This section defines the syntax and semantics of HTTP/1.1 header + fields related to range requests and partial responses. + + + +Fielding, et al. Expires February 5, 2011 [Page 8] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + +5.1. Accept-Ranges + + The "Accept-Ranges" response-header field allows a resource to + indicate its acceptance of range requests. + + Accept-Ranges = "Accept-Ranges" ":" OWS Accept-Ranges-v + Accept-Ranges-v = acceptable-ranges + acceptable-ranges = 1#range-unit / "none" + + Origin servers that accept byte-range requests MAY send + + Accept-Ranges: bytes + + but are not required to do so. Clients MAY generate range requests + without having received this header for the resource involved. Range + units are defined in Section 2. + + Servers that do not accept any kind of range request for a resource + MAY send + + Accept-Ranges: none + + to advise the client not to attempt a range request. + +5.2. Content-Range + + The "Content-Range" header field is sent with a partial + representation to specify where in the full representation the + payload body is intended to be applied. + + Range units are defined in Section 2. + + Content-Range = "Content-Range" ":" OWS Content-Range-v + Content-Range-v = content-range-spec + + content-range-spec = byte-content-range-spec + / other-content-range-spec + byte-content-range-spec = bytes-unit SP + byte-range-resp-spec "/" + ( instance-length / "*" ) + + byte-range-resp-spec = (first-byte-pos "-" last-byte-pos) + / "*" + + instance-length = 1*DIGIT + + other-content-range-spec = other-range-unit SP + other-range-resp-spec + + + +Fielding, et al. Expires February 5, 2011 [Page 9] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + other-range-resp-spec = *CHAR + + The header SHOULD indicate the total length of the full + representation, unless this length is unknown or difficult to + determine. The asterisk "*" character means that the instance-length + is unknown at the time when the response was generated. + + Unlike byte-ranges-specifier values (see Section 5.4.1), a byte- + range-resp-spec MUST only specify one range, and MUST contain + absolute byte positions for both the first and last byte of the + range. + + A byte-content-range-spec with a byte-range-resp-spec whose last- + byte-pos value is less than its first-byte-pos value, or whose + instance-length value is less than or equal to its last-byte-pos + value, is invalid. The recipient of an invalid byte-content-range- + spec MUST ignore it and any content transferred along with it. + + In the case of a byte range request: A server sending a response with + status code 416 (Requested range not satisfiable) SHOULD include a + Content-Range field with a byte-range-resp-spec of "*". The + instance-length specifies the current length of the selected + resource. A response with status code 206 (Partial Content) MUST NOT + include a Content-Range field with a byte-range-resp-spec of "*". + + Examples of byte-content-range-spec values, assuming that the + representation contains a total of 1234 bytes: + + o The first 500 bytes: + + bytes 0-499/1234 + + o The second 500 bytes: + + bytes 500-999/1234 + + o All except for the first 500 bytes: + + bytes 500-1233/1234 + + o The last 500 bytes: + + bytes 734-1233/1234 + + When an HTTP message includes the content of a single range (for + example, a response to a request for a single range, or to a request + for a set of ranges that overlap without any holes), this content is + transmitted with a Content-Range header, and a Content-Length header + + + +Fielding, et al. Expires February 5, 2011 [Page 10] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + showing the number of bytes actually transferred. For example, + + HTTP/1.1 206 Partial Content + Date: Wed, 15 Nov 1995 06:25:24 GMT + Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT + Content-Range: bytes 21010-47021/47022 + Content-Length: 26012 + Content-Type: image/gif + + When an HTTP message includes the content of multiple ranges (for + example, a response to a request for multiple non-overlapping + ranges), these are transmitted as a multipart message. The multipart + media type used for this purpose is "multipart/byteranges" as defined + in Appendix A. + + A response to a request for a single range MUST NOT be sent using the + multipart/byteranges media type. A response to a request for + multiple ranges, whose result is a single range, MAY be sent as a + multipart/byteranges media type with one part. A client that cannot + decode a multipart/byteranges message MUST NOT ask for multiple + ranges in a single request. + + When a client requests multiple ranges in one request, the server + SHOULD return them in the order that they appeared in the request. + + If the server ignores a byte-range-spec because it is syntactically + invalid, the server SHOULD treat the request as if the invalid Range + header field did not exist. (Normally, this means return a 200 + response containing the full representation). + + If the server receives a request (other than one including an If- + Range request-header field) with an unsatisfiable Range request- + header field (that is, all of whose byte-range-spec values have a + first-byte-pos value greater than the current length of the selected + resource), it SHOULD return a response code of 416 (Requested range + not satisfiable) (Section 3.2). + + Note: Clients cannot depend on servers to send a 416 (Requested + range not satisfiable) response instead of a 200 (OK) response for + an unsatisfiable Range request-header, since not all servers + implement this request-header. + +5.3. If-Range + + If a client has a partial copy of a representation in its cache, and + wishes to have an up-to-date copy of the entire representation in its + cache, it could use the Range request-header with a conditional GET + (using either or both of If-Unmodified-Since and If-Match.) However, + + + +Fielding, et al. Expires February 5, 2011 [Page 11] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + if the condition fails because the representation has been modified, + the client would then have to make a second request to obtain the + entire current representation. + + The "If-Range" request-header field allows a client to "short- + circuit" the second request. Informally, its meaning is "if the + representation is unchanged, send me the part(s) that I am missing; + otherwise, send me the entire new representation". + + If-Range = "If-Range" ":" OWS If-Range-v + If-Range-v = entity-tag / HTTP-date + + If the client has no entity-tag for a representation, but does have a + Last-Modified date, it MAY use that date in an If-Range header. (The + server can distinguish between a valid HTTP-date and any form of + entity-tag by examining no more than two characters.) The If-Range + header SHOULD only be used together with a Range header, and MUST be + ignored if the request does not include a Range header, or if the + server does not support the sub-range operation. + + If the entity-tag given in the If-Range header matches the current + cache validator for the representation, then the server SHOULD + provide the specified sub-range of the representation using a 206 + (Partial Content) response. If the cache validator does not match, + then the server SHOULD return the entire representation using a 200 + (OK) response. + +5.4. Range + +5.4.1. Byte Ranges + + Since all HTTP representations are transferred as sequences of bytes, + the concept of a byte range is meaningful for any HTTP + representation. (However, not all clients and servers need to + support byte-range operations.) + + Byte range specifications in HTTP apply to the sequence of bytes in + the representation body (not necessarily the same as the message- + body). + + A byte range operation MAY specify a single range of bytes, or a set + of ranges within a single representation. + + byte-ranges-specifier = bytes-unit "=" byte-range-set + byte-range-set = 1#( byte-range-spec / suffix-byte-range-spec ) + byte-range-spec = first-byte-pos "-" [ last-byte-pos ] + first-byte-pos = 1*DIGIT + last-byte-pos = 1*DIGIT + + + +Fielding, et al. Expires February 5, 2011 [Page 12] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + The first-byte-pos value in a byte-range-spec gives the byte-offset + of the first byte in a range. The last-byte-pos value gives the + byte-offset of the last byte in the range; that is, the byte + positions specified are inclusive. Byte offsets start at zero. + + If the last-byte-pos value is present, it MUST be greater than or + equal to the first-byte-pos in that byte-range-spec, or the byte- + range-spec is syntactically invalid. The recipient of a byte-range- + set that includes one or more syntactically invalid byte-range-spec + values MUST ignore the header field that includes that byte-range- + set. + + If the last-byte-pos value is absent, or if the value is greater than + or equal to the current length of the representation body, last-byte- + pos is taken to be equal to one less than the current length of the + representation in bytes. + + By its choice of last-byte-pos, a client can limit the number of + bytes retrieved without knowing the size of the representation. + + suffix-byte-range-spec = "-" suffix-length + suffix-length = 1*DIGIT + + A suffix-byte-range-spec is used to specify the suffix of the + representation body, of a length given by the suffix-length value. + (That is, this form specifies the last N bytes of a representation.) + If the representation is shorter than the specified suffix-length, + the entire representation is used. + + If a syntactically valid byte-range-set includes at least one byte- + range-spec whose first-byte-pos is less than the current length of + the representation, or at least one suffix-byte-range-spec with a + non-zero suffix-length, then the byte-range-set is satisfiable. + Otherwise, the byte-range-set is unsatisfiable. If the byte-range- + set is unsatisfiable, the server SHOULD return a response with a 416 + (Requested range not satisfiable) status code. Otherwise, the server + SHOULD return a response with a 206 (Partial Content) status code + containing the satisfiable ranges of the representation. + + Examples of byte-ranges-specifier values (assuming a representation + of length 10000): + + o The first 500 bytes (byte offsets 0-499, inclusive): + + bytes=0-499 + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 13] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + o The second 500 bytes (byte offsets 500-999, inclusive): + + bytes=500-999 + + o The final 500 bytes (byte offsets 9500-9999, inclusive): + + bytes=-500 + + Or: + + bytes=9500- + + o The first and last bytes only (bytes 0 and 9999): + + bytes=0-0,-1 + + o Several legal but not canonical specifications of the second 500 + bytes (byte offsets 500-999, inclusive): + + bytes=500-600,601-999 + bytes=500-700,601-999 + +5.4.2. Range Retrieval Requests + + The "Range" request-header field defines the GET method (conditional + or not) to request one or more sub-ranges of the response + representation body, instead of the entire representation body. + + Range = "Range" ":" OWS Range-v + Range-v = byte-ranges-specifier + / other-ranges-specifier + other-ranges-specifier = other-range-unit "=" other-range-set + other-range-set = 1*CHAR + + A server MAY ignore the Range header. However, HTTP/1.1 origin + servers and intermediate caches ought to support byte ranges when + possible, since Range supports efficient recovery from partially + failed transfers, and supports efficient partial retrieval of large + representations. + + If the server supports the Range header and the specified range or + ranges are appropriate for the representation: + + o The presence of a Range header in an unconditional GET modifies + what is returned if the GET is otherwise successful. In other + words, the response carries a status code of 206 (Partial Content) + instead of 200 (OK). + + + + +Fielding, et al. Expires February 5, 2011 [Page 14] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + o The presence of a Range header in a conditional GET (a request + using one or both of If-Modified-Since and If-None-Match, or one + or both of If-Unmodified-Since and If-Match) modifies what is + returned if the GET is otherwise successful and the condition is + true. It does not affect the 304 (Not Modified) response returned + if the conditional is false. + + In some cases, it might be more appropriate to use the If-Range + header (see Section 5.3) in addition to the Range header. + + If a proxy that supports ranges receives a Range request, forwards + the request to an inbound server, and receives an entire + representation in reply, it SHOULD only return the requested range to + its client. It SHOULD store the entire received response in its + cache if that is consistent with its cache allocation policies. + +6. IANA Considerations + +6.1. Status Code Registration + + The HTTP Status Code Registry located at + shall be updated + with the registrations below: + + +-------+---------------------------------+-------------+ + | Value | Description | Reference | + +-------+---------------------------------+-------------+ + | 206 | Partial Content | Section 3.1 | + | 416 | Requested Range Not Satisfiable | Section 3.2 | + +-------+---------------------------------+-------------+ + +6.2. Header Field Registration + + The Message Header Field Registry located at shall be + updated with the permanent registrations below (see [RFC3864]): + + +-------------------+----------+----------+-------------+ + | Header Field Name | Protocol | Status | Reference | + +-------------------+----------+----------+-------------+ + | Accept-Ranges | http | standard | Section 5.1 | + | Content-Range | http | standard | Section 5.2 | + | If-Range | http | standard | Section 5.3 | + | Range | http | standard | Section 5.4 | + +-------------------+----------+----------+-------------+ + + The change controller is: "IETF (iesg@ietf.org) - Internet + Engineering Task Force". + + + +Fielding, et al. Expires February 5, 2011 [Page 15] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + +6.3. Range Specifier Registration + + The registration procedure for HTTP Range Specifiers is defined by + Section 2.1 of this document. + + The HTTP Range Specifier Registry shall be created at + and be + populated with the registrations below: + + +----------------------+-------------------+----------------------+ + | Range Specifier Name | Description | Reference | + +----------------------+-------------------+----------------------+ + | bytes | a range of octets | (this specification) | + +----------------------+-------------------+----------------------+ + + The change controller is: "IETF (iesg@ietf.org) - Internet + Engineering Task Force". + +7. Security Considerations + + No additional security considerations have been identified beyond + those applicable to HTTP in general [Part1]. + +8. Acknowledgments + + Most of the specification of ranges is based on work originally done + by Ari Luotonen and John Franks, with additional input from Steve + Zilles, Daniel W. Connolly, Roy T. Fielding, Jim Gettys, Martin + Hamilton, Koen Holtman, Shel Kaplan, Paul Leach, Alex Lopez-Ortiz, + Larry Masinter, Jeff Mogul, Lou Montulli, David W. Morris, Luigi + Rizzo, and Bill Weihl. + +9. References + +9.1. Normative References + + [Part1] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 1: URIs, Connections, + and Message Parsing", draft-ietf-httpbis-p1-messaging-11 + (work in progress), August 2010. + + [Part4] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 4: Conditional + Requests", draft-ietf-httpbis-p4-conditional-11 (work in + progress), August 2010. + + + + +Fielding, et al. Expires February 5, 2011 [Page 16] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + [RFC2046] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part Two: Media Types", RFC 2046, + November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", STD 68, RFC 5234, January 2008. + +9.2. Informative References + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration + Procedures for Message Header Fields", BCP 90, RFC 3864, + September 2004. + + [RFC4288] Freed, N. and J. Klensin, "Media Type Specifications and + Registration Procedures", BCP 13, RFC 4288, December 2005. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an + IANA Considerations Section in RFCs", BCP 26, RFC 5226, + May 2008. + +Appendix A. Internet Media Type multipart/byteranges + + When an HTTP 206 (Partial Content) response message includes the + content of multiple ranges (a response to a request for multiple non- + overlapping ranges), these are transmitted as a multipart message- + body ([RFC2046], Section 5.1). The media type for this purpose is + called "multipart/byteranges". The following is to be registered + with IANA [RFC4288]. + + Note: Despite the name "multipart/byteranges" is not limited to + the byte ranges only. + + The multipart/byteranges media type includes one or more parts, each + with its own Content-Type and Content-Range fields. The required + boundary parameter specifies the boundary string used to separate + each body-part. + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 17] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + Type name: multipart + + Subtype name: byteranges + + Required parameters: boundary + + Optional parameters: none + + Encoding considerations: only "7bit", "8bit", or "binary" are + permitted + + Security considerations: none + + Interoperability considerations: none + + Published specification: This specification (see Appendix A). + + Applications that use this media type: + + Additional information: + + Magic number(s): none + + File extension(s): none + + Macintosh file type code(s): none + + Person and email address to contact for further information: See + Authors Section. + + Intended usage: COMMON + + Restrictions on usage: none + + Author/Change controller: IESG + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 18] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + For example: + + HTTP/1.1 206 Partial Content + Date: Wed, 15 Nov 1995 06:25:24 GMT + Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT + Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES + + --THIS_STRING_SEPARATES + Content-type: application/pdf + Content-range: bytes 500-999/8000 + + ...the first range... + --THIS_STRING_SEPARATES + Content-type: application/pdf + Content-range: bytes 7000-7999/8000 + + ...the second range + --THIS_STRING_SEPARATES-- + + Other example: + + HTTP/1.1 206 Partial Content + Date: Tue, 14 Nov 1995 06:25:24 GMT + Last-Modified: Tue, 14 July 04:58:08 GMT + Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES + + --THIS_STRING_SEPARATES + Content-type: video/example + Content-range: exampleunit 1.2-4.3/25 + + ...the first range... + --THIS_STRING_SEPARATES + Content-type: video/example + Content-range: exampleunit 11.2-14.3/25 + + ...the second range + --THIS_STRING_SEPARATES-- + + Notes: + + 1. Additional CRLFs MAY precede the first boundary string in the + body. + + 2. Although [RFC2046] permits the boundary string to be quoted, some + existing implementations handle a quoted boundary string + incorrectly. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 19] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + 3. A number of browsers and servers were coded to an early draft of + the byteranges specification to use a media type of multipart/ + x-byteranges, which is almost, but not quite compatible with the + version documented in HTTP/1.1. + +Appendix B. Compatibility with Previous Versions + +B.1. Changes from RFC 2616 + + Clarify that it is not ok to use a weak cache validator in a 206 + response. (Section 3.1) + + Clarify that multipart/byteranges can consist of a single part. + (Appendix A) + +Appendix C. Collected ABNF + + Accept-Ranges = "Accept-Ranges:" OWS Accept-Ranges-v + Accept-Ranges-v = acceptable-ranges + + Content-Range = "Content-Range:" OWS Content-Range-v + Content-Range-v = content-range-spec + + HTTP-date = + + If-Range = "If-Range:" OWS If-Range-v + If-Range-v = entity-tag / HTTP-date + + OWS = + + Range = "Range:" OWS Range-v + Range-v = byte-ranges-specifier / other-ranges-specifier + + acceptable-ranges = ( *( "," OWS ) range-unit *( OWS "," [ OWS + range-unit ] ) ) / "none" + + byte-content-range-spec = bytes-unit SP byte-range-resp-spec "/" ( + instance-length / "*" ) + byte-range-resp-spec = ( first-byte-pos "-" last-byte-pos ) / "*" + byte-range-set = ( *( "," OWS ) byte-range-spec ) / ( + suffix-byte-range-spec *( OWS "," [ ( OWS byte-range-spec ) / + suffix-byte-range-spec ] ) ) + byte-range-spec = first-byte-pos "-" [ last-byte-pos ] + byte-ranges-specifier = bytes-unit "=" byte-range-set + bytes-unit = "bytes" + + content-range-spec = byte-content-range-spec / + other-content-range-spec + + + +Fielding, et al. Expires February 5, 2011 [Page 20] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + entity-tag = + + first-byte-pos = 1*DIGIT + + instance-length = 1*DIGIT + + last-byte-pos = 1*DIGIT + + other-content-range-spec = other-range-unit SP other-range-resp-spec + other-range-resp-spec = *CHAR + other-range-set = 1*CHAR + other-range-unit = token + other-ranges-specifier = other-range-unit "=" other-range-set + + range-unit = bytes-unit / other-range-unit + + suffix-byte-range-spec = "-" suffix-length + suffix-length = 1*DIGIT + + token = + + ABNF diagnostics: + + ; Accept-Ranges defined but not used + ; Content-Range defined but not used + ; If-Range defined but not used + ; Range defined but not used + +Appendix D. Change Log (to be removed by RFC Editor before publication) + +D.1. Since RFC2616 + + Extracted relevant partitions from [RFC2616]. + +D.2. Since draft-ietf-httpbis-p5-range-00 + + Closed issues: + + o : "Cache + validators in 206 responses" + () + + o : "Normative and + Informative references" + + o : "Normative up- + to-date references" + + + + +Fielding, et al. Expires February 5, 2011 [Page 21] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + +D.3. Since draft-ietf-httpbis-p5-range-01 + + Closed issues: + + o : "Updating to + RFC4288" + + Ongoing work on ABNF conversion + (): + + o Add explicit references to BNF syntax and rules imported from + other parts of the specification. + +D.4. Since draft-ietf-httpbis-p5-range-02 + + Ongoing work on IANA Message Header Registration + (): + + o Reference RFC 3984, and update header registrations for headers + defined in this document. + +D.5. Since draft-ietf-httpbis-p5-range-03 + +D.6. Since draft-ietf-httpbis-p5-range-04 + + Closed issues: + + o : "multipart/ + byteranges minimum number of parts" + + Ongoing work on ABNF conversion + (): + + o Use "/" instead of "|" for alternatives. + + o Introduce new ABNF rules for "bad" whitespace ("BWS"), optional + whitespace ("OWS") and required whitespace ("RWS"). + + o Rewrite ABNFs to spell out whitespace rules, factor out header + value format definitions. + +D.7. Since draft-ietf-httpbis-p5-range-05 + + Closed issues: + + o : "State base + for *-byte-pos and suffix-length" + + + + +Fielding, et al. Expires February 5, 2011 [Page 22] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + Ongoing work on Custom Ranges + (): + + o Remove bias in favor of byte ranges; allow custom ranges in ABNF. + + Final work on ABNF conversion + (): + + o Add appendix containing collected and expanded ABNF, reorganize + ABNF introduction. + +D.8. Since draft-ietf-httpbis-p5-range-06 + + Closed issues: + + o : "base for + numeric protocol elements" + +D.9. Since draft-ietf-httpbis-p5-range-07 + + Closed issues: + + o Fixed discrepancy in the If-Range definition about allowed + validators. + + o : "multipart/ + byteranges for custom range units" + + o : "range unit + missing from other-ranges-specifier in Range header" + + o : "move IANA + registrations for optional status codes" + +D.10. Since draft-ietf-httpbis-p5-range-08 + + No significant changes. + +D.11. Since draft-ietf-httpbis-p5-range-09 + + No significant changes. + +D.12. Since draft-ietf-httpbis-p5-range-10 + + Closed issues: + + o : "Clarify + 'Requested Variant'" + + + +Fielding, et al. Expires February 5, 2011 [Page 23] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + o : "Clarify + entity / representation / variant terminology" + + o : "consider + removing the 'changes from 2068' sections" + + Ongoing work on Custom Ranges + (): + + o Add IANA registry. + +Index + + 2 + 206 Partial Content (status code) 7 + + 4 + 416 Requested Range Not Satisfiable (status code) 8 + + A + Accept-Ranges header 9 + + C + Content-Range header 9 + + G + Grammar + Accept-Ranges 9 + Accept-Ranges-v 9 + acceptable-ranges 9 + byte-content-range-spec 9 + byte-range-resp-spec 9 + byte-range-set 12 + byte-range-spec 12 + byte-ranges-specifier 12 + bytes-unit 6 + Content-Range 9 + content-range-spec 9 + Content-Range-v 9 + first-byte-pos 12 + If-Range 12 + If-Range-v 12 + instance-length 9 + last-byte-pos 12 + other-range-unit 6 + Range 14 + range-unit 6 + ranges-specifier 12 + + + +Fielding, et al. Expires February 5, 2011 [Page 24] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + suffix-byte-range-spec 13 + suffix-length 13 + + H + Headers + Accept-Ranges 9 + Content-Range 9 + If-Range 11 + Range 12 + + I + If-Range header 11 + + M + Media Type + multipart/byteranges 17 + multipart/x-byteranges 20 + multipart/byteranges Media Type 17 + multipart/x-byteranges Media Type 20 + + R + Range header 12 + + S + Status Codes + 206 Partial Content 7 + 416 Requested Range Not Satisfiable 8 + +Authors' Addresses + + Roy T. Fielding (editor) + Day Software + 23 Corporate Plaza DR, Suite 280 + Newport Beach, CA 92660 + USA + + Phone: +1-949-706-5300 + Fax: +1-949-706-5305 + EMail: fielding@gbiv.com + URI: http://roy.gbiv.com/ + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 25] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + Jim Gettys + Alcatel-Lucent Bell Labs + 21 Oak Knoll Road + Carlisle, MA 01741 + USA + + EMail: jg@freedesktop.org + URI: http://gettys.wordpress.com/ + + + Jeffrey C. Mogul + Hewlett-Packard Company + HP Labs, Large Scale Systems Group + 1501 Page Mill Road, MS 1177 + Palo Alto, CA 94304 + USA + + EMail: JeffMogul@acm.org + + + Henrik Frystyk Nielsen + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + USA + + EMail: henrikn@microsoft.com + + + Larry Masinter + Adobe Systems, Incorporated + 345 Park Ave + San Jose, CA 95110 + USA + + EMail: LMM@acm.org + URI: http://larry.masinter.net/ + + + Paul J. Leach + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + + EMail: paulle@microsoft.com + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 26] + +Internet-Draft HTTP/1.1, Part 5 August 2010 + + + Tim Berners-Lee + World Wide Web Consortium + MIT Computer Science and Artificial Intelligence Laboratory + The Stata Center, Building 32 + 32 Vassar Street + Cambridge, MA 02139 + USA + + EMail: timbl@w3.org + URI: http://www.w3.org/People/Berners-Lee/ + + + Yves Lafon (editor) + World Wide Web Consortium + W3C / ERCIM + 2004, rte des Lucioles + Sophia-Antipolis, AM 06902 + France + + EMail: ylafon@w3.org + URI: http://www.raubacapeu.net/people/yves/ + + + Julian F. Reschke (editor) + greenbytes GmbH + Hafenweg 16 + Muenster, NW 48155 + Germany + + Phone: +49 251 2807760 + Fax: +49 251 2807761 + EMail: julian.reschke@greenbytes.de + URI: http://greenbytes.de/tech/webdav/ + + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 27] + diff --git a/dav/SabreDAV/docs/draft-ietf-httpbis-p6-cache-11.txt b/dav/SabreDAV/docs/draft-ietf-httpbis-p6-cache-11.txt new file mode 100644 index 000000000..35b875615 --- /dev/null +++ b/dav/SabreDAV/docs/draft-ietf-httpbis-p6-cache-11.txt @@ -0,0 +1,2352 @@ + + + +HTTPbis Working Group R. Fielding, Ed. +Internet-Draft Day Software +Obsoletes: 2616 (if approved) J. Gettys +Intended status: Standards Track Alcatel-Lucent +Expires: February 5, 2011 J. Mogul + HP + H. Frystyk + Microsoft + L. Masinter + Adobe Systems + P. Leach + Microsoft + T. Berners-Lee + W3C/MIT + Y. Lafon, Ed. + W3C + M. Nottingham, Ed. + + J. Reschke, Ed. + greenbytes + August 4, 2010 + + + HTTP/1.1, part 6: Caching + draft-ietf-httpbis-p6-cache-11 + +Abstract + + The Hypertext Transfer Protocol (HTTP) is an application-level + protocol for distributed, collaborative, hypermedia information + systems. This document is Part 6 of the seven-part specification + that defines the protocol referred to as "HTTP/1.1" and, taken + together, obsoletes RFC 2616. Part 6 defines requirements on HTTP + caches and the associated header fields that control cache behavior + or indicate cacheable response messages. + +Editorial Note (To be removed by RFC Editor) + + Discussion of this draft should take place on the HTTPBIS working + group mailing list (ietf-http-wg@w3.org). The current issues list is + at and related + documents (including fancy diffs) can be found at + . + + The changes in this draft are summarized in Appendix C.12. + +Status of This Memo + + + + +Fielding, et al. Expires February 5, 2011 [Page 1] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on February 5, 2011. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 1.1. Purpose . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 5 + 1.3. Requirements . . . . . . . . . . . . . . . . . . . . . . . 6 + + + +Fielding, et al. Expires February 5, 2011 [Page 2] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + 1.4. Syntax Notation . . . . . . . . . . . . . . . . . . . . . 7 + 1.4.1. Core Rules . . . . . . . . . . . . . . . . . . . . . . 7 + 1.4.2. ABNF Rules defined in other Parts of the + Specification . . . . . . . . . . . . . . . . . . . . 7 + 2. Cache Operation . . . . . . . . . . . . . . . . . . . . . . . 7 + 2.1. Response Cacheability . . . . . . . . . . . . . . . . . . 7 + 2.1.1. Storing Partial and Incomplete Responses . . . . . . . 8 + 2.2. Constructing Responses from Caches . . . . . . . . . . . . 9 + 2.3. Freshness Model . . . . . . . . . . . . . . . . . . . . . 10 + 2.3.1. Calculating Freshness Lifetime . . . . . . . . . . . . 11 + 2.3.2. Calculating Age . . . . . . . . . . . . . . . . . . . 12 + 2.3.3. Serving Stale Responses . . . . . . . . . . . . . . . 13 + 2.4. Validation Model . . . . . . . . . . . . . . . . . . . . . 14 + 2.5. Request Methods that Invalidate . . . . . . . . . . . . . 14 + 2.6. Shared Caching of Authenticated Responses . . . . . . . . 15 + 2.7. Caching Negotiated Responses . . . . . . . . . . . . . . . 16 + 2.8. Combining Responses . . . . . . . . . . . . . . . . . . . 16 + 3. Header Field Definitions . . . . . . . . . . . . . . . . . . . 17 + 3.1. Age . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 + 3.2. Cache-Control . . . . . . . . . . . . . . . . . . . . . . 18 + 3.2.1. Request Cache-Control Directives . . . . . . . . . . . 18 + 3.2.2. Response Cache-Control Directives . . . . . . . . . . 20 + 3.2.3. Cache Control Extensions . . . . . . . . . . . . . . . 22 + 3.3. Expires . . . . . . . . . . . . . . . . . . . . . . . . . 24 + 3.4. Pragma . . . . . . . . . . . . . . . . . . . . . . . . . . 24 + 3.5. Vary . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 + 3.6. Warning . . . . . . . . . . . . . . . . . . . . . . . . . 26 + 4. History Lists . . . . . . . . . . . . . . . . . . . . . . . . 28 + 5. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 28 + 5.1. Cache Directive Registry . . . . . . . . . . . . . . . . . 28 + 5.2. Header Field Registration . . . . . . . . . . . . . . . . 29 + 6. Security Considerations . . . . . . . . . . . . . . . . . . . 29 + 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 30 + 8. References . . . . . . . . . . . . . . . . . . . . . . . . . . 30 + 8.1. Normative References . . . . . . . . . . . . . . . . . . . 30 + 8.2. Informative References . . . . . . . . . . . . . . . . . . 31 + Appendix A. Changes from RFC 2616 . . . . . . . . . . . . . . . . 31 + Appendix B. Collected ABNF . . . . . . . . . . . . . . . . . . . 31 + Appendix C. Change Log (to be removed by RFC Editor before + publication) . . . . . . . . . . . . . . . . . . . . 33 + C.1. Since RFC2616 . . . . . . . . . . . . . . . . . . . . . . 33 + C.2. Since draft-ietf-httpbis-p6-cache-00 . . . . . . . . . . . 33 + C.3. Since draft-ietf-httpbis-p6-cache-01 . . . . . . . . . . . 34 + C.4. Since draft-ietf-httpbis-p6-cache-02 . . . . . . . . . . . 34 + C.5. Since draft-ietf-httpbis-p6-cache-03 . . . . . . . . . . . 34 + C.6. Since draft-ietf-httpbis-p6-cache-04 . . . . . . . . . . . 34 + C.7. Since draft-ietf-httpbis-p6-cache-05 . . . . . . . . . . . 35 + C.8. Since draft-ietf-httpbis-p6-cache-06 . . . . . . . . . . . 35 + + + +Fielding, et al. Expires February 5, 2011 [Page 3] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + C.9. Since draft-ietf-httpbis-p6-cache-07 . . . . . . . . . . . 35 + C.10. Since draft-ietf-httpbis-p6-cache-08 . . . . . . . . . . . 36 + C.11. Since draft-ietf-httpbis-p6-cache-09 . . . . . . . . . . . 36 + C.12. Since draft-ietf-httpbis-p6-cache-10 . . . . . . . . . . . 37 + Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 4] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + +1. Introduction + + HTTP is typically used for distributed information systems, where + performance can be improved by the use of response caches. This + document defines aspects of HTTP/1.1 related to caching and reusing + response messages. + +1.1. Purpose + + An HTTP cache is a local store of response messages and the subsystem + that controls its message storage, retrieval, and deletion. A cache + stores cacheable responses in order to reduce the response time and + network bandwidth consumption on future, equivalent requests. Any + client or server MAY employ a cache, though a cache cannot be used by + a server that is acting as a tunnel. + + Caching would be useless if it did not significantly improve + performance. The goal of caching in HTTP/1.1 is to reuse a prior + response message to satisfy a current request. In some cases, a + stored response can be reused without the need for a network request, + reducing latency and network round-trips; a "freshness" mechanism is + used for this purpose (see Section 2.3). Even when a new request is + required, it is often possible to reuse all or parts of the payload + of a prior response to satisfy the request, thereby reducing network + bandwidth usage; a "validation" mechanism is used for this purpose + (see Section 2.4). + +1.2. Terminology + + This specification uses a number of terms to refer to the roles + played by participants in, and objects of, HTTP caching. + + cacheable + + A response is cacheable if a cache is allowed to store a copy of + the response message for use in answering subsequent requests. + Even when a response is cacheable, there might be additional + constraints on whether a cache can use the cached copy to satisfy + a particular request. + + explicit expiration time + + The time at which the origin server intends that a representation + no longer be returned by a cache without further validation. + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 5] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + heuristic expiration time + + An expiration time assigned by a cache when no explicit expiration + time is available. + + age + + The age of a response is the time since it was sent by, or + successfully validated with, the origin server. + + first-hand + + A response is first-hand if the freshness model is not in use; + i.e., its age is 0. + + freshness lifetime + + The length of time between the generation of a response and its + expiration time. + + fresh + + A response is fresh if its age has not yet exceeded its freshness + lifetime. + + stale + + A response is stale if its age has passed its freshness lifetime + (either explicit or heuristic). + + validator + + A protocol element (e.g., an entity-tag or a Last-Modified time) + that is used to find out whether a stored response has an + equivalent copy of a representation. + + shared cache + + A cache that is accessible to more than one user. A non-shared + cache is dedicated to a single user. + +1.3. Requirements + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + An implementation is not compliant if it fails to satisfy one or more + + + +Fielding, et al. Expires February 5, 2011 [Page 6] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + of the "MUST" or "REQUIRED" level requirements for the protocols it + implements. An implementation that satisfies all the "MUST" or + "REQUIRED" level and all the "SHOULD" level requirements for its + protocols is said to be "unconditionally compliant"; one that + satisfies all the "MUST" level requirements but not all the "SHOULD" + level requirements for its protocols is said to be "conditionally + compliant". + +1.4. Syntax Notation + + This specification uses the ABNF syntax defined in Section 1.2 of + [Part1] (which extends the syntax defined in [RFC5234] with a list + rule). Appendix B shows the collected ABNF, with the list rule + expanded. + + The following core rules are included by reference, as defined in + [RFC5234], Appendix B.1: ALPHA (letters), CR (carriage return), CRLF + (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), + HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed), OCTET (any 8-bit + sequence of data), SP (space), VCHAR (any visible USASCII character), + and WSP (whitespace). + +1.4.1. Core Rules + + The core rules below are defined in Section 1.2.2 of [Part1]: + + quoted-string = + token = + OWS = + +1.4.2. ABNF Rules defined in other Parts of the Specification + + The ABNF rules below are defined in other parts: + + field-name = + HTTP-date = + port = + pseudonym = + uri-host = + +2. Cache Operation + +2.1. Response Cacheability + + A cache MUST NOT store a response to any request, unless: + + o The request method is understood by the cache and defined as being + cacheable, and + + + +Fielding, et al. Expires February 5, 2011 [Page 7] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + o the response status code is understood by the cache, and + + o the "no-store" cache directive (see Section 3.2) does not appear + in request or response headers, and + + o the "private" cache response directive (see Section 3.2.2 does not + appear in the response, if the cache is shared, and + + o the "Authorization" header (see Section 3.1 of [Part7]) does not + appear in the request, if the cache is shared, unless the response + explicitly allows it (see Section 2.6), and + + o the response either: + + * contains an Expires header (see Section 3.3), or + + * contains a max-age response cache directive (see + Section 3.2.2), or + + * contains a s-maxage response cache directive and the cache is + shared, or + + * contains a Cache Control Extension (see Section 3.2.3) that + allows it to be cached, or + + * has a status code that can be served with heuristic freshness + (see Section 2.3.1.1). + + In this context, a cache has "understood" a request method or a + response status code if it recognises it and implements any cache- + specific behaviour. In particular, 206 Partial Content responses + cannot be cached by an implementation that does not handle partial + content (see Section 2.1.1). + + Note that in normal operation, most caches will not store a response + that has neither a cache validator nor an explicit expiration time, + as such responses are not usually useful to store. However, caches + are not prohibited from storing such responses. + +2.1.1. Storing Partial and Incomplete Responses + + A cache that receives an incomplete response (for example, with fewer + bytes of data than specified in a Content-Length header) can store + the response, but MUST treat it as a partial response [Part5]. + Partial responses can be combined as described in Section 4 of + [Part5]; the result might be a full response or might still be + partial. A cache MUST NOT return a partial response to a client + without explicitly marking it as such using the 206 (Partial Content) + + + +Fielding, et al. Expires February 5, 2011 [Page 8] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + status code. + + A cache that does not support the Range and Content-Range headers + MUST NOT store incomplete or partial responses. + +2.2. Constructing Responses from Caches + + For a presented request, a cache MUST NOT return a stored response, + unless: + + o The presented effective request URI (Section 4.3 of [Part1]) and + that of the stored response match, and + + o the request method associated with the stored response allows it + to be used for the presented request, and + + o selecting request-headers nominated by the stored response (if + any) match those presented (see Section 2.7), and + + o the presented request and stored response are free from directives + that would prevent its use (see Section 3.2 and Section 3.4), and + + o the stored response is either: + + * fresh (see Section 2.3), or + + * allowed to be served stale (see Section 2.3.3), or + + * successfully validated (see Section 2.4). + + When a stored response is used to satisfy a request without + validation, caches MUST include a single Age header field + (Section 3.1) in the response with a value equal to the stored + response's current_age; see Section 2.3.2. + + Requests with methods that are unsafe (Section 7.1.1 of [Part2]) MUST + be written through the cache to the origin server; i.e., a cache must + not reply to such a request before having forwarded the request and + having received a corresponding response. + + Also, note that unsafe requests might invalidate already stored + responses; see Section 2.5. + + Caches MUST use the most recent response (as determined by the Date + header) when more than one suitable response is stored. They can + also forward a request with "Cache-Control: max-age=0" or "Cache- + Control: no-cache" to disambiguate which response to use. + + + + +Fielding, et al. Expires February 5, 2011 [Page 9] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + +2.3. Freshness Model + + When a response is "fresh" in the cache, it can be used to satisfy + subsequent requests without contacting the origin server, thereby + improving efficiency. + + The primary mechanism for determining freshness is for an origin + server to provide an explicit expiration time in the future, using + either the Expires header (Section 3.3) or the max-age response cache + directive (Section 3.2.2). Generally, origin servers will assign + future explicit expiration times to responses in the belief that the + representation is not likely to change in a semantically significant + way before the expiration time is reached. + + If an origin server wishes to force a cache to validate every + request, it can assign an explicit expiration time in the past to + indicate that the response is already stale. Compliant caches will + validate the cached response before reusing it for subsequent + requests. + + Since origin servers do not always provide explicit expiration times, + HTTP caches MAY assign heuristic expiration times when explicit times + are not specified, employing algorithms that use other header values + (such as the Last-Modified time) to estimate a plausible expiration + time. The HTTP/1.1 specification does not provide specific + algorithms, but does impose worst-case constraints on their results. + + The calculation to determine if a response is fresh is: + + response_is_fresh = (freshness_lifetime > current_age) + + The freshness_lifetime is defined in Section 2.3.1; the current_age + is defined in Section 2.3.2. + + Additionally, clients might need to influence freshness calculation. + They can do this using several request cache directives, with the + effect of either increasing or loosening constraints on freshness. + See Section 3.2.1. + + [[ISSUE-no-req-for-directives: there are not requirements directly + applying to cache-request-directives and freshness.]] + + Note that freshness applies only to cache operation; it cannot be + used to force a user agent to refresh its display or reload a + resource. See Section 4 for an explanation of the difference between + caches and history mechanisms. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 10] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + +2.3.1. Calculating Freshness Lifetime + + A cache can calculate the freshness lifetime (denoted as + freshness_lifetime) of a response by using the first match of: + + o If the cache is shared and the s-maxage response cache directive + (Section 3.2.2) is present, use its value, or + + o If the max-age response cache directive (Section 3.2.2) is + present, use its value, or + + o If the Expires response header (Section 3.3) is present, use its + value minus the value of the Date response header, or + + o Otherwise, no explicit expiration time is present in the response. + A heuristic freshness lifetime might be applicable; see + Section 2.3.1.1. + + Note that this calculation is not vulnerable to clock skew, since all + of the information comes from the origin server. + +2.3.1.1. Calculating Heuristic Freshness + + If no explicit expiration time is present in a stored response that + has a status code whose definition allows heuristic freshness to be + used (including the following in Section 8 of [Part2]: 200, 203, 206, + 300, 301 and 410), a heuristic expiration time MAY be calculated. + Heuristics MUST NOT be used for response status codes that do not + explicitly allow it. + + When a heuristic is used to calculate freshness lifetime, the cache + SHOULD attach a Warning header with a 113 warn-code to the response + if its current_age is more than 24 hours and such a warning is not + already present. + + Also, if the response has a Last-Modified header (Section 6.6 of + [Part4]), the heuristic expiration value SHOULD be no more than some + fraction of the interval since that time. A typical setting of this + fraction might be 10%. + + Note: RFC 2616 ([RFC2616], Section 13.9) required that caches do + not calculate heuristic freshness for URLs with query components + (i.e., those containing '?'). In practice, this has not been + widely implemented. Therefore, servers are encouraged to send + explicit directives (e.g., Cache-Control: no-cache) if they wish + to preclude caching. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 11] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + +2.3.2. Calculating Age + + HTTP/1.1 uses the Age response-header to convey the estimated age of + the response message when obtained from a cache. The Age field value + is the cache's estimate of the amount of time since the response was + generated or validated by the origin server. In essence, the Age + value is the sum of the time that the response has been resident in + each of the caches along the path from the origin server, plus the + amount of time it has been in transit along network paths. + + The following data is used for the age calculation: + + age_value + + The term "age_value" denotes the value of the Age header + (Section 3.1), in a form appropriate for arithmetic operation; or + 0, if not available. + + date_value + + HTTP/1.1 requires origin servers to send a Date header, if + possible, with every response, giving the time at which the + response was generated. The term "date_value" denotes the value + of the Date header, in a form appropriate for arithmetic + operations. See Section 9.3 of [Part1] for the definition of the + Date header, and for requirements regarding responses without a + Date response header. + + now + + The term "now" means "the current value of the clock at the host + performing the calculation". Hosts that use HTTP, but especially + hosts running origin servers and caches, SHOULD use NTP + ([RFC1305]) or some similar protocol to synchronize their clocks + to a globally accurate time standard. + + request_time + + The current value of the clock at the host at the time the request + resulting in the stored response was made. + + response_time + + The current value of the clock at the host at the time the + response was received. + + A response's age can be calculated in two entirely independent ways: + + + + +Fielding, et al. Expires February 5, 2011 [Page 12] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + 1. the "apparent_age": response_time minus date_value, if the local + clock is reasonably well synchronized to the origin server's + clock. If the result is negative, the result is replaced by + zero. + + 2. the "corrected_age_value", if all of the caches along the + response path implement HTTP/1.1; note this value MUST be + interpreted relative to the time the request was initiated, not + the time that the response was received. + + + apparent_age = max(0, response_time - date_value); + + response_delay = response_time - request_time; + corrected_age_value = age_value + response_delay; + + These are combined as + + corrected_initial_age = max(apparent_age, corrected_age_value); + + The current_age of a stored response can then be calculated by adding + the amount of time (in seconds) since the stored response was last + validated by the origin server to the corrected_initial_age. + + resident_time = now - response_time; + current_age = corrected_initial_age + resident_time; + +2.3.3. Serving Stale Responses + + A "stale" response is one that either has explicit expiry information + or is allowed to have heuristic expiry calculated, but is not fresh + according to the calculations in Section 2.3. + + Caches MUST NOT return a stale response if it is prohibited by an + explicit in-protocol directive (e.g., by a "no-store" or "no-cache" + cache directive, a "must-revalidate" cache-response-directive, or an + applicable "s-maxage" or "proxy-revalidate" cache-response-directive; + see Section 3.2.2). + + Caches SHOULD NOT return stale responses unless they are disconnected + (i.e., it cannot contact the origin server or otherwise find a + forward path) or otherwise explicitly allowed (e.g., the max-stale + request directive; see Section 3.2.1). + + Stale responses SHOULD have a Warning header with the 110 warn-code + (see Section 3.6). Likewise, the 112 warn-code SHOULD be sent on + stale responses if the cache is disconnected. + + + + +Fielding, et al. Expires February 5, 2011 [Page 13] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + If a cache receives a first-hand response (either an entire response, + or a 304 (Not Modified) response) that it would normally forward to + the requesting client, and the received response is no longer fresh, + the cache SHOULD forward it to the requesting client without adding a + new Warning (but without removing any existing Warning headers). A + cache SHOULD NOT attempt to validate a response simply because that + response became stale in transit. + +2.4. Validation Model + + When a cache has one or more stored responses for a requested URI, + but cannot serve any of them (e.g., because they are not fresh, or + one cannot be selected; see Section 2.7), it can use the conditional + request mechanism [Part4] in the forwarded request to give the origin + server an opportunity to both select a valid stored response to be + used, and to update it. This process is known as "validating" or + "revalidating" the stored response. + + When sending such a conditional request, the cache SHOULD add an If- + Modified-Since header whose value is that of the Last-Modified header + from the selected (see Section 2.7) stored response, if available. + + Additionally, the cache SHOULD add an If-None-Match header whose + value is that of the ETag header(s) from all responses stored for the + requested URI, if present. However, if any of the stored responses + contains only partial content, its entity-tag SHOULD NOT be included + in the If-None-Match header field unless the request is for a range + that would be fully satisfied by that stored response. + + A 304 (Not Modified) response status code indicates that the stored + response can be updated and reused; see Section 2.8. + + A full response (i.e., one with a response body) indicates that none + of the stored responses nominated in the conditional request is + suitable. Instead, the full response SHOULD be used to satisfy the + request and MAY replace the stored response. + + If a cache receives a 5xx response while attempting to validate a + response, it MAY either forward this response to the requesting + client, or act as if the server failed to respond. In the latter + case, it MAY return a previously stored response (see Section 2.3.3). + +2.5. Request Methods that Invalidate + + Because unsafe methods (Section 7.1.1 of [Part2]) have the potential + for changing state on the origin server, intervening caches can use + them to keep their contents up-to-date. + + + + +Fielding, et al. Expires February 5, 2011 [Page 14] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + The following HTTP methods MUST cause a cache to invalidate the + effective Request URI (Section 4.3 of [Part1]) as well as the URI(s) + in the Location and Content-Location headers (if present): + + o PUT + + o DELETE + + o POST + + An invalidation based on a URI from a Location or Content-Location + header MUST NOT be performed if the host part of that URI differs + from the host part in the effective request URI (Section 4.3 of + [Part1]). This helps prevent denial of service attacks. + + A cache that passes through requests for methods it does not + understand SHOULD invalidate the effective request URI (Section 4.3 + of [Part1]). + + Here, "invalidate" means that the cache will either remove all stored + responses related to the effective request URI, or will mark these as + "invalid" and in need of a mandatory validation before they can be + returned in response to a subsequent request. + + Note that this does not guarantee that all appropriate responses are + invalidated. For example, the request that caused the change at the + origin server might not have gone through the cache where a response + is stored. + +2.6. Shared Caching of Authenticated Responses + + Shared caches MUST NOT use a cached response to a request with an + Authorization header (Section 3.1 of [Part7]) to satisfy any + subsequent request unless a cache directive that allows such + responses to be stored is present in the response. + + In this specification, the following Cache-Control response + directives (Section 3.2.2) have such an effect: must-revalidate, + public, s-maxage. + + Note that cached responses that contain the "must-revalidate" and/or + "s-maxage" response directives are not allowed to be served stale + (Section 2.3.3) by shared caches. In particular, a response with + either "max-age=0, must-revalidate" or "s-maxage=0" cannot be used to + satisfy a subsequent request without revalidating it on the origin + server. + + + + + +Fielding, et al. Expires February 5, 2011 [Page 15] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + +2.7. Caching Negotiated Responses + + When a cache receives a request that can be satisfied by a stored + response that has a Vary header field (Section 3.5), it MUST NOT use + that response unless all of the selecting request-headers nominated + by the Vary header match in both the original request (i.e., that + associated with the stored response), and the presented request. + + The selecting request-headers from two requests are defined to match + if and only if those in the first request can be transformed to those + in the second request by applying any of the following: + + o adding or removing whitespace, where allowed in the header's + syntax + + o combining multiple message-header fields with the same field name + (see Section 3.2 of [Part1]) + + o normalizing both header values in a way that is known to have + identical semantics, according to the header's specification + (e.g., re-ordering field values when order is not significant; + case-normalization, where values are defined to be case- + insensitive) + + If (after any normalization that might take place) a header field is + absent from a request, it can only match another request if it is + also absent there. + + A Vary header field-value of "*" always fails to match, and + subsequent requests to that resource can only be properly interpreted + by the origin server. + + The stored response with matching selecting request-headers is known + as the selected response. + + If no selected response is available, the cache MAY forward the + presented request to the origin server in a conditional request; see + Section 2.4. + +2.8. Combining Responses + + When a cache receives a 304 (Not Modified) response or a 206 (Partial + Content) response (in this section, the "new" response"), it needs to + created an updated response by combining the stored response with the + new one, so that the updated response can be used to satisfy the + request, and potentially update the cached response. + + If the new response contains an ETag, it identifies the stored + + + +Fielding, et al. Expires February 5, 2011 [Page 16] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + response to use. [[TODO-mention-CL: might need language about + Content-Location here]][[TODO-select-for-combine: Shouldn't this be + the selected response?]] + + If the new response's status code is 206 (partial content), both the + stored and new responses MUST have validators, and those validators + MUST match using the strong comparison function (see Section 4 of + [Part4]). Otherwise, the responses MUST NOT be combined. + + The stored response headers are used as those of the updated + response, except that + + o any stored Warning headers with warn-code 1xx (see Section 3.6) + MUST be deleted. + + o any stored Warning headers with warn-code 2xx MUST be retained. + + o any other headers provided in the new response MUST replace all + instances of the corresponding headers from the stored response. + + The updated response headers MUST be used to replace those of the + stored response in cache (unless the stored response is removed from + cache). In the case of a 206 response, the combined representation + MAY be stored. + +3. Header Field Definitions + + This section defines the syntax and semantics of HTTP/1.1 header + fields related to caching. + +3.1. Age + + The "Age" response-header field conveys the sender's estimate of the + amount of time since the response was generated or successfully + validated at the origin server. Age values are calculated as + specified in Section 2.3.2. + + Age = "Age" ":" OWS Age-v + Age-v = delta-seconds + + Age field-values are non-negative integers, representing time in + seconds. + + delta-seconds = 1*DIGIT + + If a cache receives a value larger than the largest positive integer + it can represent, or if any of its age calculations overflows, it + MUST transmit an Age header with a field-value of 2147483648 (2^31). + + + +Fielding, et al. Expires February 5, 2011 [Page 17] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Caches SHOULD use an arithmetic type of at least 31 bits of range. + + The presence of an Age header field in a response implies that a + response is not first-hand. However, the converse is not true, since + HTTP/1.0 caches might not implement the Age header field. + +3.2. Cache-Control + + The "Cache-Control" general-header field is used to specify + directives for caches along the request/response chain. Such cache + directives are unidirectional in that the presence of a directive in + a request does not imply that the same directive is to be given in + the response. + + HTTP/1.1 caches MUST obey the requirements of the Cache-Control + directives defined in this section. See Section 3.2.3 for + information about how Cache-Control directives defined elsewhere are + handled. + + Note: HTTP/1.0 caches might not implement Cache-Control and might + only implement Pragma: no-cache (see Section 3.4). + + Cache directives MUST be passed through by a proxy or gateway + application, regardless of their significance to that application, + since the directives might be applicable to all recipients along the + request/response chain. It is not possible to target a directive to + a specific cache. + + Cache-Control = "Cache-Control" ":" OWS Cache-Control-v + Cache-Control-v = 1#cache-directive + + cache-directive = cache-request-directive + / cache-response-directive + + cache-extension = token [ "=" ( token / quoted-string ) ] + +3.2.1. Request Cache-Control Directives + + cache-request-directive = + "no-cache" + / "no-store" + / "max-age" "=" delta-seconds + / "max-stale" [ "=" delta-seconds ] + / "min-fresh" "=" delta-seconds + / "no-transform" + / "only-if-cached" + / cache-extension + + + + +Fielding, et al. Expires February 5, 2011 [Page 18] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + no-cache + + The no-cache request directive indicates that a stored response + MUST NOT be used to satisfy the request without successful + validation on the origin server. + + no-store + + The no-store request directive indicates that a cache MUST NOT + store any part of either this request or any response to it. This + directive applies to both non-shared and shared caches. "MUST NOT + store" in this context means that the cache MUST NOT intentionally + store the information in non-volatile storage, and MUST make a + best-effort attempt to remove the information from volatile + storage as promptly as possible after forwarding it. + + This directive is NOT a reliable or sufficient mechanism for + ensuring privacy. In particular, malicious or compromised caches + might not recognize or obey this directive, and communications + networks might be vulnerable to eavesdropping. + + max-age + + The max-age request directive indicates that the client is willing + to accept a response whose age is no greater than the specified + time in seconds. Unless the max-stale request directive is also + present, the client is not willing to accept a stale response. + + max-stale + + The max-stale request directive indicates that the client is + willing to accept a response that has exceeded its expiration + time. If max-stale is assigned a value, then the client is + willing to accept a response that has exceeded its expiration time + by no more than the specified number of seconds. If no value is + assigned to max-stale, then the client is willing to accept a + stale response of any age. + + min-fresh + + The min-fresh request directive indicates that the client is + willing to accept a response whose freshness lifetime is no less + than its current age plus the specified time in seconds. That is, + the client wants a response that will still be fresh for at least + the specified number of seconds. + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 19] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + no-transform + + The no-transform request directive indicates that an intermediate + cache or proxy MUST NOT change the Content-Encoding, Content-Range + or Content-Type request headers, nor the request representation. + + only-if-cached + + The only-if-cached request directive indicates that the client + only wishes to return a stored response. If it receives this + directive, a cache SHOULD either respond using a stored response + that is consistent with the other constraints of the request, or + respond with a 504 (Gateway Timeout) status code. If a group of + caches is being operated as a unified system with good internal + connectivity, such a request MAY be forwarded within that group of + caches. + +3.2.2. Response Cache-Control Directives + + cache-response-directive = + "public" + / "private" [ "=" DQUOTE 1#field-name DQUOTE ] + / "no-cache" [ "=" DQUOTE 1#field-name DQUOTE ] + / "no-store" + / "no-transform" + / "must-revalidate" + / "proxy-revalidate" + / "max-age" "=" delta-seconds + / "s-maxage" "=" delta-seconds + / cache-extension + + public + + The public response directive indicates that the response MAY be + cached, even if it would normally be non-cacheable or cacheable + only within a non-shared cache. (See also Authorization, Section + 3.1 of [Part7], for additional details.) + + private + + The private response directive indicates that the response message + is intended for a single user and MUST NOT be stored by a shared + cache. A private (non-shared) cache MAY store the response. + + If the private response directive specifies one or more field- + names, this requirement is limited to the field-values associated + with the listed response headers. That is, the specified field- + names(s) MUST NOT be stored by a shared cache, whereas the + + + +Fielding, et al. Expires February 5, 2011 [Page 20] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + remainder of the response message MAY be. + + Note: This usage of the word private only controls where the + response can be stored; it cannot ensure the privacy of the + message content. Also, private response directives with field- + names are often handled by implementations as if an unqualified + private directive was received; i.e., the special handling for the + qualified form is not widely implemented. + + no-cache + + The no-cache response directive indicates that the response MUST + NOT be used to satisfy a subsequent request without successful + validation on the origin server. This allows an origin server to + prevent a cache from using it to satisfy a request without + contacting it, even by caches that have been configured to return + stale responses. + + If the no-cache response directive specifies one or more field- + names, this requirement is limited to the field-values associated + with the listed response headers. That is, the specified field- + name(s) MUST NOT be sent in the response to a subsequent request + without successful validation on the origin server. This allows + an origin server to prevent the re-use of certain header fields in + a response, while still allowing caching of the rest of the + response. + + Note: Most HTTP/1.0 caches will not recognize or obey this + directive. Also, no-cache response directives with field-names + are often handled by implementations as if an unqualified no-cache + directive was received; i.e., the special handling for the + qualified form is not widely implemented. + + no-store + + The no-store response directive indicates that a cache MUST NOT + store any part of either the immediate request or response. This + directive applies to both non-shared and shared caches. "MUST NOT + store" in this context means that the cache MUST NOT intentionally + store the information in non-volatile storage, and MUST make a + best-effort attempt to remove the information from volatile + storage as promptly as possible after forwarding it. + + This directive is NOT a reliable or sufficient mechanism for + ensuring privacy. In particular, malicious or compromised caches + might not recognize or obey this directive, and communications + networks might be vulnerable to eavesdropping. + + + + +Fielding, et al. Expires February 5, 2011 [Page 21] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + must-revalidate + + The must-revalidate response directive indicates that once it has + become stale, the response MUST NOT be used to satisfy subsequent + requests without successful validation on the origin server. + + The must-revalidate directive is necessary to support reliable + operation for certain protocol features. In all circumstances an + HTTP/1.1 cache MUST obey the must-revalidate directive; in + particular, if the cache cannot reach the origin server for any + reason, it MUST generate a 504 (Gateway Timeout) response. + + Servers SHOULD send the must-revalidate directive if and only if + failure to validate a request on the representation could result + in incorrect operation, such as a silently unexecuted financial + transaction. + + proxy-revalidate + + The proxy-revalidate response directive has the same meaning as + the must-revalidate response directive, except that it does not + apply to non-shared caches. + + max-age + + The max-age response directive indicates that response is to be + considered stale after its age is greater than the specified + number of seconds. + + s-maxage + + The s-maxage response directive indicates that, in shared caches, + the maximum age specified by this directive overrides the maximum + age specified by either the max-age directive or the Expires + header. The s-maxage directive also implies the semantics of the + proxy-revalidate response directive. + + no-transform + + The no-transform response directive indicates that an intermediate + cache or proxy MUST NOT change the Content-Encoding, Content-Range + or Content-Type response headers, nor the response representation. + +3.2.3. Cache Control Extensions + + The Cache-Control header field can be extended through the use of one + or more cache-extension tokens, each with an optional value. + Informational extensions (those that do not require a change in cache + + + +Fielding, et al. Expires February 5, 2011 [Page 22] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + behavior) can be added without changing the semantics of other + directives. Behavioral extensions are designed to work by acting as + modifiers to the existing base of cache directives. Both the new + directive and the standard directive are supplied, such that + applications that do not understand the new directive will default to + the behavior specified by the standard directive, and those that + understand the new directive will recognize it as modifying the + requirements associated with the standard directive. In this way, + extensions to the cache-control directives can be made without + requiring changes to the base protocol. + + This extension mechanism depends on an HTTP cache obeying all of the + cache-control directives defined for its native HTTP-version, obeying + certain extensions, and ignoring all directives that it does not + understand. + + For example, consider a hypothetical new response directive called + "community" that acts as a modifier to the private directive. We + define this new directive to mean that, in addition to any non-shared + cache, any cache that is shared only by members of the community + named within its value may cache the response. An origin server + wishing to allow the UCI community to use an otherwise private + response in their shared cache(s) could do so by including + + Cache-Control: private, community="UCI" + + A cache seeing this header field will act correctly even if the cache + does not understand the community cache-extension, since it will also + see and understand the private directive and thus default to the safe + behavior. + + Unrecognized cache directives MUST be ignored; it is assumed that any + cache directive likely to be unrecognized by an HTTP/1.1 cache will + be combined with standard directives (or the response's default + cacheability) such that the cache behavior will remain minimally + correct even if the cache does not understand the extension(s). + + The HTTP Cache Directive Registry defines the name space for the + cache directives. + + Registrations MUST include the following fields: + + o Cache Directive Name + + o Pointer to specification text + + Values to be added to this name space are subject to IETF review + ([RFC5226], Section 4.1). + + + +Fielding, et al. Expires February 5, 2011 [Page 23] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + The registry itself is maintained at + . + +3.3. Expires + + The "Expires" header field gives the date/time after which the + response is considered stale. See Section 2.3 for further discussion + of the freshness model. + + The presence of an Expires field does not imply that the original + resource will change or cease to exist at, before, or after that + time. + + The field-value is an absolute date and time as defined by HTTP-date + in Section 6.1 of [Part1]; it MUST be sent in rfc1123-date format. + + Expires = "Expires" ":" OWS Expires-v + Expires-v = HTTP-date + + For example + + Expires: Thu, 01 Dec 1994 16:00:00 GMT + + Note: If a response includes a Cache-Control field with the max- + age directive (see Section 3.2.2), that directive overrides the + Expires field. Likewise, the s-maxage directive overrides Expires + in shared caches. + + HTTP/1.1 servers SHOULD NOT send Expires dates more than one year in + the future. + + HTTP/1.1 clients and caches MUST treat other invalid date formats, + especially including the value "0", as in the past (i.e., "already + expired"). + +3.4. Pragma + + The "Pragma" general-header field is used to include implementation- + specific directives that might apply to any recipient along the + request/response chain. All pragma directives specify optional + behavior from the viewpoint of the protocol; however, some systems + MAY require that behavior be consistent with the directives. + + Pragma = "Pragma" ":" OWS Pragma-v + Pragma-v = 1#pragma-directive + pragma-directive = "no-cache" / extension-pragma + extension-pragma = token [ "=" ( token / quoted-string ) ] + + + + +Fielding, et al. Expires February 5, 2011 [Page 24] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + When the no-cache directive is present in a request message, an + application SHOULD forward the request toward the origin server even + if it has a cached copy of what is being requested. This pragma + directive has the same semantics as the no-cache response directive + (see Section 3.2.2) and is defined here for backward compatibility + with HTTP/1.0. Clients SHOULD include both header fields when a no- + cache request is sent to a server not known to be HTTP/1.1 compliant. + HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client had + sent "Cache-Control: no-cache". + + Note: Because the meaning of "Pragma: no-cache" as a response- + header field is not actually specified, it does not provide a + reliable replacement for "Cache-Control: no-cache" in a response. + + This mechanism is deprecated; no new Pragma directives will be + defined in HTTP. + +3.5. Vary + + The "Vary" response-header field conveys the set of request-header + fields that were used to select the representation. + + Caches use this information, in part, to determine whether a stored + response can be used to satisfy a given request; see Section 2.7. + determines, while the response is fresh, whether a cache is permitted + to use the response to reply to a subsequent request without + validation; see Section 2.7. + + In uncacheable or stale responses, the Vary field value advises the + user agent about the criteria that were used to select the + representation. + + Vary = "Vary" ":" OWS Vary-v + Vary-v = "*" / 1#field-name + + The set of header fields named by the Vary field value is known as + the selecting request-headers. + + Servers SHOULD include a Vary header field with any cacheable + response that is subject to server-driven negotiation. Doing so + allows a cache to properly interpret future requests on that resource + and informs the user agent about the presence of negotiation on that + resource. A server MAY include a Vary header field with a non- + cacheable response that is subject to server-driven negotiation, + since this might provide the user agent with useful information about + the dimensions over which the response varies at the time of the + response. + + + + +Fielding, et al. Expires February 5, 2011 [Page 25] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + A Vary field value of "*" signals that unspecified parameters not + limited to the request-headers (e.g., the network address of the + client), play a role in the selection of the response representation; + therefore, a cache cannot determine whether this response is + appropriate. The "*" value MUST NOT be generated by a proxy server. + + The field-names given are not limited to the set of standard request- + header fields defined by this specification. Field names are case- + insensitive. + +3.6. Warning + + The "Warning" general-header field is used to carry additional + information about the status or transformation of a message that + might not be reflected in the message. This information is typically + used to warn about possible incorrectness introduced by caching + operations or transformations applied to the payload of the message. + + Warnings can be used for other purposes, both cache-related and + otherwise. The use of a warning, rather than an error status code, + distinguishes these responses from true failures. + + Warning headers can in general be applied to any message, however + some warn-codes are specific to caches and can only be applied to + response messages. + + Warning = "Warning" ":" OWS Warning-v + Warning-v = 1#warning-value + + warning-value = warn-code SP warn-agent SP warn-text + [SP warn-date] + + warn-code = 3DIGIT + warn-agent = ( uri-host [ ":" port ] ) / pseudonym + ; the name or pseudonym of the server adding + ; the Warning header, for use in debugging + warn-text = quoted-string + warn-date = DQUOTE HTTP-date DQUOTE + + Multiple warnings can be attached to a response (either by the origin + server or by a cache), including multiple warnings with the same code + number, only differing in warn-text. + + When this occurs, the user agent SHOULD inform the user of as many of + them as possible, in the order that they appear in the response. + + Systems that generate multiple Warning headers SHOULD order them with + this user agent behavior in mind. New Warning headers SHOULD be + + + +Fielding, et al. Expires February 5, 2011 [Page 26] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + added after any existing Warning headers. + + Warnings are assigned three digit warn-codes. The first digit + indicates whether the Warning is required to be deleted from a stored + response after validation: + + o 1xx Warnings describe the freshness or validation status of the + response, and so MUST be deleted by caches after validation. They + can only be generated by a cache when validating a cached entry, + and MUST NOT be generated in any other situation. + + o 2xx Warnings describe some aspect of the representation that is + not rectified by a validation (for example, a lossy compression of + the representation) and MUST NOT be deleted by caches after + validation, unless a full response is returned, in which case they + MUST be. + + If an implementation sends a message with one or more Warning headers + to a receiver whose version is HTTP/1.0 or lower, then the sender + MUST include in each warning-value a warn-date that matches the Date + header in the message. + + If an implementation receives a message with a warning-value that + includes a warn-date, and that warn-date is different from the Date + value in the response, then that warning-value MUST be deleted from + the message before storing, forwarding, or using it. (preventing the + consequences of naive caching of Warning header fields.) If all of + the warning-values are deleted for this reason, the Warning header + MUST be deleted as well. + + The following warn-codes are defined by this specification, each with + a recommended warn-text in English, and a description of its meaning. + + 110 Response is stale + + SHOULD be included whenever the returned response is stale. + + 111 Revalidation failed + + SHOULD be included if a cache returns a stale response because an + attempt to validate the response failed, due to an inability to + reach the server. + + 112 Disconnected operation + + SHOULD be included if the cache is intentionally disconnected from + the rest of the network for a period of time. + + + + +Fielding, et al. Expires February 5, 2011 [Page 27] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + 113 Heuristic expiration + + SHOULD be included if the cache heuristically chose a freshness + lifetime greater than 24 hours and the response's age is greater + than 24 hours. + + 199 Miscellaneous warning + + The warning text can include arbitrary information to be presented + to a human user, or logged. A system receiving this warning MUST + NOT take any automated action, besides presenting the warning to + the user. + + 214 Transformation applied + + MUST be added by an intermediate proxy if it applies any + transformation to the representation, such as changing the + content-coding, media-type, or modifying the representation data, + unless this Warning code already appears in the response. + + 299 Miscellaneous persistent warning + + The warning text can include arbitrary information to be presented + to a human user, or logged. A system receiving this warning MUST + NOT take any automated action. + +4. History Lists + + User agents often have history mechanisms, such as "Back" buttons and + history lists, that can be used to redisplay a representation + retrieved earlier in a session. + + The freshness model (Section 2.3) does not necessarily apply to + history mechanisms. I.e., a history mechanism can display a previous + representation even if it has expired. + + This does not prohibit the history mechanism from telling the user + that a view might be stale, or from honoring cache directives (e.g., + Cache-Control: no-store). + +5. IANA Considerations + +5.1. Cache Directive Registry + + The registration procedure for HTTP Cache Directives is defined by + Section 3.2.3 of this document. + + The HTTP Cache Directive Registry shall be created at + + + +Fielding, et al. Expires February 5, 2011 [Page 28] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + and be + populated with the registrations below: + + +------------------------+------------------------------+ + | Cache Directive | Reference | + +------------------------+------------------------------+ + | max-age | Section 3.2.1, Section 3.2.2 | + | max-stale | Section 3.2.1 | + | min-fresh | Section 3.2.1 | + | must-revalidate | Section 3.2.2 | + | no-cache | Section 3.2.1, Section 3.2.2 | + | no-store | Section 3.2.1, Section 3.2.2 | + | no-transform | Section 3.2.1, Section 3.2.2 | + | only-if-cached | Section 3.2.1 | + | private | Section 3.2.2 | + | proxy-revalidate | Section 3.2.2 | + | public | Section 3.2.2 | + | s-maxage | Section 3.2.2 | + | stale-if-error | [RFC5861], Section 4 | + | stale-while-revalidate | [RFC5861], Section 3 | + +------------------------+------------------------------+ + +5.2. Header Field Registration + + The Message Header Field Registry located at shall be + updated with the permanent registrations below (see [RFC3864]): + + +-------------------+----------+----------+-------------+ + | Header Field Name | Protocol | Status | Reference | + +-------------------+----------+----------+-------------+ + | Age | http | standard | Section 3.1 | + | Cache-Control | http | standard | Section 3.2 | + | Expires | http | standard | Section 3.3 | + | Pragma | http | standard | Section 3.4 | + | Vary | http | standard | Section 3.5 | + | Warning | http | standard | Section 3.6 | + +-------------------+----------+----------+-------------+ + + The change controller is: "IETF (iesg@ietf.org) - Internet + Engineering Task Force". + +6. Security Considerations + + Caches expose additional potential vulnerabilities, since the + contents of the cache represent an attractive target for malicious + exploitation. Because cache contents persist after an HTTP request + is complete, an attack on the cache can reveal information long after + + + +Fielding, et al. Expires February 5, 2011 [Page 29] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + a user believes that the information has been removed from the + network. Therefore, cache contents need to be protected as sensitive + information. + +7. Acknowledgments + + Much of the content and presentation of the caching design is due to + suggestions and comments from individuals including: Shel Kaphan, + Paul Leach, Koen Holtman, David Morris, and Larry Masinter. + +8. References + +8.1. Normative References + + [Part1] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 1: URIs, Connections, + and Message Parsing", draft-ietf-httpbis-p1-messaging-11 + (work in progress), August 2010. + + [Part2] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 2: Message + Semantics", draft-ietf-httpbis-p2-semantics-11 (work in + progress), August 2010. + + [Part4] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 4: Conditional + Requests", draft-ietf-httpbis-p4-conditional-11 (work in + progress), August 2010. + + [Part5] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 5: Range Requests and + Partial Responses", draft-ietf-httpbis-p5-range-11 (work + in progress), August 2010. + + [Part7] Fielding, R., Ed., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., Berners-Lee, T., Lafon, Y., Ed., + and J. Reschke, Ed., "HTTP/1.1, part 7: Authentication", + draft-ietf-httpbis-p7-auth-11 (work in progress), + August 2010. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax + + + +Fielding, et al. Expires February 5, 2011 [Page 30] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Specifications: ABNF", STD 68, RFC 5234, January 2008. + +8.2. Informative References + + [RFC1305] Mills, D., "Network Time Protocol (Version 3) + Specification, Implementation", RFC 1305, March 1992. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration + Procedures for Message Header Fields", BCP 90, RFC 3864, + September 2004. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an + IANA Considerations Section in RFCs", BCP 26, RFC 5226, + May 2008. + + [RFC5861] Nottingham, M., "HTTP Cache-Control Extensions for Stale + Content", RFC 5861, April 2010. + +Appendix A. Changes from RFC 2616 + + Make the specified age calculation algorithm less conservative. + (Section 2.3.2) + + Remove requirement to consider Content-Location in successful + responses in order to determine the appropriate response to use. + (Section 2.4) + + Clarify denial of service attack avoidance requirement. + (Section 2.5) + + Do not mention RFC 2047 encoding and multiple languages in Warning + headers anymore, as these aspects never were implemented. + (Section 3.6) + +Appendix B. Collected ABNF + + Age = "Age:" OWS Age-v + Age-v = delta-seconds + + Cache-Control = "Cache-Control:" OWS Cache-Control-v + Cache-Control-v = *( "," OWS ) cache-directive *( OWS "," [ OWS + cache-directive ] ) + + Expires = "Expires:" OWS Expires-v + + + +Fielding, et al. Expires February 5, 2011 [Page 31] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Expires-v = HTTP-date + + HTTP-date = + + OWS = + + Pragma = "Pragma:" OWS Pragma-v + Pragma-v = *( "," OWS ) pragma-directive *( OWS "," [ OWS + pragma-directive ] ) + + Vary = "Vary:" OWS Vary-v + Vary-v = "*" / ( *( "," OWS ) field-name *( OWS "," [ OWS field-name + ] ) ) + + Warning = "Warning:" OWS Warning-v + Warning-v = *( "," OWS ) warning-value *( OWS "," [ OWS warning-value + ] ) + + cache-directive = cache-request-directive / cache-response-directive + cache-extension = token [ "=" ( token / quoted-string ) ] + cache-request-directive = "no-cache" / "no-store" / ( "max-age=" + delta-seconds ) / ( "max-stale" [ "=" delta-seconds ] ) / ( + "min-fresh=" delta-seconds ) / "no-transform" / "only-if-cached" / + cache-extension + cache-response-directive = "public" / ( "private" [ "=" DQUOTE *( "," + OWS ) field-name *( OWS "," [ OWS field-name ] ) DQUOTE ] ) / ( + "no-cache" [ "=" DQUOTE *( "," OWS ) field-name *( OWS "," [ OWS + field-name ] ) DQUOTE ] ) / "no-store" / "no-transform" / + "must-revalidate" / "proxy-revalidate" / ( "max-age=" delta-seconds + ) / ( "s-maxage=" delta-seconds ) / cache-extension + + delta-seconds = 1*DIGIT + + extension-pragma = token [ "=" ( token / quoted-string ) ] + + field-name = + + port = + pragma-directive = "no-cache" / extension-pragma + pseudonym = + + quoted-string = + + token = + + uri-host = + + warn-agent = ( uri-host [ ":" port ] ) / pseudonym + + + +Fielding, et al. Expires February 5, 2011 [Page 32] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + warn-code = 3DIGIT + warn-date = DQUOTE HTTP-date DQUOTE + warn-text = quoted-string + warning-value = warn-code SP warn-agent SP warn-text [ SP warn-date + ] + + ABNF diagnostics: + + ; Age defined but not used + ; Cache-Control defined but not used + ; Expires defined but not used + ; Pragma defined but not used + ; Vary defined but not used + ; Warning defined but not used + +Appendix C. Change Log (to be removed by RFC Editor before publication) + +C.1. Since RFC2616 + + Extracted relevant partitions from [RFC2616]. + +C.2. Since draft-ietf-httpbis-p6-cache-00 + + Closed issues: + + o : "Trailer" + () + + o : "Invalidation + after Update or Delete" + () + + o : "Normative and + Informative references" + + o : "Date reference + typo" + + o : "Connection + header text" + + o : "Informative + references" + + o : "ISO-8859-1 + Reference" + + + + + +Fielding, et al. Expires February 5, 2011 [Page 33] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + o : "Normative up- + to-date references" + + o : "typo in + 13.2.2" + + Other changes: + + o Use names of RFC4234 core rules DQUOTE and HTAB (work in progress + on ) + +C.3. Since draft-ietf-httpbis-p6-cache-01 + + Closed issues: + + o : "rel_path not + used" + + Other changes: + + o Get rid of duplicate BNF rule names ("host" -> "uri-host") (work + in progress on ) + + o Add explicit references to BNF syntax and rules imported from + other parts of the specification. + +C.4. Since draft-ietf-httpbis-p6-cache-02 + + Ongoing work on IANA Message Header Registration + (): + + o Reference RFC 3984, and update header registrations for headers + defined in this document. + +C.5. Since draft-ietf-httpbis-p6-cache-03 + + Closed issues: + + o : "Vary header + classification" + +C.6. Since draft-ietf-httpbis-p6-cache-04 + + Ongoing work on ABNF conversion + (): + + o Use "/" instead of "|" for alternatives. + + + + +Fielding, et al. Expires February 5, 2011 [Page 34] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + o Introduce new ABNF rules for "bad" whitespace ("BWS"), optional + whitespace ("OWS") and required whitespace ("RWS"). + + o Rewrite ABNFs to spell out whitespace rules, factor out header + value format definitions. + +C.7. Since draft-ietf-httpbis-p6-cache-05 + + This is a total rewrite of this part of the specification. + + Affected issues: + + o : "Definition of + 1xx Warn-Codes" + + o : "Placement of + 13.5.1 and 13.5.2" + + o : "The role of + Warning and Semantic Transparency in Caching" + + o : "Methods and + Caching" + + In addition: Final work on ABNF conversion + (): + + o Add appendix containing collected and expanded ABNF, reorganize + ABNF introduction. + +C.8. Since draft-ietf-httpbis-p6-cache-06 + + Closed issues: + + o : "base for + numeric protocol elements" + + Affected issues: + + o : Vary and non- + existant headers + +C.9. Since draft-ietf-httpbis-p6-cache-07 + + Closed issues: + + o : "Definition of + 1xx Warn-Codes" + + + +Fielding, et al. Expires February 5, 2011 [Page 35] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + o : "Content- + Location on 304 responses" + + o : "private and + no-cache CC directives with headers" + + o : "RFC2047 and + warn-text" + +C.10. Since draft-ietf-httpbis-p6-cache-08 + + Closed issues: + + o : "serving + negotiated responses from cache: header-specific canonicalization" + + o : "Effect of CC + directives on history lists" + + Affected issues: + + o : Status codes + and caching + + Partly resolved issues: + + o : "Placement of + 13.5.1 and 13.5.2" + +C.11. Since draft-ietf-httpbis-p6-cache-09 + + Closed issues: + + o : "Age + calculation" + + o : "Clarify + differences between / requirements for request and response CC + directives" + + o : "Caching + authenticated responses" + + o : "IANA registry + for cache-control directives" + + o : "Heuristic + caching of URLs with query components" + + + +Fielding, et al. Expires February 5, 2011 [Page 36] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Partly resolved issues: + + o : "Term for the + requested resource's URI" + +C.12. Since draft-ietf-httpbis-p6-cache-10 + + Closed issues: + + o : "Clarify + entity / representation / variant terminology" + + o : "consider + removing the 'changes from 2068' sections" + + o : "Allowing + heuristic caching for new status codes" + + o : "Allowing + heuristic caching for new status codes" + + o Clean up TODOs and prose in "Combining Responses." + +Index + + A + age 6 + Age header 17 + + C + cache 5 + Cache Directives + max-age 19, 22 + max-stale 19 + min-fresh 19 + must-revalidate 22 + no-cache 19, 21 + no-store 19, 21 + no-transform 20, 22 + only-if-cached 20 + private 20 + proxy-revalidate 22 + public 20 + s-maxage 22 + Cache-Control header 18 + cacheable 5 + + E + + + +Fielding, et al. Expires February 5, 2011 [Page 37] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Expires header 24 + explicit expiration time 5 + + F + first-hand 6 + fresh 6 + freshness lifetime 6 + + G + Grammar + Age 17 + Age-v 17 + Cache-Control 18 + Cache-Control-v 18 + cache-extension 18 + cache-request-directive 18 + cache-response-directive 20 + delta-seconds 17 + Expires 24 + Expires-v 24 + extension-pragma 24 + Pragma 24 + pragma-directive 24 + Pragma-v 24 + Vary 25 + Vary-v 25 + warn-agent 26 + warn-code 26 + warn-date 26 + warn-text 26 + Warning 26 + Warning-v 26 + warning-value 26 + + H + Headers + Age 17 + Cache-Control 18 + Expires 24 + Pragma 24 + Vary 25 + Warning 26 + heuristic expiration time 5 + + M + max-age + Cache Directive 19, 22 + max-stale + + + +Fielding, et al. Expires February 5, 2011 [Page 38] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Cache Directive 19 + min-fresh + Cache Directive 19 + must-revalidate + Cache Directive 22 + + N + no-cache + Cache Directive 19, 21 + no-store + Cache Directive 19, 21 + no-transform + Cache Directive 20, 22 + + O + only-if-cached + Cache Directive 20 + + P + Pragma header 24 + private + Cache Directive 20 + proxy-revalidate + Cache Directive 22 + public + Cache Directive 20 + + S + s-maxage + Cache Directive 22 + stale 6 + + V + validator 6 + Vary header 25 + + W + Warning header 26 + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 39] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + +Authors' Addresses + + Roy T. Fielding (editor) + Day Software + 23 Corporate Plaza DR, Suite 280 + Newport Beach, CA 92660 + USA + + Phone: +1-949-706-5300 + Fax: +1-949-706-5305 + EMail: fielding@gbiv.com + URI: http://roy.gbiv.com/ + + + Jim Gettys + Alcatel-Lucent Bell Labs + 21 Oak Knoll Road + Carlisle, MA 01741 + USA + + EMail: jg@freedesktop.org + URI: http://gettys.wordpress.com/ + + + Jeffrey C. Mogul + Hewlett-Packard Company + HP Labs, Large Scale Systems Group + 1501 Page Mill Road, MS 1177 + Palo Alto, CA 94304 + USA + + EMail: JeffMogul@acm.org + + + Henrik Frystyk Nielsen + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + USA + + EMail: henrikn@microsoft.com + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 40] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Larry Masinter + Adobe Systems, Incorporated + 345 Park Ave + San Jose, CA 95110 + USA + + EMail: LMM@acm.org + URI: http://larry.masinter.net/ + + + Paul J. Leach + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052 + + EMail: paulle@microsoft.com + + + Tim Berners-Lee + World Wide Web Consortium + MIT Computer Science and Artificial Intelligence Laboratory + The Stata Center, Building 32 + 32 Vassar Street + Cambridge, MA 02139 + USA + + EMail: timbl@w3.org + URI: http://www.w3.org/People/Berners-Lee/ + + + Yves Lafon (editor) + World Wide Web Consortium + W3C / ERCIM + 2004, rte des Lucioles + Sophia-Antipolis, AM 06902 + France + + EMail: ylafon@w3.org + URI: http://www.raubacapeu.net/people/yves/ + + + Mark Nottingham (editor) + + EMail: mnot@mnot.net + URI: http://www.mnot.net/ + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 41] + +Internet-Draft HTTP/1.1, Part 6 August 2010 + + + Julian F. Reschke (editor) + greenbytes GmbH + Hafenweg 16 + Muenster, NW 48155 + Germany + + Phone: +49 251 2807760 + Fax: +49 251 2807761 + EMail: julian.reschke@greenbytes.de + URI: http://greenbytes.de/tech/webdav/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fielding, et al. Expires February 5, 2011 [Page 42] + diff --git a/dav/SabreDAV/docs/draft-nottingham-http-new-status-04.txt b/dav/SabreDAV/docs/draft-nottingham-http-new-status-04.txt new file mode 100644 index 000000000..37d6808c3 --- /dev/null +++ b/dav/SabreDAV/docs/draft-nottingham-http-new-status-04.txt @@ -0,0 +1,560 @@ + + + +Network Working Group M. Nottingham +Internet-Draft Rackspace +Updates: 2616 (if approved) R. Fielding +Intended status: Standards Track Adobe +Expires: August 7, 2012 February 4, 2012 + + + Additional HTTP Status Codes + draft-nottingham-http-new-status-04 + +Abstract + + This document specifies additional HyperText Transfer Protocol (HTTP) + status codes for a variety of common situations. + +Editorial Note (To be removed by RFC Editor before publication) + + Distribution of this document is unlimited. Although this is not a + work item of the HTTPbis Working Group, comments should be sent to + the Hypertext Transfer Protocol (HTTP) mailing list at + ietf-http-wg@w3.org [1], which may be joined by sending a message + with subject "subscribe" to ietf-http-wg-request@w3.org [2]. + + Discussions of the HTTPbis Working Group are archived at + . + +Status of this Memo + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on August 7, 2012. + +Copyright Notice + + Copyright (c) 2012 IETF Trust and the persons identified as the + document authors. All rights reserved. + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 1] + +Internet-Draft Additional HTTP Status Codes February 2012 + + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 2. Requirements . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 3. 428 Precondition Required . . . . . . . . . . . . . . . . . . . 3 + 4. 429 Too Many Requests . . . . . . . . . . . . . . . . . . . . . 4 + 5. 431 Request Header Fields Too Large . . . . . . . . . . . . . . 4 + 6. 511 Network Authentication Required . . . . . . . . . . . . . . 5 + 7. Security Considerations . . . . . . . . . . . . . . . . . . . . 6 + 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 7 + 9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 8 + 9.1. Normative References . . . . . . . . . . . . . . . . . . . 8 + 9.2. Informative References . . . . . . . . . . . . . . . . . . 8 + Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . . 8 + Appendix B. Issues Raised by Captive Portals . . . . . . . . . . . 8 + Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 9 + + + + + + + + + + + + + + + + + + + + + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 2] + +Internet-Draft Additional HTTP Status Codes February 2012 + + +1. Introduction + + This document specifies additional HTTP [RFC2616] status codes for a + variety of common situations, to improve interoperability and avoid + confusion when other, less precise status codes are used. + + Note that these status codes are optional; servers cannot be required + to support them. However, because clients will treat unknown status + codes as a generic error of the same class (e.g., 499 is treated as + 400 if it is not recognized), they can be safely deployed by existing + servers (see [RFC2616] Section 6.1.1 for more information). + + +2. Requirements + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + +3. 428 Precondition Required + + The 428 status code indicates that the origin server requires the + request to be conditional. + + Its typical use is to avoid the "lost update" problem, where a client + GETs a resource's state, modifies it, and PUTs it back to the server, + when meanwhile a third party has modified the state on the server, + leading to a conflict. By requiring requests to be conditional, the + server can assure that clients are working with the correct copies. + + Responses using this status code SHOULD explain how to resubmit the + request successfully. For example: + + HTTP/1.1 428 Precondition Required + Content-Type: text/html + + + + Precondition Required + + +

Precondition Required

+

This request is required to be conditional; + try using "If-Match".

+ + + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 3] + +Internet-Draft Additional HTTP Status Codes February 2012 + + + Responses with the 428 status code MUST NOT be stored by a cache. + + +4. 429 Too Many Requests + + The 429 status code indicates that the user has sent too many + requests in a given amount of time ("rate limiting"). + + The response representations SHOULD include details explaining the + condition, and MAY include a Retry-After header indicating how long + to wait before making a new request. + + For example: + + HTTP/1.1 429 Too Many Requests + Content-Type: text/html + Retry-After: 3600 + + + + Too Many Requests + + +

Too Many Requests

+

I only allow 50 requests per hour to this Web site per + logged in user. Try again soon.

+ + + + Note that this specification does not define how the origin server + identifies the user, nor how it counts requests. For example, an + origin server that is limiting request rates can do so based upon + counts of requests on a per-resource basis, across the entire server, + or even among a set of servers. Likewise, it might identify the user + by its authentication credentials, or a stateful cookie. + + Responses with the 429 status code MUST NOT be stored by a cache. + + +5. 431 Request Header Fields Too Large + + The 431 status code indicates that the server is unwilling to process + the request because its header fields are too large. The request MAY + be resubmitted after reducing the size of the request header fields. + + It can be used both when the set of request header fields in total + are too large, and when a single header field is at fault. In the + latter case, the response representation SHOULD specify which header + + + +Nottingham & Fielding Expires August 7, 2012 [Page 4] + +Internet-Draft Additional HTTP Status Codes February 2012 + + + field was too large. + + For example: + + HTTP/1.1 431 Request Header Fields Too Large + Content-Type: text/html + + + + Request Header Fields Too Large + + +

Request Header Fields Too Large

+

The "Example" header was too large.

+ + + + Responses with the 431 status code MUST NOT be stored by a cache. + + +6. 511 Network Authentication Required + + The 511 status code indicates that the client needs to authenticate + to gain network access. + + The response representation SHOULD contain a link to a resource that + allows the user to submit credentials (e.g. with a HTML form). + + Note that the 511 response SHOULD NOT contain a challenge or the + login interface itself, because browsers would show the login + interface as being associated with the originally requested URL, + which may cause confusion. + + The 511 status SHOULD NOT be generated by origin servers; it is + intended for use by intercepting proxies that are interposed as a + means of controlling access to the network. + + Responses with the 511 status code MUST NOT be stored by a cache. + +6.1. The 511 Status Code and Captive Portals + + The 511 status code is designed to mitigate problems caused by + "captive portals" to software (especially non-browser agents) that is + expecting a response from the server that a request was made to, not + the intervening network infrastructure. It is not intended to + encouraged deployment of captive portals, only to limit the damage + caused by them. + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 5] + +Internet-Draft Additional HTTP Status Codes February 2012 + + + A network operator wishing to require some authentication, acceptance + of terms or other user interaction before granting access usually + does so by identifing clients who have not done so ("unknown + clients") using their MAC addresses. + + Unknown clients then have all traffic blocked, except for that on TCP + port 80, which is sent to a HTTP server (the "login server") + dedicated to "logging in" unknown clients, and of course traffic to + the login server itself. + + For example, a user agent might connect to a network and make the + following HTTP request on TCP port 80: + + GET /index.htm HTTP/1.1 + Host: www.example.com + + Upon receiving such a request, the login server would generate a 511 + response: + + HTTP/1.1 511 Network Authentication Required + Content-Type: text/html + + + + Network Authentication Required + + + +

You need to + authenticate with the local network in order to gain + access.

+ + + + Here, the 511 status code assures that non-browser clients will not + interpret the response as being from the origin server, and the META + HTML element redirects the user agent to the login server. + + +7. Security Considerations + +7.1. 428 Precondition Required + + The 428 status code is optional; clients cannot rely upon its use to + prevent "lost update" conflicts. + + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 6] + +Internet-Draft Additional HTTP Status Codes February 2012 + + +7.2. 429 Too Many Requests + + When a server is under attack or just receiving a very large number + of requests from a single party, responding to each with a 429 status + code will consume resources. + + Therefore, servers are not required to use the 429 status code; when + limiting resource usage, it may be more appropriate to just drop + connections, or take other steps. + +7.3. 431 Request Header Fields Too Large + + Servers are not required to use the 431 status code; when under + attack, it may be more appropriate to just drop connections, or take + other steps. + +7.4. 511 Network Authentication Required + + In common use, a response carrying the 511 status code will not come + from the origin server indicated in the request's URL. This presents + many security issues; e.g., an attacking intermediary may be + inserting cookies into the original domain's name space, may be + observing cookies or HTTP authentication credentials sent from the + user agent, and so on. + + However, these risks are not unique to the 511 status code; in other + words, a captive portal that is not using this status code introduces + the same issues. + + Also, note that captive portals using this status code on an SSL or + TLS connection (commonly, port 443) will generate a certificate error + on the client. + + +8. IANA Considerations + + The HTTP Status Codes Registry should be updated with the following + entries: + + o Code: 428 + o Description: Precondition Required + o Specification: [ this document ] + + o Code: 429 + o Description: Too Many Requests + o Specification: [ this document ] + + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 7] + +Internet-Draft Additional HTTP Status Codes February 2012 + + + o Code: 431 + o Description: Request Header Fields Too Large + o Specification: [ this document ] + + o Code: 511 + o Description: Network Authentication Required + o Specification: [ this document ] + + +9. References + +9.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + +9.2. Informative References + + [RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault, + "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791, + March 2007. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + +URIs + + [1] + + [2] + + +Appendix A. Acknowledgements + + Thanks to Jan Algermissen and Julian Reschke for their suggestions + and feedback. + + +Appendix B. Issues Raised by Captive Portals + + Since clients cannot differentiate between a portal's response and + that of the HTTP server that they intended to communicate with, a + number of issues arise. The 511 status code is intended to help + mitigate some of them. + + + +Nottingham & Fielding Expires August 7, 2012 [Page 8] + +Internet-Draft Additional HTTP Status Codes February 2012 + + + One example is the "favicon.ico" + commonly used by browsers to + identify the site being accessed. If the favicon for a given site is + fetched from a captive portal instead of the intended site (e.g., + because the user is unauthenticated), it will often "stick" in the + browser's cache (most implementations cache favicons aggressively) + beyond the portal session, so that it seems as if the portal's + favicon has "taken over" the legitimate site. + + Another browser-based issue comes about when P3P + is supported. Depending on how it is + implemented, it's possible a browser might interpret a portal's + response for the p3p.xml file as the server's, resulting in the + privacy policy (or lack thereof) advertised by the portal being + interpreted as applying to the intended site. Other Web-based + protocols such as WebFinger + , CORS + and OAuth + may also be + vulnerable to such issues. + + Although HTTP is most widely used with Web browsers, a growing number + of non-browsing applications use it as a substrate protocol. For + example, WebDAV [RFC4918] and CalDAV [RFC4791] both use HTTP as the + basis (for remote authoring and calendaring, respectively). Using + these applications from behind a captive portal can result in + spurious errors being presented to the user, and might result in + content corruption, in extreme cases. + + Similarly, other non-browser applications using HTTP can be affected + as well; e.g., widgets , software + updates, and other specialised software such as Twitter clients and + the iTunes Music Store. + + It should be noted that it's sometimes believed that using HTTP + redirection to direct traffic to the portal addresses these issues. + However, since many of these uses "follow" redirects, this is not a + good solution. + + +Authors' Addresses + + Mark Nottingham + Rackspace + + Email: mnot@mnot.net + URI: http://www.mnot.net/ + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 9] + +Internet-Draft Additional HTTP Status Codes February 2012 + + + Roy T. Fielding + Adobe Systems Incorporated + 345 Park Ave + San Jose, CA 95110 + USA + + Email: fielding@gbiv.com + URI: http://roy.gbiv.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Nottingham & Fielding Expires August 7, 2012 [Page 10] + diff --git a/dav/SabreDAV/docs/rfc2425.txt b/dav/SabreDAV/docs/rfc2425.txt new file mode 100644 index 000000000..2e37e24a4 --- /dev/null +++ b/dav/SabreDAV/docs/rfc2425.txt @@ -0,0 +1,1851 @@ + + + + + + +Network Working Group T. Howes +Request for Comments: 2425 M. Smith +Category: Standards Track Netscape Communications Corp. + F. Dawson + Lotus Development Corporation + September 1998 + + + A MIME Content-Type for Directory Information + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1998). All Rights Reserved. + +1. Abstract + + This document defines a MIME Content-Type for holding directory + information. The definition is independent of any particular + directory service or protocol. The text/directory Content-Type is + defined for holding a variety of directory information, for example, + name, or email address, or logo. The text/directory Content-Type can + also be used as the root body part in a multipart/related Content- + Type for handling more complicated situations, especially those in + which non-textual information that already has a natural MIME + representation, for example, a photograph or sound, is to be + represented. + + The text/directory Content-Type defines a general framework and + format for holding directory information in a simple "type:value" + form. We refer to "type" in this context meaning a property or + attribute with which the value is associated. Mechanisms are defined + to specify alternate languages, encodings and other meta-information. + This document also defines the procedure by which particular formats, + called profiles, for carrying application-specific information within + a text/directory Content-Type can be defined and registered, and the + conventions such formats must follow. It is expected that other + documents will be produced that define such formats for various + applications (e.g., white pages). + + + + + +Howes, et. al. Standards Track [Page 1] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" and "OPTIONAL" in this + document are to be interpreted as described in [RFC-2119]. + +2. Table of Contents + + Status of the Memo................................................ 1 + Copyright Notice.................................................. 1 + 1. Abstract...................................................... 1 + 2. Table of Contents............................................. 2 + 3. Need for a MIME Directory Type................................ 3 + 4. Overview...................................................... 4 + 5. The text/directory Content-Type............................... 4 + 5.1. MIME media type name........................................ 4 + 5.2. MIME subtype name........................................... 5 + 5.3. Required parameters......................................... 5 + 5.4. Optional parameters......................................... 5 + 5.5. Encoding considerations..................................... 5 + 5.6. Security considerations..................................... 6 + 5.7. Interoperability considerations............................. 6 + 5.8. Published specification..................................... 6 + 5.8.1. Line delimiting and folding............................... 6 + 5.8.2. ABNF content-type definition.............................. 7 + 5.8.3. Pre-defined Parameters.................................... 9 + 5.8.4. Pre-defined Value Types...................................11 + 5.9. Applications which use this media type......................14 + 5.10. Additional information.....................................14 + 5.11. Person & email address to contact for further information..14 + 5.12. Intended usage.............................................14 + 5.13. Author/Change controller...................................15 + 6. Predefined Types..............................................15 + 6.1. SOURCE Type Definition......................................15 + 6.2. NAME Type Definition........................................16 + 6.3. PROFILE Type Definition.....................................16 + 6.4. BEGIN Type Definition.......................................17 + 6.5. END Type Definition.........................................17 + 7. Use of the multipart/related Content-Type.....................18 + 8. Examples.......................................................18 + 8.1. Example 1...................................................19 + 8.2. Example 2...................................................19 + 8.3. Example 3...................................................20 + 8.4. Example 4...................................................21 + 9. Registration of new profiles..................................22 + 9.1. Define the profile..........................................22 + 9.2. Post the profile definition.................................23 + 9.3. Allow a comment period......................................23 + 9.4. Submit the profile for approval.............................23 + 10. Profile Change Control.......................................23 + + + +Howes, et. al. Standards Track [Page 2] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + 11. Registration of new types....................................24 + 11.1. Define the type............................................24 + 11.2. Post the type definition...................................25 + 11.3. Allow a comment period.....................................25 + 11.4. Submit the type for approval...............................25 + 12. Type Change Control..........................................25 + 13. Registration of new parameters...............................26 + 13.1. Define the parameter.......................................26 + 13.2. Post the parameter definition..............................27 + 13.3. Allow a comment period.....................................27 + 13.4. Submit the parameter for approval..........................27 + 14. Parameter Change Control.....................................28 + 15. Registration of new value types..............................28 + 15.1. Define the value type......................................28 + 15.2. Post the value type definition.............................29 + 15.3. Allow a comment period.....................................29 + 15.4. Submit the value type for approval.........................29 + 16. Security Considerations......................................30 + 17. Acknowledgements..............................................30 + 18. References....................................................30 + 19. Authors' Addresses...........................................32 + 20. Full Copyright Statement......................................33 + +3. Need for a MIME Directory Type + + For purposes of this document, a directory is a special-purpose + database that contains typed information. A directory usually + supports both read and search of the information it contains, and can + support creation and modification of the information as well. + Directory information is usually accessed far more often than it is + updated. Directories can be local or global in scope. They can be + distributed or centralized. The information they contain can be + replicated, with weak or strong consistency requirements. + + There are several situations in which users of Internet mail might + wish to exchange directory information: the email analogy of a + "business card" exchange; the conveyance of directory information to + a user having only email access to the Internet; the provision of + machine-parseable address information when purchasing goods or + services over the Internet; etc. As MIME [RFC-2045, RFC-2046] is + used increasingly by other protocols, most notably HTTP, it can also + be useful for these protocols to carry directory information in MIME + format. Such a format, for example, could be used to represent URC + (uniform resource characteristics) information about resources on the + World Wide Web, or to provide a rudimentary directory service over + HTTP. + + + + + +Howes, et. al. Standards Track [Page 3] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +4. Overview + + The scheme defined here for representing directory information in a + MIME Content-Type has two parts. First, the text/directory Content- + Type is defined for use in holding directory information within a + single body part, for example name, title, or email address. In its + simplest form, the format uses a "type:value" approach, which should + be easily parseable by existing MIME implementations and + understandable by users. More complicated situations can be + represented also. This document defines the general form the + information in the Content-Type should have, and the procedure by + which specific types and values (properties) for particular + applications can be defined. The framework is general enough to + handle information from any number of end directory services, + including LDAP [RFC-1777, RFC-1778], WHOIS++ [RFC-1835], and X.500 + [X500]. + + Directory entries can include far more than just textual information. + Some such information (e.g., an image or sound) overlaps with + predefined MIME Content-Types. In these cases it can be desirable to + include the information in its well-known MIME format. This situation + is handled by using a multipart/related Content-Type as defined in + [RFC-2112]. The root component of this type is a text/directory body + part specifying any in-line information, and for information + contained in other Content-Types, the Content-IDs (in URI form) of + those parts. + + In some applications, it can be useful to include a pointer (e.g, a + URI) to some directory information rather than the information + itself. This document defines a general mechanism for accomplishing + this. + +5. The text/directory Content-Type + + The text/directory Content-Type is used to hold basic directory + information and URIs referencing other information, including other + MIME body parts holding supplementary or non-textual directory + information, such as an image or sound. It is defined as follows, + using the MIME media type registration template from [RFC-2048]. + + To: ietf-types@uninett.no + Subject: Registration of MIME media type text/directory + +5.1. MIME media type name + + MIME media type name: text + + + + + +Howes, et. al. Standards Track [Page 4] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +5.2. MIME subtype name + + MIME subtype name: directory + +5.3. Required parameters + + Required parameters: charset + + The "charset" parameter is as defined in [RFC-2046] for other body + parts. It is used to identify the default character set used within + the body part. + +5.4. Optional parameters + + Optional parameters: profile + + The "profile" parameter is used to convey the type(s) of entity(ies) + to which the directory information pertains and the likely set of + information associated with the entity(ies). It is intended only as a + guide to applications interpreting the information contained within + the body part. It SHOULD NOT be used to exclude or require particular + pieces of information unless a profile definition specifically calls + for this behavior. Unless specifically forbidden by a particular + profile definition, a text/directory content type can contain + arbitrary attribute/value pairs. + + The value of the "profile" parameter is defined as follows. Profile + names are case insensitive (i.e., the profile name "vCard" is the + same as "VCARD" and "vcard" and "vcArD"). + + profile = x-name / iana-token + + x-name = "x-" 1*(ALPHA / DIGIT / "-") + ; Names beginning with "x-" or "X-" are + ; reserved for experimental use not intended for released + ; products, or for use in bilateral agreements. + + iana-token = + +5.5. Encoding considerations + + The default encoding is 8bit. Otherwise, as specified by the + Content-Transfer-Encoding header field. + + + + + + +Howes, et. al. Standards Track [Page 5] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +5.6. Security considerations + + Directory information can be public or it can be protected from + unauthorized access by the directory service in which it resides. + Once the information leaves its native service, there can be no + guarantee that the same care will be taken by all services handling + the information. Furthermore, this specification defines no access + control mechanism by which information can be protected, or by which + access control information can be conveyed. Note that the integrity + and privacy of a text/directory body part can be protected by + enclosing it within an appropriate MIME-based security mechanism. + +5.7. Interoperability considerations + + In order to make sense of directory information, applications must + share a common understanding of the types of information contained + within the Content-Type (the directory schema). This schema + information is not defined in this document, but rather in companion + documents (e.g., [MIME-VCARD]) that follow the requirements specified + in this document, or in bilateral agreements between communicating + parties. + +5.8. Published specification + + The text/directory Content-Type contains directory information, + typically pertaining to a single directory entity or group of + entities. The content consists of one or more lines in the format + given below. + +5.8.1. Line delimiting and folding + + Individual lines within the MIME text/directory Content Type body are + delimited by the [RFC-822] line break, which is a CRLF sequence + (ASCII decimal 13, followed by ASCII decimal 10). Long logical lines + of text can be split into a multiple-physical-line representation + using the following folding technique. + + A logical line MAY be continued on the next physical line anywhere + between two characters by inserting a CRLF immediately followed by a + single white space character (space, ASCII decimal 32, or horizontal + tab, ASCII decimal 9). At least one character must be present on the + folded line. Any sequence of CRLF followed immediately by a single + white space character is ignored (removed) when processing the + content type. For example the line: + + DESCRIPTION:This is a long description that exists on a long line. + + Can be represented as: + + + +Howes, et. al. Standards Track [Page 6] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + DESCRIPTION:This is a long description + that exists on a long line. + + It could also be represented as: + + DESCRIPTION:This is a long descrip + tion that exists o + n a long line. + + The process of moving from this folded multiple-line representation + of a type definition to its single line representation is called + unfolding. Unfolding is accomplished by regarding CRLF immediately + followed by a white space character (namely HTAB ASCII decimal 9 or + SPACE ASCII decimal 32) as equivalent to no characters at all (i.e., + the CRLF and single white space character are removed). + +5.8.2. ABNF content-type definition + + The following ABNF uses the notation of RFC 2234, which also defines + CRLF, WSP, DQUOTE, VCHAR, ALPHA, and DIGIT. After the unfolding of + any folded lines as described above, the syntax for a line of this + content type is as follows: + + contentline = [group "."] name *(";" param) ":" value CRLF + ; When parsing a content line, folded lines MUST first + ; be unfolded according to the unfolding procedure + ; described above. + ; When generating a content line, lines longer than 75 + ; characters SHOULD be folded according to the folding + ; procedure described above. + + group = 1*(ALPHA / DIGIT / "-") + + name = x-name / iana-token + + iana-token = 1*(ALPHA / DIGIT / "-") + ; identifier registered with IANA + + x-name = "x-" 1*(ALPHA / DIGIT / "-") + ; Names that begin with "x-" or "X-" are + ; reserved for experimental use, not intended for released + ; products, or for use in bilateral agreements. + + param = param-name "=" param-value *("," param-value) + + param-name = x-name / iana-token + + param-value = ptext / quoted-string + + + +Howes, et. al. Standards Track [Page 7] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + ptext = *SAFE-CHAR + + value = *VALUE-CHAR + / valuespec ; valuespec defined in section 5.8.4 + + quoted-string = DQUOTE *QSAFE-CHAR DQUOTE + + NON-ASCII = %x80-FF + ; use restricted by charset parameter + ; on outer MIME object (UTF-8 preferred) + + QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-ASCII + ; Any character except CTLs, DQUOTE + + SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-ASCII + ; Any character except CTLs, DQUOTE, ";", ":", "," + + VALUE-CHAR = WSP / VCHAR / NON-ASCII + ; any textual character + + A line that begins with a white space character is a continuation of + the previous line, as described above. The white space character and + immediately preceeding CRLF should be discarded when reconstructing + the original line. Note that this line-folding convention differs + from that found in RFC 822, in that the sequence found + anywhere in the content indicates a continued line and should be + removed. + + Various type names and the format of the corresponding values are + defined as specified in Section 11. Specifications MAY impose + ordering on the type constructs within a body part, though none is + required by default. The various x-name constructs are used for + bilaterally-agreed upon type names, parameter names and parameter + values, or for use in experimental settings. + + Type names and parameter names are case insensitive (e.g., the type + name "fn" is the same as "FN" and "Fn"). Parameter values MAY be case + sensitive or case insensitive, depending on their definition. + + The group construct is used to group related attributes together. + The group name is a syntactic convention used to indicate that all + type names prefaced with the same group name SHOULD be grouped + together when displayed by an application. It has no other + significance. Implementations that do not understand or support + grouping MAY simply strip off any text before a "." to the left of + the type name and present the types and values as normal. + + + + + +Howes, et. al. Standards Track [Page 8] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Each attribute defined in the text/directory body MAY have multiple + values, if allowed in the definition of the profile in which the + attribute is used. The general rule for encoding multi-valued items + is to simply create a new content line for each value (including the + type name). However, it should be noted that some value types + support encoding multiple values in a single content line by + separating the values with a comma ",". This approach has been taken + for several of the content types defined below (date, time, integer, + float), for space-saving reasons. + +5.8.3. Pre-defined Parameters + + The following parameters and value types are defined for general use. + + predefined-param = encodingparm + / valuetypeparm + / languageparm + / contextparm + + encodingparm = "encoding" "=" encodingtype + + encodingtype = "b" ; from RFC 2047 + / iana-token ; registered as described in + ; section 15 of this document + + valuetypeparm = "value" "=" valuetype + + valuetype = "uri" ; genericurl from secion 5 of RFC 1738 + / "text" + / "date" + / "time" + / "date-time" ; date time + / "integer" + / "boolean" + / "float" + / x-name + / iana-token ; registered as described in + ; section 15 of this document + + languageparm = "language" "=" Language-Tag + ; Language-Tag is defined in section 2 of RFC 1766 + + contextparm = "context" "=" context + + context = x-name + / iana-token + + + + + +Howes, et. al. Standards Track [Page 9] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + The "language" type parameter is used to identify data in multiple + languages. There is no concept of "default" language, except as + specified by any "Content-Language" MIME header parameter that is + present. The value of the "language" type parameter is a language + tag as defined in Section 2 of [RFC-1766]. + + The "context" type parameter is used to identify a context (e.g., a + protocol) used in interpreting the value. This is used, for example, + in the "source" type, defined below. + + The "encoding" type parameter is used to specify an alternate + encoding for a value. If the value contains a CRLF, it must be + encoded, since CRLF is used to separate lines in the content-type + itself. Currently, only the "b" encoding is supported. + + The "b" encoding can also be useful for binary values that are mixed + with other text information in the body part (e.g., a certificate). + Using a per-value "b" encoding in this case leaves the other + information in a more readable form. The encoded base 64 value can be + split across multiple physical lines in the content type by using the + line folding technique described above. + + The Content-Transfer-Encoding header field is used to specify the + encoding used for the body part as a whole. The "encoding" type + parameter is used to specify an encoding for a particular value + (e.g., a certificate). In this case, the Content-Transfer-Encoding + header might specify "8bit", while the one certificate value might + specify an encoding of "b" via an "encoding=b" type parameter. + + The Content-Transfer-Encoding and the encodings of individual types + given by the "encoding" type parameter are independent of one + another. When encoding a text/directory body part for transmission, + individual type encodings are performed first, then the entire body + part is encoded according to the Content-Transfer-Encoding. When + decoding a text/directory body part, the Content-Transfer-Encoding is + decoded first, and then any individual types with an "encoding" type + parameter are decoded. + + The "value" parameter is optional, and is used to identify the value + type (data type) and format of the value. The use of these + predefined formats is encouraged even if the value parameter is not + explicity used. By defining a standard set of value types and their + formats, existing parsing and processing code can be leveraged. + + Including the value type explicitly as part of each property provides + an extra hint to keep parsing simple and support more generalized + applications. For example a search engine would not have to know the + particular value types for all of the items for which it is + + + +Howes, et. al. Standards Track [Page 10] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + searching. Because the value type is explicit in the definition, the + search engine could look for dates in any item type and provide + results that can still be interpreted. + +5.8.4. Pre-defined Value Types + + The format for values corresponding to the predefined valuetype + specifications given above are defined. + + valuespec = text-list + / genericurl ; from section 5 of RFC 1738 + / date-list + / time-list + / date-time-list + / boolean + / integer-list + / float-list + / iana-valuespec + + text-list = *TEXT-LIST-CHAR *("," *TEXT-LIST-CHAR) + + TEXT-LIST-CHAR = "\\" / "\," / "\n" + / + ; Backslashes, newlines, and commas must be encoded. + ; \n or \N can be used to encode a newline. + + date-list = date *("," date) + + time-list = time *("," time) + + date-time-list = date "T" time *("," date "T" time) + + boolean = "TRUE" / "FALSE" + + integer-list = integer *("," integer) + + integer = [sign] 1*DIGIT + + float-list = float *("," float) + + float = [sign] 1*DIGIT ["." 1*DIGIT] + + sign = "+" / "-" + + date = date-fullyear ["-"] date-month ["-"] date-mday + + date-fullyear = 4 DIGIT + + + + +Howes, et. al. Standards Track [Page 11] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + date-month = 2 DIGIT ;01-12 + + date-mday = 2 DIGIT ;01-28, 01-29, 01-30, 01-31 + ;based on month/year + + time = time-hour [":"] time-minute [":"] time-second [time-secfrac] + [time-zone] + + time-hour = 2 DIGIT ;00-23 + + time-minute = 2 DIGIT ;00-59 + + time-second = 2 DIGIT ;00-60 (leap second) + + time-secfrac = "," 1*DIGIT + + time-zone = "Z" / time-numzone + + time-numzome = sign time-hour [":"] time-minute + + iana-valuespec = + + Some specific notes on the value types and formats: + + "text": The "text" value type should be used to identify values that + contain human-readable text. The character set and language in which + the text is represented is controlled by the charset content-header + and the language type parameter and content-header. + + Examples for "text": + this is a text value + this is one value,this is another + this is a single value\, with a comma encoded + + A formatted text line break in a text value type MUST be represented + as the character sequence backslash (ASCII decimal 92) followed by a + Latin small letter n (ASCII decimal 110) or a Latin capital letter N + (ASCII decimal 78), that is "\n" or "\N". + + For example a multiple line DESCRIPTION value of: + + Mythical Manager + Hyjinx Software Division + BabsCo, Inc. + + could be represented as: + + + +Howes, et. al. Standards Track [Page 12] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + DESCRIPTION:Mythical Manager\nHyjinx Software Division\n + BabsCo\, Inc.\n + + demonstrating the \n literal formatted line break technique, the + CRLF-followed-by-space line folding technique, and the backslash + escape technique. + + "uri": The "uri" value type should be used to identify values that + are referenced by a URI (including a Content-ID URI), instead of + encoded in-line. These value references might be used if the value is + too large, or otherwise undesirable to include directly. The format + for the URI is as defined in RFC 1738. + + Examples for "uri": + http://www.foobar.com/my/picture.jpg + ldap://ldap.foobar.com/cn=babs%20jensen + + "date", "time", and "date-time": Each of these value types is based + on a subset of the definitions in ISO 8601 standard. Profiles MAY + place further restrictions on "date" and "time" values. Multiple + "date" and "time" values can be specified using the comma-separated + notation, unless restricted by a profile. + + Examples for "date": + 1985-04-12 + 1996-08-05,1996-11-11 + 19850412 + + Examples for "time": + 10:22:00 + 102200 + 10:22:00.33 + 10:22:00.33Z + 10:22:33,11:22:00 + 10:22:00-08:00 + + Examples for "date-time": + 1996-10-22T14:00:00Z + 1996-08-11T12:34:56Z + 19960811T123456Z + 1996-10-22T14:00:00Z,1996-08-11T12:34:56Z + + "boolean": The "boolean" value type is used to express boolen values. + These values are case insensitive. + + Examples: TRUE + false + True + + + +Howes, et. al. Standards Track [Page 13] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + "integer": The "integer" value type is used to express signed + integers in decimal format. If sign is not specified, the value is + assumed positive "+". Multiple "integer" values can be specified + using the comma-separated notation, unless restricted by a profile. + + Examples: 1234567890 + -1234556790 + +1234556790,432109876 + + "float": The "float" value type is used to express real numbers. If + sign is not specified, the value is assumed positive "+". Multiple + "float" values can be specified using the comma-separated notation, + unless restricted by a profile. + + Examples: 20.30 + 1000000.0000001 + 1.333,3.14 + +5.9. Applications which use this media type + + Applications which use this media type: Various + +5.10. Additional information + + Additional information: None + +5.11. Person & email address to contact for further information + + Tim Howes + Netscape Communications Corp. + 501 East Middlefield Rd. + Mountain View, CA 94041 + USA + howes@netscape.com + +1 415 937 3419 + +5.12. Intended usage + + Intended usage: COMMON + + + + + + + + + + + + +Howes, et. al. Standards Track [Page 14] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +5.13. Author/Change controller + + Tim Howes + Netscape Communications Corp. + 501 East Middlefield Rd. + Mountain View, CA 94041 + USA + howes@netscape.com + +1 415 937 3419 + + Mark Smith + Netscape Communications Corp. + 501 East Middlefield Rd. + Mountain View, CA 94041 + USA + mcs@netscape.com + +1 415 937 3477 + + Frank Dawson + Lotus Development Corporation + 6544 Battleford Drive + Raleigh, NC 27613-3502 + USA + frank_dawson@lotus.com + +1-919-676-9515 + +6. Predefined Types + + The following types are generally useful regardless of the profile + being carried and are defined below using the text/directory MIME + type registration template defined in Section 11.1 of this document. + These types MAY be included in any profile, unless explicitly + forbidden in the profile definition. + +6.1. SOURCE Type Definition + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME type SOURCE + + Type name: SOURCE + + Type purpose: To identify the source of directory information + contained in the content type. + + Type encoding: 8bit + + Type valuetype: uri + + + + +Howes, et. al. Standards Track [Page 15] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Type special notes: The SOURCE type is used to provide the means by + which applications knowledgable in the given directory service + protocol can obtain additional or more up-to-date information from + the directory service. It contains a URI as defined in [RFC-1738] + and/or other information referencing the directory entity or entities + to which the information pertains. When directory information is + available from more than one source, the sending entity can pick what + it considers to be the best source, or multiple SOURCE types can be + included. The interpretation of the value for a SOURCE type can + depend on the setting of the CONTEXT type parameter. The value of the + CONTEXT type parameter MUST be compatible with the value of the uri + prefix. + + Type example: + SOURCE;CONTEXT=LDAP:ldap://ldap.host/cn=Babs%20Jensen, + %20o=Babsco,%20c=US + +6.2. NAME Type Definition + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME type NAME + + Type name: NAME + + Type purpose: To identify the displayable name of the directory + entity to which information in the content type pertains. + + Type encoding: 8bit + + Type valuetype: text + + Type special notes: The NAME type is used to convey the display name + of the entity to which the directory information pertains. + + Type example: + NAME:Babs Jensen's Contact Information + +6.3. PROFILE Type Definition + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME type PROFILE + + Type name: PROFILE + + Type purpose: To identify the type of directory entity to which + information in the content type pertains. + + Type encoding: 8bit + + + +Howes, et. al. Standards Track [Page 16] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Type valuetype: A profile name, registered as described in Section 9 + of this document or bilaterally agreed upon as described in Section + 5. + + Type special notes: The PROFILE type is used to convey the type of + the entity to which the directory information in the rest of the body + part pertains. It should be the same as the "profile" header + parameter, if present. + + Type example: + PROFILE:vCard + +6.4. BEGIN Type Definition + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME type BEGIN + + Type name: BEGIN + + Type purpose: To denote the beginning of a syntactic entity within a + text/directory content-type. + + Type encoding: 8bit + + Type valuetype: text, containing a profile name, registered as + described in Section 9 of this document or bilaterally-agreed upon as + described in Section 5. + + Type special notes: The BEGIN type is used in conjunction with the + END type to delimit a profile containing a related set of properties + within an text/directory content-type. This construct can be used + instead of or in addition to wrapping separate sets of information + inside additional MIME headers. It is provided for applications that + wish to define content that can contain multiple entities within the + same text/directory content-type or to define content that can be + identifiable outside of a MIME environment. + + Type example: + BEGIN:VCARD + +6.5. END Type Definition + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME type END + + Type name: END + + + + + +Howes, et. al. Standards Track [Page 17] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Type purpose: To denote the end of a syntactic entity within a + text/directory content-type. + + Type encoding: 8bit + + Type valuetype: text, containing a profile name, registered as + described in Section 9 of this document or bilaterally-agreed upon as + described in Section 5. + + Type special notes: The END type is used in conjunction with the + BEGIN type to delimit a profile containing a related set of + properties within an text/directory content-type. This construct can + be used instead of or in addition to wrapping separate sets of + information inside additional MIME headers. It is provided for + applications that wish to define content that can contain multiple + entities within the same text/directory content-type or to define + content that can be identifiable outside of a MIME environment. + + Type example: + END: VCARD + +7. Use of the multipart/related Content-Type + + The multipart/related Content-Type can be used to hold directory + information comprised of both text and non-text information or + directory information that already has a natural MIME representation. + The root body part within the multipart/related body part is + specified as defined in [RFC-2112] by a "start" parameter, or it is + the first body part in the absence of such a parameter. The root + body part must have a Content-Type of "text/directory". This part + holds inline information and makes reference to subsequent body parts + holding additional text or non-text directory information via their + Content-ID URIs as explained in Section 5. + + The body parts referred to do not have to be in any particular order, + except as noted above for the root body part. + +8. Examples + + The following examples are for illustrative purposes only and are not + part of the definition. + + + + + + + + + + +Howes, et. al. Standards Track [Page 18] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +8.1. Example 1 + + The first example illustrates simple use of the text/directory + Content-Type. Note that no "profile" parameter is given, so an + application may not know what kind of directory entity the + information applies to. Note also the use of both hypothetical + official and bilaterally agreed upon types. + + From: Whomever@wherever.com + To: Someone@somewhere.com + Subject: whatever + MIME-Version: 1.0 + Message-ID: + Content-Type: text/directory + Content-ID: + + cn:Babs Jensen + cn:Barbara J Jensen + sn:Jensen + email:babs@umich.edu + phone:+1 313 747-4454 + x-id:1234567890 + +8.2. Example 2 + + The next example illustrates the use of the Quoted-Printable transfer + encoding defined in [RFC 2045] to include non-ASCII character in some + of the information returned, and the use of the optional "name" and + "source" types. It also illustrates the use of an "encoding" type + parameter to encode a certificate value in "b". A "vCard" profile + [MIME- VCARD] is used for the example. + +Content-Type: text/directory; + charset="iso-8859-1"; + profile="vCard" +Content-ID: +Content-Transfer-Encoding: Quoted-Printable + +begin:VCARD +source:ldap://cn=bjorn%20Jensen, o=university%20of%20Michigan, c=US +name:Bjorn Jensen +fn:Bj=F8rn Jensen +n:Jensen;Bj=F8rn +email;type=internet:bjorn@umich.edu +tel;type=work,voice,msg:+1 313 747-4454 +key;type=x509;encoding=B:dGhpcyBjb3VsZCBiZSAKbXkgY2VydGlmaWNhdGUK +end:VCARD + + + + +Howes, et. al. Standards Track [Page 19] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +8.3. Example 3 + + The next example illustrates the use of multi-valued type parameters, + the "language" type parameter, the "value" type parameter, folding of + long lines, the \n encoding for formatted lines, attribute grouping, + and the inline "b" encoding. A "vCard" profile [MIME-VCARD] is used + for the example. + +Content-Type: text/directory; profile="vcard"; charset=iso-8859-1 +Content-ID: +Content-Transfer-Encoding: Quoted-Printable + +begin:vcard +source:ldap://cn=Meister%20Berger,o=Universitaet%20Goerlitz,c=DE +name:Meister Berger +fn:Meister Berger +n:Berger;Meister +bday;value=date:1963-09-21 +o:Universit=E6t G=F6rlitz +title:Mayor +title;language=de;value=text:Burgermeister +note:The Mayor of the great city of + Goerlitz in the great country of Germany. +email;internet:mb@goerlitz.de +home.tel;type=fax,voice,msg:+49 3581 123456 +home.label:Hufenshlagel 1234\n + 02828 Goerlitz\n + Deutschland +key;type=X509;encoding=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQ + AwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zI + ENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0ZW1zMRwwGgYDVQQD + ExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNjE5NDc1OVoXDTk3MTIwMzE5NDc + 1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYDVQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW + 9ucyBDb3JwLjEYMBYGA1UEAxMPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBF + hJob3dlc0BuZXRzY2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqG + SIb3DQEBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2dXc + oX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MBEGCWCGSAGG+E + IBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau+hUMbsQukjANBgkqhkiG9 + w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIPmx93HGp0Kgyx1jIVMyNgsemeAwBM+M + SlhMfcpbTrONwNjZYW8vJDSoi//yrZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8V + UMk1U7jt8LYpo4YULU7UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ== +end:vcard + + + + + + + + + +Howes, et. al. Standards Track [Page 20] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +8.4. Example 4 + + The final example illustrates the use of the multipart/related + Content-Type to include non-textual directory data via the "uri" + encoding to refer to other body parts within the same message, or to + external values. Note that no "profile" parameter is given, so an + application may not know what kind of directory entity the + information applies to. Note also the use of both hypothetical + official and bilaterally agreed upon types. + +Content-Type: multipart/related; + boundary=woof; + type="text/directory"; + start="" +Content-ID: + +--woof +Content-Type: text/directory; charset="iso-8859-1" +Content-ID: +Content-Transfer-Encoding: Quoted-Printable + +source:ldap://cn=Bjorn%20Jensen,o=University%20of%20Michigan,c=US +cn:Bj=F8rn Jensen +sn:Jensen +email:bjorn@umich.edu +image;value=uri:cid:id6@host.com +image;value=uri;format=jpeg:ftp://some.host/some/path.jpg +sound;value=uri:cid:id7@host.com +phone:+1 313 747-4454 + +--woof +Content-Type: image/jpeg +Content-ID: + +<...image data...> + +--woof +Content-Type: message/external-body; + name="myvoice.au"; + site="myhost.com"; + access-type=ANON-FTP; + directory="pub/myname"; + mode="image" + +Content-Type: audio/basic +Content-ID: + +--woof-- + + + +Howes, et. al. Standards Track [Page 21] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +9. Registration of new profiles + + This section defines procedures by which new profiles are registered + with the IANA and made available to the Internet community. Note that + non-IANA profiles can be used by bilateral agreement, provided the + associated profile names follow the "X-" convention defined above. + + The procedures defined here are designed to allow public comment and + review of new profiles, while posing only a small impediment to the + definition of new profiles. + + Registration of a new profile is accomplished by the following steps. + +9.1. Define the profile + + A profile is defined by completing the following template. + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME profile XXX + + Profile name: + + Profile purpose: + + Profile types: + + Profile special notes (optional): + + Intended usage: (one of COMMON, LIMITED USE or OBSOLETE) + + The explanation of what goes in each field in the template follows. + + Profile name: The name of the profile as it will appear in the + text/directory MIME Content-Type "profile" header parameter, or the + predefined "profile" type name. + + Profile purpose: The purpose of the profile (e.g., to represent + information about people, printers, documents, etc.). Give a short + but clear description. + + Profile types: The list of types associated with the profile. This + list of types is to be expected but not required in the profile, + unless otherwise noted in the profile definition. Other types not + mentioned in the profile definition MAY also be present. Note that + any new types referenced by the profile MUST be defined separately as + described in Section 10. + + + + + +Howes, et. al. Standards Track [Page 22] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Profile special notes: Any special notes about the profile, how it is + to be used, etc. This section of the template can also be used to + define an ordering on the types that appear in the Content-Type, if + such an ordering is required. + +9.2. Post the profile definition + + The profile description must be posted to the new profile discussion + list, ietf-mime-direct@imc.org + +9.3. Allow a comment period + + Discussion on the new profile must be allowed to take place on the + list for a minimum of two weeks. Consensus must be reached on the + profile before proceeding to step 4. + +9.4. Submit the profile for approval + + Once the two-week comment period has elapsed, and the proposer is + convinced consensus has been reached on the profile, the registration + application should be submitted to the Profile Reviewer for approval. + The Profile Reviewer is appointed by the Application Area Directors + and can either accept or reject the profile registration. An accepted + registration is passed on by the Profile Reviewer to the IANA for + inclusion in the official IANA profile registry. The registration may + be rejected for any of the following reasons. 1) Insufficient comment + period; 2) Consensus not reached; 3) Technical deficiencies raised on + the list or elsewhere have not been addressed. The Profile Reviewer's + decision to reject a profile can be appealed by the proposer to the + IESG, or the objections raised can be addressed by the proposer and + the profile resubmitted. + +10. Profile Change Control + + Existing profiles can be changed using the same process by which they + were registered. + + Define the change + + Post the change + + Allow a comment period + + Submit the changed profile for approval + + + + + + + +Howes, et. al. Standards Track [Page 23] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Note that the original author or any other interested party can + propose a change to an existing profile, but that such changes should + only be proposed when there are serious omissions or errors in the + published specification. The Profile Reviewer can object to a change + if it is not backwards compatible, but is not required to do so. + + Profile definitions can never be deleted from the IANA registry, but + profiles which are no longer believed to be useful can be declared + OBSOLETE by a change to their "intended use" field. + +11. Registration of new types + + This section defines procedures by which new types are registered + with the IANA. Note that non-IANA types can be used by bilateral + agreement, provided the associated types names follow the "X-" + convention defined above. + + The procedures defined here are designed to allow public comment and + review of new types, while posing only a small impediment to the + definition of new types. + + Registration of a new type is accomplished by the following steps. + +11.1. Define the type + + A type is defined by completing the following template. + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME type XXX + + Type name: + + Type purpose: + + Type encoding: + + Type valuetype: + + Type special notes (optional): + + Intended usage: (one of COMMON, LIMITED USE or OBSOLETE) + + The meaning of each field in the template is as follows. + + Type name: The name of the type, as it will appear in the body of an + text/directory MIME Content-Type "type: value" line to the left of + the colon ":". + + + + +Howes, et. al. Standards Track [Page 24] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Type purpose: The purpose of the type (e.g., to represent a name, + postal address, IP address, etc.). Give a short but clear + description. + + Type encoding: The default encoding a value of the type must have in + the body of a text/directory MIME Content-Type. + + Type valuetype: The format a value of the type must have in the body + of a text/directory MIME Content-Type. This description must be + precise and must not violate the general encoding rules defined in + section 5 of this document. + + Type special notes: Any special notes about the type, how it is to be + used, etc. + +11.2. Post the type definition + + The type description must be posted to the new type discussion list, + ietf-mime-direct@imc.org + +11.3. Allow a comment period + + Discussion on the new type must be allowed to take place on the list + for a minimum of two weeks. Consensus must be reached on the type + before proceeding to step 4. + +11.4. Submit the type for approval + + Once the two-week comment period has elapsed, and the proposer is + convinced consensus has been reached on the type, the registration + application should be submitted to the Profile Reviewer for approval. + The Profile Reviewer is appointed by the Application Area Directors + and can either accept or reject the type registration. An accepted + registration is passed on by the Profile Reviewer to the IANA for + inclusion in the official IANA profile registry. The registration can + be rejected for any of the following reasons. 1) Insufficient comment + period; 2) Consensus not reached; 3) Technical deficiencies raised on + the list or elsewhere have not been addressed. The Profile + Reviewer's decision to reject a type can be appealed by the proposer + to the IESG, or the objections raised can be addressed by the + proposer and the type resubmitted. + + + + + + + + + + +Howes, et. al. Standards Track [Page 25] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +12. Type Change Control + + Existing types can be changed using the same process by which they + were registered. + + Define the change + + Post the change + + Allow a comment period + + Submit the type for approval + + Note that the original author or any other interested party can + propose a change to an existing type, but that such changes should + only be proposed when there are serious omissions or errors in the + published specification. The Profile Reviewer can object to a change + if it is not backwards compatible, but is not required to do so. + + Type definitions can never be deleted from the IANA registry, but + types which are nolonger believed to be useful can be declared + OBSOLETE by a change to their "intended use" field. + +13. Registration of new parameters + + This section defines procedures by which new parameters are + registered with the IANA and made available to the Internet + community. Note that non-IANA parameters can be used by bilateral + agreement, provided the associated parameters names follow the "X-" + convention defined above. + + The procedures defined here are designed to allow public comment and + review of new parameters, while posing only a small impediment to the + definition of new parameters. + + Registration of a new parameter is accomplished by the following + steps. + +13.1. Define the parameter + + A parameter is defined by completing the following template. + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME type parameter XXX + + Parameter name: + + Parameter purpose: + + + +Howes, et. al. Standards Track [Page 26] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + Parameter values: + + Parameter special notes (optional): + + Intended usage: (one of COMMON, LIMITED USE or OBSOLETE) + + The explanation of what goes in each field in the template follows. + + Parameter name: The name of the parameter as it will appear in the + text/directory MIME Content-Type. + + Parameter purpose: The purpose of the parameter (e.g., to represent + the format of an image, type of a phone number, etc.). Give a short + but clear description. If defining a general paramemter like "format" + or "type" keep in mind that other applications might wish to extend + its use. + + Parameter values: The list or description of values associated with + the parameter. + + Parameter special notes: Any special notes about the parameter, how + it is to be used, etc. + +13.2. Post the parameter definition + + The parameter description must be posted to the new parameter + discussion list, ietf-mime-direct@imc.org + +13.3. Allow a comment period + + Discussion on the new parameter must be allowed to take place on the + list for a minimum of two weeks. Consensus must be reached on the + parameter before proceeding to step 4. + +13.4. Submit the parameter for approval + + Once the two-week comment period has elapsed, and the proposer is + convinced consensus has been reached on the parameter, the + registration application should be submitted to the Profile Reviewer + for approval. The Profile Reviewer is appointed by the Application + Area Directors and can either accept or reject the parameter + registration. An accepted registration is passed on by the Profile + Reviewer to the IANA for inclusion in the official IANA parameter + registry. The registration can be rejected for any of the following + reasons. 1) Insufficient comment period; 2) Consensus not reached; 3) + Technical deficiencies raised on the list or elsewhere have not been + addressed. The Profile Reviewer's decision to reject a profile can be + appealed by the proposer to the IESG, or the objections raised can be + + + +Howes, et. al. Standards Track [Page 27] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + addressed by the proposer and the parameter registration resubmitted. + +14. Parameter Change Control + + Existing parameters can be changed using the same process by which + they were registered. + + Define the change + + Post the change + + Allow a comment period + + Submit the parameter for approval + + Note that the original author or any other interested party can + propose a change to an existing parameter, but that such changes + should only be proposed when there are serious omissions or errors in + the published specification. The Profile Reviewer can object to a + change if it is not backwards compatible, but is not required to do + so. + + Parameter definitions can never be deleted from the IANA registry, + but parameters which are nolonger believed to be useful can be + declared OBSOLETE by a change to their "intended use" field. + +15. Registration of new value types + + This section defines procedures by which new value types are + registered with the IANA and made available to the Internet + community. Note that non-IANA value types can be used by bilateral + agreement, provided the associated value types names follow the "X-" + convention defined above. + + The procedures defined here are designed to allow public comment and + review of new value types, while posing only a small impediment to + the definition of new value types. + + Registration of a new value types is accomplished by the following + steps. + +15.1. Define the value type + + A value type is defined by completing the following template. + + To: ietf-mime-direct@imc.org + Subject: Registration of text/directory MIME value type XXX + + + + +Howes, et. al. Standards Track [Page 28] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + value type name: + + value type purpose: + + value type format: + + value type special notes (optional): + + Intended usage: (one of COMMON, LIMITED USE or OBSOLETE) + + The explanation of what goes in each field in the template follows. + + value type name: The name of the value type as it will appear in the + text/directory MIME Content-Type. + + value type purpose: The purpose of the value type. Give a short but + clear description. + + value type format: The definition of the format for the value, + usually using ABNF grammar. + + value type special notes: Any special notes about the value type, how + it is to be used, etc. + +15.2. Post the value type definition + + The value type description must be posted to the new value type + discussion list, ietf-mime-direct@imc.org + +15.3. Allow a comment period + + Discussion on the new value type must be allowed to take place on the + list for a minimum of two weeks. Consensus must be reached before + proceeding to step 4. + +15.4. Submit the value type for approval + + Once the two-week comment period has elapsed, and the proposer is + convinced consensus has been reached on the value type, the + registration application should be submitted to the Profile Reviewer + for approval. The Profile Reviewer is appointed by the Application + Area Directors and can either accept or reject the value type + registration. An accepted registration should be passed on by the + Profile Reviewer to the IANA for inclusion in the official IANA value + type registry. The registration can be rejected for any of the + following reasons. 1) Insufficient comment period; 2) Consensus not + reached; 3) Technical deficiencies raised on the list or elsewhere + have not been addressed. The Profile Reviewer's decision to reject a + + + +Howes, et. al. Standards Track [Page 29] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + profile can be appealed by the proposer to the IESG, or the + objections raised can be addressed by the proposer and the value type + registration resubmitted. + +16. Security Considerations + + Internet mail is subject to many well known security attacks, + including monitoring, replay, and forgery. Care should be taken by + any directory service in allowing information to leave the scope of + the service itself, where any access controls can no longer be + guaranteed. Applications should also take care to display directory + data in a "safe" environment (e.g., PostScript-valued types). + +17. Acknowledgements + + The registration procedures defined here were shamelessly lifted from + the MIME registration RFC. + + The many valuable comments contributed by members of the IETF ASID + working group are gratefully acknowledged, as are the contributions + of the Versit Consortium. Chris Newman was especially helpful in + navigating the intricacies of ABNF lore. + +18. References + + [RFC-1777] Yeong, W., Howes, T., and S. Kille, "Lightweight + Directory Access Protocol", RFC 1777, March 1995. + + [RFC-1778] Howes, T., Kille, S., Yeong, W., and C. Robbins, "The + String Representation of Standard Attribute Syntaxes", + RFC 1778, March 1995. + + [RFC-822] Crocker, D., "Standard for the Format of ARPA Internet + Text Messages", STD 11, RFC 822, August 1982. + + [RFC-2045] Borenstein, N., and N. Freed, "Multipurpose Internet + Mail Extensions (MIME) Part One: Format of Internet + Message Bodies", RFC 2045, November 1996. + + [RFC-2046] Moore, K., "Multipurpose Internet Mail Extensions (MIME) + Part Two: Media Types", RFC 2046, November 1996. + + [RFC-2048] Freed, N., Klensin, J., and J. Postel, "Multipurpose + Internet Mail Extensions (MIME) Part Four: Registration + Procedures", RFC 2048, November 1996. + + [RFC-1766] Alvestrand, H., "Tags for the Identification of + Languages", RFC 1766, March 1995. + + + +Howes, et. al. Standards Track [Page 30] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + + [RFC-2112] Levinson, E., "The MIME Multipart/Related Content-type", + RFC 2112, March 1997. + + [X500] "Information Processing Systems - Open Systems + Interconnection - The Directory: Overview of Concepts, + Models and Services", ISO/IEC JTC 1/SC21, International + Standard 9594-1, 1988. + + [RFC-1835] Deutsch, P., Schoultz, R., Faltstrom, P., and C. Weider, + "Architecture of the WHOIS++ service", RFC 1835, August + 1995. + + [RFC-1738] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform + Resource Locators (URL)", RFC 1738, December 1994. + + [MIME-VCARD] Dawson, F., and T. Howes, "VCard MIME Directory + Profile", RFC 2426, September 1998. + + [VCARD] Internet Mail Consortium, "vCard - The Electronic + Business Card", Version 2.1, + http://www.imc.com/pdi/vcard-21.txt, September, 1996. + + [RFC-2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC-2234] Crocker, D., and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", RFC 2234, November 1997. + + + + + + + + + + + + + + + + + + + + + + + + +Howes, et. al. Standards Track [Page 31] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +19. Authors' Addresses + + Tim Howes + Netscape Communications Corp. + 501 East Middlefield Rd. + Mountain View, CA 94041 + USA + + Phone: +1.415.937.3419 + EMail: howes@netscape.com + + + Mark Smith + Netscape Communications Corp. + 501 East Middlefield Rd. + Mountain View, CA 94041 + USA + + Phone: +1.415.937.3477 + EMail: mcs@netscape.com + + + Frank Dawson + Lotus Development Corporation + 6544 Battleford Drive + Raleigh, NC 27613 + USA + + Phone: +1-919-676-9515 + EMail: frank_dawson@lotus.com + + + + + + + + + + + + + + + + + + + + + +Howes, et. al. Standards Track [Page 32] + +RFC 2425 MIME Content-Type for Directory Information September 1998 + + +20. Full Copyright Statement + + Copyright (C) The Internet Society (1998). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Howes, et. al. Standards Track [Page 33] + diff --git a/dav/SabreDAV/docs/rfc2426.txt b/dav/SabreDAV/docs/rfc2426.txt new file mode 100644 index 000000000..a393a67c9 --- /dev/null +++ b/dav/SabreDAV/docs/rfc2426.txt @@ -0,0 +1,2355 @@ + + + + + + +Network Working Group F. Dawson +Request for Comments: 2426 Lotus Development Corporation +Category: Standards Track T. Howes + Netscape Communications + September 1998 + + + vCard MIME Directory Profile + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1998). All Rights Reserved. + +Abstract + + This memo defines the profile of the MIME Content-Type [MIME-DIR] for + directory information for a white-pages person object, based on a + vCard electronic business card. The profile definition is independent + of any particular directory service or protocol. The profile is + defined for representing and exchanging a variety of information + about an individual (e.g., formatted and structured name and delivery + addresses, email address, multiple telephone numbers, photograph, + logo, audio clips, etc.). The directory information used by this + profile is based on the attributes for the person object defined in + the X.520 and X.521 directory services recommendations. The profile + also provides the method for including a [VCARD] representation of a + white-pages directory entry within the MIME Content-Type defined by + the [MIME-DIR] document. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" and "OPTIONAL" in this + document are to be interpreted as described in [RFC 2119]. + + + + + + + + + + + +Dawson & Howes Standards Track [Page 1] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +Table of Contents + + Overview.........................................................3 + 1. THE VCARD MIME DIRECTORY PROFILE REGISTRATION.................4 + 2. MIME DIRECTORY FEATURES.......................................5 + 2.1 PREDEFINED TYPE USAGE ......................................5 + 2.1.1 BEGIN and END Type ......................................5 + 2.1.2 NAME Type ...............................................5 + 2.1.3 PROFILE Type ............................................5 + 2.1.4 SOURCE Type .............................................5 + 2.2 PREDEFINED TYPE PARAMETER USAGE ............................6 + 2.3 PREDEFINED VALUE TYPE USAGE ................................6 + 2.4 EXTENSIONS TO THE PREDEFINED VALUE TYPES ...................6 + 2.4.1 BINARY ..................................................6 + 2.4.2 VCARD ...................................................6 + 2.4.3 PHONE-NUMBER ............................................7 + 2.4.4 UTC-OFFSET ..............................................7 + 2.5 STRUCTURED TYPE VALUES .....................................7 + 2.6 LINE DELIMITING AND FOLDING ................................8 + 3. VCARD PROFILE FEATURES........................................8 + 3.1 IDENTIFICATION TYPES .......................................8 + 3.1.1 FN Type Definition ......................................8 + 3.1.2 N Type Definition .......................................9 + 3.1.3 NICKNAME Type Definition ................................9 + 3.1.4 PHOTO Type Definition ..................................10 + 3.1.5 BDAY Type Definition ...................................11 + 3.2 DELIVERY ADDRESSING TYPES .................................11 + 3.2.1 ADR Type Definition ....................................11 + 3.2.2 LABEL Type Definition ..................................13 + 3.3 TELECOMMUNICATIONS ADDRESSING TYPES .......................13 + 3.3.1 TEL Type Definition ....................................14 + 3.3.2 EMAIL Type Definition ..................................15 + 3.3.3 MAILER Type Definition .................................15 + 3.4 GEOGRAPHICAL TYPES ........................................16 + 3.4.1 TZ Type Definition .....................................16 + 3.4.2 GEO Type Definition ....................................16 + 3.5 ORGANIZATIONAL TYPES ......................................17 + 3.5.1 TITLE Type Definition ..................................17 + 3.5.2 ROLE Type Definition ...................................18 + 3.5.3 LOGO Type Definition ...................................18 + 3.5.4 AGENT Type Definition ..................................19 + 3.5.5 ORG Type Definition ....................................20 + 3.6 EXPLANATORY TYPES .........................................20 + 3.6.1 CATEGORIES Type Definition .............................20 + 3.6.2 NOTE Type Definition ...................................21 + 3.6.3 PRODID Type Definition .................................21 + 3.6.4 REV Type Definition ....................................22 + 3.6.5 SORT-STRING Type Definition ............................22 + + + +Dawson & Howes Standards Track [Page 2] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + 3.6.6 SOUND Type Definition ..................................23 + 3.6.7 UID Type Definition ....................................24 + 3.6.8 URL Type Definition ....................................25 + 3.6.9 VERSION Type Definition ................................25 + 3.7 SECURITY TYPES ............................................25 + 3.7.1 CLASS Type Definition ..................................26 + 3.7.2 KEY Type Definition ....................................26 + 3.8 EXTENDED TYPES ............................................27 + 4. FORMAL GRAMMAR...............................................27 + 5. DIFFERENCES FROM VCARD V2.1..................................37 + 6. ACKNOWLEDGEMENTS.............................................39 + 7. AUTHORS' ADDRESSES...........................................39 + 8. SECURITY CONSIDERATIONS......................................39 + 9. REFERENCES...................................................40 + 10. FULL COPYRIGHT STATEMENT....................................42 + +Overview + + The [MIME-DIR] document defines a MIME Content-Type for holding + different kinds of directory information. The directory information + can be based on any of a number of directory schemas. This document + defines a [MIME-DIR] usage profile for conveying directory + information based on one such schema; that of the white-pages type of + person object. + + The schema is based on the attributes for the person object defined + in the X.520 and X.521 directory services recommendations. The schema + has augmented the basic attributes defined in the X.500 series + recommendation in order to provide for an electronic representation + of the information commonly found on a paper business card. This + schema was first defined in the [VCARD] document. Hence, this [MIME- + DIR] profile is referred to as the vCard MIME Directory Profile. + + A directory entry based on this usage profile can include traditional + directory, white-pages information such as the distinguished name + used to uniquely identify the entry, a formatted representation of + the name used for user-interface or presentation purposes, both the + structured and presentation form of the delivery address, various + telephone numbers and organizational information associated with the + entry. In addition, traditional paper business card information such + as an image of an organizational logo or identify photograph can be + included in this person object. + + The vCard MIME Directory Profile also provides support for + representing other important information about the person associated + with the directory entry. For instance, the date of birth of the + person; an audio clip describing the pronunciation of the name + associated with the directory entry, or some other application of the + + + +Dawson & Howes Standards Track [Page 3] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + digital sound; longitude and latitude geo-positioning information + related to the person associated with the directory entry; date and + time that the directory information was last updated; annotations + often written on a business card; Uniform Resource Locators (URL) for + a website; public key information. The profile also provides support + for non-standard extensions to the schema. This provides the + flexibility for implementations to augment the current capabilities + of the profile in a standardized way. More information about this + electronic business card format can be found in [VCARD]. + +1. The vCard Mime Directory Profile Registration + + This profile is identified by the following [MIME-DIR] registration + template information. Subsequent sections define the profile + definition. + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME profile VCARD + + Profile name: VCARD + + Profile purpose: To hold person object or white-pages type of + directory information. The person schema captured in the directory + entries is that commonly found in an electronic business card. + + Predefined MIME Directory value specifications used: uri, date, + date-time, float + + New value specifications: This profile places further constraints on + the [MIME-DIR] text value specification. In addition, it adds a + binary, phone-number, utc-offset and vcard value specifications. + + Predefined MIME Directory types used: SOURCE, NAME, PROFILE, BEGIN, + END. + + Predefined MIME Directory parameters used: ENCODING, VALUE, CHARSET, + LANGUAGE, CONTEXT. + + New types: FN, N, NICKNAME, PHOTO, BDAY, ADR, LABEL, TEL, EMAIL, + MAILER, TZ, GEO, TITLE, ROLE, LOGO, AGENT, ORG, CATEGORIES, NOTE, + PRODID, REV, SORT-STRING, SOUND, URL, UID, VERSION, CLASS, KEY + + New parameters: TYPE + + Profile special notes: The vCard object MUST contain the FN, N and + VERSION types. The type-grouping feature of [MIME-DIR] is supported + by this profile to group related vCard properties about a directory + + + +Dawson & Howes Standards Track [Page 4] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + entry. For example, vCard properties describing WORK or HOME related + characteristics can be grouped with a unique group label. + + The profile permits the use of non-standard types (i.e., those + identified with the prefix string "X-") as a flexible method for + implementations to extend the functionality currently defined within + this profile. + +2. MIME Directory Features + + The vCard MIME Directory Profile makes use of many of the features + defined by [MIME-DIR]. The following sections either clarify or + extend the content-type definition of [MIME-DIR]. + +2.1 Predefined Type Usage + + The vCard MIME Directory Profile uses the following predefined types + from [MIME-DIR]. + +2.1.1 BEGIN and END Type + + The content entity MUST begin with the BEGIN type with a value of + "VCARD". The content entity MUST end with the END type with a value + of "VCARD". + +2.1.2 NAME Type + + If the NAME type is present, then its value is the displayable, + presentation text associated with the source for the vCard, as + specified in the SOURCE type. + +2.1.3 PROFILE Type + + If the PROFILE type is present, then its value MUST be "VCARD". + +2.1.4 SOURCE Type + + If the SOURCE type is present, then its value provides information + how to find the source for the vCard. + + + + + + + + + + + + +Dawson & Howes Standards Track [Page 5] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +2.2 Predefined Type Parameter Usage + + The vCard MIME Directory Profile uses the following predefined type + parameters as defined by [MIME-DIR]. + + - LANGUAGE + + - ENCODING + + - VALUE + +2.3 Predefined VALUE Type Usage + + The predefined data type values specified in [MIME-DIR] MUST NOT be + repeated in COMMA separated value lists except within the N, + NICKNAME, ADR and CATEGORIES value types. + + The text value type defined in [MIME-DIR] is further restricted such + that any SEMI-COLON character (ASCII decimal 59) in the value MUST be + escaped with the BACKSLASH character (ASCII decimal 92). + +2.4 Extensions To The Predefined VALUE Types + + The predefined data type values specified in [MIME-DIR] have been + extended by the vCard profile to include a number of value types that + are specific to this profile. + +2.4.1 BINARY + + The "binary" value type specifies that the type value is inline, + encoded binary data. This value type can be specified in the PHOTO, + LOGO, SOUND, and KEY types. + + If inline encoded binary data is specified, the ENCODING type + parameter MUST be used to specify the encoding format. The binary + data MUST be encoded using the "B" encoding format. Long lines of + encoded binary data SHOULD BE folded to 75 characters using the + folding method defined in [MIME-DIR]. + + The value type is defined by the following notation: + + binary = + +2.4.2 VCARD + + The "vcard" value type specifies that the type value is another + vCard. This value type can be specified in the AGENT type. The value + type is defined by this specification. Since each of the type + + + +Dawson & Howes Standards Track [Page 6] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + declarations with in the vcard value type are being specified within + a text value themselves, they MUST be terminated with the backslash + escape sequence "\n" or "\N", instead of the normal newline character + sequence CRLF. In addition, any COMMA character (ASCII decimal 44), + SEMI-COLON character (ASCII decimal 59) and COLON character (ASCII + decimal 58) MUST be escaped with the BACKSLASH character (ASCII + decimal 92). For example, with the AGENT type a value would be + specified as: + + AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n + TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n + ET:jfriday@host.com\nEND:VCARD\n + +2.4.3 PHONE-NUMBER + + The "phone-number" value type specifies that the type value is a + telephone number. This value type can be specified in the TEL type. + The value type is a text value that has the special semantics of a + telephone number as defined in [CCITT E.163] and [CCITT X.121]. + +2.4.4 UTC-OFFSET + + The "utc-offset" value type specifies that the type value is a signed + offset from UTC. This value type can be specified in the TZ type. + + The value type is an offset from Coordinated Universal Time (UTC). It + is specified as a positive or negative difference in units of hours + and minutes (e.g., +hh:mm). The time is specified as a 24-hour clock. + Hour values are from 00 to 23, and minute values are from 00 to 59. + Hour and minutes are 2-digits with high order zeroes required to + maintain digit count. The extended format for ISO 8601 UTC offsets + MUST be used. The extended format makes use of a colon character as a + separator of the hour and minute text fields. + + The value is defined by the following notation: + + time-hour = 2DIGIT ;00-23 + time-minute = 2DIGIT ;00-59 + utc-offset = ("+" / "-") time-hour ":" time-minute + +2.5 Structured Type Values + + Compound type values are delimited by a field delimiter, specified by + the SEMI-COLON character (ASCII decimal 59). A SEMI-COLON in a + component of a compound property value MUST be escaped with a + BACKSLASH character (ASCII decimal 92). + + + + + +Dawson & Howes Standards Track [Page 7] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Lists of values are delimited by a list delimiter, specified by the + COMMA character (ASCII decimal 44). A COMMA character in a value MUST + be escaped with a BACKSLASH character (ASCII decimal 92). + + This profile supports the type grouping mechanism defined in [MIME- + DIR]. Grouping of related types is a useful technique to communicate + common semantics concerning the properties of a vCard. + +2.6 Line Delimiting and Folding + + This profile supports the same line delimiting and folding methods + defined in [MIME-DIR]. Specifically, when parsing a content line, + folded lines must first be unfolded according to the unfolding + procedure described in [MIME-DIR]. After generating a content line, + lines longer than 75 characters SHOULD be folded according to the + folding procedure described in [MIME DIR]. + + Folding is done after any content encoding of a type value. Unfolding + is done before any decoding of a type value in a content line. + +3. vCard Profile Features + + The vCard MIME Directory Profile Type contains directory information, + typically pertaining to a single directory entry. The information is + described using an attribute schema that is tailored for capturing + personal contact information. The vCard can include attributes that + describe identification, delivery addressing, telecommunications + addressing, geographical, organizational, general explanatory and + security and access information about the particular object + associated with the vCard. + +3.1 Identification Types + + These types are used in the vCard profile to capture information + associated with the identification and naming of the person or + resource associated with the vCard. + +3.1.1 FN Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type FN + + Type name:FN + + Type purpose: To specify the formatted text corresponding to the name + of the object the vCard represents. + + + + +Dawson & Howes Standards Track [Page 8] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: This type is based on the semantics of the X.520 + Common Name attribute. The property MUST be present in the vCard + object. + + Type example: + + FN:Mr. John Q. Public\, Esq. + +3.1.2 N Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type N + + Type name: N + + Type purpose: To specify the components of the name of the object the + vCard represents. + + Type encoding: 8bit + + Type value: A single structured text value. Each component can have + multiple values. + + Type special note: The structured type value corresponds, in + sequence, to the Family Name, Given Name, Additional Names, Honorific + Prefixes, and Honorific Suffixes. The text components are separated + by the SEMI-COLON character (ASCII decimal 59). Individual text + components can include multiple text values (e.g., multiple + Additional Names) separated by the COMMA character (ASCII decimal + 44). This type is based on the semantics of the X.520 individual name + attributes. The property MUST be present in the vCard object. + + Type example: + + N:Public;John;Quinlan;Mr.;Esq. + + N:Stevenson;John;Philip,Paul;Dr.;Jr.,M.D.,A.C.P. + +3.1.3 NICKNAME Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type NICKNAME + + + +Dawson & Howes Standards Track [Page 9] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type name: NICKNAME + + Type purpose: To specify the text corresponding to the nickname of + the object the vCard represents. + + Type encoding: 8bit + + Type value: One or more text values separated by a COMMA character + (ASCII decimal 44). + + Type special note: The nickname is the descriptive name given instead + of or in addition to the one belonging to a person, place, or thing. + It can also be used to specify a familiar form of a proper name + specified by the FN or N types. + + Type example: + + NICKNAME:Robbie + + NICKNAME:Jim,Jimmie + +3.1.4 PHOTO Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type PHOTO + + Type name: PHOTO + + Type purpose: To specify an image or photograph information that + annotates some aspect of the object the vCard represents. + + Type encoding: The encoding MUST be reset to "b" using the ENCODING + parameter in order to specify inline, encoded binary data. If the + value is referenced by a URI value, then the default encoding of 8bit + is used and no explicit ENCODING parameter is needed. + + Type value: A single value. The default is binary value. It can also + be reset to uri value. The uri value can be used to specify a value + outside of this MIME entity. + + Type special notes: The type can include the type parameter "TYPE" to + specify the graphic image format type. The TYPE parameter values MUST + be one of the IANA registered image formats or a non-standard image + format. + + + + + + +Dawson & Howes Standards Track [Page 10] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type example: + + PHOTO;VALUE=uri:http://www.abc.com/pub/photos + /jqpublic.gif + + + PHOTO;ENCODING=b;TYPE=JPEG:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN + AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm + ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 + <...remainder of "B" encoded binary data...> + +3.1.5 BDAY Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type BDAY + + Type name: BDAY + + Type purpose: To specify the birth date of the object the vCard + represents. + + Type encoding: 8bit + + Type value: The default is a single date value. It can also be reset + to a single date-time value. + + Type examples: + + BDAY:1996-04-15 + + BDAY:1953-10-15T23:10:00Z + + BDAY:1987-09-27T08:30:00-06:00 + +3.2 Delivery Addressing Types + + These types are concerned with information related to the delivery + addressing or label for the vCard object. + +3.2.1 ADR Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type ADR + + Type name: ADR + + + + +Dawson & Howes Standards Track [Page 11] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type purpose: To specify the components of the delivery address for + the vCard object. + + Type encoding: 8bit + + Type value: A single structured text value, separated by the + SEMI-COLON character (ASCII decimal 59). + + Type special notes: The structured type value consists of a sequence + of address components. The component values MUST be specified in + their corresponding position. The structured type value corresponds, + in sequence, to the post office box; the extended address; the street + address; the locality (e.g., city); the region (e.g., state or + province); the postal code; the country name. When a component value + is missing, the associated component separator MUST still be + specified. + + The text components are separated by the SEMI-COLON character (ASCII + decimal 59). Where it makes semantic sense, individual text + components can include multiple text values (e.g., a "street" + component with multiple lines) separated by the COMMA character + (ASCII decimal 44). + + The type can include the type parameter "TYPE" to specify the + delivery address type. The TYPE parameter values can include "dom" to + indicate a domestic delivery address; "intl" to indicate an + international delivery address; "postal" to indicate a postal + delivery address; "parcel" to indicate a parcel delivery address; + "home" to indicate a delivery address for a residence; "work" to + indicate delivery address for a place of work; and "pref" to indicate + the preferred delivery address when more than one address is + specified. These type parameter values can be specified as a + parameter list (i.e., "TYPE=dom;TYPE=postal") or as a value list + (i.e., "TYPE=dom,postal"). This type is based on semantics of the + X.520 geographical and postal addressing attributes. The default is + "TYPE=intl,postal,parcel,work". The default can be overridden to some + other set of values by specifying one or more alternate values. For + example, the default can be reset to "TYPE=dom,postal,work,home" to + specify a domestic delivery address for postal delivery to a + residence that is also used for work. + + Type example: In this example the post office box and the extended + address are absent. + + ADR;TYPE=dom,home,postal,parcel:;;123 Main + Street;Any Town;CA;91921-1234 + + + + + +Dawson & Howes Standards Track [Page 12] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +3.2.2 LABEL Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type LABEL + + Type name: LABEL + + Type purpose: To specify the formatted text corresponding to delivery + address of the object the vCard represents. + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: The type value is formatted text that can be used + to present a delivery address label for the vCard object. The type + can include the type parameter "TYPE" to specify delivery label type. + The TYPE parameter values can include "dom" to indicate a domestic + delivery label; "intl" to indicate an international delivery label; + "postal" to indicate a postal delivery label; "parcel" to indicate a + parcel delivery label; "home" to indicate a delivery label for a + residence; "work" to indicate delivery label for a place of work; and + "pref" to indicate the preferred delivery label when more than one + label is specified. These type parameter values can be specified as a + parameter list (i.e., "TYPE=dom;TYPE=postal") or as a value list + (i.e., "TYPE=dom,postal"). This type is based on semantics of the + X.520 geographical and postal addressing attributes. The default is + "TYPE=intl,postal,parcel,work". The default can be overridden to some + other set of values by specifying one or more alternate values. For + example, the default can be reset to "TYPE=intl,post,parcel,home" to + specify an international delivery label for both postal and parcel + delivery to a residential location. + + Type example: A multi-line address label. + + LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\, Esq.\n + Mail Drop: TNE QB\n123 Main Street\nAny Town\, CA 91921-1234 + \nU.S.A. + +3.3 Telecommunications Addressing Types + + These types are concerned with information associated with the + telecommunications addressing of the object the vCard represents. + + + + + + + +Dawson & Howes Standards Track [Page 13] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +3.3.1 TEL Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type TEL + + Type name: TEL + + Type purpose: To specify the telephone number for telephony + communication with the object the vCard represents. + + Type encoding: 8bit + + Type value: A single phone-number value. + + Type special notes: The value of this type is specified in a + canonical form in order to specify an unambiguous representation of + the globally unique telephone endpoint. This type is based on the + X.500 Telephone Number attribute. + + The type can include the type parameter "TYPE" to specify intended + use for the telephone number. The TYPE parameter values can include: + "home" to indicate a telephone number associated with a residence, + "msg" to indicate the telephone number has voice messaging support, + "work" to indicate a telephone number associated with a place of + work, "pref" to indicate a preferred-use telephone number, "voice" to + indicate a voice telephone number, "fax" to indicate a facsimile + telephone number, "cell" to indicate a cellular telephone number, + "video" to indicate a video conferencing telephone number, "pager" to + indicate a paging device telephone number, "bbs" to indicate a + bulletin board system telephone number, "modem" to indicate a MODEM + connected telephone number, "car" to indicate a car-phone telephone + number, "isdn" to indicate an ISDN service telephone number, "pcs" to + indicate a personal communication services telephone number. The + default type is "voice". These type parameter values can be specified + as a parameter list (i.e., "TYPE=work;TYPE=voice") or as a value list + (i.e., "TYPE=work,voice"). The default can be overridden to another + set of values by specifying one or more alternate values. For + example, the default TYPE of "voice" can be reset to a WORK and HOME, + VOICE and FAX telephone number by the value list + "TYPE=work,home,voice,fax". + + Type example: + + TEL;TYPE=work,voice,pref,msg:+1-213-555-1234 + + + + + + +Dawson & Howes Standards Track [Page 14] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +3.3.2 EMAIL Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type EMAIL + + Type name: EMAIL + + Type purpose: To specify the electronic mail address for + communication with the object the vCard represents. + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: The type can include the type parameter "TYPE" to + specify the format or preference of the electronic mail address. The + TYPE parameter values can include: "internet" to indicate an Internet + addressing type, "x400" to indicate a X.400 addressing type or "pref" + to indicate a preferred-use email address when more than one is + specified. Another IANA registered address type can also be + specified. The default email type is "internet". A non-standard value + can also be specified. + + Type example: + + EMAIL;TYPE=internet:jqpublic@xyz.dom1.com + + EMAIL;TYPE=internet:jdoe@isp.net + + EMAIL;TYPE=internet,pref:jane_doe@abc.com + +3.3.3 MAILER Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type MAILER + + Type name: MAILER + + Type purpose: To specify the type of electronic mail software that is + used by the individual associated with the vCard. + + Type encoding: 8bit + + Type value: A single text value. + + + + + +Dawson & Howes Standards Track [Page 15] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type special notes: This information can provide assistance to a + correspondent regarding the type of data representation which can be + used, and how they can be packaged. This property is based on the + private MIME type X-Mailer that is generally implemented by MIME user + agent products. + + Type example: + + MAILER:PigeonMail 2.1 + +3.4 Geographical Types + + These types are concerned with information associated with + geographical positions or regions associated with the object the + vCard represents. + +3.4.1 TZ Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type TZ + + Type name: TZ + + Type purpose: To specify information related to the time zone of the + object the vCard represents. + + Type encoding: 8bit + + Type value: The default is a single utc-offset value. It can also be + reset to a single text value. + + Type special notes: The type value consists of a single value. + + Type examples: + + TZ:-05:00 + + TZ;VALUE=text:-05:00; EST; Raleigh/North America + ;This example has a single value, not a structure text value. + +3.4.2 GEO Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type GEO + + Type name: GEO + + + +Dawson & Howes Standards Track [Page 16] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type purpose: To specify information related to the global + positioning of the object the vCard represents. + + Type encoding: 8bit + + Type value: A single structured value consisting of two float values + separated by the SEMI-COLON character (ASCII decimal 59). + + Type special notes: This type specifies information related to the + global position of the object associated with the vCard. The value + specifies latitude and longitude, in that order (i.e., "LAT LON" + ordering). The longitude represents the location east and west of the + prime meridian as a positive or negative real number, respectively. + The latitude represents the location north and south of the equator + as a positive or negative real number, respectively. The longitude + and latitude values MUST be specified as decimal degrees and should + be specified to six decimal places. This will allow for granularity + within a meter of the geographical position. The text components are + separated by the SEMI-COLON character (ASCII decimal 59). The simple + formula for converting degrees-minutes-seconds into decimal degrees + is: + + decimal = degrees + minutes/60 + seconds/3600. + + Type example: + + GEO:37.386013;-122.082932 + +3.5 Organizational Types + + These types are concerned with information associated with + characteristics of the organization or organizational units of the + object the vCard represents. + +3.5.1 TITLE Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type TITLE + + Type name: TITLE + + Type purpose: To specify the job title, functional position or + function of the object the vCard represents. + + Type encoding: 8bit + + Type value: A single text value. + + + +Dawson & Howes Standards Track [Page 17] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type special notes: This type is based on the X.520 Title attribute. + + Type example: + + TITLE:Director\, Research and Development + +3.5.2 ROLE Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type ROLE + + Type name: ROLE + + Type purpose: To specify information concerning the role, occupation, + or business category of the object the vCard represents. + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: This type is based on the X.520 Business Category + explanatory attribute. This property is included as an organizational + type to avoid confusion with the semantics of the TITLE type and + incorrect usage of that type when the semantics of this type is + intended. + + Type example: + + ROLE:Programmer + +3.5.3 LOGO Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type LOGO + + Type name: LOGO + + Type purpose: To specify a graphic image of a logo associated with + the object the vCard represents. + + Type encoding: The encoding MUST be reset to "b" using the ENCODING + parameter in order to specify inline, encoded binary data. If the + value is referenced by a URI value, then the default encoding of 8bit + is used and no explicit ENCODING parameter is needed. + + + + + +Dawson & Howes Standards Track [Page 18] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type value: A single value. The default is binary value. It can also + be reset to uri value. The uri value can be used to specify a value + outside of this MIME entity. + + Type special notes: The type can include the type parameter "TYPE" to + specify the graphic image format type. The TYPE parameter values MUST + be one of the IANA registered image formats or a non-standard image + format. + + Type example: + + LOGO;VALUE=uri:http://www.abc.com/pub/logos/abccorp.jpg + + LOGO;ENCODING=b;TYPE=JPEG:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN + AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm + ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 + <...the remainder of "B" encoded binary data...> + +3.5.4 AGENT Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type AGENT + + Type name: AGENT + + Type purpose: To specify information about another person who will + act on behalf of the individual or resource associated with the + vCard. + + Type encoding: 8-bit + + Type value: The default is a single vcard value. It can also be reset + to either a single text or uri value. The text value can be used to + specify textual information. The uri value can be used to specify + information outside of this MIME entity. + + Type special notes: This type typically is used to specify an area + administrator, assistant, or secretary for the individual associated + with the vCard. A key characteristic of the Agent type is that it + represents somebody or something that is separately addressable. + + Type example: + + AGENT;VALUE=uri: + CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com + + + + + +Dawson & Howes Standards Track [Page 19] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + AGENT:BEGIN:VCARD\nFN:Susan Thomas\nTEL:+1-919-555- + 1234\nEMAIL\;INTERNET:sthomas@host.com\nEND:VCARD\n + +3.5.5 ORG Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type ORG + + Type name: ORG + + Type purpose: To specify the organizational name and units associated + with the vCard. + + Type encoding: 8bit + + Type value: A single structured text value consisting of components + separated the SEMI-COLON character (ASCII decimal 59). + + Type special notes: The type is based on the X.520 Organization Name + and Organization Unit attributes. The type value is a structured type + consisting of the organization name, followed by one or more levels + of organizational unit names. + + Type example: A type value consisting of an organizational name, + organizational unit #1 name and organizational unit #2 name. + + ORG:ABC\, Inc.;North American Division;Marketing + +3.6 Explanatory Types + + These types are concerned with additional explanations, such as that + related to informational notes or revisions specific to the vCard. + +3.6.1 CATEGORIES Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type CATEGORIES + + Type name: CATEGORIES + + Type purpose: To specify application category information about the + vCard. + + Type encoding: 8bit + + + + + +Dawson & Howes Standards Track [Page 20] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type value: One or more text values separated by a COMMA character + (ASCII decimal 44). + + Type example: + + CATEGORIES:TRAVEL AGENT + + CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY + +3.6.2 NOTE Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type NOTE + + Type name: NOTE + + Type purpose: To specify supplemental information or a comment that + is associated with the vCard. + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: The type is based on the X.520 Description + attribute. + + Type example: + + NOTE:This fax number is operational 0800 to 1715 + EST\, Mon-Fri. + +3.6.3 PRODID Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type PRODID + + Type name: PRODID + + Type purpose: To specify the identifier for the product that created + the vCard object. + + Type encoding: 8-bit + + Type value: A single text value. + + + + + +Dawson & Howes Standards Track [Page 21] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type special notes: Implementations SHOULD use a method such as that + specified for Formal Public Identifiers in ISO 9070 to assure that + the text value is unique. + + Type example: + + PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN + +3.6.4 REV Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type REV + + Type name: REV + + Type purpose: To specify revision information about the current + vCard. + + Type encoding: 8-bit + + Type value: The default is a single date-time value. Can also be + reset to a single date value. + + Type special notes: The value distinguishes the current revision of + the information in this vCard for other renditions of the + information. + + Type example: + + REV:1995-10-31T22:27:10Z + + REV:1997-11-15 + +3.6.5 SORT-STRING Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type SORT-STRING + + Type Name: SORT-STRING + + Type purpose: To specify the family name or given name text to be + used for national-language-specific sorting of the FN and N types. + + Type encoding: 8bit + + Type value: A single text value. + + + +Dawson & Howes Standards Track [Page 22] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type special notes: The sort string is used to provide family name or + given name text that is to be used in locale- or national-language- + specific sorting of the formatted name and structured name types. + Without this information, sorting algorithms could incorrectly sort + this vCard within a sequence of sorted vCards. When this type is + present in a vCard, then this family name or given name value is used + for sorting the vCard. + + Type examples: For the case of family name sorting, the following + examples define common sort string usage with the FN and N types. + + FN:Rene van der Harten + N:van der Harten;Rene;J.;Sir;R.D.O.N. + SORT-STRING:Harten + + FN:Robert Pau Shou Chang + N:Pau;Shou Chang;Robert + SORT-STRING:Pau + + FN:Osamu Koura + N:Koura;Osamu + SORT-STRING:Koura + + FN:Oscar del Pozo + N:del Pozo Triscon;Oscar + SORT-STRING:Pozo + + FN:Chistine d'Aboville + N:d'Aboville;Christine + SORT-STRING:Aboville + +3.6.6 SOUND Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type SOUND + + Type name: SOUND + + Type purpose: To specify a digital sound content information that + annotates some aspect of the vCard. By default this type is used to + specify the proper pronunciation of the name type value of the vCard. + + Type encoding: The encoding MUST be reset to "b" using the ENCODING + parameter in order to specify inline, encoded binary data. If the + value is referenced by a URI value, then the default encoding of 8bit + is used and no explicit ENCODING parameter is needed. + + + + +Dawson & Howes Standards Track [Page 23] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type value: A single value. The default is binary value. It can also + be reset to uri value. The uri value can be used to specify a value + outside of this MIME entity. + + Type special notes: The type can include the type parameter "TYPE" to + specify the audio format type. The TYPE parameter values MUST be one + of the IANA registered audio formats or a non-standard audio format. + + Type example: + + SOUND;TYPE=BASIC;VALUE=uri:CID:JOHNQPUBLIC.part8. + 19960229T080000.xyzMail@host1.com + + SOUND;TYPE=BASIC;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN + AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm + ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 + <...the remainder of "B" encoded binary data...> + +3.6.7 UID Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type UID + + Type name: UID + + Type purpose: To specify a value that represents a globally unique + identifier corresponding to the individual or resource associated + with the vCard. + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: The type is used to uniquely identify the object + that the vCard represents. + + The type can include the type parameter "TYPE" to specify the format + of the identifier. The TYPE parameter value should be an IANA + registered identifier format. The value can also be a non-standard + format. + + Type example: + + UID:19950401-080045-40000F192713-0052 + + + + + + +Dawson & Howes Standards Track [Page 24] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +3.6.8 URL Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type URL + + Type name: URL + + Type purpose: To specify a uniform resource locator associated with + the object that the vCard refers to. + + Type encoding: 8bit + + Type value: A single uri value. + + Type example: + + URL:http://www.swbyps.restaurant.french/~chezchic.html + +3.6.9 VERSION Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type VERSION + + Type name: VERSION + + Type purpose: To specify the version of the vCard specification used + to format this vCard. + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: The property MUST be present in the vCard object. + The value MUST be "3.0" if the vCard corresponds to this + specification. + + Type example: + + VERSION:3.0 + +3.7 Security Types + + These types are concerned with the security of communication pathways + or access to the vCard. + + + + + +Dawson & Howes Standards Track [Page 25] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +3.7.1 CLASS Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type CLASS + + Type name: CLASS + + Type purpose: To specify the access classification for a vCard + object. + + Type encoding: 8bit + + Type value: A single text value. + + Type special notes: An access classification is only one component of + the general security model for a directory service. The + classification attribute provides a method of capturing the intent of + the owner for general access to information described by the vCard + object. + + Type examples: + + CLASS:PUBLIC + + CLASS:PRIVATE + + CLASS:CONFIDENTIAL + +3.7.2 KEY Type Definition + + To: ietf-mime-directory@imc.org + + Subject: Registration of text/directory MIME type KEY + + Type name: KEY + + Type purpose: To specify a public key or authentication certificate + associated with the object that the vCard represents. + + Type encoding: The encoding MUST be reset to "b" using the ENCODING + parameter in order to specify inline, encoded binary data. If the + value is a text value, then the default encoding of 8bit is used and + no explicit ENCODING parameter is needed. + + Type value: A single value. The default is binary. It can also be + reset to text value. The text value can be used to specify a text + key. + + + +Dawson & Howes Standards Track [Page 26] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + Type special notes: The type can also include the type parameter TYPE + to specify the public key or authentication certificate format. The + parameter type should specify an IANA registered public key or + authentication certificate format. The parameter type can also + specify a non-standard format. + + Type example: + + KEY;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQA + wdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYX + Rpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 + ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNj + E5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYD + VQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAx + MPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRz + Y2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQ + EBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2 + dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MB + EGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau + +hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIP + mx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//y + rZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7 + UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ== + +3.8 Extended Types + + The types defined by this document can be extended with private types + using the non-standard, private values mechanism defined in [RFC + 2045]. Non-standard, private types with a name starting with "X-" may + be defined bilaterally between two cooperating agents without outside + registration or standardization. + +4. Formal Grammar + + The following formal grammar is provided to assist developers in + building parsers for the vCard. + + This syntax is written according to the form described in RFC 2234, + but it references just this small subset of RFC 2234 literals: + + ;******************************************* + ; Commonly Used Literal Definition + ;******************************************* + + ALPHA = %x41-5A / %x61-7A + ; Latin Capital Letter A-Latin Capital Letter Z / + ; Latin Small Letter a-Latin Small Letter z + + + + +Dawson & Howes Standards Track [Page 27] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + CHAR = %x01-7F + ; Any C0 Controls and Basic Latin, excluding NULL from + ; Code Charts, pages 7-6 through 7-9 in [UNICODE] + + CR = %x0D + ; Carriage Return + + LF = %0A + ; Line Feed + + CRLF = CR LF + ; Internet standard newline + + ;CTL = %x00-1F / %x7F + ; Controls. Not used, but referenced in comments. + + DIGIT = %x30-39 + ; Digit Zero-Digit Nine + + DQUOTE = %x22 + ; Quotation Mark + + HTAB = %x09 + ; Horizontal Tabulation + + SP = %x20 + ; space + + VCHAR = %x21-7E + ; Visible (printing) characters + + WSP = SP / HTAB + ; White Space + + ;******************************************* + ; Basic vCard Definition + ;******************************************* + + vcard_entity = 1*(vcard) + + vcard = [group "."] "BEGIN" ":" "VCARD" 1*CRLF + 1*(contentline) + ;A vCard object MUST include the VERSION, FN and N types. + [group "."] "END" ":" "VCARD" 1*CRLF + + contentline = [group "."] name *(";" param ) ":" value CRLF + ; When parsing a content line, folded lines must first + ; be unfolded according to the unfolding procedure + + + +Dawson & Howes Standards Track [Page 28] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + ; described above. When generating a content line, lines + ; longer than 75 characters SHOULD be folded according to + ; the folding procedure described in [MIME DIR]. + + group = 1*(ALPHA / DIGIT / "-") + + name = iana-token / x-name + ; Parsing of the param and value is + ; based on the "name" or type identifier + ; as defined in ABNF sections below + + iana-token = 1*(ALPHA / DIGIT / "-") + ; vCard type or parameter identifier registered with IANA + + x-name = "X-" 1*(ALPHA / DIGIT / "-") + ; Reserved for non-standard use + + param = param-name "=" param-value *("," param-value) + + param-name = iana-token / x-name + + param-value = ptext / quoted-string + + ptext = *SAFE-CHAR + + value = *VALUE-CHAR + + quoted-string = DQUOTE QSAFE-CHAR DQUOTE + + NON-ASCII = %x80-FF + ; Use is restricted by CHARSET parameter + ; on outer MIME object (UTF-8 preferred) + + QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-ASCII + ; Any character except CTLs, DQUOTE + + SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-ASCII + ; Any character except CTLs, DQUOTE, ";", ":", "," + + VALUE-CHAR = WSP / VCHAR / NON-ASCII + ; Any textual character + + ;******************************************* + ; vCard Type Definition + ; + ; Provides type-specific definitions for how the + ; "value" and "param" are defined. + ;******************************************* + + + +Dawson & Howes Standards Track [Page 29] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + ;For name="NAME" + param = "" + ; No parameters allowed + + value = text-value + + ;For name="PROFILE" + param = "" + ; No parameters allowed + + value = text-value + ; Value MUST be the case insensitive value "VCARD + + ;For name="SOURCE" + param = source-param + ; No parameters allowed + + value = uri + + source-param = ("VALUE" "=" "uri") + / ("CONTEXT" "=" "word") + ; Parameter value specifies the protocol context + ; for the uri value. + / (x-name "=" *SAFE-CHAR) + + ;For name="FN" + ;This type MUST be included in a vCard object. + param = text-param + ; Text parameters allowed + + value = text-value + + ;For name="N" + ;This type MUST be included in a vCard object. + + param = text-param + ; Text parameters allowed + + value = n-value + + n-value = 0*4(text-value *("," text-value) ";") + text-value *("," text-value) + ; Family; Given; Middle; Prefix; Suffix. + ; Example: Public;John;Quincy,Adams;Reverend Dr. III + + ;For name="NICKNAME" + param = text-param + ; Text parameters allowed + + + +Dawson & Howes Standards Track [Page 30] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + value = text-list + + ;For name="PHOTO" + param = img-inline-param + ; Only image parameters allowed + + param =/ img-refer-param + ; Only image parameters allowed + + value = img-inline-value + ; Value and parameter MUST match + + value =/ img-refer-value + ; Value and parameter MUST match + + ;For name="BDAY" + param = ("VALUE" "=" "date") + ; Only value parameter allowed + + param =/ ("VALUE" "=" "date-time") + ; Only value parameter allowed + + value = date-value + ; Value MUST match value type + + value =/ date-time-value + ; Value MUST match value type + + ;For name="ADR" + param = adr-param / text-param + ; Only adr and text parameters allowed + + value = adr-value + + ;For name="LABEL" + param = adr-param / text-param + ; Only adr and text parameters allowed + + value = text-value + + ;For name="TEL" + param = tel-param + ; Only tel parameters allowed + + value = phone-number-value + + tel-param = "TYPE" "=" tel-type *("," tel-type) + + + + +Dawson & Howes Standards Track [Page 31] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + tel-type = "HOME" / "WORK" / "PREF" / "VOICE" / "FAX" / "MSG" + / "CELL" / "PAGER" / "BBS" / "MODEM" / "CAR" / "ISDN" + / "VIDEO" / "PCS" / iana-token / x-name + ; Values are case insensitive + + ;For name="EMAIL" + param = email-param + ; Only email parameters allowed + + value = text-value + + email-param = "TYPE" "=" email-type ["," "PREF"] + ; Value is case insensitive + + email-type = "INTERNET" / "X400" / iana-token / "X-" word + ; Values are case insensitive + + ;For name="MAILER" + param = text-param + ; Only text parameters allowed + + value = text-value + + ;For name="TZ" + param = "" + ; No parameters allowed + + value = utc-offset-value + + ;For name="GEO" + param = "" + ; No parameters allowed + + value = float-value ";" float-value + + ;For name="TITLE" + param = text-param + ; Only text parameters allowed + + value = text-value + + ;For name="ROLE" + param = text-param + ; Only text parameters allowed + + value = text-value + + ;For name="LOGO" + + + +Dawson & Howes Standards Track [Page 32] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + param = img-inline-param / img-refer-param + ; Only image parameters allowed + + value = img-inline-value / img-refer-value + ; Value and parameter MUST match + + ;For name="AGENT" + param = agent-inline-param + + param =/ agent-refer-param + + value = agent-inline-value + ; Value and parameter MUST match + + value =/ agent-refer-value + ; Value and parameter MUST match + + agent-inline-param = "" + ; No parameters allowed + + agent-refer-param = "VALUE" "=" "uri" + ; Only value parameter allowed + + agent-inline-value = text-value + ; Value MUST be a valid vCard object + + agent-refer-value = uri + ; URI MUST refer to image content of given type + + ;For name="ORG" + + param = text-param + ; Only text parameters allowed + + value = org-value + + org-value = *(text-value ";") text-value + ; First is Organization Name, remainder are Organization Units. + + ;For name="CATEGORIES" + param = text-param + ; Only text parameters allowed + + value = text-list + + ;For name="NOTE" + param = text-param + ; Only text parameters allowed + + + +Dawson & Howes Standards Track [Page 33] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + value = text-value + + ;For name="PRODID" + param = "" + ; No parameters allowed + + value = text-value + + ;For name="REV" + param = ["VALUE" =" "date-time"] + ; Only value parameters allowed. Values are case insensitive. + + param =/ "VALUE" =" "date" + ; Only value parameters allowed. Values are case insensitive. + + value = date-time-value + + value =/ date-value + + ;For name="SORT-STRING" + param = text-param + ; Only text parameters allowed + + value = text-value + + ;For name="SOUND" + param = snd-inline-param + ; Only sound parameters allowed + + param =/ snd-refer-param + ; Only sound parameters allowed + + value = snd-line-value + ; Value MUST match value type + + value =/ snd-refer-value + ; Value MUST match value type + + snd-inline-value = binary-value CRLF + ; Value MUST be "b" encoded audio content + + snd-inline-param = ("VALUE" "=" "binary"]) + / ("ENCODING" "=" "b") + / ("TYPE" "=" *SAFE-CHAR) + ; Value MUST be an IANA registered audio type + + snd-refer-value = uri + ; URI MUST refer to audio content of given type + + + +Dawson & Howes Standards Track [Page 34] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + snd-refer-param = ("VALUE" "=" "uri") + / ("TYPE" "=" word) + ; Value MUST be an IANA registered audio type + + ;For name="UID" + param = "" + ; No parameters allowed + + value = text-value + + ;For name="URL" + param = "" + ; No parameters allowed + + value = uri + + ;For name="VERSION" + ;This type MUST be included in a vCard object. + param = "" + ; No parameters allowed + + value = text-value + ; Value MUST be "3.0" + + ;For name="CLASS" + param = "" + ; No parameters allowed + + value = "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" + / iana-token / x-name + ; Value are case insensitive + + ;For name="KEY" + param = key-txt-param + ; Only value and type parameters allowed + + param =/ key-bin-param + ; Only value and type parameters allowed + + value = text-value + + value =/ binary-value + + key-txt-param = "TYPE" "=" keytype + + key-bin-param = ("TYPE" "=" keytype) + / ("ENCODING" "=" "b") + ; Value MUST be a "b" encoded key or certificate + + + +Dawson & Howes Standards Track [Page 35] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + keytype = "X509" / "PGP" / iana-token / x-name + ; Values are case insensitive + + ;For name="X-" non-standard type + param = text-param / (x-name "=" param-value) + ; Only text or non-standard parameters allowed + + value = text-value + + ;******************************************* + ; vCard Commonly Used Parameter Definition + ;******************************************* + + text-param = ("VALUE" "=" "ptext") + / ("LANGUAGE" "=" langval) + / (x-name "=" param-value) + + langval = + + img-inline-value = binary-value + ;Value MUST be "b" encoded image content + + img-inline-param + + img-inline-param = ("VALUE" "=" "binary") + / ("ENCODING" "=" "b") + / ("TYPE" "=" param-value + ;TYPE value MUST be an IANA registered image type + + img-refer-value = uri + ;URI MUST refer to image content of given type + + img-refer-param = ("VALUE" "=" "uri") + / ("TYPE" "=" param-value) + ;TYPE value MUST be an IANA registered image type + + adr-param = ("TYPE" "=" adr-type *("," adr-type)) + / (text-param) + + adr-type = "dom" / "intl" / "postal" / "parcel" / "home" + / "work" / "pref" / iana-type / x-name + + adr-value = 0*6(text-value ";") text-value + ; PO Box, Extended Address, Street, Locality, Region, Postal + ; Code, Country Name + + + + + + +Dawson & Howes Standards Track [Page 36] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + ;******************************************* + ; vCard Type Value Definition + ;******************************************* + + text-value-list = 1*text-value *("," 1*text-value) + + text-value = *(SAFE-CHAR / ":" / DQUOTE / ESCAPED-CHAR) + + ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N") + ; \\ encodes \, \n or \N encodes newline + ; \; encodes ;, \, encodes , + + binary-value = + + date-value = + + time-value = + + date-time-value = + + phone-number-value = + + uri-value = + + utc-offset-value = ("+" / "-") time-hour ":" time-minute + time-hour = 2DIGIT ;00-23 + time-minute = 2DIGIT ;00-59 + +5. Differences From vCard v2.1 + + This specification has been reviewed by the IETF community. The + review process introduced a number of differences from the [VCARD] + version 2.1. These differences require that vCard objects conforming + to this specification have a different version number than a vCard + conforming to [VCARD]. The differences include the following: + + . The QUOTED-PRINTABLE inline encoding has been eliminated. + Only the "B" encoding of [RFC 2047] is an allowed value for + the ENCODING parameter. + + . The method for specifying CRLF character sequences in text + type values has been changed. The CRLF character sequence in + a text type value is specified with the backslash character + sequence "\n" or "\N". + + + + +Dawson & Howes Standards Track [Page 37] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + . Any COMMA or SEMICOLON in a text type value must be backslash + escaped. + + . VERSION value corresponding to this specification MUST be + "3.0". + + . The [MIME-DIR] predefined types of SOURCE, NAME and PROFILE + are allowed. + + . The [MIME-DIR] VALUE type parameter for value data typing is + allowed. In addition, there are extensions made to these type + values for additional value types used in this specification. + + . The [VCARD] CHARSET type parameter has been eliminated. + Character set can only be specified on the CHARSET parameter + on the Content-Type MIME header field. + + . The [VCARD] support for non-significant WSP character has + been eliminated. + + . The "TYPE=" prefix to parameter values is required. In + [VCARD] this was optional. + + . LOGO, PHOTO and SOUND multimedia formats MUST be either IANA + registered types or non-standard types. + + . Inline binary content must be "B" encoded and folded. A blank + line after the encoded binary content is no longer required. + + . TEL values can be identified as personal communication + services telephone numbers with the PCS type parameter value. + + . The CATEGORIES, CLASS, NICKNAME, PRODID and SORT-STRING types + have been added. + + . The VERSION, N and FN types MUST be specified in a vCard. + This identifies the version of the specification that the + object was formatted to. It also assures that every vCard + will include both a structured and formatted name that can be + used to identify the object. + + + + + + + + + + + +Dawson & Howes Standards Track [Page 38] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +6. Acknowledgements + + The many valuable comments contributed by members of the IETF ASID + working group are gratefully acknowledged, as are the contributions + by Roland Alden, Stephen Bartlett, Alec Dun, Patrik Faltstrom, Daniel + Gurney, Bruce Johnston, Daniel Klaussen, Pete Miller, Keith Moore, + Vinod Seraphin, Michelle Watkins. Chris Newman was especially helpful + in navigating the intricacies of ABNF lore. + +7. Authors' Addresses + + BEGIN:vCard + VERSION:3.0 + FN:Frank Dawson + ORG:Lotus Development Corporation + ADR;TYPE=WORK,POSTAL,PARCEL:;;6544 Battleford Drive + ;Raleigh;NC;27613-3502;U.S.A. + TEL;TYPE=VOICE,MSG,WORK:+1-919-676-9515 + TEL;TYPE=FAX,WORK:+1-919-676-9564 + EMAIL;TYPE=INTERNET,PREF:Frank_Dawson@Lotus.com + EMAIL;TYPE=INTERNET:fdawson@earthlink.net + URL:http://home.earthlink.net/~fdawson + END:vCard + + + BEGIN:vCard + VERSION:3.0 + FN:Tim Howes + ORG:Netscape Communications Corp. + ADR;TYPE=WORK:;;501 E. Middlefield Rd.;Mountain View; + CA; 94043;U.S.A. + TEL;TYPE=VOICE,MSG,WORK:+1-415-937-3419 + TEL;TYPE=FAX,WORK:+1-415-528-4164 + EMAIL;TYPE=INTERNET:howes@netscape.com + END:vCard + +8. Security Considerations + + vCards can carry cryptographic keys or certificates, as described in + Section 3.7.2. + + Section 3.7.1 specifies a desired security classification policy for + a particular vCard. That policy is not enforced in any way. + + The vCard objects have no inherent authentication or privacy, but can + easily be carried by any security mechanism that transfers MIME + objects with authentication or privacy. In cases where threats of + "spoofed" vCard information is a concern, the vCard SHOULD BE + + + +Dawson & Howes Standards Track [Page 39] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + transported using one of these secure mechanisms. + + The information in a vCard may become out of date. In cases where the + vitality of data is important to an originator of a vCard, the "URL" + type described in section 3.6.8 SHOULD BE specified. In addition, the + "REV" type described in section 3.6.4 can be specified to indicate + the last time that the vCard data was updated. + +9. References + + [ISO 8601] ISO 8601:1988 - Data elements and interchange formats - + Information interchange - Representation of dates and + times - The International Organization for + Standardization, June, 1988. + + [ISO 8601 TC] ISO 8601, Technical Corrigendum 1 - Data elements and + interchange formats - Information interchange - + Representation of dates and times - The International + Organization for Standardization, May, 1991. + + [ISO 9070] ISO 9070, Information Processing - SGML support + facilities - Registration Procedures for Public Text + Owner Identifiers, April, 1991. + + [CCITT E.163] Recommendation E.163 - Numbering Plan for The + International Telephone Service, CCITT Blue Book, + Fascicle II.2, pp. 128-134, November, 1988. + + [CCITT X.121] Recommendation X.121 - International Numbering Plan for + Public Data Networks, CCITT Blue Book, Fascicle VIII.3, + pp. 317-332, November, 1988. + + [CCITT X.520] Recommendation X.520 - The Directory - Selected + Attribute Types, November 1988. + + [CCITT X.521] Recommendation X.521 - The Directory - Selected Object + Classes, November 1988. + + [MIME-DIR] Howes, T., Smith, M., and F. Dawson, "A MIME Content- + Type for Directory Information", RFC 2425, September + 1998. + + [RFC 1738] Berners-Lee, T., Masinter, L., and M. McCahill, + "Uniform Resource Locators (URL)", RFC 1738, December + 1994. + + [RFC 1766] Alvestrand, H., "Tags for the Identification of + Languages", RFC 1766, March 1995. + + + +Dawson & Howes Standards Track [Page 40] + +RFC 2426 vCard MIME Directory Profile September 1998 + + + [RFC 1872] Levinson, E., "The MIME Multipart/Related Content- + type", RFC 1872, December 1995. + + [RFC 2045] Freed, N., and N. Borenstein, "Multipurpose Internet + Mail Extensions (MIME) - Part One: Format of Internet + Message Bodies", RFC 2045, November 1996. + + [RFC 2046] Freed, N., and N. Borenstein, "Multipurpose Internet + Mail Extensions (MIME) - Part Two: Media Types", RFC + 2046, November 1996. + + [RFC 2047] Moore, K., "Multipurpose Internet Mail Extensions + (MIME) - Part Three: Message Header Extensions for + Non-ASCII Text", RFC 2047, November 1996. + + [RFC 2048] Freed, N., Klensin, J., and J. Postel, "Multipurpose + Internet Mail Extensions (MIME) - Part Four: + Registration Procedures", RFC 2048, January 1997. + + [RFC 2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC 2234] Crocker, D., and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", RFC 2234, November 1997. + + [UNICODE] "The Unicode Standard - Version 2.0", The Unicode + Consortium, July 1996. + + [VCARD] Internet Mail Consortium, "vCard - The Electronic + Business Card Version 2.1", + http://www.imc.org/pdi/vcard-21.txt, September 18, + 1996. + + + + + + + + + + + + + + + + + + + +Dawson & Howes Standards Track [Page 41] + +RFC 2426 vCard MIME Directory Profile September 1998 + + +10. Full Copyright Statement + + Copyright (C) The Internet Society (1998). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Dawson & Howes Standards Track [Page 42] + diff --git a/dav/SabreDAV/docs/rfc2518.txt b/dav/SabreDAV/docs/rfc2518.txt new file mode 100644 index 000000000..81d40387b --- /dev/null +++ b/dav/SabreDAV/docs/rfc2518.txt @@ -0,0 +1,5267 @@ + + + + + + +Network Working Group Y. Goland +Request for Comments: 2518 Microsoft +Category: Standards Track E. Whitehead + UC Irvine + A. Faizi + Netscape + S. Carter + Novell + D. Jensen + Novell + February 1999 + + + HTTP Extensions for Distributed Authoring -- WEBDAV + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1999). All Rights Reserved. + +Abstract + + This document specifies a set of methods, headers, and content-types + ancillary to HTTP/1.1 for the management of resource properties, + creation and management of resource collections, namespace + manipulation, and resource locking (collision avoidance). + +Table of Contents + + ABSTRACT............................................................1 + 1 INTRODUCTION .....................................................5 + 2 NOTATIONAL CONVENTIONS ...........................................7 + 3 TERMINOLOGY ......................................................7 + 4 DATA MODEL FOR RESOURCE PROPERTIES ...............................8 + 4.1 The Resource Property Model ...................................8 + 4.2 Existing Metadata Proposals ...................................8 + 4.3 Properties and HTTP Headers ...................................9 + 4.4 Property Values ...............................................9 + 4.5 Property Names ...............................................10 + 4.6 Media Independent Links ......................................10 + 5 COLLECTIONS OF WEB RESOURCES ....................................11 + + + +Goland, et al. Standards Track [Page 1] + +RFC 2518 WEBDAV February 1999 + + + 5.1 HTTP URL Namespace Model .....................................11 + 5.2 Collection Resources .........................................11 + 5.3 Creation and Retrieval of Collection Resources ...............12 + 5.4 Source Resources and Output Resources ........................13 + 6 LOCKING .........................................................14 + 6.1 Exclusive Vs. Shared Locks ...................................14 + 6.2 Required Support .............................................16 + 6.3 Lock Tokens ..................................................16 + 6.4 opaquelocktoken Lock Token URI Scheme ........................16 + 6.4.1 Node Field Generation Without the IEEE 802 Address ........17 + 6.5 Lock Capability Discovery ....................................19 + 6.6 Active Lock Discovery ........................................19 + 6.7 Usage Considerations .........................................19 + 7 WRITE LOCK ......................................................20 + 7.1 Methods Restricted by Write Locks ............................20 + 7.2 Write Locks and Lock Tokens ..................................20 + 7.3 Write Locks and Properties ...................................20 + 7.4 Write Locks and Null Resources ...............................21 + 7.5 Write Locks and Collections ..................................21 + 7.6 Write Locks and the If Request Header ........................22 + 7.6.1 Example - Write Lock ......................................22 + 7.7 Write Locks and COPY/MOVE ....................................23 + 7.8 Refreshing Write Locks .......................................23 + 8 HTTP METHODS FOR DISTRIBUTED AUTHORING ..........................23 + 8.1 PROPFIND .....................................................24 + 8.1.1 Example - Retrieving Named Properties .....................25 + 8.1.2 Example - Using allprop to Retrieve All Properties ........26 + 8.1.3 Example - Using propname to Retrieve all Property Names ...29 + 8.2 PROPPATCH ....................................................31 + 8.2.1 Status Codes for use with 207 (Multi-Status) ..............31 + 8.2.2 Example - PROPPATCH .......................................32 + 8.3 MKCOL Method .................................................33 + 8.3.1 Request ...................................................33 + 8.3.2 Status Codes ..............................................33 + 8.3.3 Example - MKCOL ...........................................34 + 8.4 GET, HEAD for Collections ....................................34 + 8.5 POST for Collections .........................................35 + 8.6 DELETE .......................................................35 + 8.6.1 DELETE for Non-Collection Resources .......................35 + 8.6.2 DELETE for Collections ....................................36 + 8.7 PUT ..........................................................36 + 8.7.1 PUT for Non-Collection Resources ..........................36 + 8.7.2 PUT for Collections .......................................37 + 8.8 COPY Method ..................................................37 + 8.8.1 COPY for HTTP/1.1 resources ...............................37 + 8.8.2 COPY for Properties .......................................38 + 8.8.3 COPY for Collections ......................................38 + 8.8.4 COPY and the Overwrite Header .............................39 + + + +Goland, et al. Standards Track [Page 2] + +RFC 2518 WEBDAV February 1999 + + + 8.8.5 Status Codes ..............................................39 + 8.8.6 Example - COPY with Overwrite .............................40 + 8.8.7 Example - COPY with No Overwrite ..........................40 + 8.8.8 Example - COPY of a Collection ............................41 + 8.9 MOVE Method ..................................................42 + 8.9.1 MOVE for Properties .......................................42 + 8.9.2 MOVE for Collections ......................................42 + 8.9.3 MOVE and the Overwrite Header .............................43 + 8.9.4 Status Codes ..............................................43 + 8.9.5 Example - MOVE of a Non-Collection ........................44 + 8.9.6 Example - MOVE of a Collection ............................44 + 8.10 LOCK Method ..................................................45 + 8.10.1 Operation .................................................46 + 8.10.2 The Effect of Locks on Properties and Collections .........46 + 8.10.3 Locking Replicated Resources ..............................46 + 8.10.4 Depth and Locking .........................................46 + 8.10.5 Interaction with other Methods ............................47 + 8.10.6 Lock Compatibility Table ..................................47 + 8.10.7 Status Codes ..............................................48 + 8.10.8 Example - Simple Lock Request .............................48 + 8.10.9 Example - Refreshing a Write Lock .........................49 + 8.10.10 Example - Multi-Resource Lock Request ....................50 + 8.11 UNLOCK Method ................................................51 + 8.11.1 Example - UNLOCK ..........................................52 + 9 HTTP HEADERS FOR DISTRIBUTED AUTHORING ..........................52 + 9.1 DAV Header ...................................................52 + 9.2 Depth Header .................................................52 + 9.3 Destination Header ...........................................54 + 9.4 If Header ....................................................54 + 9.4.1 No-tag-list Production ....................................55 + 9.4.2 Tagged-list Production ....................................55 + 9.4.3 not Production ............................................56 + 9.4.4 Matching Function .........................................56 + 9.4.5 If Header and Non-DAV Compliant Proxies ...................57 + 9.5 Lock-Token Header ............................................57 + 9.6 Overwrite Header .............................................57 + 9.7 Status-URI Response Header ...................................57 + 9.8 Timeout Request Header .......................................58 + 10 STATUS CODE EXTENSIONS TO HTTP/1.1 ............................59 + 10.1 102 Processing ...............................................59 + 10.2 207 Multi-Status .............................................59 + 10.3 422 Unprocessable Entity .....................................60 + 10.4 423 Locked ...................................................60 + 10.5 424 Failed Dependency ........................................60 + 10.6 507 Insufficient Storage .....................................60 + 11 MULTI-STATUS RESPONSE .........................................60 + 12 XML ELEMENT DEFINITIONS .......................................61 + 12.1 activelock XML Element .......................................61 + + + +Goland, et al. Standards Track [Page 3] + +RFC 2518 WEBDAV February 1999 + + + 12.1.1 depth XML Element .........................................61 + 12.1.2 locktoken XML Element .....................................61 + 12.1.3 timeout XML Element .......................................61 + 12.2 collection XML Element .......................................62 + 12.3 href XML Element .............................................62 + 12.4 link XML Element .............................................62 + 12.4.1 dst XML Element ...........................................62 + 12.4.2 src XML Element ...........................................62 + 12.5 lockentry XML Element ........................................63 + 12.6 lockinfo XML Element .........................................63 + 12.7 lockscope XML Element ........................................63 + 12.7.1 exclusive XML Element .....................................63 + 12.7.2 shared XML Element ........................................63 + 12.8 locktype XML Element .........................................64 + 12.8.1 write XML Element .........................................64 + 12.9 multistatus XML Element ......................................64 + 12.9.1 response XML Element ......................................64 + 12.9.2 responsedescription XML Element ...........................65 + 12.10 owner XML Element ...........................................65 + 12.11 prop XML element ............................................66 + 12.12 propertybehavior XML element ................................66 + 12.12.1 keepalive XML element ....................................66 + 12.12.2 omit XML element .........................................67 + 12.13 propertyupdate XML element ..................................67 + 12.13.1 remove XML element .......................................67 + 12.13.2 set XML element ..........................................67 + 12.14 propfind XML Element ........................................68 + 12.14.1 allprop XML Element ......................................68 + 12.14.2 propname XML Element .....................................68 + 13 DAV PROPERTIES ................................................68 + 13.1 creationdate Property ........................................69 + 13.2 displayname Property .........................................69 + 13.3 getcontentlanguage Property ..................................69 + 13.4 getcontentlength Property ....................................69 + 13.5 getcontenttype Property ......................................70 + 13.6 getetag Property .............................................70 + 13.7 getlastmodified Property .....................................70 + 13.8 lockdiscovery Property .......................................71 + 13.8.1 Example - Retrieving the lockdiscovery Property ...........71 + 13.9 resourcetype Property ........................................72 + 13.10 source Property .............................................72 + 13.10.1 Example - A source Property ..............................72 + 13.11 supportedlock Property ......................................73 + 13.11.1 Example - Retrieving the supportedlock Property ..........73 + 14 INSTRUCTIONS FOR PROCESSING XML IN DAV ........................74 + 15 DAV COMPLIANCE CLASSES ........................................75 + 15.1 Class 1 ......................................................75 + 15.2 Class 2 ......................................................75 + + + +Goland, et al. Standards Track [Page 4] + +RFC 2518 WEBDAV February 1999 + + + 16 INTERNATIONALIZATION CONSIDERATIONS ...........................76 + 17 SECURITY CONSIDERATIONS .......................................77 + 17.1 Authentication of Clients ....................................77 + 17.2 Denial of Service ............................................78 + 17.3 Security through Obscurity ...................................78 + 17.4 Privacy Issues Connected to Locks ............................78 + 17.5 Privacy Issues Connected to Properties .......................79 + 17.6 Reduction of Security due to Source Link .....................79 + 17.7 Implications of XML External Entities ........................79 + 17.8 Risks Connected with Lock Tokens .............................80 + 18 IANA CONSIDERATIONS ...........................................80 + 19 INTELLECTUAL PROPERTY .........................................81 + 20 ACKNOWLEDGEMENTS ..............................................82 + 21 REFERENCES ....................................................82 + 21.1 Normative References .........................................82 + 21.2 Informational References .....................................83 + 22 AUTHORS' ADDRESSES ............................................84 + 23 APPENDICES ....................................................86 + 23.1 Appendix 1 - WebDAV Document Type Definition .................86 + 23.2 Appendix 2 - ISO 8601 Date and Time Profile ..................88 + 23.3 Appendix 3 - Notes on Processing XML Elements ................89 + 23.3.1 Notes on Empty XML Elements ...............................89 + 23.3.2 Notes on Illegal XML Processing ...........................89 + 23.4 Appendix 4 -- XML Namespaces for WebDAV ......................92 + 23.4.1 Introduction ..............................................92 + 23.4.2 Meaning of Qualified Names ................................92 + 24 FULL COPYRIGHT STATEMENT ......................................94 + + + +1 Introduction + + This document describes an extension to the HTTP/1.1 protocol that + allows clients to perform remote web content authoring operations. + This extension provides a coherent set of methods, headers, request + entity body formats, and response entity body formats that provide + operations for: + + Properties: The ability to create, remove, and query information + about Web pages, such as their authors, creation dates, etc. Also, + the ability to link pages of any media type to related pages. + + Collections: The ability to create sets of documents and to retrieve + a hierarchical membership listing (like a directory listing in a file + system). + + + + + + +Goland, et al. Standards Track [Page 5] + +RFC 2518 WEBDAV February 1999 + + + Locking: The ability to keep more than one person from working on a + document at the same time. This prevents the "lost update problem," + in which modifications are lost as first one author then another + writes changes without merging the other author's changes. + + Namespace Operations: The ability to instruct the server to copy and + move Web resources. + + Requirements and rationale for these operations are described in a + companion document, "Requirements for a Distributed Authoring and + Versioning Protocol for the World Wide Web" [RFC2291]. + + The sections below provide a detailed introduction to resource + properties (section 4), collections of resources (section 5), and + locking operations (section 6). These sections introduce the + abstractions manipulated by the WebDAV-specific HTTP methods + described in section 8, "HTTP Methods for Distributed Authoring". + + In HTTP/1.1, method parameter information was exclusively encoded in + HTTP headers. Unlike HTTP/1.1, WebDAV encodes method parameter + information either in an Extensible Markup Language (XML) [REC-XML] + request entity body, or in an HTTP header. The use of XML to encode + method parameters was motivated by the ability to add extra XML + elements to existing structures, providing extensibility; and by + XML's ability to encode information in ISO 10646 character sets, + providing internationalization support. As a rule of thumb, + parameters are encoded in XML entity bodies when they have unbounded + length, or when they may be shown to a human user and hence require + encoding in an ISO 10646 character set. Otherwise, parameters are + encoded within HTTP headers. Section 9 describes the new HTTP + headers used with WebDAV methods. + + In addition to encoding method parameters, XML is used in WebDAV to + encode the responses from methods, providing the extensibility and + internationalization advantages of XML for method output, as well as + input. + + XML elements used in this specification are defined in section 12. + + The XML namespace extension (Appendix 4) is also used in this + specification in order to allow for new XML elements to be added + without fear of colliding with other element names. + + While the status codes provided by HTTP/1.1 are sufficient to + describe most error conditions encountered by WebDAV methods, there + are some errors that do not fall neatly into the existing categories. + New status codes developed for the WebDAV methods are defined in + section 10. Since some WebDAV methods may operate over many + + + +Goland, et al. Standards Track [Page 6] + +RFC 2518 WEBDAV February 1999 + + + resources, the Multi-Status response has been introduced to return + status information for multiple resources. The Multi-Status response + is described in section 11. + + WebDAV employs the property mechanism to store information about the + current state of the resource. For example, when a lock is taken out + on a resource, a lock information property describes the current + state of the lock. Section 13 defines the properties used within the + WebDAV specification. + + Finishing off the specification are sections on what it means to be + compliant with this specification (section 15), on + internationalization support (section 16), and on security (section + 17). + +2 Notational Conventions + + Since this document describes a set of extensions to the HTTP/1.1 + protocol, the augmented BNF used herein to describe protocol elements + is exactly the same as described in section 2.1 of [RFC2068]. Since + this augmented BNF uses the basic production rules provided in + section 2.2 of [RFC2068], these rules apply to this document as well. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [RFC2119]. + +3 Terminology + + URI/URL - A Uniform Resource Identifier and Uniform Resource Locator, + respectively. These terms (and the distinction between them) are + defined in [RFC2396]. + + Collection - A resource that contains a set of URIs, termed member + URIs, which identify member resources and meets the requirements in + section 5 of this specification. + + Member URI - A URI which is a member of the set of URIs contained by + a collection. + + Internal Member URI - A Member URI that is immediately relative to + the URI of the collection (the definition of immediately relative is + given in section 5.2). + + Property - A name/value pair that contains descriptive information + about a resource. + + + + + +Goland, et al. Standards Track [Page 7] + +RFC 2518 WEBDAV February 1999 + + + Live Property - A property whose semantics and syntax are enforced by + the server. For example, the live "getcontentlength" property has + its value, the length of the entity returned by a GET request, + automatically calculated by the server. + + Dead Property - A property whose semantics and syntax are not + enforced by the server. The server only records the value of a dead + property; the client is responsible for maintaining the consistency + of the syntax and semantics of a dead property. + + Null Resource - A resource which responds with a 404 (Not Found) to + any HTTP/1.1 or DAV method except for PUT, MKCOL, OPTIONS and LOCK. + A NULL resource MUST NOT appear as a member of its parent collection. + +4 Data Model for Resource Properties + +4.1 The Resource Property Model + + Properties are pieces of data that describe the state of a resource. + Properties are data about data. + + Properties are used in distributed authoring environments to provide + for efficient discovery and management of resources. For example, a + 'subject' property might allow for the indexing of all resources by + their subject, and an 'author' property might allow for the discovery + of what authors have written which documents. + + The DAV property model consists of name/value pairs. The name of a + property identifies the property's syntax and semantics, and provides + an address by which to refer to its syntax and semantics. + + There are two categories of properties: "live" and "dead". A live + property has its syntax and semantics enforced by the server. Live + properties include cases where a) the value of a property is read- + only, maintained by the server, and b) the value of the property is + maintained by the client, but the server performs syntax checking on + submitted values. All instances of a given live property MUST comply + with the definition associated with that property name. A dead + property has its syntax and semantics enforced by the client; the + server merely records the value of the property verbatim. + +4.2 Existing Metadata Proposals + + Properties have long played an essential role in the maintenance of + large document repositories, and many current proposals contain some + notion of a property, or discuss web metadata more generally. These + include PICS [REC-PICS], PICS-NG, XML, Web Collections, and several + proposals on representing relationships within HTML. Work on PICS-NG + + + +Goland, et al. Standards Track [Page 8] + +RFC 2518 WEBDAV February 1999 + + + and Web Collections has been subsumed by the Resource Description + Framework (RDF) metadata activity of the World Wide Web Consortium. + RDF consists of a network-based data model and an XML representation + of that model. + + Some proposals come from a digital library perspective. These + include the Dublin Core [RFC2413] metadata set and the Warwick + Framework [WF], a container architecture for different metadata + schemas. The literature includes many examples of metadata, + including MARC [USMARC], a bibliographic metadata format, and a + technical report bibliographic format employed by the Dienst system + [RFC1807]. Additionally, the proceedings from the first IEEE Metadata + conference describe many community-specific metadata sets. + + Participants of the 1996 Metadata II Workshop in Warwick, UK [WF], + noted that "new metadata sets will develop as the networked + infrastructure matures" and "different communities will propose, + design, and be responsible for different types of metadata." These + observations can be corroborated by noting that many community- + specific sets of metadata already exist, and there is significant + motivation for the development of new forms of metadata as many + communities increasingly make their data available in digital form, + requiring a metadata format to assist data location and cataloging. + +4.3 Properties and HTTP Headers + + Properties already exist, in a limited sense, in HTTP message + headers. However, in distributed authoring environments a relatively + large number of properties are needed to describe the state of a + resource, and setting/returning them all through HTTP headers is + inefficient. Thus a mechanism is needed which allows a principal to + identify a set of properties in which the principal is interested and + to set or retrieve just those properties. + +4.4 Property Values + + The value of a property when expressed in XML MUST be well formed. + + XML has been chosen because it is a flexible, self-describing, + structured data format that supports rich schema definitions, and + because of its support for multiple character sets. XML's self- + describing nature allows any property's value to be extended by + adding new elements. Older clients will not break when they + encounter extensions because they will still have the data specified + in the original schema and will ignore elements they do not + understand. XML's support for multiple character sets allows any + human-readable property to be encoded and read in a character set + familiar to the user. XML's support for multiple human languages, + + + +Goland, et al. Standards Track [Page 9] + +RFC 2518 WEBDAV February 1999 + + + using the "xml:lang" attribute, handles cases where the same + character set is employed by multiple human languages. + +4.5 Property Names + + A property name is a universally unique identifier that is associated + with a schema that provides information about the syntax and + semantics of the property. + + Because a property's name is universally unique, clients can depend + upon consistent behavior for a particular property across multiple + resources, on the same and across different servers, so long as that + property is "live" on the resources in question, and the + implementation of the live property is faithful to its definition. + + The XML namespace mechanism, which is based on URIs [RFC2396], is + used to name properties because it prevents namespace collisions and + provides for varying degrees of administrative control. + + The property namespace is flat; that is, no hierarchy of properties + is explicitly recognized. Thus, if a property A and a property A/B + exist on a resource, there is no recognition of any relationship + between the two properties. It is expected that a separate + specification will eventually be produced which will address issues + relating to hierarchical properties. + + Finally, it is not possible to define the same property twice on a + single resource, as this would cause a collision in the resource's + property namespace. + +4.6 Media Independent Links + + Although HTML resources support links to other resources, the Web + needs more general support for links between resources of any media + type (media types are also known as MIME types, or content types). + WebDAV provides such links. A WebDAV link is a special type of + property value, formally defined in section 12.4, that allows typed + connections to be established between resources of any media type. + The property value consists of source and destination Uniform + Resource Identifiers (URIs); the property name identifies the link + type. + + + + + + + + + + +Goland, et al. Standards Track [Page 10] + +RFC 2518 WEBDAV February 1999 + + +5 Collections of Web Resources + + This section provides a description of a new type of Web resource, + the collection, and discusses its interactions with the HTTP URL + namespace. The purpose of a collection resource is to model + collection-like objects (e.g., file system directories) within a + server's namespace. + + All DAV compliant resources MUST support the HTTP URL namespace model + specified herein. + +5.1 HTTP URL Namespace Model + + The HTTP URL namespace is a hierarchical namespace where the + hierarchy is delimited with the "/" character. + + An HTTP URL namespace is said to be consistent if it meets the + following conditions: for every URL in the HTTP hierarchy there + exists a collection that contains that URL as an internal member. + The root, or top-level collection of the namespace under + consideration is exempt from the previous rule. + + Neither HTTP/1.1 nor WebDAV require that the entire HTTP URL + namespace be consistent. However, certain WebDAV methods are + prohibited from producing results that cause namespace + inconsistencies. + + Although implicit in [RFC2068] and [RFC2396], any resource, including + collection resources, MAY be identified by more than one URI. For + example, a resource could be identified by multiple HTTP URLs. + +5.2 Collection Resources + + A collection is a resource whose state consists of at least a list of + internal member URIs and a set of properties, but which may have + additional state such as entity bodies returned by GET. An internal + member URI MUST be immediately relative to a base URI of the + collection. That is, the internal member URI is equal to a + containing collection's URI plus an additional segment for non- + collection resources, or additional segment plus trailing slash "/" + for collection resources, where segment is defined in section 3.3 of + [RFC2396]. + + Any given internal member URI MUST only belong to the collection + once, i.e., it is illegal to have multiple instances of the same URI + in a collection. Properties defined on collections behave exactly as + do properties on non-collection resources. + + + + +Goland, et al. Standards Track [Page 11] + +RFC 2518 WEBDAV February 1999 + + + For all WebDAV compliant resources A and B, identified by URIs U and + V, for which U is immediately relative to V, B MUST be a collection + that has U as an internal member URI. So, if the resource with URL + http://foo.com/bar/blah is WebDAV compliant and if the resource with + URL http://foo.com/bar/ is WebDAV compliant then the resource with + URL http://foo.com/bar/ must be a collection and must contain URL + http://foo.com/bar/blah as an internal member. + + Collection resources MAY list the URLs of non-WebDAV compliant + children in the HTTP URL namespace hierarchy as internal members but + are not required to do so. For example, if the resource with URL + http://foo.com/bar/blah is not WebDAV compliant and the URL + http://foo.com/bar/ identifies a collection then URL + http://foo.com/bar/blah may or may not be an internal member of the + collection with URL http://foo.com/bar/. + + If a WebDAV compliant resource has no WebDAV compliant children in + the HTTP URL namespace hierarchy then the WebDAV compliant resource + is not required to be a collection. + + There is a standing convention that when a collection is referred to + by its name without a trailing slash, the trailing slash is + automatically appended. Due to this, a resource may accept a URI + without a trailing "/" to point to a collection. In this case it + SHOULD return a content-location header in the response pointing to + the URI ending with the "/". For example, if a client invokes a + method on http://foo.bar/blah (no trailing slash), the resource + http://foo.bar/blah/ (trailing slash) may respond as if the operation + were invoked on it, and should return a content-location header with + http://foo.bar/blah/ in it. In general clients SHOULD use the "/" + form of collection names. + + A resource MAY be a collection but not be WebDAV compliant. That is, + the resource may comply with all the rules set out in this + specification regarding how a collection is to behave without + necessarily supporting all methods that a WebDAV compliant resource + is required to support. In such a case the resource may return the + DAV:resourcetype property with the value DAV:collection but MUST NOT + return a DAV header containing the value "1" on an OPTIONS response. + +5.3 Creation and Retrieval of Collection Resources + + This document specifies the MKCOL method to create new collection + resources, rather than using the existing HTTP/1.1 PUT or POST + method, for the following reasons: + + + + + + +Goland, et al. Standards Track [Page 12] + +RFC 2518 WEBDAV February 1999 + + + In HTTP/1.1, the PUT method is defined to store the request body at + the location specified by the Request-URI. While a description + format for a collection can readily be constructed for use with PUT, + the implications of sending such a description to the server are + undesirable. For example, if a description of a collection that + omitted some existing resources were PUT to a server, this might be + interpreted as a command to remove those members. This would extend + PUT to perform DELETE functionality, which is undesirable since it + changes the semantics of PUT, and makes it difficult to control + DELETE functionality with an access control scheme based on methods. + + While the POST method is sufficiently open-ended that a "create a + collection" POST command could be constructed, this is undesirable + because it would be difficult to separate access control for + collection creation from other uses of POST. + + The exact definition of the behavior of GET and PUT on collections is + defined later in this document. + +5.4 Source Resources and Output Resources + + For many resources, the entity returned by a GET method exactly + matches the persistent state of the resource, for example, a GIF file + stored on a disk. For this simple case, the URI at which a resource + is accessed is identical to the URI at which the source (the + persistent state) of the resource is accessed. This is also the case + for HTML source files that are not processed by the server prior to + transmission. + + However, the server can sometimes process HTML resources before they + are transmitted as a return entity body. For example, a server- + side-include directive within an HTML file might instruct a server to + replace the directive with another value, such as the current date. + In this case, what is returned by GET (HTML plus date) differs from + the persistent state of the resource (HTML plus directive). + Typically there is no way to access the HTML resource containing the + unprocessed directive. + + Sometimes the entity returned by GET is the output of a data- + producing process that is described by one or more source resources + (that may not even have a location in the URI namespace). A single + data-producing process may dynamically generate the state of a + potentially large number of output resources. An example of this is + a CGI script that describes a "finger" gateway process that maps part + of the namespace of a server into finger requests, such as + http://www.foo.bar.org/finger_gateway/user@host. + + + + + +Goland, et al. Standards Track [Page 13] + +RFC 2518 WEBDAV February 1999 + + + In the absence of distributed authoring capabilities, it is + acceptable to have no mapping of source resource(s) to the URI + namespace. In fact, preventing access to the source resource(s) has + desirable security benefits. However, if remote editing of the + source resource(s) is desired, the source resource(s) should be given + a location in the URI namespace. This source location should not be + one of the locations at which the generated output is retrievable, + since in general it is impossible for the server to differentiate + requests for source resources from requests for process output + resources. There is often a many-to-many relationship between source + resources and output resources. + + On WebDAV compliant servers the URI of the source resource(s) may be + stored in a link on the output resource with type DAV:source (see + section 13.10 for a description of the source link property). + Storing the source URIs in links on the output resources places the + burden of discovering the source on the authoring client. Note that + the value of a source link is not guaranteed to point to the correct + source. Source links may break or incorrect values may be entered. + Also note that not all servers will allow the client to set the + source link value. For example a server which generates source links + on the fly for its CGI files will most likely not allow a client to + set the source link value. + +6 Locking + + The ability to lock a resource provides a mechanism for serializing + access to that resource. Using a lock, an authoring client can + provide a reasonable guarantee that another principal will not modify + a resource while it is being edited. In this way, a client can + prevent the "lost update" problem. + + This specification allows locks to vary over two client-specified + parameters, the number of principals involved (exclusive vs. shared) + and the type of access to be granted. This document defines locking + for only one access type, write. However, the syntax is extensible, + and permits the eventual specification of locking for other access + types. + +6.1 Exclusive Vs. Shared Locks + + The most basic form of lock is an exclusive lock. This is a lock + where the access right in question is only granted to a single + principal. The need for this arbitration results from a desire to + avoid having to merge results. + + + + + + +Goland, et al. Standards Track [Page 14] + +RFC 2518 WEBDAV February 1999 + + + However, there are times when the goal of a lock is not to exclude + others from exercising an access right but rather to provide a + mechanism for principals to indicate that they intend to exercise + their access rights. Shared locks are provided for this case. A + shared lock allows multiple principals to receive a lock. Hence any + principal with appropriate access can get the lock. + + With shared locks there are two trust sets that affect a resource. + The first trust set is created by access permissions. Principals who + are trusted, for example, may have permission to write to the + resource. Among those who have access permission to write to the + resource, the set of principals who have taken out a shared lock also + must trust each other, creating a (typically) smaller trust set + within the access permission write set. + + Starting with every possible principal on the Internet, in most + situations the vast majority of these principals will not have write + access to a given resource. Of the small number who do have write + access, some principals may decide to guarantee their edits are free + from overwrite conflicts by using exclusive write locks. Others may + decide they trust their collaborators will not overwrite their work + (the potential set of collaborators being the set of principals who + have write permission) and use a shared lock, which informs their + collaborators that a principal may be working on the resource. + + The WebDAV extensions to HTTP do not need to provide all of the + communications paths necessary for principals to coordinate their + activities. When using shared locks, principals may use any out of + band communication channel to coordinate their work (e.g., face-to- + face interaction, written notes, post-it notes on the screen, + telephone conversation, Email, etc.) The intent of a shared lock is + to let collaborators know who else may be working on a resource. + + Shared locks are included because experience from web distributed + authoring systems has indicated that exclusive locks are often too + rigid. An exclusive lock is used to enforce a particular editing + process: take out an exclusive lock, read the resource, perform + edits, write the resource, release the lock. This editing process + has the problem that locks are not always properly released, for + example when a program crashes, or when a lock owner leaves without + unlocking a resource. While both timeouts and administrative action + can be used to remove an offending lock, neither mechanism may be + available when needed; the timeout may be long or the administrator + may not be available. + + + + + + + +Goland, et al. Standards Track [Page 15] + +RFC 2518 WEBDAV February 1999 + + +6.2 Required Support + + A WebDAV compliant server is not required to support locking in any + form. If the server does support locking it may choose to support + any combination of exclusive and shared locks for any access types. + + The reason for this flexibility is that locking policy strikes to the + very heart of the resource management and versioning systems employed + by various storage repositories. These repositories require control + over what sort of locking will be made available. For example, some + repositories only support shared write locks while others only + provide support for exclusive write locks while yet others use no + locking at all. As each system is sufficiently different to merit + exclusion of certain locking features, this specification leaves + locking as the sole axis of negotiation within WebDAV. + +6.3 Lock Tokens + + A lock token is a type of state token, represented as a URI, which + identifies a particular lock. A lock token is returned by every + successful LOCK operation in the lockdiscovery property in the + response body, and can also be found through lock discovery on a + resource. + + Lock token URIs MUST be unique across all resources for all time. + This uniqueness constraint allows lock tokens to be submitted across + resources and servers without fear of confusion. + + This specification provides a lock token URI scheme called + opaquelocktoken that meets the uniqueness requirements. However + resources are free to return any URI scheme so long as it meets the + uniqueness requirements. + + Having a lock token provides no special access rights. Anyone can + find out anyone else's lock token by performing lock discovery. + Locks MUST be enforced based upon whatever authentication mechanism + is used by the server, not based on the secrecy of the token values. + +6.4 opaquelocktoken Lock Token URI Scheme + + The opaquelocktoken URI scheme is designed to be unique across all + resources for all time. Due to this uniqueness quality, a client may + submit an opaque lock token in an If header on a resource other than + the one that returned it. + + All resources MUST recognize the opaquelocktoken scheme and, at + minimum, recognize that the lock token does not refer to an + outstanding lock on the resource. + + + +Goland, et al. Standards Track [Page 16] + +RFC 2518 WEBDAV February 1999 + + + In order to guarantee uniqueness across all resources for all time + the opaquelocktoken requires the use of the Universal Unique + Identifier (UUID) mechanism, as described in [ISO-11578]. + + Opaquelocktoken generators, however, have a choice of how they create + these tokens. They can either generate a new UUID for every lock + token they create or they can create a single UUID and then add + extension characters. If the second method is selected then the + program generating the extensions MUST guarantee that the same + extension will never be used twice with the associated UUID. + + OpaqueLockToken-URI = "opaquelocktoken:" UUID [Extension] ; The UUID + production is the string representation of a UUID, as defined in + [ISO-11578]. Note that white space (LWS) is not allowed between + elements of this production. + + Extension = path ; path is defined in section 3.2.1 of RFC 2068 + [RFC2068] + +6.4.1 Node Field Generation Without the IEEE 802 Address + + UUIDs, as defined in [ISO-11578], contain a "node" field that + contains one of the IEEE 802 addresses for the server machine. As + noted in section 17.8, there are several security risks associated + with exposing a machine's IEEE 802 address. This section provides an + alternate mechanism for generating the "node" field of a UUID which + does not employ an IEEE 802 address. WebDAV servers MAY use this + algorithm for creating the node field when generating UUIDs. The + text in this section is originally from an Internet-Draft by Paul + Leach and Rich Salz, who are noted here to properly attribute their + work. + + The ideal solution is to obtain a 47 bit cryptographic quality random + number, and use it as the low 47 bits of the node ID, with the most + significant bit of the first octet of the node ID set to 1. This bit + is the unicast/multicast bit, which will never be set in IEEE 802 + addresses obtained from network cards; hence, there can never be a + conflict between UUIDs generated by machines with and without network + cards. + + If a system does not have a primitive to generate cryptographic + quality random numbers, then in most systems there are usually a + fairly large number of sources of randomness available from which one + can be generated. Such sources are system specific, but often + include: + + + + + + +Goland, et al. Standards Track [Page 17] + +RFC 2518 WEBDAV February 1999 + + + - the percent of memory in use + - the size of main memory in bytes + - the amount of free main memory in bytes + - the size of the paging or swap file in bytes + - free bytes of paging or swap file + - the total size of user virtual address space in bytes + - the total available user address space bytes + - the size of boot disk drive in bytes + - the free disk space on boot drive in bytes + - the current time + - the amount of time since the system booted + - the individual sizes of files in various system directories + - the creation, last read, and modification times of files in + various system directories + - the utilization factors of various system resources (heap, etc.) + - current mouse cursor position + - current caret position + - current number of running processes, threads + - handles or IDs of the desktop window and the active window + - the value of stack pointer of the caller + - the process and thread ID of caller + - various processor architecture specific performance counters + (instructions executed, cache misses, TLB misses) + + (Note that it is precisely the above kinds of sources of randomness + that are used to seed cryptographic quality random number generators + on systems without special hardware for their construction.) + + In addition, items such as the computer's name and the name of the + operating system, while not strictly speaking random, will help + differentiate the results from those obtained by other systems. + + The exact algorithm to generate a node ID using these data is system + specific, because both the data available and the functions to obtain + them are often very system specific. However, assuming that one can + concatenate all the values from the randomness sources into a buffer, + and that a cryptographic hash function such as MD5 is available, then + any 6 bytes of the MD5 hash of the buffer, with the multicast bit + (the high bit of the first byte) set will be an appropriately random + node ID. + + Other hash functions, such as SHA-1, can also be used. The only + requirement is that the result be suitably random _ in the sense that + the outputs from a set uniformly distributed inputs are themselves + uniformly distributed, and that a single bit change in the input can + be expected to cause half of the output bits to change. + + + + + +Goland, et al. Standards Track [Page 18] + +RFC 2518 WEBDAV February 1999 + + +6.5 Lock Capability Discovery + + Since server lock support is optional, a client trying to lock a + resource on a server can either try the lock and hope for the best, + or perform some form of discovery to determine what lock capabilities + the server supports. This is known as lock capability discovery. + Lock capability discovery differs from discovery of supported access + control types, since there may be access control types without + corresponding lock types. A client can determine what lock types the + server supports by retrieving the supportedlock property. + + Any DAV compliant resource that supports the LOCK method MUST support + the supportedlock property. + +6.6 Active Lock Discovery + + If another principal locks a resource that a principal wishes to + access, it is useful for the second principal to be able to find out + who the first principal is. For this purpose the lockdiscovery + property is provided. This property lists all outstanding locks, + describes their type, and where available, provides their lock token. + + Any DAV compliant resource that supports the LOCK method MUST support + the lockdiscovery property. + +6.7 Usage Considerations + + Although the locking mechanisms specified here provide some help in + preventing lost updates, they cannot guarantee that updates will + never be lost. Consider the following scenario: + + Two clients A and B are interested in editing the resource ' + index.html'. Client A is an HTTP client rather than a WebDAV client, + and so does not know how to perform locking. + Client A doesn't lock the document, but does a GET and begins + editing. + Client B does LOCK, performs a GET and begins editing. + Client B finishes editing, performs a PUT, then an UNLOCK. + Client A performs a PUT, overwriting and losing all of B's changes. + + There are several reasons why the WebDAV protocol itself cannot + prevent this situation. First, it cannot force all clients to use + locking because it must be compatible with HTTP clients that do not + comprehend locking. Second, it cannot require servers to support + locking because of the variety of repository implementations, some of + which rely on reservations and merging rather than on locking. + Finally, being stateless, it cannot enforce a sequence of operations + like LOCK / GET / PUT / UNLOCK. + + + +Goland, et al. Standards Track [Page 19] + +RFC 2518 WEBDAV February 1999 + + + WebDAV servers that support locking can reduce the likelihood that + clients will accidentally overwrite each other's changes by requiring + clients to lock resources before modifying them. Such servers would + effectively prevent HTTP 1.0 and HTTP 1.1 clients from modifying + resources. + + WebDAV clients can be good citizens by using a lock / retrieve / + write /unlock sequence of operations (at least by default) whenever + they interact with a WebDAV server that supports locking. + + HTTP 1.1 clients can be good citizens, avoiding overwriting other + clients' changes, by using entity tags in If-Match headers with any + requests that would modify resources. + + Information managers may attempt to prevent overwrites by + implementing client-side procedures requiring locking before + modifying WebDAV resources. + +7 Write Lock + + This section describes the semantics specific to the write lock type. + The write lock is a specific instance of a lock type, and is the only + lock type described in this specification. + +7.1 Methods Restricted by Write Locks + + A write lock MUST prevent a principal without the lock from + successfully executing a PUT, POST, PROPPATCH, LOCK, UNLOCK, MOVE, + DELETE, or MKCOL on the locked resource. All other current methods, + GET in particular, function independently of the lock. + + Note, however, that as new methods are created it will be necessary + to specify how they interact with a write lock. + +7.2 Write Locks and Lock Tokens + + A successful request for an exclusive or shared write lock MUST + result in the generation of a unique lock token associated with the + requesting principal. Thus if five principals have a shared write + lock on the same resource there will be five lock tokens, one for + each principal. + +7.3 Write Locks and Properties + + While those without a write lock may not alter a property on a + resource it is still possible for the values of live properties to + change, even while locked, due to the requirements of their schemas. + + + + +Goland, et al. Standards Track [Page 20] + +RFC 2518 WEBDAV February 1999 + + + Only dead properties and live properties defined to respect locks are + guaranteed not to change while write locked. + +7.4 Write Locks and Null Resources + + It is possible to assert a write lock on a null resource in order to + lock the name. + + A write locked null resource, referred to as a lock-null resource, + MUST respond with a 404 (Not Found) or 405 (Method Not Allowed) to + any HTTP/1.1 or DAV methods except for PUT, MKCOL, OPTIONS, PROPFIND, + LOCK, and UNLOCK. A lock-null resource MUST appear as a member of + its parent collection. Additionally the lock-null resource MUST have + defined on it all mandatory DAV properties. Most of these + properties, such as all the get* properties, will have no value as a + lock-null resource does not support the GET method. Lock-Null + resources MUST have defined values for lockdiscovery and + supportedlock properties. + + Until a method such as PUT or MKCOL is successfully executed on the + lock-null resource the resource MUST stay in the lock-null state. + However, once a PUT or MKCOL is successfully executed on a lock-null + resource the resource ceases to be in the lock-null state. + + If the resource is unlocked, for any reason, without a PUT, MKCOL, or + similar method having been successfully executed upon it then the + resource MUST return to the null state. + +7.5 Write Locks and Collections + + A write lock on a collection, whether created by a "Depth: 0" or + "Depth: infinity" lock request, prevents the addition or removal of + member URIs of the collection by non-lock owners. As a consequence, + when a principal issues a PUT or POST request to create a new + resource under a URI which needs to be an internal member of a write + locked collection to maintain HTTP namespace consistency, or issues a + DELETE to remove a resource which has a URI which is an existing + internal member URI of a write locked collection, this request MUST + fail if the principal does not have a write lock on the collection. + + However, if a write lock request is issued to a collection containing + member URIs identifying resources that are currently locked in a + manner which conflicts with the write lock, the request MUST fail + with a 423 (Locked) status code. + + If a lock owner causes the URI of a resource to be added as an + internal member URI of a locked collection then the new resource MUST + be automatically added to the lock. This is the only mechanism that + + + +Goland, et al. Standards Track [Page 21] + +RFC 2518 WEBDAV February 1999 + + + allows a resource to be added to a write lock. Thus, for example, if + the collection /a/b/ is write locked and the resource /c is moved to + /a/b/c then resource /a/b/c will be added to the write lock. + +7.6 Write Locks and the If Request Header + + If a user agent is not required to have knowledge about a lock when + requesting an operation on a locked resource, the following scenario + might occur. Program A, run by User A, takes out a write lock on a + resource. Program B, also run by User A, has no knowledge of the + lock taken out by Program A, yet performs a PUT to the locked + resource. In this scenario, the PUT succeeds because locks are + associated with a principal, not a program, and thus program B, + because it is acting with principal A's credential, is allowed to + perform the PUT. However, had program B known about the lock, it + would not have overwritten the resource, preferring instead to + present a dialog box describing the conflict to the user. Due to + this scenario, a mechanism is needed to prevent different programs + from accidentally ignoring locks taken out by other programs with the + same authorization. + + In order to prevent these collisions a lock token MUST be submitted + by an authorized principal in the If header for all locked resources + that a method may interact with or the method MUST fail. For + example, if a resource is to be moved and both the source and + destination are locked then two lock tokens must be submitted, one + for the source and the other for the destination. + +7.6.1 Example - Write Lock + + >>Request + + COPY /~fielding/index.html HTTP/1.1 + Host: www.ics.uci.edu + Destination: http://www.ics.uci.edu/users/f/fielding/index.html + If: + () + + >>Response + + HTTP/1.1 204 No Content + + In this example, even though both the source and destination are + locked, only one lock token must be submitted, for the lock on the + destination. This is because the source resource is not modified by + a COPY, and hence unaffected by the write lock. In this example, user + agent authentication has previously occurred via a mechanism outside + the scope of the HTTP protocol, in the underlying transport layer. + + + +Goland, et al. Standards Track [Page 22] + +RFC 2518 WEBDAV February 1999 + + +7.7 Write Locks and COPY/MOVE + + A COPY method invocation MUST NOT duplicate any write locks active on + the source. However, as previously noted, if the COPY copies the + resource into a collection that is locked with "Depth: infinity", + then the resource will be added to the lock. + + A successful MOVE request on a write locked resource MUST NOT move + the write lock with the resource. However, the resource is subject to + being added to an existing lock at the destination, as specified in + section 7.5. For example, if the MOVE makes the resource a child of a + collection that is locked with "Depth: infinity", then the resource + will be added to that collection's lock. Additionally, if a resource + locked with "Depth: infinity" is moved to a destination that is + within the scope of the same lock (e.g., within the namespace tree + covered by the lock), the moved resource will again be a added to the + lock. In both these examples, as specified in section 7.6, an If + header must be submitted containing a lock token for both the source + and destination. + +7.8 Refreshing Write Locks + + A client MUST NOT submit the same write lock request twice. Note + that a client is always aware it is resubmitting the same lock + request because it must include the lock token in the If header in + order to make the request for a resource that is already locked. + + However, a client may submit a LOCK method with an If header but + without a body. This form of LOCK MUST only be used to "refresh" a + lock. Meaning, at minimum, that any timers associated with the lock + MUST be re-set. + + A server may return a Timeout header with a lock refresh that is + different than the Timeout header returned when the lock was + originally requested. Additionally clients may submit Timeout + headers of arbitrary value with their lock refresh requests. + Servers, as always, may ignore Timeout headers submitted by the + client. + + If an error is received in response to a refresh LOCK request the + client SHOULD assume that the lock was not refreshed. + +8 HTTP Methods for Distributed Authoring + + The following new HTTP methods use XML as a request and response + format. All DAV compliant clients and resources MUST use XML parsers + that are compliant with [REC-XML]. All XML used in either requests + or responses MUST be, at minimum, well formed. If a server receives + + + +Goland, et al. Standards Track [Page 23] + +RFC 2518 WEBDAV February 1999 + + + ill-formed XML in a request it MUST reject the entire request with a + 400 (Bad Request). If a client receives ill-formed XML in a response + then it MUST NOT assume anything about the outcome of the executed + method and SHOULD treat the server as malfunctioning. + +8.1 PROPFIND + + The PROPFIND method retrieves properties defined on the resource + identified by the Request-URI, if the resource does not have any + internal members, or on the resource identified by the Request-URI + and potentially its member resources, if the resource is a collection + that has internal member URIs. All DAV compliant resources MUST + support the PROPFIND method and the propfind XML element (section + 12.14) along with all XML elements defined for use with that element. + + A client may submit a Depth header with a value of "0", "1", or + "infinity" with a PROPFIND on a collection resource with internal + member URIs. DAV compliant servers MUST support the "0", "1" and + "infinity" behaviors. By default, the PROPFIND method without a Depth + header MUST act as if a "Depth: infinity" header was included. + + A client may submit a propfind XML element in the body of the request + method describing what information is being requested. It is + possible to request particular property values, all property values, + or a list of the names of the resource's properties. A client may + choose not to submit a request body. An empty PROPFIND request body + MUST be treated as a request for the names and values of all + properties. + + All servers MUST support returning a response of content type + text/xml or application/xml that contains a multistatus XML element + that describes the results of the attempts to retrieve the various + properties. + + If there is an error retrieving a property then a proper error result + MUST be included in the response. A request to retrieve the value of + a property which does not exist is an error and MUST be noted, if the + response uses a multistatus XML element, with a response XML element + which contains a 404 (Not Found) status value. + + Consequently, the multistatus XML element for a collection resource + with member URIs MUST include a response XML element for each member + URI of the collection, to whatever depth was requested. Each response + XML element MUST contain an href XML element that gives the URI of + the resource on which the properties in the prop XML element are + defined. Results for a PROPFIND on a collection resource with + internal member URIs are returned as a flat list whose order of + entries is not significant. + + + +Goland, et al. Standards Track [Page 24] + +RFC 2518 WEBDAV February 1999 + + + In the case of allprop and propname, if a principal does not have the + right to know whether a particular property exists then the property + should be silently excluded from the response. + + The results of this method SHOULD NOT be cached. + +8.1.1 Example - Retrieving Named Properties + + >>Request + + PROPFIND /file HTTP/1.1 + Host: www.foo.bar + Content-type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/file + + + + Box type A + + + J.J. Johnson + + + HTTP/1.1 200 OK + + + + + + +Goland, et al. Standards Track [Page 25] + +RFC 2518 WEBDAV February 1999 + + + HTTP/1.1 403 Forbidden + The user does not have access to + the DingALing property. + + + + There has been an access violation error. + + + + In this example, PROPFIND is executed on a non-collection resource + http://www.foo.bar/file. The propfind XML element specifies the name + of four properties whose values are being requested. In this case + only two properties were returned, since the principal issuing the + request did not have sufficient access rights to see the third and + fourth properties. + +8.1.2 Example - Using allprop to Retrieve All Properties + + >>Request + + PROPFIND /container/ HTTP/1.1 + Host: www.foo.bar + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/container/ + + + + Box type A + + + + + +Goland, et al. Standards Track [Page 26] + +RFC 2518 WEBDAV February 1999 + + + Hadrian + + + 1997-12-01T17:42:21-08:00 + + + Example collection + + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + http://www.foo.bar/container/front.html + + + + Box type B + + + 1997-12-01T18:27:21-08:00 + + + Example HTML resource + + + 4525 + + + text/html + + + zzyzx + + + Monday, 12-Jan-98 09:25:56 GMT + + + + +Goland, et al. Standards Track [Page 27] + +RFC 2518 WEBDAV February 1999 + + + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + + In this example, PROPFIND was invoked on the resource + http://www.foo.bar/container/ with a Depth header of 1, meaning the + request applies to the resource and its children, and a propfind XML + element containing the allprop XML element, meaning the request + should return the name and value of all properties defined on each + resource. + + The resource http://www.foo.bar/container/ has six properties defined + on it: + + http://www.foo.bar/boxschema/bigbox, + http://www.foo.bar/boxschema/author, DAV:creationdate, + DAV:displayname, DAV:resourcetype, and DAV:supportedlock. + + The last four properties are WebDAV-specific, defined in section 13. + Since GET is not supported on this resource, the get* properties + (e.g., getcontentlength) are not defined on this resource. The DAV- + specific properties assert that "container" was created on December + 1, 1997, at 5:42:21PM, in a time zone 8 hours west of GMT + (creationdate), has a name of "Example collection" (displayname), a + collection resource type (resourcetype), and supports exclusive write + and shared write locks (supportedlock). + + The resource http://www.foo.bar/container/front.html has nine + properties defined on it: + + http://www.foo.bar/boxschema/bigbox (another instance of the "bigbox" + property type), DAV:creationdate, DAV:displayname, + DAV:getcontentlength, DAV:getcontenttype, DAV:getetag, + DAV:getlastmodified, DAV:resourcetype, and DAV:supportedlock. + + + + +Goland, et al. Standards Track [Page 28] + +RFC 2518 WEBDAV February 1999 + + + The DAV-specific properties assert that "front.html" was created on + December 1, 1997, at 6:27:21PM, in a time zone 8 hours west of GMT + (creationdate), has a name of "Example HTML resource" (displayname), + a content length of 4525 bytes (getcontentlength), a MIME type of + "text/html" (getcontenttype), an entity tag of "zzyzx" (getetag), was + last modified on Monday, January 12, 1998, at 09:25:56 GMT + (getlastmodified), has an empty resource type, meaning that it is not + a collection (resourcetype), and supports both exclusive write and + shared write locks (supportedlock). + +8.1.3 Example - Using propname to Retrieve all Property Names + + >>Request + + PROPFIND /container/ HTTP/1.1 + Host: www.foo.bar + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/container/ + + + + + + + + + + HTTP/1.1 200 OK + + + + http://www.foo.bar/container/front.html + + + +Goland, et al. Standards Track [Page 29] + +RFC 2518 WEBDAV February 1999 + + + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + + + In this example, PROPFIND is invoked on the collection resource + http://www.foo.bar/container/, with a propfind XML element containing + the propname XML element, meaning the name of all properties should + be returned. Since no Depth header is present, it assumes its + default value of "infinity", meaning the name of the properties on + the collection and all its progeny should be returned. + + Consistent with the previous example, resource + http://www.foo.bar/container/ has six properties defined on it, + http://www.foo.bar/boxschema/bigbox, + http://www.foo.bar/boxschema/author, DAV:creationdate, + DAV:displayname, DAV:resourcetype, and DAV:supportedlock. + + The resource http://www.foo.bar/container/index.html, a member of the + "container" collection, has nine properties defined on it, + http://www.foo.bar/boxschema/bigbox, DAV:creationdate, + DAV:displayname, DAV:getcontentlength, DAV:getcontenttype, + DAV:getetag, DAV:getlastmodified, DAV:resourcetype, and + DAV:supportedlock. + + This example also demonstrates the use of XML namespace scoping, and + the default namespace. Since the "xmlns" attribute does not contain + an explicit "shorthand name" (prefix) letter, the namespace applies + by default to all enclosed elements. Hence, all elements which do + not explicitly state the namespace to which they belong are members + of the "DAV:" namespace schema. + + + + + + + +Goland, et al. Standards Track [Page 30] + +RFC 2518 WEBDAV February 1999 + + +8.2 PROPPATCH + + The PROPPATCH method processes instructions specified in the request + body to set and/or remove properties defined on the resource + identified by the Request-URI. + + All DAV compliant resources MUST support the PROPPATCH method and + MUST process instructions that are specified using the + propertyupdate, set, and remove XML elements of the DAV schema. + Execution of the directives in this method is, of course, subject to + access control constraints. DAV compliant resources SHOULD support + the setting of arbitrary dead properties. + + The request message body of a PROPPATCH method MUST contain the + propertyupdate XML element. Instruction processing MUST occur in the + order instructions are received (i.e., from top to bottom). + Instructions MUST either all be executed or none executed. Thus if + any error occurs during processing all executed instructions MUST be + undone and a proper error result returned. Instruction processing + details can be found in the definition of the set and remove + instructions in section 12.13. + +8.2.1 Status Codes for use with 207 (Multi-Status) + + The following are examples of response codes one would expect to be + used in a 207 (Multi-Status) response for this method. Note, + however, that unless explicitly prohibited any 2/3/4/5xx series + response code may be used in a 207 (Multi-Status) response. + + 200 (OK) - The command succeeded. As there can be a mixture of sets + and removes in a body, a 201 (Created) seems inappropriate. + + 403 (Forbidden) - The client, for reasons the server chooses not to + specify, cannot alter one of the properties. + + 409 (Conflict) - The client has provided a value whose semantics are + not appropriate for the property. This includes trying to set read- + only properties. + + 423 (Locked) - The specified resource is locked and the client either + is not a lock owner or the lock type requires a lock token to be + submitted and the client did not submit it. + + 507 (Insufficient Storage) - The server did not have sufficient space + to record the property. + + + + + + +Goland, et al. Standards Track [Page 31] + +RFC 2518 WEBDAV February 1999 + + +8.2.2 Example - PROPPATCH + + >>Request + + PROPPATCH /bar.html HTTP/1.1 + Host: www.foo.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + Jim Whitehead + Roy Fielding + + + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.com/bar.html + + + HTTP/1.1 424 Failed Dependency + + + + HTTP/1.1 409 Conflict + + Copyright Owner can not be deleted or + altered. + + + + + +Goland, et al. Standards Track [Page 32] + +RFC 2518 WEBDAV February 1999 + + + In this example, the client requests the server to set the value of + the http://www.w3.com/standards/z39.50/Authors property, and to + remove the property http://www.w3.com/standards/z39.50/Copyright- + Owner. Since the Copyright-Owner property could not be removed, no + property modifications occur. The 424 (Failed Dependency) status + code for the Authors property indicates this action would have + succeeded if it were not for the conflict with removing the + Copyright-Owner property. + +8.3 MKCOL Method + + The MKCOL method is used to create a new collection. All DAV + compliant resources MUST support the MKCOL method. + +8.3.1 Request + + MKCOL creates a new collection resource at the location specified by + the Request-URI. If the resource identified by the Request-URI is + non-null then the MKCOL MUST fail. During MKCOL processing, a server + MUST make the Request-URI a member of its parent collection, unless + the Request-URI is "/". If no such ancestor exists, the method MUST + fail. When the MKCOL operation creates a new collection resource, + all ancestors MUST already exist, or the method MUST fail with a 409 + (Conflict) status code. For example, if a request to create + collection /a/b/c/d/ is made, and neither /a/b/ nor /a/b/c/ exists, + the request must fail. + + When MKCOL is invoked without a request body, the newly created + collection SHOULD have no members. + + A MKCOL request message may contain a message body. The behavior of + a MKCOL request when the body is present is limited to creating + collections, members of a collection, bodies of members and + properties on the collections or members. If the server receives a + MKCOL request entity type it does not support or understand it MUST + respond with a 415 (Unsupported Media Type) status code. The exact + behavior of MKCOL for various request media types is undefined in + this document, and will be specified in separate documents. + +8.3.2 Status Codes + + Responses from a MKCOL request MUST NOT be cached as MKCOL has non- + idempotent semantics. + + 201 (Created) - The collection or structured resource was created in + its entirety. + + + + + +Goland, et al. Standards Track [Page 33] + +RFC 2518 WEBDAV February 1999 + + + 403 (Forbidden) - This indicates at least one of two conditions: 1) + the server does not allow the creation of collections at the given + location in its namespace, or 2) the parent collection of the + Request-URI exists but cannot accept members. + + 405 (Method Not Allowed) - MKCOL can only be executed on a + deleted/non-existent resource. + + 409 (Conflict) - A collection cannot be made at the Request-URI until + one or more intermediate collections have been created. + + 415 (Unsupported Media Type)- The server does not support the request + type of the body. + + 507 (Insufficient Storage) - The resource does not have sufficient + space to record the state of the resource after the execution of this + method. + +8.3.3 Example - MKCOL + + This example creates a collection called /webdisc/xfiles/ on the + server www.server.org. + + >>Request + + MKCOL /webdisc/xfiles/ HTTP/1.1 + Host: www.server.org + + >>Response + + HTTP/1.1 201 Created + +8.4 GET, HEAD for Collections + + The semantics of GET are unchanged when applied to a collection, + since GET is defined as, "retrieve whatever information (in the form + of an entity) is identified by the Request-URI" [RFC2068]. GET when + applied to a collection may return the contents of an "index.html" + resource, a human-readable view of the contents of the collection, or + something else altogether. Hence it is possible that the result of a + GET on a collection will bear no correlation to the membership of the + collection. + + Similarly, since the definition of HEAD is a GET without a response + message body, the semantics of HEAD are unmodified when applied to + collection resources. + + + + + +Goland, et al. Standards Track [Page 34] + +RFC 2518 WEBDAV February 1999 + + +8.5 POST for Collections + + Since by definition the actual function performed by POST is + determined by the server and often depends on the particular + resource, the behavior of POST when applied to collections cannot be + meaningfully modified because it is largely undefined. Thus the + semantics of POST are unmodified when applied to a collection. + +8.6 DELETE + + 8.6.1 DELETE for Non-Collection Resources + + If the DELETE method is issued to a non-collection resource whose + URIs are an internal member of one or more collections, then during + DELETE processing a server MUST remove any URI for the resource + identified by the Request-URI from collections which contain it as a + member. + +8.6.2 DELETE for Collections + + The DELETE method on a collection MUST act as if a "Depth: infinity" + header was used on it. A client MUST NOT submit a Depth header with + a DELETE on a collection with any value but infinity. + + DELETE instructs that the collection specified in the Request-URI and + all resources identified by its internal member URIs are to be + deleted. + + If any resource identified by a member URI cannot be deleted then all + of the member's ancestors MUST NOT be deleted, so as to maintain + namespace consistency. + + Any headers included with DELETE MUST be applied in processing every + resource to be deleted. + + When the DELETE method has completed processing it MUST result in a + consistent namespace. + + If an error occurs with a resource other than the resource identified + in the Request-URI then the response MUST be a 207 (Multi-Status). + 424 (Failed Dependency) errors SHOULD NOT be in the 207 (Multi- + Status). They can be safely left out because the client will know + that the ancestors of a resource could not be deleted when the client + receives an error for the ancestor's progeny. Additionally 204 (No + Content) errors SHOULD NOT be returned in the 207 (Multi-Status). + The reason for this prohibition is that 204 (No Content) is the + default success code. + + + + +Goland, et al. Standards Track [Page 35] + +RFC 2518 WEBDAV February 1999 + + +8.6.2.1 Example - DELETE + + >>Request + + DELETE /container/ HTTP/1.1 + Host: www.foo.bar + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/container/resource3 + HTTP/1.1 423 Locked + + + + In this example the attempt to delete + http://www.foo.bar/container/resource3 failed because it is locked, + and no lock token was submitted with the request. Consequently, the + attempt to delete http://www.foo.bar/container/ also failed. Thus the + client knows that the attempt to delete http://www.foo.bar/container/ + must have also failed since the parent can not be deleted unless its + child has also been deleted. Even though a Depth header has not been + included, a depth of infinity is assumed because the method is on a + collection. + +8.7 PUT + +8.7.1 PUT for Non-Collection Resources + + A PUT performed on an existing resource replaces the GET response + entity of the resource. Properties defined on the resource may be + recomputed during PUT processing but are not otherwise affected. For + example, if a server recognizes the content type of the request body, + it may be able to automatically extract information that could be + profitably exposed as properties. + + A PUT that would result in the creation of a resource without an + appropriately scoped parent collection MUST fail with a 409 + (Conflict). + + + + + + +Goland, et al. Standards Track [Page 36] + +RFC 2518 WEBDAV February 1999 + + +8.7.2 PUT for Collections + + As defined in the HTTP/1.1 specification [RFC2068], the "PUT method + requests that the enclosed entity be stored under the supplied + Request-URI." Since submission of an entity representing a + collection would implicitly encode creation and deletion of + resources, this specification intentionally does not define a + transmission format for creating a collection using PUT. Instead, + the MKCOL method is defined to create collections. + + When the PUT operation creates a new non-collection resource all + ancestors MUST already exist. If all ancestors do not exist, the + method MUST fail with a 409 (Conflict) status code. For example, if + resource /a/b/c/d.html is to be created and /a/b/c/ does not exist, + then the request must fail. + +8.8 COPY Method + + The COPY method creates a duplicate of the source resource, + identified by the Request-URI, in the destination resource, + identified by the URI in the Destination header. The Destination + header MUST be present. The exact behavior of the COPY method + depends on the type of the source resource. + + All WebDAV compliant resources MUST support the COPY method. + However, support for the COPY method does not guarantee the ability + to copy a resource. For example, separate programs may control + resources on the same server. As a result, it may not be possible to + copy a resource to a location that appears to be on the same server. + +8.8.1 COPY for HTTP/1.1 resources + + When the source resource is not a collection the result of the COPY + method is the creation of a new resource at the destination whose + state and behavior match that of the source resource as closely as + possible. After a successful COPY invocation, all properties on the + source resource MUST be duplicated on the destination resource, + subject to modifying headers and XML elements, following the + definition for copying properties. Since the environment at the + destination may be different than at the source due to factors + outside the scope of control of the server, such as the absence of + resources required for correct operation, it may not be possible to + completely duplicate the behavior of the resource at the destination. + Subsequent alterations to the destination resource will not modify + the source resource. Subsequent alterations to the source resource + will not modify the destination resource. + + + + + +Goland, et al. Standards Track [Page 37] + +RFC 2518 WEBDAV February 1999 + + +8.8.2. COPY for Properties + + The following section defines how properties on a resource are + handled during a COPY operation. + + Live properties SHOULD be duplicated as identically behaving live + properties at the destination resource. If a property cannot be + copied live, then its value MUST be duplicated, octet-for-octet, in + an identically named, dead property on the destination resource + subject to the effects of the propertybehavior XML element. + + The propertybehavior XML element can specify that properties are + copied on best effort, that all live properties must be successfully + copied or the method must fail, or that a specified list of live + properties must be successfully copied or the method must fail. The + propertybehavior XML element is defined in section 12.12. + +8.8.3 COPY for Collections + + The COPY method on a collection without a Depth header MUST act as if + a Depth header with value "infinity" was included. A client may + submit a Depth header on a COPY on a collection with a value of "0" + or "infinity". DAV compliant servers MUST support the "0" and + "infinity" Depth header behaviors. + + A COPY of depth infinity instructs that the collection resource + identified by the Request-URI is to be copied to the location + identified by the URI in the Destination header, and all its internal + member resources are to be copied to a location relative to it, + recursively through all levels of the collection hierarchy. + + A COPY of "Depth: 0" only instructs that the collection and its + properties but not resources identified by its internal member URIs, + are to be copied. + + Any headers included with a COPY MUST be applied in processing every + resource to be copied with the exception of the Destination header. + + The Destination header only specifies the destination URI for the + Request-URI. When applied to members of the collection identified by + the Request-URI the value of Destination is to be modified to reflect + the current location in the hierarchy. So, if the Request- URI is + /a/ with Host header value http://fun.com/ and the Destination is + http://fun.com/b/ then when http://fun.com/a/c/d is processed it must + use a Destination of http://fun.com/b/c/d. + + + + + + +Goland, et al. Standards Track [Page 38] + +RFC 2518 WEBDAV February 1999 + + + When the COPY method has completed processing it MUST have created a + consistent namespace at the destination (see section 5.1 for the + definition of namespace consistency). However, if an error occurs + while copying an internal collection, the server MUST NOT copy any + resources identified by members of this collection (i.e., the server + must skip this subtree), as this would create an inconsistent + namespace. After detecting an error, the COPY operation SHOULD try to + finish as much of the original copy operation as possible (i.e., the + server should still attempt to copy other subtrees and their members, + that are not descendents of an error-causing collection). So, for + example, if an infinite depth copy operation is performed on + collection /a/, which contains collections /a/b/ and /a/c/, and an + error occurs copying /a/b/, an attempt should still be made to copy + /a/c/. Similarly, after encountering an error copying a non- + collection resource as part of an infinite depth copy, the server + SHOULD try to finish as much of the original copy operation as + possible. + + If an error in executing the COPY method occurs with a resource other + than the resource identified in the Request-URI then the response + MUST be a 207 (Multi-Status). + + The 424 (Failed Dependency) status code SHOULD NOT be returned in the + 207 (Multi-Status) response from a COPY method. These responses can + be safely omitted because the client will know that the progeny of a + resource could not be copied when the client receives an error for + the parent. Additionally 201 (Created)/204 (No Content) status codes + SHOULD NOT be returned as values in 207 (Multi-Status) responses from + COPY methods. They, too, can be safely omitted because they are the + default success codes. + +8.8.4 COPY and the Overwrite Header + + If a resource exists at the destination and the Overwrite header is + "T" then prior to performing the copy the server MUST perform a + DELETE with "Depth: infinity" on the destination resource. If the + Overwrite header is set to "F" then the operation will fail. + +8.8.5 Status Codes + + 201 (Created) - The source resource was successfully copied. The + copy operation resulted in the creation of a new resource. + + 204 (No Content) - The source resource was successfully copied to a + pre-existing destination resource. + + 403 (Forbidden) _ The source and destination URIs are the same. + + + + +Goland, et al. Standards Track [Page 39] + +RFC 2518 WEBDAV February 1999 + + + 409 (Conflict) _ A resource cannot be created at the destination + until one or more intermediate collections have been created. + + 412 (Precondition Failed) - The server was unable to maintain the + liveness of the properties listed in the propertybehavior XML element + or the Overwrite header is "F" and the state of the destination + resource is non-null. + + 423 (Locked) - The destination resource was locked. + + 502 (Bad Gateway) - This may occur when the destination is on another + server and the destination server refuses to accept the resource. + + 507 (Insufficient Storage) - The destination resource does not have + sufficient space to record the state of the resource after the + execution of this method. + +8.8.6 Example - COPY with Overwrite + + This example shows resource + http://www.ics.uci.edu/~fielding/index.html being copied to the + location http://www.ics.uci.edu/users/f/fielding/index.html. The 204 + (No Content) status code indicates the existing resource at the + destination was overwritten. + + >>Request + + COPY /~fielding/index.html HTTP/1.1 + Host: www.ics.uci.edu + Destination: http://www.ics.uci.edu/users/f/fielding/index.html + + >>Response + + HTTP/1.1 204 No Content + +8.8.7 Example - COPY with No Overwrite + + The following example shows the same copy operation being performed, + but with the Overwrite header set to "F." A response of 412 + (Precondition Failed) is returned because the destination resource + has a non-null state. + + >>Request + + COPY /~fielding/index.html HTTP/1.1 + Host: www.ics.uci.edu + Destination: http://www.ics.uci.edu/users/f/fielding/index.html + Overwrite: F + + + +Goland, et al. Standards Track [Page 40] + +RFC 2518 WEBDAV February 1999 + + + >>Response + + HTTP/1.1 412 Precondition Failed + +8.8.8 Example - COPY of a Collection + + >>Request + + COPY /container/ HTTP/1.1 + Host: www.foo.bar + Destination: http://www.foo.bar/othercontainer/ + Depth: infinity + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + * + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/othercontainer/R2/ + HTTP/1.1 412 Precondition Failed + + + + The Depth header is unnecessary as the default behavior of COPY on a + collection is to act as if a "Depth: infinity" header had been + submitted. In this example most of the resources, along with the + collection, were copied successfully. However the collection R2 + failed, most likely due to a problem with maintaining the liveness of + properties (this is specified by the propertybehavior XML element). + Because there was an error copying R2, none of R2's members were + copied. However no errors were listed for those members due to the + error minimization rules given in section 8.8.3. + + + + + + + + +Goland, et al. Standards Track [Page 41] + +RFC 2518 WEBDAV February 1999 + + +8.9 MOVE Method + + The MOVE operation on a non-collection resource is the logical + equivalent of a copy (COPY), followed by consistency maintenance + processing, followed by a delete of the source, where all three + actions are performed atomically. The consistency maintenance step + allows the server to perform updates caused by the move, such as + updating all URIs other than the Request-URI which identify the + source resource, to point to the new destination resource. + Consequently, the Destination header MUST be present on all MOVE + methods and MUST follow all COPY requirements for the COPY part of + the MOVE method. All DAV compliant resources MUST support the MOVE + method. However, support for the MOVE method does not guarantee the + ability to move a resource to a particular destination. + + For example, separate programs may actually control different sets of + resources on the same server. Therefore, it may not be possible to + move a resource within a namespace that appears to belong to the same + server. + + If a resource exists at the destination, the destination resource + will be DELETEd as a side-effect of the MOVE operation, subject to + the restrictions of the Overwrite header. + +8.9.1 MOVE for Properties + + The behavior of properties on a MOVE, including the effects of the + propertybehavior XML element, MUST be the same as specified in + section 8.8.2. + +8.9.2 MOVE for Collections + + A MOVE with "Depth: infinity" instructs that the collection + identified by the Request-URI be moved to the URI specified in the + Destination header, and all resources identified by its internal + member URIs are to be moved to locations relative to it, recursively + through all levels of the collection hierarchy. + + The MOVE method on a collection MUST act as if a "Depth: infinity" + header was used on it. A client MUST NOT submit a Depth header on a + MOVE on a collection with any value but "infinity". + + Any headers included with MOVE MUST be applied in processing every + resource to be moved with the exception of the Destination header. + + The behavior of the Destination header is the same as given for COPY + on collections. + + + + +Goland, et al. Standards Track [Page 42] + +RFC 2518 WEBDAV February 1999 + + + When the MOVE method has completed processing it MUST have created a + consistent namespace at both the source and destination (see section + 5.1 for the definition of namespace consistency). However, if an + error occurs while moving an internal collection, the server MUST NOT + move any resources identified by members of the failed collection + (i.e., the server must skip the error-causing subtree), as this would + create an inconsistent namespace. In this case, after detecting the + error, the move operation SHOULD try to finish as much of the + original move as possible (i.e., the server should still attempt to + move other subtrees and the resources identified by their members, + that are not descendents of an error-causing collection). So, for + example, if an infinite depth move is performed on collection /a/, + which contains collections /a/b/ and /a/c/, and an error occurs + moving /a/b/, an attempt should still be made to try moving /a/c/. + Similarly, after encountering an error moving a non-collection + resource as part of an infinite depth move, the server SHOULD try to + finish as much of the original move operation as possible. + + If an error occurs with a resource other than the resource identified + in the Request-URI then the response MUST be a 207 (Multi-Status). + + The 424 (Failed Dependency) status code SHOULD NOT be returned in the + 207 (Multi-Status) response from a MOVE method. These errors can be + safely omitted because the client will know that the progeny of a + resource could not be moved when the client receives an error for the + parent. Additionally 201 (Created)/204 (No Content) responses SHOULD + NOT be returned as values in 207 (Multi-Status) responses from a + MOVE. These responses can be safely omitted because they are the + default success codes. + +8.9.3 MOVE and the Overwrite Header + + If a resource exists at the destination and the Overwrite header is + "T" then prior to performing the move the server MUST perform a + DELETE with "Depth: infinity" on the destination resource. If the + Overwrite header is set to "F" then the operation will fail. + +8.9.4 Status Codes + + 201 (Created) - The source resource was successfully moved, and a new + resource was created at the destination. + + 204 (No Content) - The source resource was successfully moved to a + pre-existing destination resource. + + 403 (Forbidden) _ The source and destination URIs are the same. + + + + + +Goland, et al. Standards Track [Page 43] + +RFC 2518 WEBDAV February 1999 + + + 409 (Conflict) _ A resource cannot be created at the destination + until one or more intermediate collections have been created. + + 412 (Precondition Failed) - The server was unable to maintain the + liveness of the properties listed in the propertybehavior XML element + or the Overwrite header is "F" and the state of the destination + resource is non-null. + + 423 (Locked) - The source or the destination resource was locked. + + 502 (Bad Gateway) - This may occur when the destination is on another + server and the destination server refuses to accept the resource. + +8.9.5 Example - MOVE of a Non-Collection + + This example shows resource + http://www.ics.uci.edu/~fielding/index.html being moved to the + location http://www.ics.uci.edu/users/f/fielding/index.html. The + contents of the destination resource would have been overwritten if + the destination resource had been non-null. In this case, since + there was nothing at the destination resource, the response code is + 201 (Created). + + >>Request + + MOVE /~fielding/index.html HTTP/1.1 + Host: www.ics.uci.edu + Destination: http://www.ics.uci.edu/users/f/fielding/index.html + + >>Response + + HTTP/1.1 201 Created + Location: http://www.ics.uci.edu/users/f/fielding/index.html + + +8.9.6 Example - MOVE of a Collection + + >>Request + + MOVE /container/ HTTP/1.1 + Host: www.foo.bar + Destination: http://www.foo.bar/othercontainer/ + Overwrite: F + If: () + () + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + +Goland, et al. Standards Track [Page 44] + +RFC 2518 WEBDAV February 1999 + + + + + * + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/othercontainer/C2/ + HTTP/1.1 423 Locked + + + + In this example the client has submitted a number of lock tokens with + the request. A lock token will need to be submitted for every + resource, both source and destination, anywhere in the scope of the + method, that is locked. In this case the proper lock token was not + submitted for the destination http://www.foo.bar/othercontainer/C2/. + This means that the resource /container/C2/ could not be moved. + Because there was an error copying /container/C2/, none of + /container/C2's members were copied. However no errors were listed + for those members due to the error minimization rules given in + section 8.8.3. User agent authentication has previously occurred via + a mechanism outside the scope of the HTTP protocol, in an underlying + transport layer. + +8.10 LOCK Method + + The following sections describe the LOCK method, which is used to + take out a lock of any access type. These sections on the LOCK + method describe only those semantics that are specific to the LOCK + method and are independent of the access type of the lock being + requested. + + Any resource which supports the LOCK method MUST, at minimum, support + the XML request and response formats defined herein. + + + + + + + + + +Goland, et al. Standards Track [Page 45] + +RFC 2518 WEBDAV February 1999 + + +8.10.1 Operation + + A LOCK method invocation creates the lock specified by the lockinfo + XML element on the Request-URI. Lock method requests SHOULD have a + XML request body which contains an owner XML element for this lock + request, unless this is a refresh request. The LOCK request may have + a Timeout header. + + Clients MUST assume that locks may arbitrarily disappear at any time, + regardless of the value given in the Timeout header. The Timeout + header only indicates the behavior of the server if "extraordinary" + circumstances do not occur. For example, an administrator may remove + a lock at any time or the system may crash in such a way that it + loses the record of the lock's existence. The response MUST contain + the value of the lockdiscovery property in a prop XML element. + + In order to indicate the lock token associated with a newly created + lock, a Lock-Token response header MUST be included in the response + for every successful LOCK request for a new lock. Note that the + Lock-Token header would not be returned in the response for a + successful refresh LOCK request because a new lock was not created. + +8.10.2 The Effect of Locks on Properties and Collections + + The scope of a lock is the entire state of the resource, including + its body and associated properties. As a result, a lock on a + resource MUST also lock the resource's properties. + + For collections, a lock also affects the ability to add or remove + members. The nature of the effect depends upon the type of access + control involved. + +8.10.3 Locking Replicated Resources + + A resource may be made available through more than one URI. However + locks apply to resources, not URIs. Therefore a LOCK request on a + resource MUST NOT succeed if can not be honored by all the URIs + through which the resource is addressable. + +8.10.4 Depth and Locking + + The Depth header may be used with the LOCK method. Values other than + 0 or infinity MUST NOT be used with the Depth header on a LOCK + method. All resources that support the LOCK method MUST support the + Depth header. + + A Depth header of value 0 means to just lock the resource specified + by the Request-URI. + + + +Goland, et al. Standards Track [Page 46] + +RFC 2518 WEBDAV February 1999 + + + If the Depth header is set to infinity then the resource specified in + the Request-URI along with all its internal members, all the way down + the hierarchy, are to be locked. A successful result MUST return a + single lock token which represents all the resources that have been + locked. If an UNLOCK is successfully executed on this token, all + associated resources are unlocked. If the lock cannot be granted to + all resources, a 409 (Conflict) status code MUST be returned with a + response entity body containing a multistatus XML element describing + which resource(s) prevented the lock from being granted. Hence, + partial success is not an option. Either the entire hierarchy is + locked or no resources are locked. + + If no Depth header is submitted on a LOCK request then the request + MUST act as if a "Depth:infinity" had been submitted. + +8.10.5 Interaction with other Methods + + The interaction of a LOCK with various methods is dependent upon the + lock type. However, independent of lock type, a successful DELETE of + a resource MUST cause all of its locks to be removed. + +8.10.6 Lock Compatibility Table + + The table below describes the behavior that occurs when a lock + request is made on a resource. + + Current lock state/ | Shared Lock | Exclusive + Lock request | | Lock + =====================+=================+============== + None | True | True + ---------------------+-----------------+-------------- + Shared Lock | True | False + ---------------------+-----------------+-------------- + Exclusive Lock | False | False* + ------------------------------------------------------ + + Legend: True = lock may be granted. False = lock MUST NOT be + granted. *=It is illegal for a principal to request the same lock + twice. + + The current lock state of a resource is given in the leftmost column, + and lock requests are listed in the first row. The intersection of a + row and column gives the result of a lock request. For example, if a + shared lock is held on a resource, and an exclusive lock is + requested, the table entry is "false", indicating the lock must not + be granted. + + + + + +Goland, et al. Standards Track [Page 47] + +RFC 2518 WEBDAV February 1999 + + +8.10.7 Status Codes + + 200 (OK) - The lock request succeeded and the value of the + lockdiscovery property is included in the body. + + 412 (Precondition Failed) - The included lock token was not + enforceable on this resource or the server could not satisfy the + request in the lockinfo XML element. + + 423 (Locked) - The resource is locked, so the method has been + rejected. + +8.10.8 Example - Simple Lock Request + + >>Request + + LOCK /workspace/webdav/proposal.doc HTTP/1.1 + Host: webdav.sb.aol.com + Timeout: Infinite, Second-4100000000 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="ejw", + realm="ejw@webdav.sb.aol.com", nonce="...", + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + + + + + + http://www.ics.uci.edu/~ejw/contact.html + + + + >>Response + + HTTP/1.1 200 OK + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + Infinity + + + +Goland, et al. Standards Track [Page 48] + +RFC 2518 WEBDAV February 1999 + + + + + http://www.ics.uci.edu/~ejw/contact.html + + + Second-604800 + + + opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6be4 + + + + + + + This example shows the successful creation of an exclusive write lock + on resource http://webdav.sb.aol.com/workspace/webdav/proposal.doc. + The resource http://www.ics.uci.edu/~ejw/contact.html contains + contact information for the owner of the lock. The server has an + activity-based timeout policy in place on this resource, which causes + the lock to automatically be removed after 1 week (604800 seconds). + Note that the nonce, response, and opaque fields have not been + calculated in the Authorization request header. + +8.10.9 Example - Refreshing a Write Lock + + >>Request + + LOCK /workspace/webdav/proposal.doc HTTP/1.1 + Host: webdav.sb.aol.com + Timeout: Infinite, Second-4100000000 + If: () + Authorization: Digest username="ejw", + realm="ejw@webdav.sb.aol.com", nonce="...", + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + >>Response + + HTTP/1.1 200 OK + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + +Goland, et al. Standards Track [Page 49] + +RFC 2518 WEBDAV February 1999 + + + + Infinity + + + http://www.ics.uci.edu/~ejw/contact.html + + + Second-604800 + + + opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6be4 + + + + + + + This request would refresh the lock, resetting any time outs. Notice + that the client asked for an infinite time out but the server choose + to ignore the request. In this example, the nonce, response, and + opaque fields have not been calculated in the Authorization request + header. + +8.10.10 Example - Multi-Resource Lock Request + + >>Request + + LOCK /webdav/ HTTP/1.1 + Host: webdav.sb.aol.com + Timeout: Infinite, Second-4100000000 + Depth: infinity + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="ejw", + realm="ejw@webdav.sb.aol.com", nonce="...", + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + + + + + + http://www.ics.uci.edu/~ejw/contact.html + + + + >>Response + + + +Goland, et al. Standards Track [Page 50] + +RFC 2518 WEBDAV February 1999 + + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://webdav.sb.aol.com/webdav/secret + HTTP/1.1 403 Forbidden + + + http://webdav.sb.aol.com/webdav/ + + + HTTP/1.1 424 Failed Dependency + + + + + This example shows a request for an exclusive write lock on a + collection and all its children. In this request, the client has + specified that it desires an infinite length lock, if available, + otherwise a timeout of 4.1 billion seconds, if available. The request + entity body contains the contact information for the principal taking + out the lock, in this case a web page URL. + + The error is a 403 (Forbidden) response on the resource + http://webdav.sb.aol.com/webdav/secret. Because this resource could + not be locked, none of the resources were locked. Note also that the + lockdiscovery property for the Request-URI has been included as + required. In this example the lockdiscovery property is empty which + means that there are no outstanding locks on the resource. + + In this example, the nonce, response, and opaque fields have not been + calculated in the Authorization request header. + +8.11 UNLOCK Method + + The UNLOCK method removes the lock identified by the lock token in + the Lock-Token request header from the Request-URI, and all other + resources included in the lock. If all resources which have been + locked under the submitted lock token can not be unlocked then the + UNLOCK request MUST fail. + + Any DAV compliant resource which supports the LOCK method MUST + support the UNLOCK method. + + + + + +Goland, et al. Standards Track [Page 51] + +RFC 2518 WEBDAV February 1999 + + +8.11.1 Example - UNLOCK + + >>Request + + UNLOCK /workspace/webdav/info.doc HTTP/1.1 + Host: webdav.sb.aol.com + Lock-Token: + Authorization: Digest username="ejw", + realm="ejw@webdav.sb.aol.com", nonce="...", + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + >>Response + + HTTP/1.1 204 No Content + + In this example, the lock identified by the lock token + "opaquelocktoken:a515cfa4-5da4-22e1-f5b5-00a0451e6bf7" is + successfully removed from the resource + http://webdav.sb.aol.com/workspace/webdav/info.doc. If this lock + included more than just one resource, the lock is removed from all + resources included in the lock. The 204 (No Content) status code is + used instead of 200 (OK) because there is no response entity body. + + In this example, the nonce, response, and opaque fields have not been + calculated in the Authorization request header. + +9 HTTP Headers for Distributed Authoring + +9.1 DAV Header + + DAV = "DAV" ":" "1" ["," "2"] ["," 1#extend] + + This header indicates that the resource supports the DAV schema and + protocol as specified. All DAV compliant resources MUST return the + DAV header on all OPTIONS responses. + + The value is a list of all compliance classes that the resource + supports. Note that above a comma has already been added to the 2. + This is because a resource can not be level 2 compliant unless it is + also level 1 compliant. Please refer to section 15 for more details. + In general, however, support for one compliance class does not entail + support for any other. + +9.2 Depth Header + + Depth = "Depth" ":" ("0" | "1" | "infinity") + + + + +Goland, et al. Standards Track [Page 52] + +RFC 2518 WEBDAV February 1999 + + + The Depth header is used with methods executed on resources which + could potentially have internal members to indicate whether the + method is to be applied only to the resource ("Depth: 0"), to the + resource and its immediate children, ("Depth: 1"), or the resource + and all its progeny ("Depth: infinity"). + + The Depth header is only supported if a method's definition + explicitly provides for such support. + + The following rules are the default behavior for any method that + supports the Depth header. A method may override these defaults by + defining different behavior in its definition. + + Methods which support the Depth header may choose not to support all + of the header's values and may define, on a case by case basis, the + behavior of the method if a Depth header is not present. For example, + the MOVE method only supports "Depth: infinity" and if a Depth header + is not present will act as if a "Depth: infinity" header had been + applied. + + Clients MUST NOT rely upon methods executing on members of their + hierarchies in any particular order or on the execution being atomic + unless the particular method explicitly provides such guarantees. + + Upon execution, a method with a Depth header will perform as much of + its assigned task as possible and then return a response specifying + what it was able to accomplish and what it failed to do. + + So, for example, an attempt to COPY a hierarchy may result in some of + the members being copied and some not. + + Any headers on a method that has a defined interaction with the Depth + header MUST be applied to all resources in the scope of the method + except where alternative behavior is explicitly defined. For example, + an If-Match header will have its value applied against every resource + in the method's scope and will cause the method to fail if the header + fails to match. + + If a resource, source or destination, within the scope of the method + with a Depth header is locked in such a way as to prevent the + successful execution of the method, then the lock token for that + resource MUST be submitted with the request in the If request header. + + The Depth header only specifies the behavior of the method with + regards to internal children. If a resource does not have internal + children then the Depth header MUST be ignored. + + + + + +Goland, et al. Standards Track [Page 53] + +RFC 2518 WEBDAV February 1999 + + + Please note, however, that it is always an error to submit a value + for the Depth header that is not allowed by the method's definition. + Thus submitting a "Depth: 1" on a COPY, even if the resource does not + have internal members, will result in a 400 (Bad Request). The method + should fail not because the resource doesn't have internal members, + but because of the illegal value in the header. + +9.3 Destination Header + + Destination = "Destination" ":" absoluteURI + + The Destination header specifies the URI which identifies a + destination resource for methods such as COPY and MOVE, which take + two URIs as parameters. Note that the absoluteURI production is + defined in [RFC2396]. + +9.4 If Header + + If = "If" ":" ( 1*No-tag-list | 1*Tagged-list) + No-tag-list = List + Tagged-list = Resource 1*List + Resource = Coded-URL + List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")" + State-token = Coded-URL + Coded-URL = "<" absoluteURI ">" + + The If header is intended to have similar functionality to the If- + Match header defined in section 14.25 of [RFC2068]. However the If + header is intended for use with any URI which represents state + information, referred to as a state token, about a resource as well + as ETags. A typical example of a state token is a lock token, and + lock tokens are the only state tokens defined in this specification. + + All DAV compliant resources MUST honor the If header. + + The If header's purpose is to describe a series of state lists. If + the state of the resource to which the header is applied does not + match any of the specified state lists then the request MUST fail + with a 412 (Precondition Failed). If one of the described state + lists matches the state of the resource then the request may succeed. + + Note that the absoluteURI production is defined in [RFC2396]. + + + + + + + + + +Goland, et al. Standards Track [Page 54] + +RFC 2518 WEBDAV February 1999 + + +9.4.1 No-tag-list Production + + The No-tag-list production describes a series of state tokens and + ETags. If multiple No-tag-list productions are used then one only + needs to match the state of the resource for the method to be allowed + to continue. + + If a method, due to the presence of a Depth or Destination header, is + applied to multiple resources then the No-tag-list production MUST be + applied to each resource the method is applied to. + +9.4.1.1 Example - No-tag-list If Header + + If: ( ["I am an ETag"]) (["I am another + ETag"]) + + The previous header would require that any resources within the scope + of the method must either be locked with the specified lock token and + in the state identified by the "I am an ETag" ETag or in the state + identified by the second ETag "I am another ETag". To put the matter + more plainly one can think of the previous If header as being in the + form (or (and ["I am an ETag"]) (and + ["I am another ETag"])). + +9.4.2 Tagged-list Production + + The tagged-list production scopes a list production. That is, it + specifies that the lists following the resource specification only + apply to the specified resource. The scope of the resource + production begins with the list production immediately following the + resource production and ends with the next resource production, if + any. + + When the If header is applied to a particular resource, the Tagged- + list productions MUST be searched to determine if any of the listed + resources match the operand resource(s) for the current method. If + none of the resource productions match the current resource then the + header MUST be ignored. If one of the resource productions does + match the name of the resource under consideration then the list + productions following the resource production MUST be applied to the + resource in the manner specified in the previous section. + + The same URI MUST NOT appear more than once in a resource production + in an If header. + + + + + + + +Goland, et al. Standards Track [Page 55] + +RFC 2518 WEBDAV February 1999 + + +9.4.2.1 Example - Tagged List If header + + COPY /resource1 HTTP/1.1 + Host: www.foo.bar + Destination: http://www.foo.bar/resource2 + If: ( + [W/"A weak ETag"]) (["strong ETag"]) + (["another strong ETag"]) + + In this example http://www.foo.bar/resource1 is being copied to + http://www.foo.bar/resource2. When the method is first applied to + http://www.foo.bar/resource1, resource1 must be in the state + specified by "( [W/"A weak ETag"]) + (["strong ETag"])", that is, it either must be locked with a lock + token of "locktoken:a-write-lock-token" and have a weak entity tag + W/"A weak ETag" or it must have a strong entity tag "strong ETag". + + That is the only success condition since the resource + http://www.bar.bar/random never has the method applied to it (the + only other resource listed in the If header) and + http://www.foo.bar/resource2 is not listed in the If header. + +9.4.3 not Production + + Every state token or ETag is either current, and hence describes the + state of a resource, or is not current, and does not describe the + state of a resource. The boolean operation of matching a state token + or ETag to the current state of a resource thus resolves to a true or + false value. The not production is used to reverse that value. The + scope of the not production is the state-token or entity-tag + immediately following it. + + If: (Not ) + + When submitted with a request, this If header requires that all + operand resources must not be locked with locktoken:write1 and must + be locked with locktoken:write2. + +9.4.4 Matching Function + + When performing If header processing, the definition of a matching + state token or entity tag is as follows. + + Matching entity tag: Where the entity tag matches an entity tag + associated with that resource. + + Matching state token: Where there is an exact match between the state + token in the If header and any state token on the resource. + + + +Goland, et al. Standards Track [Page 56] + +RFC 2518 WEBDAV February 1999 + + +9.4.5 If Header and Non-DAV Compliant Proxies + + Non-DAV compliant proxies will not honor the If header, since they + will not understand the If header, and HTTP requires non-understood + headers to be ignored. When communicating with HTTP/1.1 proxies, the + "Cache-Control: no-cache" request header MUST be used so as to + prevent the proxy from improperly trying to service the request from + its cache. When dealing with HTTP/1.0 proxies the "Pragma: no-cache" + request header MUST be used for the same reason. + +9.5 Lock-Token Header + + Lock-Token = "Lock-Token" ":" Coded-URL + + The Lock-Token request header is used with the UNLOCK method to + identify the lock to be removed. The lock token in the Lock-Token + request header MUST identify a lock that contains the resource + identified by Request-URI as a member. + + The Lock-Token response header is used with the LOCK method to + indicate the lock token created as a result of a successful LOCK + request to create a new lock. + +9.6 Overwrite Header + + Overwrite = "Overwrite" ":" ("T" | "F") + + The Overwrite header specifies whether the server should overwrite + the state of a non-null destination resource during a COPY or MOVE. + A value of "F" states that the server must not perform the COPY or + MOVE operation if the state of the destination resource is non-null. + If the overwrite header is not included in a COPY or MOVE request + then the resource MUST treat the request as if it has an overwrite + header of value "T". While the Overwrite header appears to duplicate + the functionality of the If-Match: * header of HTTP/1.1, If-Match + applies only to the Request-URI, and not to the Destination of a COPY + or MOVE. + + If a COPY or MOVE is not performed due to the value of the Overwrite + header, the method MUST fail with a 412 (Precondition Failed) status + code. + + All DAV compliant resources MUST support the Overwrite header. + +9.7 Status-URI Response Header + + The Status-URI response header may be used with the 102 (Processing) + status code to inform the client as to the status of a method. + + + +Goland, et al. Standards Track [Page 57] + +RFC 2518 WEBDAV February 1999 + + + Status-URI = "Status-URI" ":" *(Status-Code Coded-URL) ; Status-Code + is defined in 6.1.1 of [RFC2068] + + The URIs listed in the header are source resources which have been + affected by the outstanding method. The status code indicates the + resolution of the method on the identified resource. So, for + example, if a MOVE method on a collection is outstanding and a 102 + (Processing) response with a Status-URI response header is returned, + the included URIs will indicate resources that have had move + attempted on them and what the result was. + +9.8 Timeout Request Header + + TimeOut = "Timeout" ":" 1#TimeType + TimeType = ("Second-" DAVTimeOutVal | "Infinite" | Other) + DAVTimeOutVal = 1*digit + Other = "Extend" field-value ; See section 4.2 of [RFC2068] + + Clients may include Timeout headers in their LOCK requests. However, + the server is not required to honor or even consider these requests. + Clients MUST NOT submit a Timeout request header with any method + other than a LOCK method. + + A Timeout request header MUST contain at least one TimeType and may + contain multiple TimeType entries. The purpose of listing multiple + TimeType entries is to indicate multiple different values and value + types that are acceptable to the client. The client lists the + TimeType entries in order of preference. + + Timeout response values MUST use a Second value, Infinite, or a + TimeType the client has indicated familiarity with. The server may + assume a client is familiar with any TimeType submitted in a Timeout + header. + + The "Second" TimeType specifies the number of seconds that will + elapse between granting of the lock at the server, and the automatic + removal of the lock. The timeout value for TimeType "Second" MUST + NOT be greater than 2^32-1. + + The timeout counter SHOULD be restarted any time an owner of the lock + sends a method to any member of the lock, including unsupported + methods, or methods which are unsuccessful. However the lock MUST be + refreshed if a refresh LOCK method is successfully received. + + If the timeout expires then the lock may be lost. Specifically, if + the server wishes to harvest the lock upon time-out, the server + SHOULD act as if an UNLOCK method was executed by the server on the + resource using the lock token of the timed-out lock, performed with + + + +Goland, et al. Standards Track [Page 58] + +RFC 2518 WEBDAV February 1999 + + + its override authority. Thus logs should be updated with the + disposition of the lock, notifications should be sent, etc., just as + they would be for an UNLOCK request. + + Servers are advised to pay close attention to the values submitted by + clients, as they will be indicative of the type of activity the + client intends to perform. For example, an applet running in a + browser may need to lock a resource, but because of the instability + of the environment within which the applet is running, the applet may + be turned off without warning. As a result, the applet is likely to + ask for a relatively small timeout value so that if the applet dies, + the lock can be quickly harvested. However, a document management + system is likely to ask for an extremely long timeout because its + user may be planning on going off-line. + + A client MUST NOT assume that just because the time-out has expired + the lock has been lost. + +10 Status Code Extensions to HTTP/1.1 + + The following status codes are added to those defined in HTTP/1.1 + [RFC2068]. + +10.1 102 Processing + + The 102 (Processing) status code is an interim response used to + inform the client that the server has accepted the complete request, + but has not yet completed it. This status code SHOULD only be sent + when the server has a reasonable expectation that the request will + take significant time to complete. As guidance, if a method is taking + longer than 20 seconds (a reasonable, but arbitrary value) to process + the server SHOULD return a 102 (Processing) response. The server MUST + send a final response after the request has been completed. + + Methods can potentially take a long period of time to process, + especially methods that support the Depth header. In such cases the + client may time-out the connection while waiting for a response. To + prevent this the server may return a 102 (Processing) status code to + indicate to the client that the server is still processing the + method. + +10.2 207 Multi-Status + + The 207 (Multi-Status) status code provides status for multiple + independent operations (see section 11 for more information). + + + + + + +Goland, et al. Standards Track [Page 59] + +RFC 2518 WEBDAV February 1999 + + +10.3 422 Unprocessable Entity + + The 422 (Unprocessable Entity) status code means the server + understands the content type of the request entity (hence a + 415(Unsupported Media Type) status code is inappropriate), and the + syntax of the request entity is correct (thus a 400 (Bad Request) + status code is inappropriate) but was unable to process the contained + instructions. For example, this error condition may occur if an XML + request body contains well-formed (i.e., syntactically correct), but + semantically erroneous XML instructions. + +10.4 423 Locked + + The 423 (Locked) status code means the source or destination resource + of a method is locked. + +10.5 424 Failed Dependency + + The 424 (Failed Dependency) status code means that the method could + not be performed on the resource because the requested action + depended on another action and that action failed. For example, if a + command in a PROPPATCH method fails then, at minimum, the rest of the + commands will also fail with 424 (Failed Dependency). + +10.6 507 Insufficient Storage + + The 507 (Insufficient Storage) status code means the method could not + be performed on the resource because the server is unable to store + the representation needed to successfully complete the request. This + condition is considered to be temporary. If the request which + received this status code was the result of a user action, the + request MUST NOT be repeated until it is requested by a separate user + action. + +11 Multi-Status Response + + The default 207 (Multi-Status) response body is a text/xml or + application/xml HTTP entity that contains a single XML element called + multistatus, which contains a set of XML elements called response + which contain 200, 300, 400, and 500 series status codes generated + during the method invocation. 100 series status codes SHOULD NOT be + recorded in a response XML element. + + + + + + + + + +Goland, et al. Standards Track [Page 60] + +RFC 2518 WEBDAV February 1999 + + +12 XML Element Definitions + + In the section below, the final line of each section gives the + element type declaration using the format defined in [REC-XML]. The + "Value" field, where present, specifies further restrictions on the + allowable contents of the XML element using BNF (i.e., to further + restrict the values of a PCDATA element). + +12.1 activelock XML Element + + Name: activelock + Namespace: DAV: + Purpose: Describes a lock on a resource. + + + +12.1.1 depth XML Element + + Name: depth + Namespace: DAV: + Purpose: The value of the Depth header. + Value: "0" | "1" | "infinity" + + + +12.1.2 locktoken XML Element + + Name: locktoken + Namespace: DAV: + Purpose: The lock token associated with a lock. + Description: The href contains one or more opaque lock token URIs + which all refer to the same lock (i.e., the OpaqueLockToken-URI + production in section 6.4). + + + +12.1.3 timeout XML Element + + Name: timeout + Namespace: DAV: + Purpose: The timeout associated with a lock + Value: TimeType ;Defined in section 9.8 + + + + + + + + +Goland, et al. Standards Track [Page 61] + +RFC 2518 WEBDAV February 1999 + + +12.2 collection XML Element + + Name: collection + Namespace: DAV: + Purpose: Identifies the associated resource as a collection. The + resourcetype property of a collection resource MUST have this value. + + + +12.3 href XML Element + + Name: href + Namespace: DAV: + Purpose: Identifies the content of the element as a URI. + Value: URI ; See section 3.2.1 of [RFC2068] + + + +12.4 link XML Element + + Name: link + Namespace: DAV: + Purpose: Identifies the property as a link and contains the source + and destination of that link. + Description: The link XML element is used to provide the sources and + destinations of a link. The name of the property containing the link + XML element provides the type of the link. Link is a multi-valued + element, so multiple links may be used together to indicate multiple + links with the same type. The values in the href XML elements inside + the src and dst XML elements of the link XML element MUST NOT be + rejected if they point to resources which do not exist. + + + +12.4.1 dst XML Element + + Name: dst + Namespace: DAV: + Purpose: Indicates the destination of a link + Value: URI + + + +12.4.2 src XML Element + + Name: src + Namespace: DAV: + Purpose: Indicates the source of a link. + + + +Goland, et al. Standards Track [Page 62] + +RFC 2518 WEBDAV February 1999 + + + Value: URI + + + +12.5 lockentry XML Element + + Name: lockentry + Namespace: DAV: + Purpose: Defines the types of locks that can be used with the + resource. + + + +12.6 lockinfo XML Element + + Name: lockinfo + Namespace: DAV: + Purpose: The lockinfo XML element is used with a LOCK method to + specify the type of lock the client wishes to have created. + + + +12.7 lockscope XML Element + + Name: lockscope + Namespace: DAV: + Purpose: Specifies whether a lock is an exclusive lock, or a + shared lock. + + + +12.7.1 exclusive XML Element + + Name: exclusive + Namespace: DAV: + Purpose: Specifies an exclusive lock + + + +12.7.2 shared XML Element + + Name: shared + Namespace: DAV: + Purpose: Specifies a shared lock + + + + + + + +Goland, et al. Standards Track [Page 63] + +RFC 2518 WEBDAV February 1999 + + +12.8 locktype XML Element + + Name: locktype + Namespace: DAV: + Purpose: Specifies the access type of a lock. At present, this + specification only defines one lock type, the write lock. + + + +12.8.1 write XML Element + + Name: write + Namespace: DAV: + Purpose: Specifies a write lock. + + + +12.9 multistatus XML Element + + Name: multistatus + Namespace: DAV: + Purpose: Contains multiple response messages. + Description: The responsedescription at the top level is used to + provide a general message describing the overarching nature of the + response. If this value is available an application may use it + instead of presenting the individual response descriptions contained + within the responses. + + + +12.9.1 response XML Element + + Name: response + Namespace: DAV: + Purpose: Holds a single response describing the effect of a + method on resource and/or its properties. + Description: A particular href MUST NOT appear more than once as the + child of a response XML element under a multistatus XML element. + This requirement is necessary in order to keep processing costs for a + response to linear time. Essentially, this prevents having to search + in order to group together all the responses by href. There are, + however, no requirements regarding ordering based on href values. + + + + + + + + +Goland, et al. Standards Track [Page 64] + +RFC 2518 WEBDAV February 1999 + + +12.9.1.1 propstat XML Element + + Name: propstat + Namespace: DAV: + Purpose: Groups together a prop and status element that is + associated with a particular href element. + Description: The propstat XML element MUST contain one prop XML + element and one status XML element. The contents of the prop XML + element MUST only list the names of properties to which the result in + the status element applies. + + + +12.9.1.2 status XML Element + + Name: status + Namespace: DAV: + Purpose: Holds a single HTTP status-line + Value: status-line ;status-line defined in [RFC2068] + + + +12.9.2 responsedescription XML Element + + Name: responsedescription + Namespace: DAV: + Purpose: Contains a message that can be displayed to the user + explaining the nature of the response. + Description: This XML element provides information suitable to be + presented to a user. + + + +12.10 owner XML Element + + Name: owner + Namespace: DAV: + Purpose: Provides information about the principal taking out a + lock. + Description: The owner XML element provides information sufficient + for either directly contacting a principal (such as a telephone + number or Email URI), or for discovering the principal (such as the + URL of a homepage) who owns a lock. + + + + + + + + +Goland, et al. Standards Track [Page 65] + +RFC 2518 WEBDAV February 1999 + + +12.11 prop XML element + + Name: prop + Namespace: DAV: + Purpose: Contains properties related to a resource. + Description: The prop XML element is a generic container for + properties defined on resources. All elements inside a prop XML + element MUST define properties related to the resource. No other + elements may be used inside of a prop element. + + + +12.12 propertybehavior XML element + + Name: propertybehavior Namespace: DAV: Purpose: Specifies + how properties are handled during a COPY or MOVE. + Description: The propertybehavior XML element specifies how + properties are handled during a COPY or MOVE. If this XML element is + not included in the request body then the server is expected to act + as defined by the default property handling behavior of the + associated method. All WebDAV compliant resources MUST support the + propertybehavior XML element. + + + +12.12.1 keepalive XML element + + Name: keepalive + Namespace: DAV: + Purpose: Specifies requirements for the copying/moving of live + properties. + Description: If a list of URIs is included as the value of keepalive + then the named properties MUST be "live" after they are copied + (moved) to the destination resource of a COPY (or MOVE). If the + value "*" is given for the keepalive XML element, this designates + that all live properties on the source resource MUST be live on the + destination. If the requirements specified by the keepalive element + can not be honored then the method MUST fail with a 412 (Precondition + Failed). All DAV compliant resources MUST support the keepalive XML + element for use with the COPY and MOVE methods. + Value: "*" ; #PCDATA value can only be "*" + + + + + + + + + + +Goland, et al. Standards Track [Page 66] + +RFC 2518 WEBDAV February 1999 + + +12.12.2 omit XML element + + Name: omit + Namespace: DAV: + Purpose: The omit XML element instructs the server that it should + use best effort to copy properties but a failure to copy a property + MUST NOT cause the method to fail. Description: The default behavior + for a COPY or MOVE is to copy/move all properties or fail the method. + In certain circumstances, such as when a server copies a resource + over another protocol such as FTP, it may not be possible to + copy/move the properties associated with the resource. Thus any + attempt to copy/move over FTP would always have to fail because + properties could not be moved over, even as dead properties. All DAV + compliant resources MUST support the omit XML element on COPY/MOVE + methods. + + + +12.13 propertyupdate XML element + + Name: propertyupdate + Namespace: DAV: + Purpose: Contains a request to alter the properties on a + resource. + Description: This XML element is a container for the information + required to modify the properties on the resource. This XML element + is multi-valued. + + + +12.13.1 remove XML element + + Name: remove + Namespace: DAV: + Purpose: Lists the DAV properties to be removed from a resource. + Description: Remove instructs that the properties specified in prop + should be removed. Specifying the removal of a property that does + not exist is not an error. All the XML elements in a prop XML + element inside of a remove XML element MUST be empty, as only the + names of properties to be removed are required. + + + +12.13.2 set XML element + + Name: set + Namespace: DAV: + Purpose: Lists the DAV property values to be set for a resource. + + + +Goland, et al. Standards Track [Page 67] + +RFC 2518 WEBDAV February 1999 + + + Description: The set XML element MUST contain only a prop XML + element. The elements contained by the prop XML element inside the + set XML element MUST specify the name and value of properties that + are set on the resource identified by Request-URI. If a property + already exists then its value is replaced. Language tagging + information in the property's value (in the "xml:lang" attribute, if + present) MUST be persistently stored along with the property, and + MUST be subsequently retrievable using PROPFIND. + + + +12.14 propfind XML Element + + Name: propfind + Namespace: DAV: + Purpose: Specifies the properties to be returned from a PROPFIND + method. Two special elements are specified for use with propfind, + allprop and propname. If prop is used inside propfind it MUST only + contain property names, not values. + + + +12.14.1 allprop XML Element + + Name: allprop Namespace: DAV: Purpose: The allprop XML + element specifies that all property names and values on the resource + are to be returned. + + + +12.14.2 propname XML Element + + Name: propname Namespace: DAV: Purpose: The propname XML + element specifies that only a list of property names on the resource + is to be returned. + + + +13 DAV Properties + + For DAV properties, the name of the property is also the same as the + name of the XML element that contains its value. In the section + below, the final line of each section gives the element type + declaration using the format defined in [REC-XML]. The "Value" field, + where present, specifies further restrictions on the allowable + contents of the XML element using BNF (i.e., to further restrict the + values of a PCDATA element). + + + + +Goland, et al. Standards Track [Page 68] + +RFC 2518 WEBDAV February 1999 + + +13.1 creationdate Property + + Name: creationdate + Namespace: DAV: + Purpose: Records the time and date the resource was created. + Value: date-time ; See Appendix 2 + Description: The creationdate property should be defined on all DAV + compliant resources. If present, it contains a timestamp of the + moment when the resource was created (i.e., the moment it had non- + null state). + + + +13.2 displayname Property + + Name: displayname + Namespace: DAV: + Purpose: Provides a name for the resource that is suitable for + presentation to a user. + Description: The displayname property should be defined on all DAV + compliant resources. If present, the property contains a description + of the resource that is suitable for presentation to a user. + + + +13.3 getcontentlanguage Property + + Name: getcontentlanguage + Namespace: DAV: + Purpose: Contains the Content-Language header returned by a GET + without accept headers + Description: The getcontentlanguage property MUST be defined on any + DAV compliant resource that returns the Content-Language header on a + GET. + Value: language-tag ;language-tag is defined in section 14.13 + of [RFC2068] + + + +13.4 getcontentlength Property + + Name: getcontentlength + Namespace: DAV: + Purpose: Contains the Content-Length header returned by a GET + without accept headers. + Description: The getcontentlength property MUST be defined on any + DAV compliant resource that returns the Content-Length header in + response to a GET. + + + +Goland, et al. Standards Track [Page 69] + +RFC 2518 WEBDAV February 1999 + + + Value: content-length ; see section 14.14 of [RFC2068] + + + +13.5 getcontenttype Property + + Name: getcontenttype + Namespace: DAV: + Purpose: Contains the Content-Type header returned by a GET + without accept headers. + Description: This getcontenttype property MUST be defined on any DAV + compliant resource that returns the Content-Type header in response + to a GET. + Value: media-type ; defined in section 3.7 of [RFC2068] + + + +13.6 getetag Property + + Name: getetag + Namespace: DAV: + Purpose: Contains the ETag header returned by a GET without + accept headers. + Description: The getetag property MUST be defined on any DAV + compliant resource that returns the Etag header. + Value: entity-tag ; defined in section 3.11 of [RFC2068] + + + +13.7 getlastmodified Property + + Name: getlastmodified + Namespace: DAV: + Purpose: Contains the Last-Modified header returned by a GET + method without accept headers. + Description: Note that the last-modified date on a resource may + reflect changes in any part of the state of the resource, not + necessarily just a change to the response to the GET method. For + example, a change in a property may cause the last-modified date to + change. The getlastmodified property MUST be defined on any DAV + compliant resource that returns the Last-Modified header in response + to a GET. + Value: HTTP-date ; defined in section 3.3.1 of [RFC2068] + + + + + + + + +Goland, et al. Standards Track [Page 70] + +RFC 2518 WEBDAV February 1999 + + +13.8 lockdiscovery Property + + Name: lockdiscovery + Namespace: DAV: + Purpose: Describes the active locks on a resource + Description: The lockdiscovery property returns a listing of who has + a lock, what type of lock he has, the timeout type and the time + remaining on the timeout, and the associated lock token. The server + is free to withhold any or all of this information if the requesting + principal does not have sufficient access rights to see the requested + data. + + + +13.8.1 Example - Retrieving the lockdiscovery Property + + >>Request + + PROPFIND /container/ HTTP/1.1 + Host: www.foo.bar + Content-Length: xxxx + Content-Type: text/xml; charset="utf-8" + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/container/ + + + + + + + 0 + Jane Smith + Infinite + + + + +Goland, et al. Standards Track [Page 71] + +RFC 2518 WEBDAV February 1999 + + + + opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76 + + + + + + HTTP/1.1 200 OK + + + + + This resource has a single exclusive write lock on it, with an + infinite timeout. + +13.9 resourcetype Property + + Name: resourcetype + Namespace: DAV: + Purpose: Specifies the nature of the resource. + Description: The resourcetype property MUST be defined on all DAV + compliant resources. The default value is empty. + + + +13.10 source Property + + Name: source + Namespace: DAV: + Purpose: The destination of the source link identifies the + resource that contains the unprocessed source of the link's source. + Description: The source of the link (src) is typically the URI of the + output resource on which the link is defined, and there is typically + only one destination (dst) of the link, which is the URI where the + unprocessed source of the resource may be accessed. When more than + one link destination exists, this specification asserts no policy on + ordering. + + + +13.10.1 Example - A source Property + + + + + + Source + http://foo.bar/program + + + +Goland, et al. Standards Track [Page 72] + +RFC 2518 WEBDAV February 1999 + + + http://foo.bar/src/main.c + + + Library + http://foo.bar/program + http://foo.bar/src/main.lib + + + Makefile + http://foo.bar/program + http://foo.bar/src/makefile + + + + + In this example the resource http://foo.bar/program has a source + property that contains three links. Each link contains three + elements, two of which, src and dst, are part of the DAV schema + defined in this document, and one which is defined by the schema + http://www.foocorp.com/project/ (Source, Library, and Makefile). A + client which only implements the elements in the DAV spec will not + understand the foocorp elements and will ignore them, thus seeing the + expected source and destination links. An enhanced client may know + about the foocorp elements and be able to present the user with + additional information about the links. This example demonstrates + the power of XML markup, allowing element values to be enhanced + without breaking older clients. + +13.11 supportedlock Property + + Name: supportedlock + Namespace: DAV: + Purpose: To provide a listing of the lock capabilities supported + by the resource. + Description: The supportedlock property of a resource returns a + listing of the combinations of scope and access types which may be + specified in a lock request on the resource. Note that the actual + contents are themselves controlled by access controls so a server is + not required to provide information the client is not authorized to + see. + + + +13.11.1 Example - Retrieving the supportedlock Property + + >>Request + + PROPFIND /container/ HTTP/1.1 + + + +Goland, et al. Standards Track [Page 73] + +RFC 2518 WEBDAV February 1999 + + + Host: www.foo.bar + Content-Length: xxxx + Content-Type: text/xml; charset="utf-8" + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.foo.bar/container/ + + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + +14 Instructions for Processing XML in DAV + + All DAV compliant resources MUST ignore any unknown XML element and + all its children encountered while processing a DAV method that uses + XML as its command language. + + This restriction also applies to the processing, by clients, of DAV + property values where unknown XML elements SHOULD be ignored unless + the property's schema declares otherwise. + + + + + +Goland, et al. Standards Track [Page 74] + +RFC 2518 WEBDAV February 1999 + + + This restriction does not apply to setting dead DAV properties on the + server where the server MUST record unknown XML elements. + + Additionally, this restriction does not apply to the use of XML where + XML happens to be the content type of the entity body, for example, + when used as the body of a PUT. + + Since XML can be transported as text/xml or application/xml, a DAV + server MUST accept DAV method requests with XML parameters + transported as either text/xml or application/xml, and DAV client + MUST accept XML responses using either text/xml or application/xml. + +15 DAV Compliance Classes + + A DAV compliant resource can choose from two classes of compliance. + A client can discover the compliance classes of a resource by + executing OPTIONS on the resource, and examining the "DAV" header + which is returned. + + Since this document describes extensions to the HTTP/1.1 protocol, + minimally all DAV compliant resources, clients, and proxies MUST be + compliant with [RFC2068]. + + Compliance classes are not necessarily sequential. A resource that is + class 2 compliant must also be class 1 compliant; but if additional + compliance classes are defined later, a resource that is class 1, 2, + and 4 compliant might not be class 3 compliant. Also note that + identifiers other than numbers may be used as compliance class + identifiers. + +15.1 Class 1 + + A class 1 compliant resource MUST meet all "MUST" requirements in all + sections of this document. + + Class 1 compliant resources MUST return, at minimum, the value "1" in + the DAV header on all responses to the OPTIONS method. + +15.2 Class 2 + + A class 2 compliant resource MUST meet all class 1 requirements and + support the LOCK method, the supportedlock property, the + lockdiscovery property, the Time-Out response header and the Lock- + Token request header. A class "2" compliant resource SHOULD also + support the Time-Out request header and the owner XML element. + + Class 2 compliant resources MUST return, at minimum, the values "1" + and "2" in the DAV header on all responses to the OPTIONS method. + + + +Goland, et al. Standards Track [Page 75] + +RFC 2518 WEBDAV February 1999 + + +16 Internationalization Considerations + + In the realm of internationalization, this specification complies + with the IETF Character Set Policy [RFC2277]. In this specification, + human-readable fields can be found either in the value of a property, + or in an error message returned in a response entity body. In both + cases, the human-readable content is encoded using XML, which has + explicit provisions for character set tagging and encoding, and + requires that XML processors read XML elements encoded, at minimum, + using the UTF-8 [UTF-8] encoding of the ISO 10646 multilingual plane. + XML examples in this specification demonstrate use of the charset + parameter of the Content-Type header, as defined in [RFC2376], as + well as the XML "encoding" attribute, which together provide charset + identification information for MIME and XML processors. + + XML also provides a language tagging capability for specifying the + language of the contents of a particular XML element. XML uses + either IANA registered language tags (see [RFC1766]) or ISO 639 + language tags [ISO-639] in the "xml:lang" attribute of an XML element + to identify the language of its content and attributes. + + WebDAV applications MUST support the character set tagging, character + set encoding, and the language tagging functionality of the XML + specification. Implementors of WebDAV applications are strongly + encouraged to read "XML Media Types" [RFC2376] for instruction on + which MIME media type to use for XML transport, and on use of the + charset parameter of the Content-Type header. + + Names used within this specification fall into three categories: + names of protocol elements such as methods and headers, names of XML + elements, and names of properties. Naming of protocol elements + follows the precedent of HTTP, using English names encoded in USASCII + for methods and headers. Since these protocol elements are not + visible to users, and are in fact simply long token identifiers, they + do not need to support encoding in multiple character sets. + Similarly, though the names of XML elements used in this + specification are English names encoded in UTF-8, these names are not + visible to the user, and hence do not need to support multiple + character set encodings. + + The name of a property defined on a resource is a URI. Although some + applications (e.g., a generic property viewer) will display property + URIs directly to their users, it is expected that the typical + application will use a fixed set of properties, and will provide a + mapping from the property name URI to a human-readable field when + displaying the property name to a user. It is only in the case where + + + + + +Goland, et al. Standards Track [Page 76] + +RFC 2518 WEBDAV February 1999 + + + the set of properties is not known ahead of time that an application + need display a property name URI to a user. We recommend that + applications provide human-readable property names wherever feasible. + + For error reporting, we follow the convention of HTTP/1.1 status + codes, including with each status code a short, English description + of the code (e.g., 423 (Locked)). While the possibility exists that + a poorly crafted user agent would display this message to a user, + internationalized applications will ignore this message, and display + an appropriate message in the user's language and character set. + + Since interoperation of clients and servers does not require locale + information, this specification does not specify any mechanism for + transmission of this information. + +17 Security Considerations + + This section is provided to detail issues concerning security + implications of which WebDAV applications need to be aware. + + All of the security considerations of HTTP/1.1 (discussed in + [RFC2068]) and XML (discussed in [RFC2376]) also apply to WebDAV. In + addition, the security risks inherent in remote authoring require + stronger authentication technology, introduce several new privacy + concerns, and may increase the hazards from poor server design. + These issues are detailed below. + +17.1 Authentication of Clients + + Due to their emphasis on authoring, WebDAV servers need to use + authentication technology to protect not just access to a network + resource, but the integrity of the resource as well. Furthermore, + the introduction of locking functionality requires support for + authentication. + + A password sent in the clear over an insecure channel is an + inadequate means for protecting the accessibility and integrity of a + resource as the password may be intercepted. Since Basic + authentication for HTTP/1.1 performs essentially clear text + transmission of a password, Basic authentication MUST NOT be used to + authenticate a WebDAV client to a server unless the connection is + secure. Furthermore, a WebDAV server MUST NOT send Basic + authentication credentials in a WWW-Authenticate header unless the + connection is secure. Examples of secure connections include a + Transport Layer Security (TLS) connection employing a strong cipher + suite with mutual authentication of client and server, or a + connection over a network which is physically secure, for example, an + isolated network in a building with restricted access. + + + +Goland, et al. Standards Track [Page 77] + +RFC 2518 WEBDAV February 1999 + + + WebDAV applications MUST support the Digest authentication scheme + [RFC2069]. Since Digest authentication verifies that both parties to + a communication know a shared secret, a password, without having to + send that secret in the clear, Digest authentication avoids the + security problems inherent in Basic authentication while providing a + level of authentication which is useful in a wide range of scenarios. + +17.2 Denial of Service + + Denial of service attacks are of special concern to WebDAV servers. + WebDAV plus HTTP enables denial of service attacks on every part of a + system's resources. + + The underlying storage can be attacked by PUTting extremely large + files. + + Asking for recursive operations on large collections can attack + processing time. + + Making multiple pipelined requests on multiple connections can attack + network connections. + + WebDAV servers need to be aware of the possibility of a denial of + service attack at all levels. + +17.3 Security through Obscurity + + WebDAV provides, through the PROPFIND method, a mechanism for listing + the member resources of a collection. This greatly diminishes the + effectiveness of security or privacy techniques that rely only on the + difficulty of discovering the names of network resources. Users of + WebDAV servers are encouraged to use access control techniques to + prevent unwanted access to resources, rather than depending on the + relative obscurity of their resource names. + +17.4 Privacy Issues Connected to Locks + + When submitting a lock request a user agent may also submit an owner + XML field giving contact information for the person taking out the + lock (for those cases where a person, rather than a robot, is taking + out the lock). This contact information is stored in a lockdiscovery + property on the resource, and can be used by other collaborators to + begin negotiation over access to the resource. However, in many + cases this contact information can be very private, and should not be + widely disseminated. Servers SHOULD limit read access to the + lockdiscovery property as appropriate. Furthermore, user agents + + + + + +Goland, et al. Standards Track [Page 78] + +RFC 2518 WEBDAV February 1999 + + + SHOULD provide control over whether contact information is sent at + all, and if contact information is sent, control over exactly what + information is sent. + +17.5 Privacy Issues Connected to Properties + + Since property values are typically used to hold information such as + the author of a document, there is the possibility that privacy + concerns could arise stemming from widespread access to a resource's + property data. To reduce the risk of inadvertent release of private + information via properties, servers are encouraged to develop access + control mechanisms that separate read access to the resource body and + read access to the resource's properties. This allows a user to + control the dissemination of their property data without overly + restricting access to the resource's contents. + +17.6 Reduction of Security due to Source Link + + HTTP/1.1 warns against providing read access to script code because + it may contain sensitive information. Yet WebDAV, via its source + link facility, can potentially provide a URI for script resources so + they may be authored. For HTTP/1.1, a server could reasonably + prevent access to source resources due to the predominance of read- + only access. WebDAV, with its emphasis on authoring, encourages read + and write access to source resources, and provides the source link + facility to identify the source. This reduces the security benefits + of eliminating access to source resources. Users and administrators + of WebDAV servers should be very cautious when allowing remote + authoring of scripts, limiting read and write access to the source + resources to authorized principals. + +17.7 Implications of XML External Entities + + XML supports a facility known as "external entities", defined in + section 4.2.2 of [REC-XML], which instruct an XML processor to + retrieve and perform an inline include of XML located at a particular + URI. An external XML entity can be used to append or modify the + document type declaration (DTD) associated with an XML document. An + external XML entity can also be used to include XML within the + content of an XML document. For non-validating XML, such as the XML + used in this specification, including an external XML entity is not + required by [REC-XML]. However, [REC-XML] does state that an XML + processor may, at its discretion, include the external XML entity. + + External XML entities have no inherent trustworthiness and are + subject to all the attacks that are endemic to any HTTP GET request. + Furthermore, it is possible for an external XML entity to modify the + DTD, and hence affect the final form of an XML document, in the worst + + + +Goland, et al. Standards Track [Page 79] + +RFC 2518 WEBDAV February 1999 + + + case significantly modifying its semantics, or exposing the XML + processor to the security risks discussed in [RFC2376]. Therefore, + implementers must be aware that external XML entities should be + treated as untrustworthy. + + There is also the scalability risk that would accompany a widely + deployed application which made use of external XML entities. In + this situation, it is possible that there would be significant + numbers of requests for one external XML entity, potentially + overloading any server which fields requests for the resource + containing the external XML entity. + +17.8 Risks Connected with Lock Tokens + + This specification, in section 6.4, requires the use of Universal + Unique Identifiers (UUIDs) for lock tokens, in order to guarantee + their uniqueness across space and time. UUIDs, as defined in [ISO- + 11578], contain a "node" field which "consists of the IEEE address, + usually the host address. For systems with multiple IEEE 802 nodes, + any available node address can be used." Since a WebDAV server will + issue many locks over its lifetime, the implication is that it will + also be publicly exposing its IEEE 802 address. + + There are several risks associated with exposure of IEEE 802 + addresses. Using the IEEE 802 address: + + * It is possible to track the movement of hardware from subnet to + subnet. + + * It may be possible to identify the manufacturer of the hardware + running a WebDAV server. + + * It may be possible to determine the number of each type of computer + running WebDAV. + + Section 6.4.1 of this specification details an alternate mechanism + for generating the "node" field of a UUID without using an IEEE 802 + address, which alleviates the risks associated with exposure of IEEE + 802 addresses by using an alternate source of uniqueness. + +18 IANA Considerations + + This document defines two namespaces, the namespace of property + names, and the namespace of WebDAV-specific XML elements used within + property values. + + + + + + +Goland, et al. Standards Track [Page 80] + +RFC 2518 WEBDAV February 1999 + + + URIs are used for both names, for several reasons. Assignment of a + URI does not require a request to a central naming authority, and + hence allow WebDAV property names and XML elements to be quickly + defined by any WebDAV user or application. URIs also provide a + unique address space, ensuring that the distributed users of WebDAV + will not have collisions among the property names and XML elements + they create. + + This specification defines a distinguished set of property names and + XML elements that are understood by all WebDAV applications. The + property names and XML elements in this specification are all derived + from the base URI DAV: by adding a suffix to this URI, for example, + DAV:creationdate for the "creationdate" property. + + This specification also defines a URI scheme for the encoding of lock + tokens, the opaquelocktoken URI scheme described in section 6.4. + + To ensure correct interoperation based on this specification, IANA + must reserve the URI namespaces starting with "DAV:" and with + "opaquelocktoken:" for use by this specification, its revisions, and + related WebDAV specifications. + +19 Intellectual Property + + The following notice is copied from RFC 2026 [RFC2026], section 10.4, + and describes the position of the IETF concerning intellectual + property claims made against this document. + + The IETF takes no position regarding the validity or scope of any + intellectual property or other rights that might be claimed to + pertain to the implementation or use other technology described in + this document or the extent to which any license under such rights + might or might not be available; neither does it represent that it + has made any effort to identify any such rights. Information on the + IETF's procedures with respect to rights in standards-track and + standards-related documentation can be found in BCP-11. Copies of + claims of rights made available for publication and any assurances of + licenses to be made available, or the result of an attempt made to + obtain a general license or permission for the use of such + proprietary rights by implementors or users of this specification can + be obtained from the IETF Secretariat. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights which may cover technology that may be required to practice + this standard. Please address the information to the IETF Executive + Director. + + + + +Goland, et al. Standards Track [Page 81] + +RFC 2518 WEBDAV February 1999 + + +20 Acknowledgements + + A specification such as this thrives on piercing critical review and + withers from apathetic neglect. The authors gratefully acknowledge + the contributions of the following people, whose insights were so + valuable at every stage of our work. + + Terry Allen, Harald Alvestrand, Jim Amsden, Becky Anderson, Alan + Babich, Sanford Barr, Dylan Barrell, Bernard Chester, Tim Berners- + Lee, Dan Connolly, Jim Cunningham, Ron Daniel, Jr., Jim Davis, Keith + Dawson, Mark Day, Brian Deen, Martin Duerst, David Durand, Lee + Farrell, Chuck Fay, Wesley Felter, Roy Fielding, Mark Fisher, Alan + Freier, George Florentine, Jim Gettys, Phill Hallam-Baker, Dennis + Hamilton, Steve Henning, Mead Himelstein, Alex Hopmann, Andre van der + Hoek, Ben Laurie, Paul Leach, Ora Lassila, Karen MacArthur, Steven + Martin, Larry Masinter, Michael Mealling, Keith Moore, Thomas Narten, + Henrik Nielsen, Kenji Ota, Bob Parker, Glenn Peterson, Jon Radoff, + Saveen Reddy, Henry Sanders, Christopher Seiwald, Judith Slein, Mike + Spreitzer, Einar Stefferud, Greg Stein, Ralph Swick, Kenji Takahashi, + Richard N. Taylor, Robert Thau, John Turner, Sankar Virdhagriswaran, + Fabio Vitali, Gregory Woodhouse, and Lauren Wood. + + Two from this list deserve special mention. The contributions by + Larry Masinter have been invaluable, both in helping the formation of + the working group and in patiently coaching the authors along the + way. In so many ways he has set high standards we have toiled to + meet. The contributions of Judith Slein in clarifying the + requirements, and in patiently reviewing draft after draft, both + improved this specification and expanded our minds on document + management. + + We would also like to thank John Turner for developing the XML DTD. + +21 References + +21.1 Normative References + + [RFC1766] Alvestrand, H., "Tags for the Identification of + Languages", RFC 1766, March 1995. + + [RFC2277] Alvestrand, H., "IETF Policy on Character Sets and + Languages", BCP 18, RFC 2277, January 1998. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + + + + + +Goland, et al. Standards Track [Page 82] + +RFC 2518 WEBDAV February 1999 + + + [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, + "Uniform Resource Identifiers (URI): Generic Syntax", + RFC 2396, August 1998. + + [REC-XML] T. Bray, J. Paoli, C. M. Sperberg-McQueen, + "Extensible Markup Language (XML)." World Wide Web + Consortium Recommendation REC-xml-19980210. + http://www.w3.org/TR/1998/REC-xml-19980210. + + [REC-XML-NAMES] T. Bray, D. Hollander, A. Layman, "Namespaces in + XML". World Wide Web Consortium Recommendation REC- + xml-names-19990114. http://www.w3.org/TR/1999/REC- + xml-names-19990114/ + + [RFC2069] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, + P, Luotonen, A., Sink, E. and L. Stewart, "An + Extension to HTTP : Digest Access Authentication", + RFC 2069, January 1997. + + [RFC2068] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and + T. Berners-Lee, "Hypertext Transfer Protocol -- + HTTP/1.1", RFC 2068, January 1997. + + [ISO-639] ISO (International Organization for Standardization). + ISO 639:1988. "Code for the representation of names + of languages." + + [ISO-8601] ISO (International Organization for Standardization). + ISO 8601:1988. "Data elements and interchange formats + - Information interchange - Representation of dates + and times." + + [ISO-11578] ISO (International Organization for Standardization). + ISO/IEC 11578:1996. "Information technology - Open + Systems Interconnection - Remote Procedure Call + (RPC)" + + [RFC2141] Moats, R., "URN Syntax", RFC 2141, May 1997. + + [UTF-8] Yergeau, F., "UTF-8, a transformation format of + Unicode and ISO 10646", RFC 2279, January 1998. + +21.2 Informational References + + [RFC2026] Bradner, S., "The Internet Standards Process - Revision + 3", BCP 9, RFC 2026, October 1996. + + + + + +Goland, et al. Standards Track [Page 83] + +RFC 2518 WEBDAV February 1999 + + + [RFC1807] Lasher, R. and D. Cohen, "A Format for Bibliographic + Records", RFC 1807, June 1995. + + [WF] C. Lagoze, "The Warwick Framework: A Container + Architecture for Diverse Sets of Metadata", D-Lib + Magazine, July/August 1996. + http://www.dlib.org/dlib/july96/lagoze/07lagoze.html + + [USMARC] Network Development and MARC Standards, Office, ed. 1994. + "USMARC Format for Bibliographic Data", 1994. Washington, + DC: Cataloging Distribution Service, Library of Congress. + + [REC-PICS] J. Miller, T. Krauskopf, P. Resnick, W. Treese, "PICS + Label Distribution Label Syntax and Communication + Protocols" Version 1.1, World Wide Web Consortium + Recommendation REC-PICS-labels-961031. + http://www.w3.org/pub/WWW/TR/REC-PICS-labels-961031.html. + + [RFC2291] Slein, J., Vitali, F., Whitehead, E. and D. Durand, + "Requirements for Distributed Authoring and Versioning + Protocol for the World Wide Web", RFC 2291, February 1998. + + [RFC2413] Weibel, S., Kunze, J., Lagoze, C. and M. Wolf, "Dublin + Core Metadata for Resource Discovery", RFC 2413, September + 1998. + + [RFC2376] Whitehead, E. and M. Murata, "XML Media Types", RFC 2376, + July 1998. + +22 Authors' Addresses + + Y. Y. Goland + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 + + EMail: yarong@microsoft.com + + + E. J. Whitehead, Jr. + Dept. Of Information and Computer Science + University of California, Irvine + Irvine, CA 92697-3425 + + EMail: ejw@ics.uci.edu + + + + + + +Goland, et al. Standards Track [Page 84] + +RFC 2518 WEBDAV February 1999 + + + A. Faizi + Netscape + 685 East Middlefield Road + Mountain View, CA 94043 + + EMail: asad@netscape.com + + + S. R. Carter + Novell + 1555 N. Technology Way + M/S ORM F111 + Orem, UT 84097-2399 + + EMail: srcarter@novell.com + + + D. Jensen + Novell + 1555 N. Technology Way + M/S ORM F111 + Orem, UT 84097-2399 + + EMail: dcjensen@novell.com + + + + + + + + + + + + + + + + + + + + + + + + + + + +Goland, et al. Standards Track [Page 85] + +RFC 2518 WEBDAV February 1999 + + +23 Appendices + +23.1 Appendix 1 - WebDAV Document Type Definition + + This section provides a document type definition, following the rules + in [REC-XML], for the XML elements used in the protocol stream and in + the values of properties. It collects the element definitions given + in sections 12 and 13. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Goland, et al. Standards Track [Page 86] + +RFC 2518 WEBDAV February 1999 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> + + + + + + + + + + + + + + + + + + + + + +Goland, et al. Standards Track [Page 87] + +RFC 2518 WEBDAV February 1999 + + +23.2 Appendix 2 - ISO 8601 Date and Time Profile + + The creationdate property specifies the use of the ISO 8601 date + format [ISO-8601]. This section defines a profile of the ISO 8601 + date format for use with this specification. This profile is quoted + from an Internet-Draft by Chris Newman, and is mentioned here to + properly attribute his work. + + date-time = full-date "T" full-time + + full-date = date-fullyear "-" date-month "-" date-mday + full-time = partial-time time-offset + + date-fullyear = 4DIGIT + date-month = 2DIGIT ; 01-12 + date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on + month/year + time-hour = 2DIGIT ; 00-23 + time-minute = 2DIGIT ; 00-59 + time-second = 2DIGIT ; 00-59, 00-60 based on leap second rules + time-secfrac = "." 1*DIGIT + time-numoffset = ("+" / "-") time-hour ":" time-minute + time-offset = "Z" / time-numoffset + + partial-time = time-hour ":" time-minute ":" time-second + [time-secfrac] + + Numeric offsets are calculated as local time minus UTC (Coordinated + Universal Time). So the equivalent time in UTC can be determined by + subtracting the offset from the local time. For example, 18:50:00- + 04:00 is the same time as 22:58:00Z. + + If the time in UTC is known, but the offset to local time is unknown, + this can be represented with an offset of "-00:00". This differs + from an offset of "Z" which implies that UTC is the preferred + reference point for the specified time. + + + + + + + + + + + + + + + +Goland, et al. Standards Track [Page 88] + +RFC 2518 WEBDAV February 1999 + + +23.3 Appendix 3 - Notes on Processing XML Elements + +23.3.1 Notes on Empty XML Elements + + XML supports two mechanisms for indicating that an XML element does + not have any content. The first is to declare an XML element of the + form . The second is to declare an XML element of the form + . The two XML elements are semantically identical. + + It is a violation of the XML specification to use the form if + the associated DTD declares the element to be EMPTY (e.g., ). If such a statement is included, then the empty element + format, must be used. If the element is not declared to be + EMPTY, then either form or may be used for empty + elements. + + 23.3.2 Notes on Illegal XML Processing + + XML is a flexible data format that makes it easy to submit data that + appears legal but in fact is not. The philosophy of "Be flexible in + what you accept and strict in what you send" still applies, but it + must not be applied inappropriately. XML is extremely flexible in + dealing with issues of white space, element ordering, inserting new + elements, etc. This flexibility does not require extension, + especially not in the area of the meaning of elements. + + There is no kindness in accepting illegal combinations of XML + elements. At best it will cause an unwanted result and at worst it + can cause real damage. + +23.3.2.1 Example - XML Syntax Error + + The following request body for a PROPFIND method is illegal. + + + + + + + + The definition of the propfind element only allows for the allprop or + the propname element, not both. Thus the above is an error and must + be responded to with a 400 (Bad Request). + + + + + + + + +Goland, et al. Standards Track [Page 89] + +RFC 2518 WEBDAV February 1999 + + + Imagine, however, that a server wanted to be "kind" and decided to + pick the allprop element as the true element and respond to it. A + client running over a bandwidth limited line who intended to execute + a propname would be in for a big surprise if the server treated the + command as an allprop. + + Additionally, if a server were lenient and decided to reply to this + request, the results would vary randomly from server to server, with + some servers executing the allprop directive, and others executing + the propname directive. This reduces interoperability rather than + increasing it. + +23.3.2.2 Example - Unknown XML Element + + The previous example was illegal because it contained two elements + that were explicitly banned from appearing together in the propfind + element. However, XML is an extensible language, so one can imagine + new elements being defined for use with propfind. Below is the + request body of a PROPFIND and, like the previous example, must be + rejected with a 400 (Bad Request) by a server that does not + understand the expired-props element. + + + + + + + To understand why a 400 (Bad Request) is returned let us look at the + request body as the server unfamiliar with expired-props sees it. + + + + + + As the server does not understand the expired-props element, + according to the WebDAV-specific XML processing rules specified in + section 14, it must ignore it. Thus the server sees an empty + propfind, which by the definition of the propfind element is illegal. + + Please note that had the extension been additive it would not + necessarily have resulted in a 400 (Bad Request). For example, + imagine the following request body for a PROPFIND: + + + + + + +Goland, et al. Standards Track [Page 90] + +RFC 2518 WEBDAV February 1999 + + + + *boss* + + + The previous example contains the fictitious element leave-out. Its + purpose is to prevent the return of any property whose name matches + the submitted pattern. If the previous example were submitted to a + server unfamiliar with leave-out, the only result would be that the + leave-out element would be ignored and a propname would be executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Goland, et al. Standards Track [Page 91] + +RFC 2518 WEBDAV February 1999 + + +23.4 Appendix 4 -- XML Namespaces for WebDAV + +23.4.1 Introduction + + All DAV compliant systems MUST support the XML namespace extensions + as specified in [REC-XML-NAMES]. + +23.4.2 Meaning of Qualified Names + + [Note to the reader: This section does not appear in [REC-XML-NAMES], + but is necessary to avoid ambiguity for WebDAV XML processors.] + + WebDAV compliant XML processors MUST interpret a qualified name as a + URI constructed by appending the LocalPart to the namespace name URI. + + Example + + + + Johnny Updraft + + + + + In this example, the qualified element name "del:glider" is + interpreted as the URL "http://www.del.jensen.org/glider". + + + + Johnny Updraft + + + + + Even though this example is syntactically different from the previous + example, it is semantically identical. Each instance of the + namespace name "bar" is replaced with "http://www.del.jensen.org/" + and then appended to the local name for each element tag. The + resulting tag names in this example are exactly the same as for the + previous example. + + + + Johnny Updraft + + + + + + + +Goland, et al. Standards Track [Page 92] + +RFC 2518 WEBDAV February 1999 + + + This example is semantically identical to the two previous ones. + Each instance of the namespace name "foo" is replaced with + "http://www.del.jensen.org/glide" which is then appended to the local + name for each element tag, the resulting tag names are identical to + those in the previous examples. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Goland, et al. Standards Track [Page 93] + +RFC 2518 WEBDAV February 1999 + + +24. Full Copyright Statement + + Copyright (C) The Internet Society (1999). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + + + + + + + + + + + + + + + + + + + + +Goland, et al. Standards Track [Page 94] + diff --git a/dav/SabreDAV/docs/rfc2616.txt b/dav/SabreDAV/docs/rfc2616.txt new file mode 100644 index 000000000..45d7d08b8 --- /dev/null +++ b/dav/SabreDAV/docs/rfc2616.txt @@ -0,0 +1,9859 @@ + + + + + + +Network Working Group R. Fielding +Request for Comments: 2616 UC Irvine +Obsoletes: 2068 J. Gettys +Category: Standards Track Compaq/W3C + J. Mogul + Compaq + H. Frystyk + W3C/MIT + L. Masinter + Xerox + P. Leach + Microsoft + T. Berners-Lee + W3C/MIT + June 1999 + + + Hypertext Transfer Protocol -- HTTP/1.1 + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1999). All Rights Reserved. + +Abstract + + The Hypertext Transfer Protocol (HTTP) is an application-level + protocol for distributed, collaborative, hypermedia information + systems. It is a generic, stateless, protocol which can be used for + many tasks beyond its use for hypertext, such as name servers and + distributed object management systems, through extension of its + request methods, error codes and headers [47]. A feature of HTTP is + the typing and negotiation of data representation, allowing systems + to be built independently of the data being transferred. + + HTTP has been in use by the World-Wide Web global information + initiative since 1990. This specification defines the protocol + referred to as "HTTP/1.1", and is an update to RFC 2068 [33]. + + + + + + +Fielding, et al. Standards Track [Page 1] + +RFC 2616 HTTP/1.1 June 1999 + + +Table of Contents + + 1 Introduction ...................................................7 + 1.1 Purpose......................................................7 + 1.2 Requirements .................................................8 + 1.3 Terminology ..................................................8 + 1.4 Overall Operation ...........................................12 + 2 Notational Conventions and Generic Grammar ....................14 + 2.1 Augmented BNF ...............................................14 + 2.2 Basic Rules .................................................15 + 3 Protocol Parameters ...........................................17 + 3.1 HTTP Version ................................................17 + 3.2 Uniform Resource Identifiers ................................18 + 3.2.1 General Syntax ...........................................19 + 3.2.2 http URL .................................................19 + 3.2.3 URI Comparison ...........................................20 + 3.3 Date/Time Formats ...........................................20 + 3.3.1 Full Date ................................................20 + 3.3.2 Delta Seconds ............................................21 + 3.4 Character Sets ..............................................21 + 3.4.1 Missing Charset ..........................................22 + 3.5 Content Codings .............................................23 + 3.6 Transfer Codings ............................................24 + 3.6.1 Chunked Transfer Coding ..................................25 + 3.7 Media Types .................................................26 + 3.7.1 Canonicalization and Text Defaults .......................27 + 3.7.2 Multipart Types ..........................................27 + 3.8 Product Tokens ..............................................28 + 3.9 Quality Values ..............................................29 + 3.10 Language Tags ...............................................29 + 3.11 Entity Tags .................................................30 + 3.12 Range Units .................................................30 + 4 HTTP Message ..................................................31 + 4.1 Message Types ...............................................31 + 4.2 Message Headers .............................................31 + 4.3 Message Body ................................................32 + 4.4 Message Length ..............................................33 + 4.5 General Header Fields .......................................34 + 5 Request .......................................................35 + 5.1 Request-Line ................................................35 + 5.1.1 Method ...................................................36 + 5.1.2 Request-URI ..............................................36 + 5.2 The Resource Identified by a Request ........................38 + 5.3 Request Header Fields .......................................38 + 6 Response ......................................................39 + 6.1 Status-Line .................................................39 + 6.1.1 Status Code and Reason Phrase ............................39 + 6.2 Response Header Fields ......................................41 + + + +Fielding, et al. Standards Track [Page 2] + +RFC 2616 HTTP/1.1 June 1999 + + + 7 Entity ........................................................42 + 7.1 Entity Header Fields ........................................42 + 7.2 Entity Body .................................................43 + 7.2.1 Type .....................................................43 + 7.2.2 Entity Length ............................................43 + 8 Connections ...................................................44 + 8.1 Persistent Connections ......................................44 + 8.1.1 Purpose ..................................................44 + 8.1.2 Overall Operation ........................................45 + 8.1.3 Proxy Servers ............................................46 + 8.1.4 Practical Considerations .................................46 + 8.2 Message Transmission Requirements ...........................47 + 8.2.1 Persistent Connections and Flow Control ..................47 + 8.2.2 Monitoring Connections for Error Status Messages .........48 + 8.2.3 Use of the 100 (Continue) Status .........................48 + 8.2.4 Client Behavior if Server Prematurely Closes Connection ..50 + 9 Method Definitions ............................................51 + 9.1 Safe and Idempotent Methods .................................51 + 9.1.1 Safe Methods .............................................51 + 9.1.2 Idempotent Methods .......................................51 + 9.2 OPTIONS .....................................................52 + 9.3 GET .........................................................53 + 9.4 HEAD ........................................................54 + 9.5 POST ........................................................54 + 9.6 PUT .........................................................55 + 9.7 DELETE ......................................................56 + 9.8 TRACE .......................................................56 + 9.9 CONNECT .....................................................57 + 10 Status Code Definitions ......................................57 + 10.1 Informational 1xx ...........................................57 + 10.1.1 100 Continue .............................................58 + 10.1.2 101 Switching Protocols ..................................58 + 10.2 Successful 2xx ..............................................58 + 10.2.1 200 OK ...................................................58 + 10.2.2 201 Created ..............................................59 + 10.2.3 202 Accepted .............................................59 + 10.2.4 203 Non-Authoritative Information ........................59 + 10.2.5 204 No Content ...........................................60 + 10.2.6 205 Reset Content ........................................60 + 10.2.7 206 Partial Content ......................................60 + 10.3 Redirection 3xx .............................................61 + 10.3.1 300 Multiple Choices .....................................61 + 10.3.2 301 Moved Permanently ....................................62 + 10.3.3 302 Found ................................................62 + 10.3.4 303 See Other ............................................63 + 10.3.5 304 Not Modified .........................................63 + 10.3.6 305 Use Proxy ............................................64 + 10.3.7 306 (Unused) .............................................64 + + + +Fielding, et al. Standards Track [Page 3] + +RFC 2616 HTTP/1.1 June 1999 + + + 10.3.8 307 Temporary Redirect ...................................65 + 10.4 Client Error 4xx ............................................65 + 10.4.1 400 Bad Request .........................................65 + 10.4.2 401 Unauthorized ........................................66 + 10.4.3 402 Payment Required ....................................66 + 10.4.4 403 Forbidden ...........................................66 + 10.4.5 404 Not Found ...........................................66 + 10.4.6 405 Method Not Allowed ..................................66 + 10.4.7 406 Not Acceptable ......................................67 + 10.4.8 407 Proxy Authentication Required .......................67 + 10.4.9 408 Request Timeout .....................................67 + 10.4.10 409 Conflict ............................................67 + 10.4.11 410 Gone ................................................68 + 10.4.12 411 Length Required .....................................68 + 10.4.13 412 Precondition Failed .................................68 + 10.4.14 413 Request Entity Too Large ............................69 + 10.4.15 414 Request-URI Too Long ................................69 + 10.4.16 415 Unsupported Media Type ..............................69 + 10.4.17 416 Requested Range Not Satisfiable .....................69 + 10.4.18 417 Expectation Failed ..................................70 + 10.5 Server Error 5xx ............................................70 + 10.5.1 500 Internal Server Error ................................70 + 10.5.2 501 Not Implemented ......................................70 + 10.5.3 502 Bad Gateway ..........................................70 + 10.5.4 503 Service Unavailable ..................................70 + 10.5.5 504 Gateway Timeout ......................................71 + 10.5.6 505 HTTP Version Not Supported ...........................71 + 11 Access Authentication ........................................71 + 12 Content Negotiation ..........................................71 + 12.1 Server-driven Negotiation ...................................72 + 12.2 Agent-driven Negotiation ....................................73 + 12.3 Transparent Negotiation .....................................74 + 13 Caching in HTTP ..............................................74 + 13.1.1 Cache Correctness ........................................75 + 13.1.2 Warnings .................................................76 + 13.1.3 Cache-control Mechanisms .................................77 + 13.1.4 Explicit User Agent Warnings .............................78 + 13.1.5 Exceptions to the Rules and Warnings .....................78 + 13.1.6 Client-controlled Behavior ...............................79 + 13.2 Expiration Model ............................................79 + 13.2.1 Server-Specified Expiration ..............................79 + 13.2.2 Heuristic Expiration .....................................80 + 13.2.3 Age Calculations .........................................80 + 13.2.4 Expiration Calculations ..................................83 + 13.2.5 Disambiguating Expiration Values .........................84 + 13.2.6 Disambiguating Multiple Responses ........................84 + 13.3 Validation Model ............................................85 + 13.3.1 Last-Modified Dates ......................................86 + + + +Fielding, et al. Standards Track [Page 4] + +RFC 2616 HTTP/1.1 June 1999 + + + 13.3.2 Entity Tag Cache Validators ..............................86 + 13.3.3 Weak and Strong Validators ...............................86 + 13.3.4 Rules for When to Use Entity Tags and Last-Modified Dates.89 + 13.3.5 Non-validating Conditionals ..............................90 + 13.4 Response Cacheability .......................................91 + 13.5 Constructing Responses From Caches ..........................92 + 13.5.1 End-to-end and Hop-by-hop Headers ........................92 + 13.5.2 Non-modifiable Headers ...................................92 + 13.5.3 Combining Headers ........................................94 + 13.5.4 Combining Byte Ranges ....................................95 + 13.6 Caching Negotiated Responses ................................95 + 13.7 Shared and Non-Shared Caches ................................96 + 13.8 Errors or Incomplete Response Cache Behavior ................97 + 13.9 Side Effects of GET and HEAD ................................97 + 13.10 Invalidation After Updates or Deletions ...................97 + 13.11 Write-Through Mandatory ...................................98 + 13.12 Cache Replacement .........................................99 + 13.13 History Lists .............................................99 + 14 Header Field Definitions ....................................100 + 14.1 Accept .....................................................100 + 14.2 Accept-Charset .............................................102 + 14.3 Accept-Encoding ............................................102 + 14.4 Accept-Language ............................................104 + 14.5 Accept-Ranges ..............................................105 + 14.6 Age ........................................................106 + 14.7 Allow ......................................................106 + 14.8 Authorization ..............................................107 + 14.9 Cache-Control ..............................................108 + 14.9.1 What is Cacheable .......................................109 + 14.9.2 What May be Stored by Caches ............................110 + 14.9.3 Modifications of the Basic Expiration Mechanism .........111 + 14.9.4 Cache Revalidation and Reload Controls ..................113 + 14.9.5 No-Transform Directive ..................................115 + 14.9.6 Cache Control Extensions ................................116 + 14.10 Connection ...............................................117 + 14.11 Content-Encoding .........................................118 + 14.12 Content-Language .........................................118 + 14.13 Content-Length ...........................................119 + 14.14 Content-Location .........................................120 + 14.15 Content-MD5 ..............................................121 + 14.16 Content-Range ............................................122 + 14.17 Content-Type .............................................124 + 14.18 Date .....................................................124 + 14.18.1 Clockless Origin Server Operation ......................125 + 14.19 ETag .....................................................126 + 14.20 Expect ...................................................126 + 14.21 Expires ..................................................127 + 14.22 From .....................................................128 + + + +Fielding, et al. Standards Track [Page 5] + +RFC 2616 HTTP/1.1 June 1999 + + + 14.23 Host .....................................................128 + 14.24 If-Match .................................................129 + 14.25 If-Modified-Since ........................................130 + 14.26 If-None-Match ............................................132 + 14.27 If-Range .................................................133 + 14.28 If-Unmodified-Since ......................................134 + 14.29 Last-Modified ............................................134 + 14.30 Location .................................................135 + 14.31 Max-Forwards .............................................136 + 14.32 Pragma ...................................................136 + 14.33 Proxy-Authenticate .......................................137 + 14.34 Proxy-Authorization ......................................137 + 14.35 Range ....................................................138 + 14.35.1 Byte Ranges ...........................................138 + 14.35.2 Range Retrieval Requests ..............................139 + 14.36 Referer ..................................................140 + 14.37 Retry-After ..............................................141 + 14.38 Server ...................................................141 + 14.39 TE .......................................................142 + 14.40 Trailer ..................................................143 + 14.41 Transfer-Encoding..........................................143 + 14.42 Upgrade ..................................................144 + 14.43 User-Agent ...............................................145 + 14.44 Vary .....................................................145 + 14.45 Via ......................................................146 + 14.46 Warning ..................................................148 + 14.47 WWW-Authenticate .........................................150 + 15 Security Considerations .......................................150 + 15.1 Personal Information....................................151 + 15.1.1 Abuse of Server Log Information .........................151 + 15.1.2 Transfer of Sensitive Information .......................151 + 15.1.3 Encoding Sensitive Information in URI's .................152 + 15.1.4 Privacy Issues Connected to Accept Headers ..............152 + 15.2 Attacks Based On File and Path Names .......................153 + 15.3 DNS Spoofing ...............................................154 + 15.4 Location Headers and Spoofing ..............................154 + 15.5 Content-Disposition Issues .................................154 + 15.6 Authentication Credentials and Idle Clients ................155 + 15.7 Proxies and Caching ........................................155 + 15.7.1 Denial of Service Attacks on Proxies....................156 + 16 Acknowledgments .............................................156 + 17 References ..................................................158 + 18 Authors' Addresses ..........................................162 + 19 Appendices ..................................................164 + 19.1 Internet Media Type message/http and application/http ......164 + 19.2 Internet Media Type multipart/byteranges ...................165 + 19.3 Tolerant Applications ......................................166 + 19.4 Differences Between HTTP Entities and RFC 2045 Entities ....167 + + + +Fielding, et al. Standards Track [Page 6] + +RFC 2616 HTTP/1.1 June 1999 + + + 19.4.1 MIME-Version ............................................167 + 19.4.2 Conversion to Canonical Form ............................167 + 19.4.3 Conversion of Date Formats ..............................168 + 19.4.4 Introduction of Content-Encoding ........................168 + 19.4.5 No Content-Transfer-Encoding ............................168 + 19.4.6 Introduction of Transfer-Encoding .......................169 + 19.4.7 MHTML and Line Length Limitations .......................169 + 19.5 Additional Features ........................................169 + 19.5.1 Content-Disposition .....................................170 + 19.6 Compatibility with Previous Versions .......................170 + 19.6.1 Changes from HTTP/1.0 ...................................171 + 19.6.2 Compatibility with HTTP/1.0 Persistent Connections ......172 + 19.6.3 Changes from RFC 2068 ...................................172 + 20 Index .......................................................175 + 21 Full Copyright Statement ....................................176 + +1 Introduction + +1.1 Purpose + + The Hypertext Transfer Protocol (HTTP) is an application-level + protocol for distributed, collaborative, hypermedia information + systems. HTTP has been in use by the World-Wide Web global + information initiative since 1990. The first version of HTTP, + referred to as HTTP/0.9, was a simple protocol for raw data transfer + across the Internet. HTTP/1.0, as defined by RFC 1945 [6], improved + the protocol by allowing messages to be in the format of MIME-like + messages, containing metainformation about the data transferred and + modifiers on the request/response semantics. However, HTTP/1.0 does + not sufficiently take into consideration the effects of hierarchical + proxies, caching, the need for persistent connections, or virtual + hosts. In addition, the proliferation of incompletely-implemented + applications calling themselves "HTTP/1.0" has necessitated a + protocol version change in order for two communicating applications + to determine each other's true capabilities. + + This specification defines the protocol referred to as "HTTP/1.1". + This protocol includes more stringent requirements than HTTP/1.0 in + order to ensure reliable implementation of its features. + + Practical information systems require more functionality than simple + retrieval, including search, front-end update, and annotation. HTTP + allows an open-ended set of methods and headers that indicate the + purpose of a request [47]. It builds on the discipline of reference + provided by the Uniform Resource Identifier (URI) [3], as a location + (URL) [4] or name (URN) [20], for indicating the resource to which a + + + + + +Fielding, et al. Standards Track [Page 7] + +RFC 2616 HTTP/1.1 June 1999 + + + method is to be applied. Messages are passed in a format similar to + that used by Internet mail [9] as defined by the Multipurpose + Internet Mail Extensions (MIME) [7]. + + HTTP is also used as a generic protocol for communication between + user agents and proxies/gateways to other Internet systems, including + those supported by the SMTP [16], NNTP [13], FTP [18], Gopher [2], + and WAIS [10] protocols. In this way, HTTP allows basic hypermedia + access to resources available from diverse applications. + +1.2 Requirements + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [34]. + + An implementation is not compliant if it fails to satisfy one or more + of the MUST or REQUIRED level requirements for the protocols it + implements. An implementation that satisfies all the MUST or REQUIRED + level and all the SHOULD level requirements for its protocols is said + to be "unconditionally compliant"; one that satisfies all the MUST + level requirements but not all the SHOULD level requirements for its + protocols is said to be "conditionally compliant." + +1.3 Terminology + + This specification uses a number of terms to refer to the roles + played by participants in, and objects of, the HTTP communication. + + connection + A transport layer virtual circuit established between two programs + for the purpose of communication. + + message + The basic unit of HTTP communication, consisting of a structured + sequence of octets matching the syntax defined in section 4 and + transmitted via the connection. + + request + An HTTP request message, as defined in section 5. + + response + An HTTP response message, as defined in section 6. + + + + + + + + +Fielding, et al. Standards Track [Page 8] + +RFC 2616 HTTP/1.1 June 1999 + + + resource + A network data object or service that can be identified by a URI, + as defined in section 3.2. Resources may be available in multiple + representations (e.g. multiple languages, data formats, size, and + resolutions) or vary in other ways. + + entity + The information transferred as the payload of a request or + response. An entity consists of metainformation in the form of + entity-header fields and content in the form of an entity-body, as + described in section 7. + + representation + An entity included with a response that is subject to content + negotiation, as described in section 12. There may exist multiple + representations associated with a particular response status. + + content negotiation + The mechanism for selecting the appropriate representation when + servicing a request, as described in section 12. The + representation of entities in any response can be negotiated + (including error responses). + + variant + A resource may have one, or more than one, representation(s) + associated with it at any given instant. Each of these + representations is termed a `varriant'. Use of the term `variant' + does not necessarily imply that the resource is subject to content + negotiation. + + client + A program that establishes connections for the purpose of sending + requests. + + user agent + The client which initiates a request. These are often browsers, + editors, spiders (web-traversing robots), or other end user tools. + + server + An application program that accepts connections in order to + service requests by sending back responses. Any given program may + be capable of being both a client and a server; our use of these + terms refers only to the role being performed by the program for a + particular connection, rather than to the program's capabilities + in general. Likewise, any server may act as an origin server, + proxy, gateway, or tunnel, switching behavior based on the nature + of each request. + + + + +Fielding, et al. Standards Track [Page 9] + +RFC 2616 HTTP/1.1 June 1999 + + + origin server + The server on which a given resource resides or is to be created. + + proxy + An intermediary program which acts as both a server and a client + for the purpose of making requests on behalf of other clients. + Requests are serviced internally or by passing them on, with + possible translation, to other servers. A proxy MUST implement + both the client and server requirements of this specification. A + "transparent proxy" is a proxy that does not modify the request or + response beyond what is required for proxy authentication and + identification. A "non-transparent proxy" is a proxy that modifies + the request or response in order to provide some added service to + the user agent, such as group annotation services, media type + transformation, protocol reduction, or anonymity filtering. Except + where either transparent or non-transparent behavior is explicitly + stated, the HTTP proxy requirements apply to both types of + proxies. + + gateway + A server which acts as an intermediary for some other server. + Unlike a proxy, a gateway receives requests as if it were the + origin server for the requested resource; the requesting client + may not be aware that it is communicating with a gateway. + + tunnel + An intermediary program which is acting as a blind relay between + two connections. Once active, a tunnel is not considered a party + to the HTTP communication, though the tunnel may have been + initiated by an HTTP request. The tunnel ceases to exist when both + ends of the relayed connections are closed. + + cache + A program's local store of response messages and the subsystem + that controls its message storage, retrieval, and deletion. A + cache stores cacheable responses in order to reduce the response + time and network bandwidth consumption on future, equivalent + requests. Any client or server may include a cache, though a cache + cannot be used by a server that is acting as a tunnel. + + cacheable + A response is cacheable if a cache is allowed to store a copy of + the response message for use in answering subsequent requests. The + rules for determining the cacheability of HTTP responses are + defined in section 13. Even if a resource is cacheable, there may + be additional constraints on whether a cache can use the cached + copy for a particular request. + + + + +Fielding, et al. Standards Track [Page 10] + +RFC 2616 HTTP/1.1 June 1999 + + + first-hand + A response is first-hand if it comes directly and without + unnecessary delay from the origin server, perhaps via one or more + proxies. A response is also first-hand if its validity has just + been checked directly with the origin server. + + explicit expiration time + The time at which the origin server intends that an entity should + no longer be returned by a cache without further validation. + + heuristic expiration time + An expiration time assigned by a cache when no explicit expiration + time is available. + + age + The age of a response is the time since it was sent by, or + successfully validated with, the origin server. + + freshness lifetime + The length of time between the generation of a response and its + expiration time. + + fresh + A response is fresh if its age has not yet exceeded its freshness + lifetime. + + stale + A response is stale if its age has passed its freshness lifetime. + + semantically transparent + A cache behaves in a "semantically transparent" manner, with + respect to a particular response, when its use affects neither the + requesting client nor the origin server, except to improve + performance. When a cache is semantically transparent, the client + receives exactly the same response (except for hop-by-hop headers) + that it would have received had its request been handled directly + by the origin server. + + validator + A protocol element (e.g., an entity tag or a Last-Modified time) + that is used to find out whether a cache entry is an equivalent + copy of an entity. + + upstream/downstream + Upstream and downstream describe the flow of a message: all + messages flow from upstream to downstream. + + + + + +Fielding, et al. Standards Track [Page 11] + +RFC 2616 HTTP/1.1 June 1999 + + + inbound/outbound + Inbound and outbound refer to the request and response paths for + messages: "inbound" means "traveling toward the origin server", + and "outbound" means "traveling toward the user agent" + +1.4 Overall Operation + + The HTTP protocol is a request/response protocol. A client sends a + request to the server in the form of a request method, URI, and + protocol version, followed by a MIME-like message containing request + modifiers, client information, and possible body content over a + connection with a server. The server responds with a status line, + including the message's protocol version and a success or error code, + followed by a MIME-like message containing server information, entity + metainformation, and possible entity-body content. The relationship + between HTTP and MIME is described in appendix 19.4. + + Most HTTP communication is initiated by a user agent and consists of + a request to be applied to a resource on some origin server. In the + simplest case, this may be accomplished via a single connection (v) + between the user agent (UA) and the origin server (O). + + request chain ------------------------> + UA -------------------v------------------- O + <----------------------- response chain + + A more complicated situation occurs when one or more intermediaries + are present in the request/response chain. There are three common + forms of intermediary: proxy, gateway, and tunnel. A proxy is a + forwarding agent, receiving requests for a URI in its absolute form, + rewriting all or part of the message, and forwarding the reformatted + request toward the server identified by the URI. A gateway is a + receiving agent, acting as a layer above some other server(s) and, if + necessary, translating the requests to the underlying server's + protocol. A tunnel acts as a relay point between two connections + without changing the messages; tunnels are used when the + communication needs to pass through an intermediary (such as a + firewall) even when the intermediary cannot understand the contents + of the messages. + + request chain --------------------------------------> + UA -----v----- A -----v----- B -----v----- C -----v----- O + <------------------------------------- response chain + + The figure above shows three intermediaries (A, B, and C) between the + user agent and origin server. A request or response message that + travels the whole chain will pass through four separate connections. + This distinction is important because some HTTP communication options + + + +Fielding, et al. Standards Track [Page 12] + +RFC 2616 HTTP/1.1 June 1999 + + + may apply only to the connection with the nearest, non-tunnel + neighbor, only to the end-points of the chain, or to all connections + along the chain. Although the diagram is linear, each participant may + be engaged in multiple, simultaneous communications. For example, B + may be receiving requests from many clients other than A, and/or + forwarding requests to servers other than C, at the same time that it + is handling A's request. + + Any party to the communication which is not acting as a tunnel may + employ an internal cache for handling requests. The effect of a cache + is that the request/response chain is shortened if one of the + participants along the chain has a cached response applicable to that + request. The following illustrates the resulting chain if B has a + cached copy of an earlier response from O (via C) for a request which + has not been cached by UA or A. + + request chain ----------> + UA -----v----- A -----v----- B - - - - - - C - - - - - - O + <--------- response chain + + Not all responses are usefully cacheable, and some requests may + contain modifiers which place special requirements on cache behavior. + HTTP requirements for cache behavior and cacheable responses are + defined in section 13. + + In fact, there are a wide variety of architectures and configurations + of caches and proxies currently being experimented with or deployed + across the World Wide Web. These systems include national hierarchies + of proxy caches to save transoceanic bandwidth, systems that + broadcast or multicast cache entries, organizations that distribute + subsets of cached data via CD-ROM, and so on. HTTP systems are used + in corporate intranets over high-bandwidth links, and for access via + PDAs with low-power radio links and intermittent connectivity. The + goal of HTTP/1.1 is to support the wide diversity of configurations + already deployed while introducing protocol constructs that meet the + needs of those who build web applications that require high + reliability and, failing that, at least reliable indications of + failure. + + HTTP communication usually takes place over TCP/IP connections. The + default port is TCP 80 [19], but other ports can be used. This does + not preclude HTTP from being implemented on top of any other protocol + on the Internet, or on other networks. HTTP only presumes a reliable + transport; any protocol that provides such guarantees can be used; + the mapping of the HTTP/1.1 request and response structures onto the + transport data units of the protocol in question is outside the scope + of this specification. + + + + +Fielding, et al. Standards Track [Page 13] + +RFC 2616 HTTP/1.1 June 1999 + + + In HTTP/1.0, most implementations used a new connection for each + request/response exchange. In HTTP/1.1, a connection may be used for + one or more request/response exchanges, although connections may be + closed for a variety of reasons (see section 8.1). + +2 Notational Conventions and Generic Grammar + +2.1 Augmented BNF + + All of the mechanisms specified in this document are described in + both prose and an augmented Backus-Naur Form (BNF) similar to that + used by RFC 822 [9]. Implementors will need to be familiar with the + notation in order to understand this specification. The augmented BNF + includes the following constructs: + + name = definition + The name of a rule is simply the name itself (without any + enclosing "<" and ">") and is separated from its definition by the + equal "=" character. White space is only significant in that + indentation of continuation lines is used to indicate a rule + definition that spans more than one line. Certain basic rules are + in uppercase, such as SP, LWS, HT, CRLF, DIGIT, ALPHA, etc. Angle + brackets are used within definitions whenever their presence will + facilitate discerning the use of rule names. + + "literal" + Quotation marks surround literal text. Unless stated otherwise, + the text is case-insensitive. + + rule1 | rule2 + Elements separated by a bar ("|") are alternatives, e.g., "yes | + no" will accept yes or no. + + (rule1 rule2) + Elements enclosed in parentheses are treated as a single element. + Thus, "(elem (foo | bar) elem)" allows the token sequences "elem + foo elem" and "elem bar elem". + + *rule + The character "*" preceding an element indicates repetition. The + full form is "*element" indicating at least and at most + occurrences of element. Default values are 0 and infinity so + that "*(element)" allows any number, including zero; "1*element" + requires at least one; and "1*2element" allows one or two. + + [rule] + Square brackets enclose optional elements; "[foo bar]" is + equivalent to "*1(foo bar)". + + + +Fielding, et al. Standards Track [Page 14] + +RFC 2616 HTTP/1.1 June 1999 + + + N rule + Specific repetition: "(element)" is equivalent to + "*(element)"; that is, exactly occurrences of (element). + Thus 2DIGIT is a 2-digit number, and 3ALPHA is a string of three + alphabetic characters. + + #rule + A construct "#" is defined, similar to "*", for defining lists of + elements. The full form is "#element" indicating at least + and at most elements, each separated by one or more commas + (",") and OPTIONAL linear white space (LWS). This makes the usual + form of lists very easy; a rule such as + ( *LWS element *( *LWS "," *LWS element )) + can be shown as + 1#element + Wherever this construct is used, null elements are allowed, but do + not contribute to the count of elements present. That is, + "(element), , (element) " is permitted, but counts as only two + elements. Therefore, where at least one element is required, at + least one non-null element MUST be present. Default values are 0 + and infinity so that "#element" allows any number, including zero; + "1#element" requires at least one; and "1#2element" allows one or + two. + + ; comment + A semi-colon, set off some distance to the right of rule text, + starts a comment that continues to the end of line. This is a + simple way of including useful notes in parallel with the + specifications. + + implied *LWS + The grammar described by this specification is word-based. Except + where noted otherwise, linear white space (LWS) can be included + between any two adjacent words (token or quoted-string), and + between adjacent words and separators, without changing the + interpretation of a field. At least one delimiter (LWS and/or + + separators) MUST exist between any two tokens (for the definition + of "token" below), since they would otherwise be interpreted as a + single token. + +2.2 Basic Rules + + The following rules are used throughout this specification to + describe basic parsing constructs. The US-ASCII coded character set + is defined by ANSI X3.4-1986 [21]. + + + + + +Fielding, et al. Standards Track [Page 15] + +RFC 2616 HTTP/1.1 June 1999 + + + OCTET = + CHAR = + UPALPHA = + LOALPHA = + ALPHA = UPALPHA | LOALPHA + DIGIT = + CTL = + CR = + LF = + SP = + HT = + <"> = + + HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all + protocol elements except the entity-body (see appendix 19.3 for + tolerant applications). The end-of-line marker within an entity-body + is defined by its associated media type, as described in section 3.7. + + CRLF = CR LF + + HTTP/1.1 header field values can be folded onto multiple lines if the + continuation line begins with a space or horizontal tab. All linear + white space, including folding, has the same semantics as SP. A + recipient MAY replace any linear white space with a single SP before + interpreting the field value or forwarding the message downstream. + + LWS = [CRLF] 1*( SP | HT ) + + The TEXT rule is only used for descriptive field contents and values + that are not intended to be interpreted by the message parser. Words + of *TEXT MAY contain characters from character sets other than ISO- + 8859-1 [22] only when encoded according to the rules of RFC 2047 + [14]. + + TEXT = + + A CRLF is allowed in the definition of TEXT only as part of a header + field continuation. It is expected that the folding LWS will be + replaced with a single SP before interpretation of the TEXT value. + + Hexadecimal numeric characters are used in several protocol elements. + + HEX = "A" | "B" | "C" | "D" | "E" | "F" + | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT + + + + + +Fielding, et al. Standards Track [Page 16] + +RFC 2616 HTTP/1.1 June 1999 + + + Many HTTP/1.1 header field values consist of words separated by LWS + or special characters. These special characters MUST be in a quoted + string to be used within a parameter value (as defined in section + 3.6). + + token = 1* + separators = "(" | ")" | "<" | ">" | "@" + | "," | ";" | ":" | "\" | <"> + | "/" | "[" | "]" | "?" | "=" + | "{" | "}" | SP | HT + + Comments can be included in some HTTP header fields by surrounding + the comment text with parentheses. Comments are only allowed in + fields containing "comment" as part of their field value definition. + In all other fields, parentheses are considered part of the field + value. + + comment = "(" *( ctext | quoted-pair | comment ) ")" + ctext = + + A string of text is parsed as a single word if it is quoted using + double-quote marks. + + quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) + qdtext = > + + The backslash character ("\") MAY be used as a single-character + quoting mechanism only within quoted-string and comment constructs. + + quoted-pair = "\" CHAR + +3 Protocol Parameters + +3.1 HTTP Version + + HTTP uses a "." numbering scheme to indicate versions + of the protocol. The protocol versioning policy is intended to allow + the sender to indicate the format of a message and its capacity for + understanding further HTTP communication, rather than the features + obtained via that communication. No change is made to the version + number for the addition of message components which do not affect + communication behavior or which only add to extensible field values. + The number is incremented when the changes made to the + protocol add features which do not change the general message parsing + algorithm, but which may add to the message semantics and imply + additional capabilities of the sender. The number is + incremented when the format of a message within the protocol is + changed. See RFC 2145 [36] for a fuller explanation. + + + +Fielding, et al. Standards Track [Page 17] + +RFC 2616 HTTP/1.1 June 1999 + + + The version of an HTTP message is indicated by an HTTP-Version field + in the first line of the message. + + HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT + + Note that the major and minor numbers MUST be treated as separate + integers and that each MAY be incremented higher than a single digit. + Thus, HTTP/2.4 is a lower version than HTTP/2.13, which in turn is + lower than HTTP/12.3. Leading zeros MUST be ignored by recipients and + MUST NOT be sent. + + An application that sends a request or response message that includes + HTTP-Version of "HTTP/1.1" MUST be at least conditionally compliant + with this specification. Applications that are at least conditionally + compliant with this specification SHOULD use an HTTP-Version of + "HTTP/1.1" in their messages, and MUST do so for any message that is + not compatible with HTTP/1.0. For more details on when to send + specific HTTP-Version values, see RFC 2145 [36]. + + The HTTP version of an application is the highest HTTP version for + which the application is at least conditionally compliant. + + Proxy and gateway applications need to be careful when forwarding + messages in protocol versions different from that of the application. + Since the protocol version indicates the protocol capability of the + sender, a proxy/gateway MUST NOT send a message with a version + indicator which is greater than its actual version. If a higher + version request is received, the proxy/gateway MUST either downgrade + the request version, or respond with an error, or switch to tunnel + behavior. + + Due to interoperability problems with HTTP/1.0 proxies discovered + since the publication of RFC 2068[33], caching proxies MUST, gateways + MAY, and tunnels MUST NOT upgrade the request to the highest version + they support. The proxy/gateway's response to that request MUST be in + the same major version as the request. + + Note: Converting between versions of HTTP may involve modification + of header fields required or forbidden by the versions involved. + +3.2 Uniform Resource Identifiers + + URIs have been known by many names: WWW addresses, Universal Document + Identifiers, Universal Resource Identifiers [3], and finally the + combination of Uniform Resource Locators (URL) [4] and Names (URN) + [20]. As far as HTTP is concerned, Uniform Resource Identifiers are + simply formatted strings which identify--via name, location, or any + other characteristic--a resource. + + + +Fielding, et al. Standards Track [Page 18] + +RFC 2616 HTTP/1.1 June 1999 + + +3.2.1 General Syntax + + URIs in HTTP can be represented in absolute form or relative to some + known base URI [11], depending upon the context of their use. The two + forms are differentiated by the fact that absolute URIs always begin + with a scheme name followed by a colon. For definitive information on + URL syntax and semantics, see "Uniform Resource Identifiers (URI): + Generic Syntax and Semantics," RFC 2396 [42] (which replaces RFCs + 1738 [4] and RFC 1808 [11]). This specification adopts the + definitions of "URI-reference", "absoluteURI", "relativeURI", "port", + "host","abs_path", "rel_path", and "authority" from that + specification. + + The HTTP protocol does not place any a priori limit on the length of + a URI. Servers MUST be able to handle the URI of any resource they + serve, and SHOULD be able to handle URIs of unbounded length if they + provide GET-based forms that could generate such URIs. A server + SHOULD return 414 (Request-URI Too Long) status if a URI is longer + than the server can handle (see section 10.4.15). + + Note: Servers ought to be cautious about depending on URI lengths + above 255 bytes, because some older client or proxy + implementations might not properly support these lengths. + +3.2.2 http URL + + The "http" scheme is used to locate network resources via the HTTP + protocol. This section defines the scheme-specific syntax and + semantics for http URLs. + + http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]] + + If the port is empty or not given, port 80 is assumed. The semantics + are that the identified resource is located at the server listening + for TCP connections on that port of that host, and the Request-URI + for the resource is abs_path (section 5.1.2). The use of IP addresses + in URLs SHOULD be avoided whenever possible (see RFC 1900 [24]). If + the abs_path is not present in the URL, it MUST be given as "/" when + used as a Request-URI for a resource (section 5.1.2). If a proxy + receives a host name which is not a fully qualified domain name, it + MAY add its domain to the host name it received. If a proxy receives + a fully qualified domain name, the proxy MUST NOT change the host + name. + + + + + + + + +Fielding, et al. Standards Track [Page 19] + +RFC 2616 HTTP/1.1 June 1999 + + +3.2.3 URI Comparison + + When comparing two URIs to decide if they match or not, a client + SHOULD use a case-sensitive octet-by-octet comparison of the entire + URIs, with these exceptions: + + - A port that is empty or not given is equivalent to the default + port for that URI-reference; + + - Comparisons of host names MUST be case-insensitive; + + - Comparisons of scheme names MUST be case-insensitive; + + - An empty abs_path is equivalent to an abs_path of "/". + + Characters other than those in the "reserved" and "unsafe" sets (see + RFC 2396 [42]) are equivalent to their ""%" HEX HEX" encoding. + + For example, the following three URIs are equivalent: + + http://abc.com:80/~smith/home.html + http://ABC.com/%7Esmith/home.html + http://ABC.com:/%7esmith/home.html + +3.3 Date/Time Formats + +3.3.1 Full Date + + HTTP applications have historically allowed three different formats + for the representation of date/time stamps: + + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + + The first format is preferred as an Internet standard and represents + a fixed-length subset of that defined by RFC 1123 [8] (an update to + RFC 822 [9]). The second format is in common use, but is based on the + obsolete RFC 850 [12] date format and lacks a four-digit year. + HTTP/1.1 clients and servers that parse the date value MUST accept + all three formats (for compatibility with HTTP/1.0), though they MUST + only generate the RFC 1123 format for representing HTTP-date values + in header fields. See section 19.3 for further information. + + Note: Recipients of date values are encouraged to be robust in + accepting date values that may have been sent by non-HTTP + applications, as is sometimes the case when retrieving or posting + messages via proxies/gateways to SMTP or NNTP. + + + +Fielding, et al. Standards Track [Page 20] + +RFC 2616 HTTP/1.1 June 1999 + + + All HTTP date/time stamps MUST be represented in Greenwich Mean Time + (GMT), without exception. For the purposes of HTTP, GMT is exactly + equal to UTC (Coordinated Universal Time). This is indicated in the + first two formats by the inclusion of "GMT" as the three-letter + abbreviation for time zone, and MUST be assumed when reading the + asctime format. HTTP-date is case sensitive and MUST NOT include + additional LWS beyond that specifically included as SP in the + grammar. + + HTTP-date = rfc1123-date | rfc850-date | asctime-date + rfc1123-date = wkday "," SP date1 SP time SP "GMT" + rfc850-date = weekday "," SP date2 SP time SP "GMT" + asctime-date = wkday SP date3 SP time SP 4DIGIT + date1 = 2DIGIT SP month SP 4DIGIT + ; day month year (e.g., 02 Jun 1982) + date2 = 2DIGIT "-" month "-" 2DIGIT + ; day-month-year (e.g., 02-Jun-82) + date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) + ; month day (e.g., Jun 2) + time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + ; 00:00:00 - 23:59:59 + wkday = "Mon" | "Tue" | "Wed" + | "Thu" | "Fri" | "Sat" | "Sun" + weekday = "Monday" | "Tuesday" | "Wednesday" + | "Thursday" | "Friday" | "Saturday" | "Sunday" + month = "Jan" | "Feb" | "Mar" | "Apr" + | "May" | "Jun" | "Jul" | "Aug" + | "Sep" | "Oct" | "Nov" | "Dec" + + Note: HTTP requirements for the date/time stamp format apply only + to their usage within the protocol stream. Clients and servers are + not required to use these formats for user presentation, request + logging, etc. + +3.3.2 Delta Seconds + + Some HTTP header fields allow a time value to be specified as an + integer number of seconds, represented in decimal, after the time + that the message was received. + + delta-seconds = 1*DIGIT + +3.4 Character Sets + + HTTP uses the same definition of the term "character set" as that + described for MIME: + + + + + +Fielding, et al. Standards Track [Page 21] + +RFC 2616 HTTP/1.1 June 1999 + + + The term "character set" is used in this document to refer to a + method used with one or more tables to convert a sequence of octets + into a sequence of characters. Note that unconditional conversion in + the other direction is not required, in that not all characters may + be available in a given character set and a character set may provide + more than one sequence of octets to represent a particular character. + This definition is intended to allow various kinds of character + encoding, from simple single-table mappings such as US-ASCII to + complex table switching methods such as those that use ISO-2022's + techniques. However, the definition associated with a MIME character + set name MUST fully specify the mapping to be performed from octets + to characters. In particular, use of external profiling information + to determine the exact mapping is not permitted. + + Note: This use of the term "character set" is more commonly + referred to as a "character encoding." However, since HTTP and + MIME share the same registry, it is important that the terminology + also be shared. + + HTTP character sets are identified by case-insensitive tokens. The + complete set of tokens is defined by the IANA Character Set registry + [19]. + + charset = token + + Although HTTP allows an arbitrary token to be used as a charset + value, any token that has a predefined value within the IANA + Character Set registry [19] MUST represent the character set defined + by that registry. Applications SHOULD limit their use of character + sets to those defined by the IANA registry. + + Implementors should be aware of IETF character set requirements [38] + [41]. + +3.4.1 Missing Charset + + Some HTTP/1.0 software has interpreted a Content-Type header without + charset parameter incorrectly to mean "recipient should guess." + Senders wishing to defeat this behavior MAY include a charset + parameter even when the charset is ISO-8859-1 and SHOULD do so when + it is known that it will not confuse the recipient. + + Unfortunately, some older HTTP/1.0 clients did not deal properly with + an explicit charset parameter. HTTP/1.1 recipients MUST respect the + charset label provided by the sender; and those user agents that have + a provision to "guess" a charset MUST use the charset from the + + + + + +Fielding, et al. Standards Track [Page 22] + +RFC 2616 HTTP/1.1 June 1999 + + + content-type field if they support that charset, rather than the + recipient's preference, when initially displaying a document. See + section 3.7.1. + +3.5 Content Codings + + Content coding values indicate an encoding transformation that has + been or can be applied to an entity. Content codings are primarily + used to allow a document to be compressed or otherwise usefully + transformed without losing the identity of its underlying media type + and without loss of information. Frequently, the entity is stored in + coded form, transmitted directly, and only decoded by the recipient. + + content-coding = token + + All content-coding values are case-insensitive. HTTP/1.1 uses + content-coding values in the Accept-Encoding (section 14.3) and + Content-Encoding (section 14.11) header fields. Although the value + describes the content-coding, what is more important is that it + indicates what decoding mechanism will be required to remove the + encoding. + + The Internet Assigned Numbers Authority (IANA) acts as a registry for + content-coding value tokens. Initially, the registry contains the + following tokens: + + gzip An encoding format produced by the file compression program + "gzip" (GNU zip) as described in RFC 1952 [25]. This format is a + Lempel-Ziv coding (LZ77) with a 32 bit CRC. + + compress + The encoding format produced by the common UNIX file compression + program "compress". This format is an adaptive Lempel-Ziv-Welch + coding (LZW). + + Use of program names for the identification of encoding formats + is not desirable and is discouraged for future encodings. Their + use here is representative of historical practice, not good + design. For compatibility with previous implementations of HTTP, + applications SHOULD consider "x-gzip" and "x-compress" to be + equivalent to "gzip" and "compress" respectively. + + deflate + The "zlib" format defined in RFC 1950 [31] in combination with + the "deflate" compression mechanism described in RFC 1951 [29]. + + + + + + +Fielding, et al. Standards Track [Page 23] + +RFC 2616 HTTP/1.1 June 1999 + + + identity + The default (identity) encoding; the use of no transformation + whatsoever. This content-coding is used only in the Accept- + Encoding header, and SHOULD NOT be used in the Content-Encoding + header. + + New content-coding value tokens SHOULD be registered; to allow + interoperability between clients and servers, specifications of the + content coding algorithms needed to implement a new value SHOULD be + publicly available and adequate for independent implementation, and + conform to the purpose of content coding defined in this section. + +3.6 Transfer Codings + + Transfer-coding values are used to indicate an encoding + transformation that has been, can be, or may need to be applied to an + entity-body in order to ensure "safe transport" through the network. + This differs from a content coding in that the transfer-coding is a + property of the message, not of the original entity. + + transfer-coding = "chunked" | transfer-extension + transfer-extension = token *( ";" parameter ) + + Parameters are in the form of attribute/value pairs. + + parameter = attribute "=" value + attribute = token + value = token | quoted-string + + All transfer-coding values are case-insensitive. HTTP/1.1 uses + transfer-coding values in the TE header field (section 14.39) and in + the Transfer-Encoding header field (section 14.41). + + Whenever a transfer-coding is applied to a message-body, the set of + transfer-codings MUST include "chunked", unless the message is + terminated by closing the connection. When the "chunked" transfer- + coding is used, it MUST be the last transfer-coding applied to the + message-body. The "chunked" transfer-coding MUST NOT be applied more + than once to a message-body. These rules allow the recipient to + determine the transfer-length of the message (section 4.4). + + Transfer-codings are analogous to the Content-Transfer-Encoding + values of MIME [7], which were designed to enable safe transport of + binary data over a 7-bit transport service. However, safe transport + has a different focus for an 8bit-clean transfer protocol. In HTTP, + the only unsafe characteristic of message-bodies is the difficulty in + determining the exact body length (section 7.2.2), or the desire to + encrypt data over a shared transport. + + + +Fielding, et al. Standards Track [Page 24] + +RFC 2616 HTTP/1.1 June 1999 + + + The Internet Assigned Numbers Authority (IANA) acts as a registry for + transfer-coding value tokens. Initially, the registry contains the + following tokens: "chunked" (section 3.6.1), "identity" (section + 3.6.2), "gzip" (section 3.5), "compress" (section 3.5), and "deflate" + (section 3.5). + + New transfer-coding value tokens SHOULD be registered in the same way + as new content-coding value tokens (section 3.5). + + A server which receives an entity-body with a transfer-coding it does + not understand SHOULD return 501 (Unimplemented), and close the + connection. A server MUST NOT send transfer-codings to an HTTP/1.0 + client. + +3.6.1 Chunked Transfer Coding + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing entity-header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + Chunked-Body = *chunk + last-chunk + trailer + CRLF + + chunk = chunk-size [ chunk-extension ] CRLF + chunk-data CRLF + chunk-size = 1*HEX + last-chunk = 1*("0") [ chunk-extension ] CRLF + + chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) + chunk-ext-name = token + chunk-ext-val = token | quoted-string + chunk-data = chunk-size(OCTET) + trailer = *(entity-header CRLF) + + The chunk-size field is a string of hex digits indicating the size of + the chunk. The chunked encoding is ended by any chunk whose size is + zero, followed by the trailer, which is terminated by an empty line. + + The trailer allows the sender to include additional HTTP header + fields at the end of the message. The Trailer header field can be + used to indicate which header fields are included in a trailer (see + section 14.40). + + + + +Fielding, et al. Standards Track [Page 25] + +RFC 2616 HTTP/1.1 June 1999 + + + A server using chunked transfer-coding in a response MUST NOT use the + trailer for any header fields unless at least one of the following is + true: + + a)the request included a TE header field that indicates "trailers" is + acceptable in the transfer-coding of the response, as described in + section 14.39; or, + + b)the server is the origin server for the response, the trailer + fields consist entirely of optional metadata, and the recipient + could use the message (in a manner acceptable to the origin server) + without receiving this metadata. In other words, the origin server + is willing to accept the possibility that the trailer fields might + be silently discarded along the path to the client. + + This requirement prevents an interoperability failure when the + message is being received by an HTTP/1.1 (or later) proxy and + forwarded to an HTTP/1.0 recipient. It avoids a situation where + compliance with the protocol would have necessitated a possibly + infinite buffer on the proxy. + + An example process for decoding a Chunked-Body is presented in + appendix 19.4.6. + + All HTTP/1.1 applications MUST be able to receive and decode the + "chunked" transfer-coding, and MUST ignore chunk-extension extensions + they do not understand. + +3.7 Media Types + + HTTP uses Internet Media Types [17] in the Content-Type (section + 14.17) and Accept (section 14.1) header fields in order to provide + open and extensible data typing and type negotiation. + + media-type = type "/" subtype *( ";" parameter ) + type = token + subtype = token + + Parameters MAY follow the type/subtype in the form of attribute/value + pairs (as defined in section 3.6). + + The type, subtype, and parameter attribute names are case- + insensitive. Parameter values might or might not be case-sensitive, + depending on the semantics of the parameter name. Linear white space + (LWS) MUST NOT be used between the type and subtype, nor between an + attribute and its value. The presence or absence of a parameter might + be significant to the processing of a media-type, depending on its + definition within the media type registry. + + + +Fielding, et al. Standards Track [Page 26] + +RFC 2616 HTTP/1.1 June 1999 + + + Note that some older HTTP applications do not recognize media type + parameters. When sending data to older HTTP applications, + implementations SHOULD only use media type parameters when they are + required by that type/subtype definition. + + Media-type values are registered with the Internet Assigned Number + Authority (IANA [19]). The media type registration process is + outlined in RFC 1590 [17]. Use of non-registered media types is + discouraged. + +3.7.1 Canonicalization and Text Defaults + + Internet media types are registered with a canonical form. An + entity-body transferred via HTTP messages MUST be represented in the + appropriate canonical form prior to its transmission except for + "text" types, as defined in the next paragraph. + + When in canonical form, media subtypes of the "text" type use CRLF as + the text line break. HTTP relaxes this requirement and allows the + transport of text media with plain CR or LF alone representing a line + break when it is done consistently for an entire entity-body. HTTP + applications MUST accept CRLF, bare CR, and bare LF as being + representative of a line break in text media received via HTTP. In + addition, if the text is represented in a character set that does not + use octets 13 and 10 for CR and LF respectively, as is the case for + some multi-byte character sets, HTTP allows the use of whatever octet + sequences are defined by that character set to represent the + equivalent of CR and LF for line breaks. This flexibility regarding + line breaks applies only to text media in the entity-body; a bare CR + or LF MUST NOT be substituted for CRLF within any of the HTTP control + structures (such as header fields and multipart boundaries). + + If an entity-body is encoded with a content-coding, the underlying + data MUST be in a form defined above prior to being encoded. + + The "charset" parameter is used with some media types to define the + character set (section 3.4) of the data. When no explicit charset + parameter is provided by the sender, media subtypes of the "text" + type are defined to have a default charset value of "ISO-8859-1" when + received via HTTP. Data in character sets other than "ISO-8859-1" or + its subsets MUST be labeled with an appropriate charset value. See + section 3.4.1 for compatibility problems. + +3.7.2 Multipart Types + + MIME provides for a number of "multipart" types -- encapsulations of + one or more entities within a single message-body. All multipart + types share a common syntax, as defined in section 5.1.1 of RFC 2046 + + + +Fielding, et al. Standards Track [Page 27] + +RFC 2616 HTTP/1.1 June 1999 + + + [40], and MUST include a boundary parameter as part of the media type + value. The message body is itself a protocol element and MUST + therefore use only CRLF to represent line breaks between body-parts. + Unlike in RFC 2046, the epilogue of any multipart message MUST be + empty; HTTP applications MUST NOT transmit the epilogue (even if the + original multipart contains an epilogue). These restrictions exist in + order to preserve the self-delimiting nature of a multipart message- + body, wherein the "end" of the message-body is indicated by the + ending multipart boundary. + + In general, HTTP treats a multipart message-body no differently than + any other media type: strictly as payload. The one exception is the + "multipart/byteranges" type (appendix 19.2) when it appears in a 206 + (Partial Content) response, which will be interpreted by some HTTP + caching mechanisms as described in sections 13.5.4 and 14.16. In all + other cases, an HTTP user agent SHOULD follow the same or similar + behavior as a MIME user agent would upon receipt of a multipart type. + The MIME header fields within each body-part of a multipart message- + body do not have any significance to HTTP beyond that defined by + their MIME semantics. + + In general, an HTTP user agent SHOULD follow the same or similar + behavior as a MIME user agent would upon receipt of a multipart type. + If an application receives an unrecognized multipart subtype, the + application MUST treat it as being equivalent to "multipart/mixed". + + Note: The "multipart/form-data" type has been specifically defined + for carrying form data suitable for processing via the POST + request method, as described in RFC 1867 [15]. + +3.8 Product Tokens + + Product tokens are used to allow communicating applications to + identify themselves by software name and version. Most fields using + product tokens also allow sub-products which form a significant part + of the application to be listed, separated by white space. By + convention, the products are listed in order of their significance + for identifying the application. + + product = token ["/" product-version] + product-version = token + + Examples: + + User-Agent: CERN-LineMode/2.15 libwww/2.17b3 + Server: Apache/0.8.4 + + + + + +Fielding, et al. Standards Track [Page 28] + +RFC 2616 HTTP/1.1 June 1999 + + + Product tokens SHOULD be short and to the point. They MUST NOT be + used for advertising or other non-essential information. Although any + token character MAY appear in a product-version, this token SHOULD + only be used for a version identifier (i.e., successive versions of + the same product SHOULD only differ in the product-version portion of + the product value). + +3.9 Quality Values + + HTTP content negotiation (section 12) uses short "floating point" + numbers to indicate the relative importance ("weight") of various + negotiable parameters. A weight is normalized to a real number in + the range 0 through 1, where 0 is the minimum and 1 the maximum + value. If a parameter has a quality value of 0, then content with + this parameter is `not acceptable' for the client. HTTP/1.1 + applications MUST NOT generate more than three digits after the + decimal point. User configuration of these values SHOULD also be + limited in this fashion. + + qvalue = ( "0" [ "." 0*3DIGIT ] ) + | ( "1" [ "." 0*3("0") ] ) + + "Quality values" is a misnomer, since these values merely represent + relative degradation in desired quality. + +3.10 Language Tags + + A language tag identifies a natural language spoken, written, or + otherwise conveyed by human beings for communication of information + to other human beings. Computer languages are explicitly excluded. + HTTP uses language tags within the Accept-Language and Content- + Language fields. + + The syntax and registry of HTTP language tags is the same as that + defined by RFC 1766 [1]. In summary, a language tag is composed of 1 + or more parts: A primary language tag and a possibly empty series of + subtags: + + language-tag = primary-tag *( "-" subtag ) + primary-tag = 1*8ALPHA + subtag = 1*8ALPHA + + White space is not allowed within the tag and all tags are case- + insensitive. The name space of language tags is administered by the + IANA. Example tags include: + + en, en-US, en-cockney, i-cherokee, x-pig-latin + + + + +Fielding, et al. Standards Track [Page 29] + +RFC 2616 HTTP/1.1 June 1999 + + + where any two-letter primary-tag is an ISO-639 language abbreviation + and any two-letter initial subtag is an ISO-3166 country code. (The + last three tags above are not registered tags; all but the last are + examples of tags which could be registered in future.) + +3.11 Entity Tags + + Entity tags are used for comparing two or more entities from the same + requested resource. HTTP/1.1 uses entity tags in the ETag (section + 14.19), If-Match (section 14.24), If-None-Match (section 14.26), and + If-Range (section 14.27) header fields. The definition of how they + are used and compared as cache validators is in section 13.3.3. An + entity tag consists of an opaque quoted string, possibly prefixed by + a weakness indicator. + + entity-tag = [ weak ] opaque-tag + weak = "W/" + opaque-tag = quoted-string + + A "strong entity tag" MAY be shared by two entities of a resource + only if they are equivalent by octet equality. + + A "weak entity tag," indicated by the "W/" prefix, MAY be shared by + two entities of a resource only if the entities are equivalent and + could be substituted for each other with no significant change in + semantics. A weak entity tag can only be used for weak comparison. + + An entity tag MUST be unique across all versions of all entities + associated with a particular resource. A given entity tag value MAY + be used for entities obtained by requests on different URIs. The use + of the same entity tag value in conjunction with entities obtained by + requests on different URIs does not imply the equivalence of those + entities. + +3.12 Range Units + + HTTP/1.1 allows a client to request that only part (a range of) the + response entity be included within the response. HTTP/1.1 uses range + units in the Range (section 14.35) and Content-Range (section 14.16) + header fields. An entity can be broken down into subranges according + to various structural units. + + range-unit = bytes-unit | other-range-unit + bytes-unit = "bytes" + other-range-unit = token + + The only range unit defined by HTTP/1.1 is "bytes". HTTP/1.1 + implementations MAY ignore ranges specified using other units. + + + +Fielding, et al. Standards Track [Page 30] + +RFC 2616 HTTP/1.1 June 1999 + + + HTTP/1.1 has been designed to allow implementations of applications + that do not depend on knowledge of ranges. + +4 HTTP Message + +4.1 Message Types + + HTTP messages consist of requests from client to server and responses + from server to client. + + HTTP-message = Request | Response ; HTTP/1.1 messages + + Request (section 5) and Response (section 6) messages use the generic + message format of RFC 822 [9] for transferring entities (the payload + of the message). Both types of message consist of a start-line, zero + or more header fields (also known as "headers"), an empty line (i.e., + a line with nothing preceding the CRLF) indicating the end of the + header fields, and possibly a message-body. + + generic-message = start-line + *(message-header CRLF) + CRLF + [ message-body ] + start-line = Request-Line | Status-Line + + In the interest of robustness, servers SHOULD ignore any empty + line(s) received where a Request-Line is expected. In other words, if + the server is reading the protocol stream at the beginning of a + message and receives a CRLF first, it should ignore the CRLF. + + Certain buggy HTTP/1.0 client implementations generate extra CRLF's + after a POST request. To restate what is explicitly forbidden by the + BNF, an HTTP/1.1 client MUST NOT preface or follow a request with an + extra CRLF. + +4.2 Message Headers + + HTTP header fields, which include general-header (section 4.5), + request-header (section 5.3), response-header (section 6.2), and + entity-header (section 7.1) fields, follow the same generic format as + that given in Section 3.1 of RFC 822 [9]. Each header field consists + of a name followed by a colon (":") and the field value. Field names + are case-insensitive. The field value MAY be preceded by any amount + of LWS, though a single SP is preferred. Header fields can be + extended over multiple lines by preceding each extra line with at + least one SP or HT. Applications ought to follow "common form", where + one is known or indicated, when generating HTTP constructs, since + there might exist some implementations that fail to accept anything + + + +Fielding, et al. Standards Track [Page 31] + +RFC 2616 HTTP/1.1 June 1999 + + + beyond the common forms. + + message-header = field-name ":" [ field-value ] + field-name = token + field-value = *( field-content | LWS ) + field-content = + + The field-content does not include any leading or trailing LWS: + linear white space occurring before the first non-whitespace + character of the field-value or after the last non-whitespace + character of the field-value. Such leading or trailing LWS MAY be + removed without changing the semantics of the field value. Any LWS + that occurs between field-content MAY be replaced with a single SP + before interpreting the field value or forwarding the message + downstream. + + The order in which header fields with differing field names are + received is not significant. However, it is "good practice" to send + general-header fields first, followed by request-header or response- + header fields, and ending with the entity-header fields. + + Multiple message-header fields with the same field-name MAY be + present in a message if and only if the entire field-value for that + header field is defined as a comma-separated list [i.e., #(values)]. + It MUST be possible to combine the multiple header fields into one + "field-name: field-value" pair, without changing the semantics of the + message, by appending each subsequent field-value to the first, each + separated by a comma. The order in which header fields with the same + field-name are received is therefore significant to the + interpretation of the combined field value, and thus a proxy MUST NOT + change the order of these field values when a message is forwarded. + +4.3 Message Body + + The message-body (if any) of an HTTP message is used to carry the + entity-body associated with the request or response. The message-body + differs from the entity-body only when a transfer-coding has been + applied, as indicated by the Transfer-Encoding header field (section + 14.41). + + message-body = entity-body + | + + Transfer-Encoding MUST be used to indicate any transfer-codings + applied by an application to ensure safe and proper transfer of the + message. Transfer-Encoding is a property of the message, not of the + + + +Fielding, et al. Standards Track [Page 32] + +RFC 2616 HTTP/1.1 June 1999 + + + entity, and thus MAY be added or removed by any application along the + request/response chain. (However, section 3.6 places restrictions on + when certain transfer-codings may be used.) + + The rules for when a message-body is allowed in a message differ for + requests and responses. + + The presence of a message-body in a request is signaled by the + inclusion of a Content-Length or Transfer-Encoding header field in + the request's message-headers. A message-body MUST NOT be included in + a request if the specification of the request method (section 5.1.1) + does not allow sending an entity-body in requests. A server SHOULD + read and forward a message-body on any request; if the request method + does not include defined semantics for an entity-body, then the + message-body SHOULD be ignored when handling the request. + + For response messages, whether or not a message-body is included with + a message is dependent on both the request method and the response + status code (section 6.1.1). All responses to the HEAD request method + MUST NOT include a message-body, even though the presence of entity- + header fields might lead one to believe they do. All 1xx + (informational), 204 (no content), and 304 (not modified) responses + MUST NOT include a message-body. All other responses do include a + message-body, although it MAY be of zero length. + +4.4 Message Length + + The transfer-length of a message is the length of the message-body as + it appears in the message; that is, after any transfer-codings have + been applied. When a message-body is included with a message, the + transfer-length of that body is determined by one of the following + (in order of precedence): + + 1.Any response message which "MUST NOT" include a message-body (such + as the 1xx, 204, and 304 responses and any response to a HEAD + request) is always terminated by the first empty line after the + header fields, regardless of the entity-header fields present in + the message. + + 2.If a Transfer-Encoding header field (section 14.41) is present and + has any value other than "identity", then the transfer-length is + defined by use of the "chunked" transfer-coding (section 3.6), + unless the message is terminated by closing the connection. + + 3.If a Content-Length header field (section 14.13) is present, its + decimal value in OCTETs represents both the entity-length and the + transfer-length. The Content-Length header field MUST NOT be sent + if these two lengths are different (i.e., if a Transfer-Encoding + + + +Fielding, et al. Standards Track [Page 33] + +RFC 2616 HTTP/1.1 June 1999 + + + header field is present). If a message is received with both a + Transfer-Encoding header field and a Content-Length header field, + the latter MUST be ignored. + + 4.If the message uses the media type "multipart/byteranges", and the + ransfer-length is not otherwise specified, then this self- + elimiting media type defines the transfer-length. This media type + UST NOT be used unless the sender knows that the recipient can arse + it; the presence in a request of a Range header with ultiple byte- + range specifiers from a 1.1 client implies that the lient can parse + multipart/byteranges responses. + + A range header might be forwarded by a 1.0 proxy that does not + understand multipart/byteranges; in this case the server MUST + delimit the message using methods defined in items 1,3 or 5 of + this section. + + 5.By the server closing the connection. (Closing the connection + cannot be used to indicate the end of a request body, since that + would leave no possibility for the server to send back a response.) + + For compatibility with HTTP/1.0 applications, HTTP/1.1 requests + containing a message-body MUST include a valid Content-Length header + field unless the server is known to be HTTP/1.1 compliant. If a + request contains a message-body and a Content-Length is not given, + the server SHOULD respond with 400 (bad request) if it cannot + determine the length of the message, or with 411 (length required) if + it wishes to insist on receiving a valid Content-Length. + + All HTTP/1.1 applications that receive entities MUST accept the + "chunked" transfer-coding (section 3.6), thus allowing this mechanism + to be used for messages when the message length cannot be determined + in advance. + + Messages MUST NOT include both a Content-Length header field and a + non-identity transfer-coding. If the message does include a non- + identity transfer-coding, the Content-Length MUST be ignored. + + When a Content-Length is given in a message where a message-body is + allowed, its field value MUST exactly match the number of OCTETs in + the message-body. HTTP/1.1 user agents MUST notify the user when an + invalid length is received and detected. + +4.5 General Header Fields + + There are a few header fields which have general applicability for + both request and response messages, but which do not apply to the + entity being transferred. These header fields apply only to the + + + +Fielding, et al. Standards Track [Page 34] + +RFC 2616 HTTP/1.1 June 1999 + + + message being transmitted. + + general-header = Cache-Control ; Section 14.9 + | Connection ; Section 14.10 + | Date ; Section 14.18 + | Pragma ; Section 14.32 + | Trailer ; Section 14.40 + | Transfer-Encoding ; Section 14.41 + | Upgrade ; Section 14.42 + | Via ; Section 14.45 + | Warning ; Section 14.46 + + General-header field names can be extended reliably only in + combination with a change in the protocol version. However, new or + experimental header fields may be given the semantics of general + header fields if all parties in the communication recognize them to + be general-header fields. Unrecognized header fields are treated as + entity-header fields. + +5 Request + + A request message from a client to a server includes, within the + first line of that message, the method to be applied to the resource, + the identifier of the resource, and the protocol version in use. + + Request = Request-Line ; Section 5.1 + *(( general-header ; Section 4.5 + | request-header ; Section 5.3 + | entity-header ) CRLF) ; Section 7.1 + CRLF + [ message-body ] ; Section 4.3 + +5.1 Request-Line + + The Request-Line begins with a method token, followed by the + Request-URI and the protocol version, and ending with CRLF. The + elements are separated by SP characters. No CR or LF is allowed + except in the final CRLF sequence. + + Request-Line = Method SP Request-URI SP HTTP-Version CRLF + + + + + + + + + + + +Fielding, et al. Standards Track [Page 35] + +RFC 2616 HTTP/1.1 June 1999 + + +5.1.1 Method + + The Method token indicates the method to be performed on the + resource identified by the Request-URI. The method is case-sensitive. + + Method = "OPTIONS" ; Section 9.2 + | "GET" ; Section 9.3 + | "HEAD" ; Section 9.4 + | "POST" ; Section 9.5 + | "PUT" ; Section 9.6 + | "DELETE" ; Section 9.7 + | "TRACE" ; Section 9.8 + | "CONNECT" ; Section 9.9 + | extension-method + extension-method = token + + The list of methods allowed by a resource can be specified in an + Allow header field (section 14.7). The return code of the response + always notifies the client whether a method is currently allowed on a + resource, since the set of allowed methods can change dynamically. An + origin server SHOULD return the status code 405 (Method Not Allowed) + if the method is known by the origin server but not allowed for the + requested resource, and 501 (Not Implemented) if the method is + unrecognized or not implemented by the origin server. The methods GET + and HEAD MUST be supported by all general-purpose servers. All other + methods are OPTIONAL; however, if the above methods are implemented, + they MUST be implemented with the same semantics as those specified + in section 9. + +5.1.2 Request-URI + + The Request-URI is a Uniform Resource Identifier (section 3.2) and + identifies the resource upon which to apply the request. + + Request-URI = "*" | absoluteURI | abs_path | authority + + The four options for Request-URI are dependent on the nature of the + request. The asterisk "*" means that the request does not apply to a + particular resource, but to the server itself, and is only allowed + when the method used does not necessarily apply to a resource. One + example would be + + OPTIONS * HTTP/1.1 + + The absoluteURI form is REQUIRED when the request is being made to a + proxy. The proxy is requested to forward the request or service it + from a valid cache, and return the response. Note that the proxy MAY + forward the request on to another proxy or directly to the server + + + +Fielding, et al. Standards Track [Page 36] + +RFC 2616 HTTP/1.1 June 1999 + + + specified by the absoluteURI. In order to avoid request loops, a + proxy MUST be able to recognize all of its server names, including + any aliases, local variations, and the numeric IP address. An example + Request-Line would be: + + GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1 + + To allow for transition to absoluteURIs in all requests in future + versions of HTTP, all HTTP/1.1 servers MUST accept the absoluteURI + form in requests, even though HTTP/1.1 clients will only generate + them in requests to proxies. + + The authority form is only used by the CONNECT method (section 9.9). + + The most common form of Request-URI is that used to identify a + resource on an origin server or gateway. In this case the absolute + path of the URI MUST be transmitted (see section 3.2.1, abs_path) as + the Request-URI, and the network location of the URI (authority) MUST + be transmitted in a Host header field. For example, a client wishing + to retrieve the resource above directly from the origin server would + create a TCP connection to port 80 of the host "www.w3.org" and send + the lines: + + GET /pub/WWW/TheProject.html HTTP/1.1 + Host: www.w3.org + + followed by the remainder of the Request. Note that the absolute path + cannot be empty; if none is present in the original URI, it MUST be + given as "/" (the server root). + + The Request-URI is transmitted in the format specified in section + 3.2.1. If the Request-URI is encoded using the "% HEX HEX" encoding + [42], the origin server MUST decode the Request-URI in order to + properly interpret the request. Servers SHOULD respond to invalid + Request-URIs with an appropriate status code. + + A transparent proxy MUST NOT rewrite the "abs_path" part of the + received Request-URI when forwarding it to the next inbound server, + except as noted above to replace a null abs_path with "/". + + Note: The "no rewrite" rule prevents the proxy from changing the + meaning of the request when the origin server is improperly using + a non-reserved URI character for a reserved purpose. Implementors + should be aware that some pre-HTTP/1.1 proxies have been known to + rewrite the Request-URI. + + + + + + +Fielding, et al. Standards Track [Page 37] + +RFC 2616 HTTP/1.1 June 1999 + + +5.2 The Resource Identified by a Request + + The exact resource identified by an Internet request is determined by + examining both the Request-URI and the Host header field. + + An origin server that does not allow resources to differ by the + requested host MAY ignore the Host header field value when + determining the resource identified by an HTTP/1.1 request. (But see + section 19.6.1.1 for other requirements on Host support in HTTP/1.1.) + + An origin server that does differentiate resources based on the host + requested (sometimes referred to as virtual hosts or vanity host + names) MUST use the following rules for determining the requested + resource on an HTTP/1.1 request: + + 1. If Request-URI is an absoluteURI, the host is part of the + Request-URI. Any Host header field value in the request MUST be + ignored. + + 2. If the Request-URI is not an absoluteURI, and the request includes + a Host header field, the host is determined by the Host header + field value. + + 3. If the host as determined by rule 1 or 2 is not a valid host on + the server, the response MUST be a 400 (Bad Request) error message. + + Recipients of an HTTP/1.0 request that lacks a Host header field MAY + attempt to use heuristics (e.g., examination of the URI path for + something unique to a particular host) in order to determine what + exact resource is being requested. + +5.3 Request Header Fields + + The request-header fields allow the client to pass additional + information about the request, and about the client itself, to the + server. These fields act as request modifiers, with semantics + equivalent to the parameters on a programming language method + invocation. + + request-header = Accept ; Section 14.1 + | Accept-Charset ; Section 14.2 + | Accept-Encoding ; Section 14.3 + | Accept-Language ; Section 14.4 + | Authorization ; Section 14.8 + | Expect ; Section 14.20 + | From ; Section 14.22 + | Host ; Section 14.23 + | If-Match ; Section 14.24 + + + +Fielding, et al. Standards Track [Page 38] + +RFC 2616 HTTP/1.1 June 1999 + + + | If-Modified-Since ; Section 14.25 + | If-None-Match ; Section 14.26 + | If-Range ; Section 14.27 + | If-Unmodified-Since ; Section 14.28 + | Max-Forwards ; Section 14.31 + | Proxy-Authorization ; Section 14.34 + | Range ; Section 14.35 + | Referer ; Section 14.36 + | TE ; Section 14.39 + | User-Agent ; Section 14.43 + + Request-header field names can be extended reliably only in + combination with a change in the protocol version. However, new or + experimental header fields MAY be given the semantics of request- + header fields if all parties in the communication recognize them to + be request-header fields. Unrecognized header fields are treated as + entity-header fields. + +6 Response + + After receiving and interpreting a request message, a server responds + with an HTTP response message. + + Response = Status-Line ; Section 6.1 + *(( general-header ; Section 4.5 + | response-header ; Section 6.2 + | entity-header ) CRLF) ; Section 7.1 + CRLF + [ message-body ] ; Section 7.2 + +6.1 Status-Line + + The first line of a Response message is the Status-Line, consisting + of the protocol version followed by a numeric status code and its + associated textual phrase, with each element separated by SP + characters. No CR or LF is allowed except in the final CRLF sequence. + + Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF + +6.1.1 Status Code and Reason Phrase + + The Status-Code element is a 3-digit integer result code of the + attempt to understand and satisfy the request. These codes are fully + defined in section 10. The Reason-Phrase is intended to give a short + textual description of the Status-Code. The Status-Code is intended + for use by automata and the Reason-Phrase is intended for the human + user. The client is not required to examine or display the Reason- + Phrase. + + + +Fielding, et al. Standards Track [Page 39] + +RFC 2616 HTTP/1.1 June 1999 + + + The first digit of the Status-Code defines the class of response. The + last two digits do not have any categorization role. There are 5 + values for the first digit: + + - 1xx: Informational - Request received, continuing process + + - 2xx: Success - The action was successfully received, + understood, and accepted + + - 3xx: Redirection - Further action must be taken in order to + complete the request + + - 4xx: Client Error - The request contains bad syntax or cannot + be fulfilled + + - 5xx: Server Error - The server failed to fulfill an apparently + valid request + + The individual values of the numeric status codes defined for + HTTP/1.1, and an example set of corresponding Reason-Phrase's, are + presented below. The reason phrases listed here are only + recommendations -- they MAY be replaced by local equivalents without + affecting the protocol. + + Status-Code = + "100" ; Section 10.1.1: Continue + | "101" ; Section 10.1.2: Switching Protocols + | "200" ; Section 10.2.1: OK + | "201" ; Section 10.2.2: Created + | "202" ; Section 10.2.3: Accepted + | "203" ; Section 10.2.4: Non-Authoritative Information + | "204" ; Section 10.2.5: No Content + | "205" ; Section 10.2.6: Reset Content + | "206" ; Section 10.2.7: Partial Content + | "300" ; Section 10.3.1: Multiple Choices + | "301" ; Section 10.3.2: Moved Permanently + | "302" ; Section 10.3.3: Found + | "303" ; Section 10.3.4: See Other + | "304" ; Section 10.3.5: Not Modified + | "305" ; Section 10.3.6: Use Proxy + | "307" ; Section 10.3.8: Temporary Redirect + | "400" ; Section 10.4.1: Bad Request + | "401" ; Section 10.4.2: Unauthorized + | "402" ; Section 10.4.3: Payment Required + | "403" ; Section 10.4.4: Forbidden + | "404" ; Section 10.4.5: Not Found + | "405" ; Section 10.4.6: Method Not Allowed + | "406" ; Section 10.4.7: Not Acceptable + + + +Fielding, et al. Standards Track [Page 40] + +RFC 2616 HTTP/1.1 June 1999 + + + | "407" ; Section 10.4.8: Proxy Authentication Required + | "408" ; Section 10.4.9: Request Time-out + | "409" ; Section 10.4.10: Conflict + | "410" ; Section 10.4.11: Gone + | "411" ; Section 10.4.12: Length Required + | "412" ; Section 10.4.13: Precondition Failed + | "413" ; Section 10.4.14: Request Entity Too Large + | "414" ; Section 10.4.15: Request-URI Too Large + | "415" ; Section 10.4.16: Unsupported Media Type + | "416" ; Section 10.4.17: Requested range not satisfiable + | "417" ; Section 10.4.18: Expectation Failed + | "500" ; Section 10.5.1: Internal Server Error + | "501" ; Section 10.5.2: Not Implemented + | "502" ; Section 10.5.3: Bad Gateway + | "503" ; Section 10.5.4: Service Unavailable + | "504" ; Section 10.5.5: Gateway Time-out + | "505" ; Section 10.5.6: HTTP Version not supported + | extension-code + + extension-code = 3DIGIT + Reason-Phrase = * + + HTTP status codes are extensible. HTTP applications are not required + to understand the meaning of all registered status codes, though such + understanding is obviously desirable. However, applications MUST + understand the class of any status code, as indicated by the first + digit, and treat any unrecognized response as being equivalent to the + x00 status code of that class, with the exception that an + unrecognized response MUST NOT be cached. For example, if an + unrecognized status code of 431 is received by the client, it can + safely assume that there was something wrong with its request and + treat the response as if it had received a 400 status code. In such + cases, user agents SHOULD present to the user the entity returned + with the response, since that entity is likely to include human- + readable information which will explain the unusual status. + +6.2 Response Header Fields + + The response-header fields allow the server to pass additional + information about the response which cannot be placed in the Status- + Line. These header fields give information about the server and about + further access to the resource identified by the Request-URI. + + response-header = Accept-Ranges ; Section 14.5 + | Age ; Section 14.6 + | ETag ; Section 14.19 + | Location ; Section 14.30 + | Proxy-Authenticate ; Section 14.33 + + + +Fielding, et al. Standards Track [Page 41] + +RFC 2616 HTTP/1.1 June 1999 + + + | Retry-After ; Section 14.37 + | Server ; Section 14.38 + | Vary ; Section 14.44 + | WWW-Authenticate ; Section 14.47 + + Response-header field names can be extended reliably only in + combination with a change in the protocol version. However, new or + experimental header fields MAY be given the semantics of response- + header fields if all parties in the communication recognize them to + be response-header fields. Unrecognized header fields are treated as + entity-header fields. + +7 Entity + + Request and Response messages MAY transfer an entity if not otherwise + restricted by the request method or response status code. An entity + consists of entity-header fields and an entity-body, although some + responses will only include the entity-headers. + + In this section, both sender and recipient refer to either the client + or the server, depending on who sends and who receives the entity. + +7.1 Entity Header Fields + + Entity-header fields define metainformation about the entity-body or, + if no body is present, about the resource identified by the request. + Some of this metainformation is OPTIONAL; some might be REQUIRED by + portions of this specification. + + entity-header = Allow ; Section 14.7 + | Content-Encoding ; Section 14.11 + | Content-Language ; Section 14.12 + | Content-Length ; Section 14.13 + | Content-Location ; Section 14.14 + | Content-MD5 ; Section 14.15 + | Content-Range ; Section 14.16 + | Content-Type ; Section 14.17 + | Expires ; Section 14.21 + | Last-Modified ; Section 14.29 + | extension-header + + extension-header = message-header + + The extension-header mechanism allows additional entity-header fields + to be defined without changing the protocol, but these fields cannot + be assumed to be recognizable by the recipient. Unrecognized header + fields SHOULD be ignored by the recipient and MUST be forwarded by + transparent proxies. + + + +Fielding, et al. Standards Track [Page 42] + +RFC 2616 HTTP/1.1 June 1999 + + +7.2 Entity Body + + The entity-body (if any) sent with an HTTP request or response is in + a format and encoding defined by the entity-header fields. + + entity-body = *OCTET + + An entity-body is only present in a message when a message-body is + present, as described in section 4.3. The entity-body is obtained + from the message-body by decoding any Transfer-Encoding that might + have been applied to ensure safe and proper transfer of the message. + +7.2.1 Type + + When an entity-body is included with a message, the data type of that + body is determined via the header fields Content-Type and Content- + Encoding. These define a two-layer, ordered encoding model: + + entity-body := Content-Encoding( Content-Type( data ) ) + + Content-Type specifies the media type of the underlying data. + Content-Encoding may be used to indicate any additional content + codings applied to the data, usually for the purpose of data + compression, that are a property of the requested resource. There is + no default encoding. + + Any HTTP/1.1 message containing an entity-body SHOULD include a + Content-Type header field defining the media type of that body. If + and only if the media type is not given by a Content-Type field, the + recipient MAY attempt to guess the media type via inspection of its + content and/or the name extension(s) of the URI used to identify the + resource. If the media type remains unknown, the recipient SHOULD + treat it as type "application/octet-stream". + +7.2.2 Entity Length + + The entity-length of a message is the length of the message-body + before any transfer-codings have been applied. Section 4.4 defines + how the transfer-length of a message-body is determined. + + + + + + + + + + + + +Fielding, et al. Standards Track [Page 43] + +RFC 2616 HTTP/1.1 June 1999 + + +8 Connections + +8.1 Persistent Connections + +8.1.1 Purpose + + Prior to persistent connections, a separate TCP connection was + established to fetch each URL, increasing the load on HTTP servers + and causing congestion on the Internet. The use of inline images and + other associated data often require a client to make multiple + requests of the same server in a short amount of time. Analysis of + these performance problems and results from a prototype + implementation are available [26] [30]. Implementation experience and + measurements of actual HTTP/1.1 (RFC 2068) implementations show good + results [39]. Alternatives have also been explored, for example, + T/TCP [27]. + + Persistent HTTP connections have a number of advantages: + + - By opening and closing fewer TCP connections, CPU time is saved + in routers and hosts (clients, servers, proxies, gateways, + tunnels, or caches), and memory used for TCP protocol control + blocks can be saved in hosts. + + - HTTP requests and responses can be pipelined on a connection. + Pipelining allows a client to make multiple requests without + waiting for each response, allowing a single TCP connection to + be used much more efficiently, with much lower elapsed time. + + - Network congestion is reduced by reducing the number of packets + caused by TCP opens, and by allowing TCP sufficient time to + determine the congestion state of the network. + + - Latency on subsequent requests is reduced since there is no time + spent in TCP's connection opening handshake. + + - HTTP can evolve more gracefully, since errors can be reported + without the penalty of closing the TCP connection. Clients using + future versions of HTTP might optimistically try a new feature, + but if communicating with an older server, retry with old + semantics after an error is reported. + + HTTP implementations SHOULD implement persistent connections. + + + + + + + + +Fielding, et al. Standards Track [Page 44] + +RFC 2616 HTTP/1.1 June 1999 + + +8.1.2 Overall Operation + + A significant difference between HTTP/1.1 and earlier versions of + HTTP is that persistent connections are the default behavior of any + HTTP connection. That is, unless otherwise indicated, the client + SHOULD assume that the server will maintain a persistent connection, + even after error responses from the server. + + Persistent connections provide a mechanism by which a client and a + server can signal the close of a TCP connection. This signaling takes + place using the Connection header field (section 14.10). Once a close + has been signaled, the client MUST NOT send any more requests on that + connection. + +8.1.2.1 Negotiation + + An HTTP/1.1 server MAY assume that a HTTP/1.1 client intends to + maintain a persistent connection unless a Connection header including + the connection-token "close" was sent in the request. If the server + chooses to close the connection immediately after sending the + response, it SHOULD send a Connection header including the + connection-token close. + + An HTTP/1.1 client MAY expect a connection to remain open, but would + decide to keep it open based on whether the response from a server + contains a Connection header with the connection-token close. In case + the client does not want to maintain a connection for more than that + request, it SHOULD send a Connection header including the + connection-token close. + + If either the client or the server sends the close token in the + Connection header, that request becomes the last one for the + connection. + + Clients and servers SHOULD NOT assume that a persistent connection is + maintained for HTTP versions less than 1.1 unless it is explicitly + signaled. See section 19.6.2 for more information on backward + compatibility with HTTP/1.0 clients. + + In order to remain persistent, all messages on the connection MUST + have a self-defined message length (i.e., one not defined by closure + of the connection), as described in section 4.4. + + + + + + + + + +Fielding, et al. Standards Track [Page 45] + +RFC 2616 HTTP/1.1 June 1999 + + +8.1.2.2 Pipelining + + A client that supports persistent connections MAY "pipeline" its + requests (i.e., send multiple requests without waiting for each + response). A server MUST send its responses to those requests in the + same order that the requests were received. + + Clients which assume persistent connections and pipeline immediately + after connection establishment SHOULD be prepared to retry their + connection if the first pipelined attempt fails. If a client does + such a retry, it MUST NOT pipeline before it knows the connection is + persistent. Clients MUST also be prepared to resend their requests if + the server closes the connection before sending all of the + corresponding responses. + + Clients SHOULD NOT pipeline requests using non-idempotent methods or + non-idempotent sequences of methods (see section 9.1.2). Otherwise, a + premature termination of the transport connection could lead to + indeterminate results. A client wishing to send a non-idempotent + request SHOULD wait to send that request until it has received the + response status for the previous request. + +8.1.3 Proxy Servers + + It is especially important that proxies correctly implement the + properties of the Connection header field as specified in section + 14.10. + + The proxy server MUST signal persistent connections separately with + its clients and the origin servers (or other proxy servers) that it + connects to. Each persistent connection applies to only one transport + link. + + A proxy server MUST NOT establish a HTTP/1.1 persistent connection + with an HTTP/1.0 client (but see RFC 2068 [33] for information and + discussion of the problems with the Keep-Alive header implemented by + many HTTP/1.0 clients). + +8.1.4 Practical Considerations + + Servers will usually have some time-out value beyond which they will + no longer maintain an inactive connection. Proxy servers might make + this a higher value since it is likely that the client will be making + more connections through the same server. The use of persistent + connections places no requirements on the length (or existence) of + this time-out for either the client or the server. + + + + + +Fielding, et al. Standards Track [Page 46] + +RFC 2616 HTTP/1.1 June 1999 + + + When a client or server wishes to time-out it SHOULD issue a graceful + close on the transport connection. Clients and servers SHOULD both + constantly watch for the other side of the transport close, and + respond to it as appropriate. If a client or server does not detect + the other side's close promptly it could cause unnecessary resource + drain on the network. + + A client, server, or proxy MAY close the transport connection at any + time. For example, a client might have started to send a new request + at the same time that the server has decided to close the "idle" + connection. From the server's point of view, the connection is being + closed while it was idle, but from the client's point of view, a + request is in progress. + + This means that clients, servers, and proxies MUST be able to recover + from asynchronous close events. Client software SHOULD reopen the + transport connection and retransmit the aborted sequence of requests + without user interaction so long as the request sequence is + idempotent (see section 9.1.2). Non-idempotent methods or sequences + MUST NOT be automatically retried, although user agents MAY offer a + human operator the choice of retrying the request(s). Confirmation by + user-agent software with semantic understanding of the application + MAY substitute for user confirmation. The automatic retry SHOULD NOT + be repeated if the second sequence of requests fails. + + Servers SHOULD always respond to at least one request per connection, + if at all possible. Servers SHOULD NOT close a connection in the + middle of transmitting a response, unless a network or client failure + is suspected. + + Clients that use persistent connections SHOULD limit the number of + simultaneous connections that they maintain to a given server. A + single-user client SHOULD NOT maintain more than 2 connections with + any server or proxy. A proxy SHOULD use up to 2*N connections to + another server or proxy, where N is the number of simultaneously + active users. These guidelines are intended to improve HTTP response + times and avoid congestion. + +8.2 Message Transmission Requirements + +8.2.1 Persistent Connections and Flow Control + + HTTP/1.1 servers SHOULD maintain persistent connections and use TCP's + flow control mechanisms to resolve temporary overloads, rather than + terminating connections with the expectation that clients will retry. + The latter technique can exacerbate network congestion. + + + + + +Fielding, et al. Standards Track [Page 47] + +RFC 2616 HTTP/1.1 June 1999 + + +8.2.2 Monitoring Connections for Error Status Messages + + An HTTP/1.1 (or later) client sending a message-body SHOULD monitor + the network connection for an error status while it is transmitting + the request. If the client sees an error status, it SHOULD + immediately cease transmitting the body. If the body is being sent + using a "chunked" encoding (section 3.6), a zero length chunk and + empty trailer MAY be used to prematurely mark the end of the message. + If the body was preceded by a Content-Length header, the client MUST + close the connection. + +8.2.3 Use of the 100 (Continue) Status + + The purpose of the 100 (Continue) status (see section 10.1.1) is to + allow a client that is sending a request message with a request body + to determine if the origin server is willing to accept the request + (based on the request headers) before the client sends the request + body. In some cases, it might either be inappropriate or highly + inefficient for the client to send the body if the server will reject + the message without looking at the body. + + Requirements for HTTP/1.1 clients: + + - If a client will wait for a 100 (Continue) response before + sending the request body, it MUST send an Expect request-header + field (section 14.20) with the "100-continue" expectation. + + - A client MUST NOT send an Expect request-header field (section + 14.20) with the "100-continue" expectation if it does not intend + to send a request body. + + Because of the presence of older implementations, the protocol allows + ambiguous situations in which a client may send "Expect: 100- + continue" without receiving either a 417 (Expectation Failed) status + or a 100 (Continue) status. Therefore, when a client sends this + header field to an origin server (possibly via a proxy) from which it + has never seen a 100 (Continue) status, the client SHOULD NOT wait + for an indefinite period before sending the request body. + + Requirements for HTTP/1.1 origin servers: + + - Upon receiving a request which includes an Expect request-header + field with the "100-continue" expectation, an origin server MUST + either respond with 100 (Continue) status and continue to read + from the input stream, or respond with a final status code. The + origin server MUST NOT wait for the request body before sending + the 100 (Continue) response. If it responds with a final status + code, it MAY close the transport connection or it MAY continue + + + +Fielding, et al. Standards Track [Page 48] + +RFC 2616 HTTP/1.1 June 1999 + + + to read and discard the rest of the request. It MUST NOT + perform the requested method if it returns a final status code. + + - An origin server SHOULD NOT send a 100 (Continue) response if + the request message does not include an Expect request-header + field with the "100-continue" expectation, and MUST NOT send a + 100 (Continue) response if such a request comes from an HTTP/1.0 + (or earlier) client. There is an exception to this rule: for + compatibility with RFC 2068, a server MAY send a 100 (Continue) + status in response to an HTTP/1.1 PUT or POST request that does + not include an Expect request-header field with the "100- + continue" expectation. This exception, the purpose of which is + to minimize any client processing delays associated with an + undeclared wait for 100 (Continue) status, applies only to + HTTP/1.1 requests, and not to requests with any other HTTP- + version value. + + - An origin server MAY omit a 100 (Continue) response if it has + already received some or all of the request body for the + corresponding request. + + - An origin server that sends a 100 (Continue) response MUST + ultimately send a final status code, once the request body is + received and processed, unless it terminates the transport + connection prematurely. + + - If an origin server receives a request that does not include an + Expect request-header field with the "100-continue" expectation, + the request includes a request body, and the server responds + with a final status code before reading the entire request body + from the transport connection, then the server SHOULD NOT close + the transport connection until it has read the entire request, + or until the client closes the connection. Otherwise, the client + might not reliably receive the response message. However, this + requirement is not be construed as preventing a server from + defending itself against denial-of-service attacks, or from + badly broken client implementations. + + Requirements for HTTP/1.1 proxies: + + - If a proxy receives a request that includes an Expect request- + header field with the "100-continue" expectation, and the proxy + either knows that the next-hop server complies with HTTP/1.1 or + higher, or does not know the HTTP version of the next-hop + server, it MUST forward the request, including the Expect header + field. + + + + + +Fielding, et al. Standards Track [Page 49] + +RFC 2616 HTTP/1.1 June 1999 + + + - If the proxy knows that the version of the next-hop server is + HTTP/1.0 or lower, it MUST NOT forward the request, and it MUST + respond with a 417 (Expectation Failed) status. + + - Proxies SHOULD maintain a cache recording the HTTP version + numbers received from recently-referenced next-hop servers. + + - A proxy MUST NOT forward a 100 (Continue) response if the + request message was received from an HTTP/1.0 (or earlier) + client and did not include an Expect request-header field with + the "100-continue" expectation. This requirement overrides the + general rule for forwarding of 1xx responses (see section 10.1). + +8.2.4 Client Behavior if Server Prematurely Closes Connection + + If an HTTP/1.1 client sends a request which includes a request body, + but which does not include an Expect request-header field with the + "100-continue" expectation, and if the client is not directly + connected to an HTTP/1.1 origin server, and if the client sees the + connection close before receiving any status from the server, the + client SHOULD retry the request. If the client does retry this + request, it MAY use the following "binary exponential backoff" + algorithm to be assured of obtaining a reliable response: + + 1. Initiate a new connection to the server + + 2. Transmit the request-headers + + 3. Initialize a variable R to the estimated round-trip time to the + server (e.g., based on the time it took to establish the + connection), or to a constant value of 5 seconds if the round- + trip time is not available. + + 4. Compute T = R * (2**N), where N is the number of previous + retries of this request. + + 5. Wait either for an error response from the server, or for T + seconds (whichever comes first) + + 6. If no error response is received, after T seconds transmit the + body of the request. + + 7. If client sees that the connection is closed prematurely, + repeat from step 1 until the request is accepted, an error + response is received, or the user becomes impatient and + terminates the retry process. + + + + + +Fielding, et al. Standards Track [Page 50] + +RFC 2616 HTTP/1.1 June 1999 + + + If at any point an error status is received, the client + + - SHOULD NOT continue and + + - SHOULD close the connection if it has not completed sending the + request message. + +9 Method Definitions + + The set of common methods for HTTP/1.1 is defined below. Although + this set can be expanded, additional methods cannot be assumed to + share the same semantics for separately extended clients and servers. + + The Host request-header field (section 14.23) MUST accompany all + HTTP/1.1 requests. + +9.1 Safe and Idempotent Methods + +9.1.1 Safe Methods + + Implementors should be aware that the software represents the user in + their interactions over the Internet, and should be careful to allow + the user to be aware of any actions they might take which may have an + unexpected significance to themselves or others. + + In particular, the convention has been established that the GET and + HEAD methods SHOULD NOT have the significance of taking an action + other than retrieval. These methods ought to be considered "safe". + This allows user agents to represent other methods, such as POST, PUT + and DELETE, in a special way, so that the user is made aware of the + fact that a possibly unsafe action is being requested. + + Naturally, it is not possible to ensure that the server does not + generate side-effects as a result of performing a GET request; in + fact, some dynamic resources consider that a feature. The important + distinction here is that the user did not request the side-effects, + so therefore cannot be held accountable for them. + +9.1.2 Idempotent Methods + + Methods can also have the property of "idempotence" in that (aside + from error or expiration issues) the side-effects of N > 0 identical + requests is the same as for a single request. The methods GET, HEAD, + PUT and DELETE share this property. Also, the methods OPTIONS and + TRACE SHOULD NOT have side effects, and so are inherently idempotent. + + + + + + +Fielding, et al. Standards Track [Page 51] + +RFC 2616 HTTP/1.1 June 1999 + + + However, it is possible that a sequence of several requests is non- + idempotent, even if all of the methods executed in that sequence are + idempotent. (A sequence is idempotent if a single execution of the + entire sequence always yields a result that is not changed by a + reexecution of all, or part, of that sequence.) For example, a + sequence is non-idempotent if its result depends on a value that is + later modified in the same sequence. + + A sequence that never has side effects is idempotent, by definition + (provided that no concurrent operations are being executed on the + same set of resources). + +9.2 OPTIONS + + The OPTIONS method represents a request for information about the + communication options available on the request/response chain + identified by the Request-URI. This method allows the client to + determine the options and/or requirements associated with a resource, + or the capabilities of a server, without implying a resource action + or initiating a resource retrieval. + + Responses to this method are not cacheable. + + If the OPTIONS request includes an entity-body (as indicated by the + presence of Content-Length or Transfer-Encoding), then the media type + MUST be indicated by a Content-Type field. Although this + specification does not define any use for such a body, future + extensions to HTTP might use the OPTIONS body to make more detailed + queries on the server. A server that does not support such an + extension MAY discard the request body. + + If the Request-URI is an asterisk ("*"), the OPTIONS request is + intended to apply to the server in general rather than to a specific + resource. Since a server's communication options typically depend on + the resource, the "*" request is only useful as a "ping" or "no-op" + type of method; it does nothing beyond allowing the client to test + the capabilities of the server. For example, this can be used to test + a proxy for HTTP/1.1 compliance (or lack thereof). + + If the Request-URI is not an asterisk, the OPTIONS request applies + only to the options that are available when communicating with that + resource. + + A 200 response SHOULD include any header fields that indicate + optional features implemented by the server and applicable to that + resource (e.g., Allow), possibly including extensions not defined by + this specification. The response body, if any, SHOULD also include + information about the communication options. The format for such a + + + +Fielding, et al. Standards Track [Page 52] + +RFC 2616 HTTP/1.1 June 1999 + + + body is not defined by this specification, but might be defined by + future extensions to HTTP. Content negotiation MAY be used to select + the appropriate response format. If no response body is included, the + response MUST include a Content-Length field with a field-value of + "0". + + The Max-Forwards request-header field MAY be used to target a + specific proxy in the request chain. When a proxy receives an OPTIONS + request on an absoluteURI for which request forwarding is permitted, + the proxy MUST check for a Max-Forwards field. If the Max-Forwards + field-value is zero ("0"), the proxy MUST NOT forward the message; + instead, the proxy SHOULD respond with its own communication options. + If the Max-Forwards field-value is an integer greater than zero, the + proxy MUST decrement the field-value when it forwards the request. If + no Max-Forwards field is present in the request, then the forwarded + request MUST NOT include a Max-Forwards field. + +9.3 GET + + The GET method means retrieve whatever information (in the form of an + entity) is identified by the Request-URI. If the Request-URI refers + to a data-producing process, it is the produced data which shall be + returned as the entity in the response and not the source text of the + process, unless that text happens to be the output of the process. + + The semantics of the GET method change to a "conditional GET" if the + request message includes an If-Modified-Since, If-Unmodified-Since, + If-Match, If-None-Match, or If-Range header field. A conditional GET + method requests that the entity be transferred only under the + circumstances described by the conditional header field(s). The + conditional GET method is intended to reduce unnecessary network + usage by allowing cached entities to be refreshed without requiring + multiple requests or transferring data already held by the client. + + The semantics of the GET method change to a "partial GET" if the + request message includes a Range header field. A partial GET requests + that only part of the entity be transferred, as described in section + 14.35. The partial GET method is intended to reduce unnecessary + network usage by allowing partially-retrieved entities to be + completed without transferring data already held by the client. + + The response to a GET request is cacheable if and only if it meets + the requirements for HTTP caching described in section 13. + + See section 15.1.3 for security considerations when used for forms. + + + + + + +Fielding, et al. Standards Track [Page 53] + +RFC 2616 HTTP/1.1 June 1999 + + +9.4 HEAD + + The HEAD method is identical to GET except that the server MUST NOT + return a message-body in the response. The metainformation contained + in the HTTP headers in response to a HEAD request SHOULD be identical + to the information sent in response to a GET request. This method can + be used for obtaining metainformation about the entity implied by the + request without transferring the entity-body itself. This method is + often used for testing hypertext links for validity, accessibility, + and recent modification. + + The response to a HEAD request MAY be cacheable in the sense that the + information contained in the response MAY be used to update a + previously cached entity from that resource. If the new field values + indicate that the cached entity differs from the current entity (as + would be indicated by a change in Content-Length, Content-MD5, ETag + or Last-Modified), then the cache MUST treat the cache entry as + stale. + +9.5 POST + + The POST method is used to request that the origin server accept the + entity enclosed in the request as a new subordinate of the resource + identified by the Request-URI in the Request-Line. POST is designed + to allow a uniform method to cover the following functions: + + - Annotation of existing resources; + + - Posting a message to a bulletin board, newsgroup, mailing list, + or similar group of articles; + + - Providing a block of data, such as the result of submitting a + form, to a data-handling process; + + - Extending a database through an append operation. + + The actual function performed by the POST method is determined by the + server and is usually dependent on the Request-URI. The posted entity + is subordinate to that URI in the same way that a file is subordinate + to a directory containing it, a news article is subordinate to a + newsgroup to which it is posted, or a record is subordinate to a + database. + + The action performed by the POST method might not result in a + resource that can be identified by a URI. In this case, either 200 + (OK) or 204 (No Content) is the appropriate response status, + depending on whether or not the response includes an entity that + describes the result. + + + +Fielding, et al. Standards Track [Page 54] + +RFC 2616 HTTP/1.1 June 1999 + + + If a resource has been created on the origin server, the response + SHOULD be 201 (Created) and contain an entity which describes the + status of the request and refers to the new resource, and a Location + header (see section 14.30). + + Responses to this method are not cacheable, unless the response + includes appropriate Cache-Control or Expires header fields. However, + the 303 (See Other) response can be used to direct the user agent to + retrieve a cacheable resource. + + POST requests MUST obey the message transmission requirements set out + in section 8.2. + + See section 15.1.3 for security considerations. + +9.6 PUT + + The PUT method requests that the enclosed entity be stored under the + supplied Request-URI. If the Request-URI refers to an already + existing resource, the enclosed entity SHOULD be considered as a + modified version of the one residing on the origin server. If the + Request-URI does not point to an existing resource, and that URI is + capable of being defined as a new resource by the requesting user + agent, the origin server can create the resource with that URI. If a + new resource is created, the origin server MUST inform the user agent + via the 201 (Created) response. If an existing resource is modified, + either the 200 (OK) or 204 (No Content) response codes SHOULD be sent + to indicate successful completion of the request. If the resource + could not be created or modified with the Request-URI, an appropriate + error response SHOULD be given that reflects the nature of the + problem. The recipient of the entity MUST NOT ignore any Content-* + (e.g. Content-Range) headers that it does not understand or implement + and MUST return a 501 (Not Implemented) response in such cases. + + If the request passes through a cache and the Request-URI identifies + one or more currently cached entities, those entries SHOULD be + treated as stale. Responses to this method are not cacheable. + + The fundamental difference between the POST and PUT requests is + reflected in the different meaning of the Request-URI. The URI in a + POST request identifies the resource that will handle the enclosed + entity. That resource might be a data-accepting process, a gateway to + some other protocol, or a separate entity that accepts annotations. + In contrast, the URI in a PUT request identifies the entity enclosed + with the request -- the user agent knows what URI is intended and the + server MUST NOT attempt to apply the request to some other resource. + If the server desires that the request be applied to a different URI, + + + + +Fielding, et al. Standards Track [Page 55] + +RFC 2616 HTTP/1.1 June 1999 + + + it MUST send a 301 (Moved Permanently) response; the user agent MAY + then make its own decision regarding whether or not to redirect the + request. + + A single resource MAY be identified by many different URIs. For + example, an article might have a URI for identifying "the current + version" which is separate from the URI identifying each particular + version. In this case, a PUT request on a general URI might result in + several other URIs being defined by the origin server. + + HTTP/1.1 does not define how a PUT method affects the state of an + origin server. + + PUT requests MUST obey the message transmission requirements set out + in section 8.2. + + Unless otherwise specified for a particular entity-header, the + entity-headers in the PUT request SHOULD be applied to the resource + created or modified by the PUT. + +9.7 DELETE + + The DELETE method requests that the origin server delete the resource + identified by the Request-URI. This method MAY be overridden by human + intervention (or other means) on the origin server. The client cannot + be guaranteed that the operation has been carried out, even if the + status code returned from the origin server indicates that the action + has been completed successfully. However, the server SHOULD NOT + indicate success unless, at the time the response is given, it + intends to delete the resource or move it to an inaccessible + location. + + A successful response SHOULD be 200 (OK) if the response includes an + entity describing the status, 202 (Accepted) if the action has not + yet been enacted, or 204 (No Content) if the action has been enacted + but the response does not include an entity. + + If the request passes through a cache and the Request-URI identifies + one or more currently cached entities, those entries SHOULD be + treated as stale. Responses to this method are not cacheable. + +9.8 TRACE + + The TRACE method is used to invoke a remote, application-layer loop- + back of the request message. The final recipient of the request + SHOULD reflect the message received back to the client as the + entity-body of a 200 (OK) response. The final recipient is either the + + + + +Fielding, et al. Standards Track [Page 56] + +RFC 2616 HTTP/1.1 June 1999 + + + origin server or the first proxy or gateway to receive a Max-Forwards + value of zero (0) in the request (see section 14.31). A TRACE request + MUST NOT include an entity. + + TRACE allows the client to see what is being received at the other + end of the request chain and use that data for testing or diagnostic + information. The value of the Via header field (section 14.45) is of + particular interest, since it acts as a trace of the request chain. + Use of the Max-Forwards header field allows the client to limit the + length of the request chain, which is useful for testing a chain of + proxies forwarding messages in an infinite loop. + + If the request is valid, the response SHOULD contain the entire + request message in the entity-body, with a Content-Type of + "message/http". Responses to this method MUST NOT be cached. + +9.9 CONNECT + + This specification reserves the method name CONNECT for use with a + proxy that can dynamically switch to being a tunnel (e.g. SSL + tunneling [44]). + +10 Status Code Definitions + + Each Status-Code is described below, including a description of which + method(s) it can follow and any metainformation required in the + response. + +10.1 Informational 1xx + + This class of status code indicates a provisional response, + consisting only of the Status-Line and optional headers, and is + terminated by an empty line. There are no required headers for this + class of status code. Since HTTP/1.0 did not define any 1xx status + codes, servers MUST NOT send a 1xx response to an HTTP/1.0 client + except under experimental conditions. + + A client MUST be prepared to accept one or more 1xx status responses + prior to a regular response, even if the client does not expect a 100 + (Continue) status message. Unexpected 1xx status responses MAY be + ignored by a user agent. + + Proxies MUST forward 1xx responses, unless the connection between the + proxy and its client has been closed, or unless the proxy itself + requested the generation of the 1xx response. (For example, if a + + + + + + +Fielding, et al. Standards Track [Page 57] + +RFC 2616 HTTP/1.1 June 1999 + + + proxy adds a "Expect: 100-continue" field when it forwards a request, + then it need not forward the corresponding 100 (Continue) + response(s).) + +10.1.1 100 Continue + + The client SHOULD continue with its request. This interim response is + used to inform the client that the initial part of the request has + been received and has not yet been rejected by the server. The client + SHOULD continue by sending the remainder of the request or, if the + request has already been completed, ignore this response. The server + MUST send a final response after the request has been completed. See + section 8.2.3 for detailed discussion of the use and handling of this + status code. + +10.1.2 101 Switching Protocols + + The server understands and is willing to comply with the client's + request, via the Upgrade message header field (section 14.42), for a + change in the application protocol being used on this connection. The + server will switch protocols to those defined by the response's + Upgrade header field immediately after the empty line which + terminates the 101 response. + + The protocol SHOULD be switched only when it is advantageous to do + so. For example, switching to a newer version of HTTP is advantageous + over older versions, and switching to a real-time, synchronous + protocol might be advantageous when delivering resources that use + such features. + +10.2 Successful 2xx + + This class of status code indicates that the client's request was + successfully received, understood, and accepted. + +10.2.1 200 OK + + The request has succeeded. The information returned with the response + is dependent on the method used in the request, for example: + + GET an entity corresponding to the requested resource is sent in + the response; + + HEAD the entity-header fields corresponding to the requested + resource are sent in the response without any message-body; + + POST an entity describing or containing the result of the action; + + + + +Fielding, et al. Standards Track [Page 58] + +RFC 2616 HTTP/1.1 June 1999 + + + TRACE an entity containing the request message as received by the + end server. + +10.2.2 201 Created + + The request has been fulfilled and resulted in a new resource being + created. The newly created resource can be referenced by the URI(s) + returned in the entity of the response, with the most specific URI + for the resource given by a Location header field. The response + SHOULD include an entity containing a list of resource + characteristics and location(s) from which the user or user agent can + choose the one most appropriate. The entity format is specified by + the media type given in the Content-Type header field. The origin + server MUST create the resource before returning the 201 status code. + If the action cannot be carried out immediately, the server SHOULD + respond with 202 (Accepted) response instead. + + A 201 response MAY contain an ETag response header field indicating + the current value of the entity tag for the requested variant just + created, see section 14.19. + +10.2.3 202 Accepted + + The request has been accepted for processing, but the processing has + not been completed. The request might or might not eventually be + acted upon, as it might be disallowed when processing actually takes + place. There is no facility for re-sending a status code from an + asynchronous operation such as this. + + The 202 response is intentionally non-committal. Its purpose is to + allow a server to accept a request for some other process (perhaps a + batch-oriented process that is only run once per day) without + requiring that the user agent's connection to the server persist + until the process is completed. The entity returned with this + response SHOULD include an indication of the request's current status + and either a pointer to a status monitor or some estimate of when the + user can expect the request to be fulfilled. + +10.2.4 203 Non-Authoritative Information + + The returned metainformation in the entity-header is not the + definitive set as available from the origin server, but is gathered + from a local or a third-party copy. The set presented MAY be a subset + or superset of the original version. For example, including local + annotation information about the resource might result in a superset + of the metainformation known by the origin server. Use of this + response code is not required and is only appropriate when the + response would otherwise be 200 (OK). + + + +Fielding, et al. Standards Track [Page 59] + +RFC 2616 HTTP/1.1 June 1999 + + +10.2.5 204 No Content + + The server has fulfilled the request but does not need to return an + entity-body, and might want to return updated metainformation. The + response MAY include new or updated metainformation in the form of + entity-headers, which if present SHOULD be associated with the + requested variant. + + If the client is a user agent, it SHOULD NOT change its document view + from that which caused the request to be sent. This response is + primarily intended to allow input for actions to take place without + causing a change to the user agent's active document view, although + any new or updated metainformation SHOULD be applied to the document + currently in the user agent's active view. + + The 204 response MUST NOT include a message-body, and thus is always + terminated by the first empty line after the header fields. + +10.2.6 205 Reset Content + + The server has fulfilled the request and the user agent SHOULD reset + the document view which caused the request to be sent. This response + is primarily intended to allow input for actions to take place via + user input, followed by a clearing of the form in which the input is + given so that the user can easily initiate another input action. The + response MUST NOT include an entity. + +10.2.7 206 Partial Content + + The server has fulfilled the partial GET request for the resource. + The request MUST have included a Range header field (section 14.35) + indicating the desired range, and MAY have included an If-Range + header field (section 14.27) to make the request conditional. + + The response MUST include the following header fields: + + - Either a Content-Range header field (section 14.16) indicating + the range included with this response, or a multipart/byteranges + Content-Type including Content-Range fields for each part. If a + Content-Length header field is present in the response, its + value MUST match the actual number of OCTETs transmitted in the + message-body. + + - Date + + - ETag and/or Content-Location, if the header would have been sent + in a 200 response to the same request + + + + +Fielding, et al. Standards Track [Page 60] + +RFC 2616 HTTP/1.1 June 1999 + + + - Expires, Cache-Control, and/or Vary, if the field-value might + differ from that sent in any previous response for the same + variant + + If the 206 response is the result of an If-Range request that used a + strong cache validator (see section 13.3.3), the response SHOULD NOT + include other entity-headers. If the response is the result of an + If-Range request that used a weak validator, the response MUST NOT + include other entity-headers; this prevents inconsistencies between + cached entity-bodies and updated headers. Otherwise, the response + MUST include all of the entity-headers that would have been returned + with a 200 (OK) response to the same request. + + A cache MUST NOT combine a 206 response with other previously cached + content if the ETag or Last-Modified headers do not match exactly, + see 13.5.4. + + A cache that does not support the Range and Content-Range headers + MUST NOT cache 206 (Partial) responses. + +10.3 Redirection 3xx + + This class of status code indicates that further action needs to be + taken by the user agent in order to fulfill the request. The action + required MAY be carried out by the user agent without interaction + with the user if and only if the method used in the second request is + GET or HEAD. A client SHOULD detect infinite redirection loops, since + such loops generate network traffic for each redirection. + + Note: previous versions of this specification recommended a + maximum of five redirections. Content developers should be aware + that there might be clients that implement such a fixed + limitation. + +10.3.1 300 Multiple Choices + + The requested resource corresponds to any one of a set of + representations, each with its own specific location, and agent- + driven negotiation information (section 12) is being provided so that + the user (or user agent) can select a preferred representation and + redirect its request to that location. + + Unless it was a HEAD request, the response SHOULD include an entity + containing a list of resource characteristics and location(s) from + which the user or user agent can choose the one most appropriate. The + entity format is specified by the media type given in the Content- + Type header field. Depending upon the format and the capabilities of + + + + +Fielding, et al. Standards Track [Page 61] + +RFC 2616 HTTP/1.1 June 1999 + + + the user agent, selection of the most appropriate choice MAY be + performed automatically. However, this specification does not define + any standard for such automatic selection. + + If the server has a preferred choice of representation, it SHOULD + include the specific URI for that representation in the Location + field; user agents MAY use the Location field value for automatic + redirection. This response is cacheable unless indicated otherwise. + +10.3.2 301 Moved Permanently + + The requested resource has been assigned a new permanent URI and any + future references to this resource SHOULD use one of the returned + URIs. Clients with link editing capabilities ought to automatically + re-link references to the Request-URI to one or more of the new + references returned by the server, where possible. This response is + cacheable unless indicated otherwise. + + The new permanent URI SHOULD be given by the Location field in the + response. Unless the request method was HEAD, the entity of the + response SHOULD contain a short hypertext note with a hyperlink to + the new URI(s). + + If the 301 status code is received in response to a request other + than GET or HEAD, the user agent MUST NOT automatically redirect the + request unless it can be confirmed by the user, since this might + change the conditions under which the request was issued. + + Note: When automatically redirecting a POST request after + receiving a 301 status code, some existing HTTP/1.0 user agents + will erroneously change it into a GET request. + +10.3.3 302 Found + + The requested resource resides temporarily under a different URI. + Since the redirection might be altered on occasion, the client SHOULD + continue to use the Request-URI for future requests. This response + is only cacheable if indicated by a Cache-Control or Expires header + field. + + The temporary URI SHOULD be given by the Location field in the + response. Unless the request method was HEAD, the entity of the + response SHOULD contain a short hypertext note with a hyperlink to + the new URI(s). + + + + + + + +Fielding, et al. Standards Track [Page 62] + +RFC 2616 HTTP/1.1 June 1999 + + + If the 302 status code is received in response to a request other + than GET or HEAD, the user agent MUST NOT automatically redirect the + request unless it can be confirmed by the user, since this might + change the conditions under which the request was issued. + + Note: RFC 1945 and RFC 2068 specify that the client is not allowed + to change the method on the redirected request. However, most + existing user agent implementations treat 302 as if it were a 303 + response, performing a GET on the Location field-value regardless + of the original request method. The status codes 303 and 307 have + been added for servers that wish to make unambiguously clear which + kind of reaction is expected of the client. + +10.3.4 303 See Other + + The response to the request can be found under a different URI and + SHOULD be retrieved using a GET method on that resource. This method + exists primarily to allow the output of a POST-activated script to + redirect the user agent to a selected resource. The new URI is not a + substitute reference for the originally requested resource. The 303 + response MUST NOT be cached, but the response to the second + (redirected) request might be cacheable. + + The different URI SHOULD be given by the Location field in the + response. Unless the request method was HEAD, the entity of the + response SHOULD contain a short hypertext note with a hyperlink to + the new URI(s). + + Note: Many pre-HTTP/1.1 user agents do not understand the 303 + status. When interoperability with such clients is a concern, the + 302 status code may be used instead, since most user agents react + to a 302 response as described here for 303. + +10.3.5 304 Not Modified + + If the client has performed a conditional GET request and access is + allowed, but the document has not been modified, the server SHOULD + respond with this status code. The 304 response MUST NOT contain a + message-body, and thus is always terminated by the first empty line + after the header fields. + + The response MUST include the following header fields: + + - Date, unless its omission is required by section 14.18.1 + + + + + + + +Fielding, et al. Standards Track [Page 63] + +RFC 2616 HTTP/1.1 June 1999 + + + If a clockless origin server obeys these rules, and proxies and + clients add their own Date to any response received without one (as + already specified by [RFC 2068], section 14.19), caches will operate + correctly. + + - ETag and/or Content-Location, if the header would have been sent + in a 200 response to the same request + + - Expires, Cache-Control, and/or Vary, if the field-value might + differ from that sent in any previous response for the same + variant + + If the conditional GET used a strong cache validator (see section + 13.3.3), the response SHOULD NOT include other entity-headers. + Otherwise (i.e., the conditional GET used a weak validator), the + response MUST NOT include other entity-headers; this prevents + inconsistencies between cached entity-bodies and updated headers. + + If a 304 response indicates an entity not currently cached, then the + cache MUST disregard the response and repeat the request without the + conditional. + + If a cache uses a received 304 response to update a cache entry, the + cache MUST update the entry to reflect any new field values given in + the response. + +10.3.6 305 Use Proxy + + The requested resource MUST be accessed through the proxy given by + the Location field. The Location field gives the URI of the proxy. + The recipient is expected to repeat this single request via the + proxy. 305 responses MUST only be generated by origin servers. + + Note: RFC 2068 was not clear that 305 was intended to redirect a + single request, and to be generated by origin servers only. Not + observing these limitations has significant security consequences. + +10.3.7 306 (Unused) + + The 306 status code was used in a previous version of the + specification, is no longer used, and the code is reserved. + + + + + + + + + + +Fielding, et al. Standards Track [Page 64] + +RFC 2616 HTTP/1.1 June 1999 + + +10.3.8 307 Temporary Redirect + + The requested resource resides temporarily under a different URI. + Since the redirection MAY be altered on occasion, the client SHOULD + continue to use the Request-URI for future requests. This response + is only cacheable if indicated by a Cache-Control or Expires header + field. + + The temporary URI SHOULD be given by the Location field in the + response. Unless the request method was HEAD, the entity of the + response SHOULD contain a short hypertext note with a hyperlink to + the new URI(s) , since many pre-HTTP/1.1 user agents do not + understand the 307 status. Therefore, the note SHOULD contain the + information necessary for a user to repeat the original request on + the new URI. + + If the 307 status code is received in response to a request other + than GET or HEAD, the user agent MUST NOT automatically redirect the + request unless it can be confirmed by the user, since this might + change the conditions under which the request was issued. + +10.4 Client Error 4xx + + The 4xx class of status code is intended for cases in which the + client seems to have erred. Except when responding to a HEAD request, + the server SHOULD include an entity containing an explanation of the + error situation, and whether it is a temporary or permanent + condition. These status codes are applicable to any request method. + User agents SHOULD display any included entity to the user. + + If the client is sending data, a server implementation using TCP + SHOULD be careful to ensure that the client acknowledges receipt of + the packet(s) containing the response, before the server closes the + input connection. If the client continues sending data to the server + after the close, the server's TCP stack will send a reset packet to + the client, which may erase the client's unacknowledged input buffers + before they can be read and interpreted by the HTTP application. + +10.4.1 400 Bad Request + + The request could not be understood by the server due to malformed + syntax. The client SHOULD NOT repeat the request without + modifications. + + + + + + + + +Fielding, et al. Standards Track [Page 65] + +RFC 2616 HTTP/1.1 June 1999 + + +10.4.2 401 Unauthorized + + The request requires user authentication. The response MUST include a + WWW-Authenticate header field (section 14.47) containing a challenge + applicable to the requested resource. The client MAY repeat the + request with a suitable Authorization header field (section 14.8). If + the request already included Authorization credentials, then the 401 + response indicates that authorization has been refused for those + credentials. If the 401 response contains the same challenge as the + prior response, and the user agent has already attempted + authentication at least once, then the user SHOULD be presented the + entity that was given in the response, since that entity might + include relevant diagnostic information. HTTP access authentication + is explained in "HTTP Authentication: Basic and Digest Access + Authentication" [43]. + +10.4.3 402 Payment Required + + This code is reserved for future use. + +10.4.4 403 Forbidden + + The server understood the request, but is refusing to fulfill it. + Authorization will not help and the request SHOULD NOT be repeated. + If the request method was not HEAD and the server wishes to make + public why the request has not been fulfilled, it SHOULD describe the + reason for the refusal in the entity. If the server does not wish to + make this information available to the client, the status code 404 + (Not Found) can be used instead. + +10.4.5 404 Not Found + + The server has not found anything matching the Request-URI. No + indication is given of whether the condition is temporary or + permanent. The 410 (Gone) status code SHOULD be used if the server + knows, through some internally configurable mechanism, that an old + resource is permanently unavailable and has no forwarding address. + This status code is commonly used when the server does not wish to + reveal exactly why the request has been refused, or when no other + response is applicable. + +10.4.6 405 Method Not Allowed + + The method specified in the Request-Line is not allowed for the + resource identified by the Request-URI. The response MUST include an + Allow header containing a list of valid methods for the requested + resource. + + + + +Fielding, et al. Standards Track [Page 66] + +RFC 2616 HTTP/1.1 June 1999 + + +10.4.7 406 Not Acceptable + + The resource identified by the request is only capable of generating + response entities which have content characteristics not acceptable + according to the accept headers sent in the request. + + Unless it was a HEAD request, the response SHOULD include an entity + containing a list of available entity characteristics and location(s) + from which the user or user agent can choose the one most + appropriate. The entity format is specified by the media type given + in the Content-Type header field. Depending upon the format and the + capabilities of the user agent, selection of the most appropriate + choice MAY be performed automatically. However, this specification + does not define any standard for such automatic selection. + + Note: HTTP/1.1 servers are allowed to return responses which are + not acceptable according to the accept headers sent in the + request. In some cases, this may even be preferable to sending a + 406 response. User agents are encouraged to inspect the headers of + an incoming response to determine if it is acceptable. + + If the response could be unacceptable, a user agent SHOULD + temporarily stop receipt of more data and query the user for a + decision on further actions. + +10.4.8 407 Proxy Authentication Required + + This code is similar to 401 (Unauthorized), but indicates that the + client must first authenticate itself with the proxy. The proxy MUST + return a Proxy-Authenticate header field (section 14.33) containing a + challenge applicable to the proxy for the requested resource. The + client MAY repeat the request with a suitable Proxy-Authorization + header field (section 14.34). HTTP access authentication is explained + in "HTTP Authentication: Basic and Digest Access Authentication" + [43]. + +10.4.9 408 Request Timeout + + The client did not produce a request within the time that the server + was prepared to wait. The client MAY repeat the request without + modifications at any later time. + +10.4.10 409 Conflict + + The request could not be completed due to a conflict with the current + state of the resource. This code is only allowed in situations where + it is expected that the user might be able to resolve the conflict + and resubmit the request. The response body SHOULD include enough + + + +Fielding, et al. Standards Track [Page 67] + +RFC 2616 HTTP/1.1 June 1999 + + + information for the user to recognize the source of the conflict. + Ideally, the response entity would include enough information for the + user or user agent to fix the problem; however, that might not be + possible and is not required. + + Conflicts are most likely to occur in response to a PUT request. For + example, if versioning were being used and the entity being PUT + included changes to a resource which conflict with those made by an + earlier (third-party) request, the server might use the 409 response + to indicate that it can't complete the request. In this case, the + response entity would likely contain a list of the differences + between the two versions in a format defined by the response + Content-Type. + +10.4.11 410 Gone + + The requested resource is no longer available at the server and no + forwarding address is known. This condition is expected to be + considered permanent. Clients with link editing capabilities SHOULD + delete references to the Request-URI after user approval. If the + server does not know, or has no facility to determine, whether or not + the condition is permanent, the status code 404 (Not Found) SHOULD be + used instead. This response is cacheable unless indicated otherwise. + + The 410 response is primarily intended to assist the task of web + maintenance by notifying the recipient that the resource is + intentionally unavailable and that the server owners desire that + remote links to that resource be removed. Such an event is common for + limited-time, promotional services and for resources belonging to + individuals no longer working at the server's site. It is not + necessary to mark all permanently unavailable resources as "gone" or + to keep the mark for any length of time -- that is left to the + discretion of the server owner. + +10.4.12 411 Length Required + + The server refuses to accept the request without a defined Content- + Length. The client MAY repeat the request if it adds a valid + Content-Length header field containing the length of the message-body + in the request message. + +10.4.13 412 Precondition Failed + + The precondition given in one or more of the request-header fields + evaluated to false when it was tested on the server. This response + code allows the client to place preconditions on the current resource + metainformation (header field data) and thus prevent the requested + method from being applied to a resource other than the one intended. + + + +Fielding, et al. Standards Track [Page 68] + +RFC 2616 HTTP/1.1 June 1999 + + +10.4.14 413 Request Entity Too Large + + The server is refusing to process a request because the request + entity is larger than the server is willing or able to process. The + server MAY close the connection to prevent the client from continuing + the request. + + If the condition is temporary, the server SHOULD include a Retry- + After header field to indicate that it is temporary and after what + time the client MAY try again. + +10.4.15 414 Request-URI Too Long + + The server is refusing to service the request because the Request-URI + is longer than the server is willing to interpret. This rare + condition is only likely to occur when a client has improperly + converted a POST request to a GET request with long query + information, when the client has descended into a URI "black hole" of + redirection (e.g., a redirected URI prefix that points to a suffix of + itself), or when the server is under attack by a client attempting to + exploit security holes present in some servers using fixed-length + buffers for reading or manipulating the Request-URI. + +10.4.16 415 Unsupported Media Type + + The server is refusing to service the request because the entity of + the request is in a format not supported by the requested resource + for the requested method. + +10.4.17 416 Requested Range Not Satisfiable + + A server SHOULD return a response with this status code if a request + included a Range request-header field (section 14.35), and none of + the range-specifier values in this field overlap the current extent + of the selected resource, and the request did not include an If-Range + request-header field. (For byte-ranges, this means that the first- + byte-pos of all of the byte-range-spec values were greater than the + current length of the selected resource.) + + When this status code is returned for a byte-range request, the + response SHOULD include a Content-Range entity-header field + specifying the current length of the selected resource (see section + 14.16). This response MUST NOT use the multipart/byteranges content- + type. + + + + + + + +Fielding, et al. Standards Track [Page 69] + +RFC 2616 HTTP/1.1 June 1999 + + +10.4.18 417 Expectation Failed + + The expectation given in an Expect request-header field (see section + 14.20) could not be met by this server, or, if the server is a proxy, + the server has unambiguous evidence that the request could not be met + by the next-hop server. + +10.5 Server Error 5xx + + Response status codes beginning with the digit "5" indicate cases in + which the server is aware that it has erred or is incapable of + performing the request. Except when responding to a HEAD request, the + server SHOULD include an entity containing an explanation of the + error situation, and whether it is a temporary or permanent + condition. User agents SHOULD display any included entity to the + user. These response codes are applicable to any request method. + +10.5.1 500 Internal Server Error + + The server encountered an unexpected condition which prevented it + from fulfilling the request. + +10.5.2 501 Not Implemented + + The server does not support the functionality required to fulfill the + request. This is the appropriate response when the server does not + recognize the request method and is not capable of supporting it for + any resource. + +10.5.3 502 Bad Gateway + + The server, while acting as a gateway or proxy, received an invalid + response from the upstream server it accessed in attempting to + fulfill the request. + +10.5.4 503 Service Unavailable + + The server is currently unable to handle the request due to a + temporary overloading or maintenance of the server. The implication + is that this is a temporary condition which will be alleviated after + some delay. If known, the length of the delay MAY be indicated in a + Retry-After header. If no Retry-After is given, the client SHOULD + handle the response as it would for a 500 response. + + Note: The existence of the 503 status code does not imply that a + server must use it when becoming overloaded. Some servers may wish + to simply refuse the connection. + + + + +Fielding, et al. Standards Track [Page 70] + +RFC 2616 HTTP/1.1 June 1999 + + +10.5.5 504 Gateway Timeout + + The server, while acting as a gateway or proxy, did not receive a + timely response from the upstream server specified by the URI (e.g. + HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed + to access in attempting to complete the request. + + Note: Note to implementors: some deployed proxies are known to + return 400 or 500 when DNS lookups time out. + +10.5.6 505 HTTP Version Not Supported + + The server does not support, or refuses to support, the HTTP protocol + version that was used in the request message. The server is + indicating that it is unable or unwilling to complete the request + using the same major version as the client, as described in section + 3.1, other than with this error message. The response SHOULD contain + an entity describing why that version is not supported and what other + protocols are supported by that server. + +11 Access Authentication + + HTTP provides several OPTIONAL challenge-response authentication + mechanisms which can be used by a server to challenge a client + request and by a client to provide authentication information. The + general framework for access authentication, and the specification of + "basic" and "digest" authentication, are specified in "HTTP + Authentication: Basic and Digest Access Authentication" [43]. This + specification adopts the definitions of "challenge" and "credentials" + from that specification. + +12 Content Negotiation + + Most HTTP responses include an entity which contains information for + interpretation by a human user. Naturally, it is desirable to supply + the user with the "best available" entity corresponding to the + request. Unfortunately for servers and caches, not all users have the + same preferences for what is "best," and not all user agents are + equally capable of rendering all entity types. For that reason, HTTP + has provisions for several mechanisms for "content negotiation" -- + the process of selecting the best representation for a given response + when there are multiple representations available. + + Note: This is not called "format negotiation" because the + alternate representations may be of the same media type, but use + different capabilities of that type, be in different languages, + etc. + + + + +Fielding, et al. Standards Track [Page 71] + +RFC 2616 HTTP/1.1 June 1999 + + + Any response containing an entity-body MAY be subject to negotiation, + including error responses. + + There are two kinds of content negotiation which are possible in + HTTP: server-driven and agent-driven negotiation. These two kinds of + negotiation are orthogonal and thus may be used separately or in + combination. One method of combination, referred to as transparent + negotiation, occurs when a cache uses the agent-driven negotiation + information provided by the origin server in order to provide + server-driven negotiation for subsequent requests. + +12.1 Server-driven Negotiation + + If the selection of the best representation for a response is made by + an algorithm located at the server, it is called server-driven + negotiation. Selection is based on the available representations of + the response (the dimensions over which it can vary; e.g. language, + content-coding, etc.) and the contents of particular header fields in + the request message or on other information pertaining to the request + (such as the network address of the client). + + Server-driven negotiation is advantageous when the algorithm for + selecting from among the available representations is difficult to + describe to the user agent, or when the server desires to send its + "best guess" to the client along with the first response (hoping to + avoid the round-trip delay of a subsequent request if the "best + guess" is good enough for the user). In order to improve the server's + guess, the user agent MAY include request header fields (Accept, + Accept-Language, Accept-Encoding, etc.) which describe its + preferences for such a response. + + Server-driven negotiation has disadvantages: + + 1. It is impossible for the server to accurately determine what + might be "best" for any given user, since that would require + complete knowledge of both the capabilities of the user agent + and the intended use for the response (e.g., does the user want + to view it on screen or print it on paper?). + + 2. Having the user agent describe its capabilities in every + request can be both very inefficient (given that only a small + percentage of responses have multiple representations) and a + potential violation of the user's privacy. + + 3. It complicates the implementation of an origin server and the + algorithms for generating responses to a request. + + + + + +Fielding, et al. Standards Track [Page 72] + +RFC 2616 HTTP/1.1 June 1999 + + + 4. It may limit a public cache's ability to use the same response + for multiple user's requests. + + HTTP/1.1 includes the following request-header fields for enabling + server-driven negotiation through description of user agent + capabilities and user preferences: Accept (section 14.1), Accept- + Charset (section 14.2), Accept-Encoding (section 14.3), Accept- + Language (section 14.4), and User-Agent (section 14.43). However, an + origin server is not limited to these dimensions and MAY vary the + response based on any aspect of the request, including information + outside the request-header fields or within extension header fields + not defined by this specification. + + The Vary header field can be used to express the parameters the + server uses to select a representation that is subject to server- + driven negotiation. See section 13.6 for use of the Vary header field + by caches and section 14.44 for use of the Vary header field by + servers. + +12.2 Agent-driven Negotiation + + With agent-driven negotiation, selection of the best representation + for a response is performed by the user agent after receiving an + initial response from the origin server. Selection is based on a list + of the available representations of the response included within the + header fields or entity-body of the initial response, with each + representation identified by its own URI. Selection from among the + representations may be performed automatically (if the user agent is + capable of doing so) or manually by the user selecting from a + generated (possibly hypertext) menu. + + Agent-driven negotiation is advantageous when the response would vary + over commonly-used dimensions (such as type, language, or encoding), + when the origin server is unable to determine a user agent's + capabilities from examining the request, and generally when public + caches are used to distribute server load and reduce network usage. + + Agent-driven negotiation suffers from the disadvantage of needing a + second request to obtain the best alternate representation. This + second request is only efficient when caching is used. In addition, + this specification does not define any mechanism for supporting + automatic selection, though it also does not prevent any such + mechanism from being developed as an extension and used within + HTTP/1.1. + + + + + + + +Fielding, et al. Standards Track [Page 73] + +RFC 2616 HTTP/1.1 June 1999 + + + HTTP/1.1 defines the 300 (Multiple Choices) and 406 (Not Acceptable) + status codes for enabling agent-driven negotiation when the server is + unwilling or unable to provide a varying response using server-driven + negotiation. + +12.3 Transparent Negotiation + + Transparent negotiation is a combination of both server-driven and + agent-driven negotiation. When a cache is supplied with a form of the + list of available representations of the response (as in agent-driven + negotiation) and the dimensions of variance are completely understood + by the cache, then the cache becomes capable of performing server- + driven negotiation on behalf of the origin server for subsequent + requests on that resource. + + Transparent negotiation has the advantage of distributing the + negotiation work that would otherwise be required of the origin + server and also removing the second request delay of agent-driven + negotiation when the cache is able to correctly guess the right + response. + + This specification does not define any mechanism for transparent + negotiation, though it also does not prevent any such mechanism from + being developed as an extension that could be used within HTTP/1.1. + +13 Caching in HTTP + + HTTP is typically used for distributed information systems, where + performance can be improved by the use of response caches. The + HTTP/1.1 protocol includes a number of elements intended to make + caching work as well as possible. Because these elements are + inextricable from other aspects of the protocol, and because they + interact with each other, it is useful to describe the basic caching + design of HTTP separately from the detailed descriptions of methods, + headers, response codes, etc. + + Caching would be useless if it did not significantly improve + performance. The goal of caching in HTTP/1.1 is to eliminate the need + to send requests in many cases, and to eliminate the need to send + full responses in many other cases. The former reduces the number of + network round-trips required for many operations; we use an + "expiration" mechanism for this purpose (see section 13.2). The + latter reduces network bandwidth requirements; we use a "validation" + mechanism for this purpose (see section 13.3). + + Requirements for performance, availability, and disconnected + operation require us to be able to relax the goal of semantic + transparency. The HTTP/1.1 protocol allows origin servers, caches, + + + +Fielding, et al. Standards Track [Page 74] + +RFC 2616 HTTP/1.1 June 1999 + + + and clients to explicitly reduce transparency when necessary. + However, because non-transparent operation may confuse non-expert + users, and might be incompatible with certain server applications + (such as those for ordering merchandise), the protocol requires that + transparency be relaxed + + - only by an explicit protocol-level request when relaxed by + client or origin server + + - only with an explicit warning to the end user when relaxed by + cache or client + + Therefore, the HTTP/1.1 protocol provides these important elements: + + 1. Protocol features that provide full semantic transparency when + this is required by all parties. + + 2. Protocol features that allow an origin server or user agent to + explicitly request and control non-transparent operation. + + 3. Protocol features that allow a cache to attach warnings to + responses that do not preserve the requested approximation of + semantic transparency. + + A basic principle is that it must be possible for the clients to + detect any potential relaxation of semantic transparency. + + Note: The server, cache, or client implementor might be faced with + design decisions not explicitly discussed in this specification. + If a decision might affect semantic transparency, the implementor + ought to err on the side of maintaining transparency unless a + careful and complete analysis shows significant benefits in + breaking transparency. + +13.1.1 Cache Correctness + + A correct cache MUST respond to a request with the most up-to-date + response held by the cache that is appropriate to the request (see + sections 13.2.5, 13.2.6, and 13.12) which meets one of the following + conditions: + + 1. It has been checked for equivalence with what the origin server + would have returned by revalidating the response with the + origin server (section 13.3); + + + + + + + +Fielding, et al. Standards Track [Page 75] + +RFC 2616 HTTP/1.1 June 1999 + + + 2. It is "fresh enough" (see section 13.2). In the default case, + this means it meets the least restrictive freshness requirement + of the client, origin server, and cache (see section 14.9); if + the origin server so specifies, it is the freshness requirement + of the origin server alone. + + If a stored response is not "fresh enough" by the most + restrictive freshness requirement of both the client and the + origin server, in carefully considered circumstances the cache + MAY still return the response with the appropriate Warning + header (see section 13.1.5 and 14.46), unless such a response + is prohibited (e.g., by a "no-store" cache-directive, or by a + "no-cache" cache-request-directive; see section 14.9). + + 3. It is an appropriate 304 (Not Modified), 305 (Proxy Redirect), + or error (4xx or 5xx) response message. + + If the cache can not communicate with the origin server, then a + correct cache SHOULD respond as above if the response can be + correctly served from the cache; if not it MUST return an error or + warning indicating that there was a communication failure. + + If a cache receives a response (either an entire response, or a 304 + (Not Modified) response) that it would normally forward to the + requesting client, and the received response is no longer fresh, the + cache SHOULD forward it to the requesting client without adding a new + Warning (but without removing any existing Warning headers). A cache + SHOULD NOT attempt to revalidate a response simply because that + response became stale in transit; this might lead to an infinite + loop. A user agent that receives a stale response without a Warning + MAY display a warning indication to the user. + +13.1.2 Warnings + + Whenever a cache returns a response that is neither first-hand nor + "fresh enough" (in the sense of condition 2 in section 13.1.1), it + MUST attach a warning to that effect, using a Warning general-header. + The Warning header and the currently defined warnings are described + in section 14.46. The warning allows clients to take appropriate + action. + + Warnings MAY be used for other purposes, both cache-related and + otherwise. The use of a warning, rather than an error status code, + distinguish these responses from true failures. + + Warnings are assigned three digit warn-codes. The first digit + indicates whether the Warning MUST or MUST NOT be deleted from a + stored cache entry after a successful revalidation: + + + +Fielding, et al. Standards Track [Page 76] + +RFC 2616 HTTP/1.1 June 1999 + + + 1xx Warnings that describe the freshness or revalidation status of + the response, and so MUST be deleted after a successful + revalidation. 1XX warn-codes MAY be generated by a cache only when + validating a cached entry. It MUST NOT be generated by clients. + + 2xx Warnings that describe some aspect of the entity body or entity + headers that is not rectified by a revalidation (for example, a + lossy compression of the entity bodies) and which MUST NOT be + deleted after a successful revalidation. + + See section 14.46 for the definitions of the codes themselves. + + HTTP/1.0 caches will cache all Warnings in responses, without + deleting the ones in the first category. Warnings in responses that + are passed to HTTP/1.0 caches carry an extra warning-date field, + which prevents a future HTTP/1.1 recipient from believing an + erroneously cached Warning. + + Warnings also carry a warning text. The text MAY be in any + appropriate natural language (perhaps based on the client's Accept + headers), and include an OPTIONAL indication of what character set is + used. + + Multiple warnings MAY be attached to a response (either by the origin + server or by a cache), including multiple warnings with the same code + number. For example, a server might provide the same warning with + texts in both English and Basque. + + When multiple warnings are attached to a response, it might not be + practical or reasonable to display all of them to the user. This + version of HTTP does not specify strict priority rules for deciding + which warnings to display and in what order, but does suggest some + heuristics. + +13.1.3 Cache-control Mechanisms + + The basic cache mechanisms in HTTP/1.1 (server-specified expiration + times and validators) are implicit directives to caches. In some + cases, a server or client might need to provide explicit directives + to the HTTP caches. We use the Cache-Control header for this purpose. + + The Cache-Control header allows a client or server to transmit a + variety of directives in either requests or responses. These + directives typically override the default caching algorithms. As a + general rule, if there is any apparent conflict between header + values, the most restrictive interpretation is applied (that is, the + one that is most likely to preserve semantic transparency). However, + + + + +Fielding, et al. Standards Track [Page 77] + +RFC 2616 HTTP/1.1 June 1999 + + + in some cases, cache-control directives are explicitly specified as + weakening the approximation of semantic transparency (for example, + "max-stale" or "public"). + + The cache-control directives are described in detail in section 14.9. + +13.1.4 Explicit User Agent Warnings + + Many user agents make it possible for users to override the basic + caching mechanisms. For example, the user agent might allow the user + to specify that cached entities (even explicitly stale ones) are + never validated. Or the user agent might habitually add "Cache- + Control: max-stale=3600" to every request. The user agent SHOULD NOT + default to either non-transparent behavior, or behavior that results + in abnormally ineffective caching, but MAY be explicitly configured + to do so by an explicit action of the user. + + If the user has overridden the basic caching mechanisms, the user + agent SHOULD explicitly indicate to the user whenever this results in + the display of information that might not meet the server's + transparency requirements (in particular, if the displayed entity is + known to be stale). Since the protocol normally allows the user agent + to determine if responses are stale or not, this indication need only + be displayed when this actually happens. The indication need not be a + dialog box; it could be an icon (for example, a picture of a rotting + fish) or some other indicator. + + If the user has overridden the caching mechanisms in a way that would + abnormally reduce the effectiveness of caches, the user agent SHOULD + continually indicate this state to the user (for example, by a + display of a picture of currency in flames) so that the user does not + inadvertently consume excess resources or suffer from excessive + latency. + +13.1.5 Exceptions to the Rules and Warnings + + In some cases, the operator of a cache MAY choose to configure it to + return stale responses even when not requested by clients. This + decision ought not be made lightly, but may be necessary for reasons + of availability or performance, especially when the cache is poorly + connected to the origin server. Whenever a cache returns a stale + response, it MUST mark it as such (using a Warning header) enabling + the client software to alert the user that there might be a potential + problem. + + + + + + + +Fielding, et al. Standards Track [Page 78] + +RFC 2616 HTTP/1.1 June 1999 + + + It also allows the user agent to take steps to obtain a first-hand or + fresh response. For this reason, a cache SHOULD NOT return a stale + response if the client explicitly requests a first-hand or fresh one, + unless it is impossible to comply for technical or policy reasons. + +13.1.6 Client-controlled Behavior + + While the origin server (and to a lesser extent, intermediate caches, + by their contribution to the age of a response) are the primary + source of expiration information, in some cases the client might need + to control a cache's decision about whether to return a cached + response without validating it. Clients do this using several + directives of the Cache-Control header. + + A client's request MAY specify the maximum age it is willing to + accept of an unvalidated response; specifying a value of zero forces + the cache(s) to revalidate all responses. A client MAY also specify + the minimum time remaining before a response expires. Both of these + options increase constraints on the behavior of caches, and so cannot + further relax the cache's approximation of semantic transparency. + + A client MAY also specify that it will accept stale responses, up to + some maximum amount of staleness. This loosens the constraints on the + caches, and so might violate the origin server's specified + constraints on semantic transparency, but might be necessary to + support disconnected operation, or high availability in the face of + poor connectivity. + +13.2 Expiration Model + +13.2.1 Server-Specified Expiration + + HTTP caching works best when caches can entirely avoid making + requests to the origin server. The primary mechanism for avoiding + requests is for an origin server to provide an explicit expiration + time in the future, indicating that a response MAY be used to satisfy + subsequent requests. In other words, a cache can return a fresh + response without first contacting the server. + + Our expectation is that servers will assign future explicit + expiration times to responses in the belief that the entity is not + likely to change, in a semantically significant way, before the + expiration time is reached. This normally preserves semantic + transparency, as long as the server's expiration times are carefully + chosen. + + + + + + +Fielding, et al. Standards Track [Page 79] + +RFC 2616 HTTP/1.1 June 1999 + + + The expiration mechanism applies only to responses taken from a cache + and not to first-hand responses forwarded immediately to the + requesting client. + + If an origin server wishes to force a semantically transparent cache + to validate every request, it MAY assign an explicit expiration time + in the past. This means that the response is always stale, and so the + cache SHOULD validate it before using it for subsequent requests. See + section 14.9.4 for a more restrictive way to force revalidation. + + If an origin server wishes to force any HTTP/1.1 cache, no matter how + it is configured, to validate every request, it SHOULD use the "must- + revalidate" cache-control directive (see section 14.9). + + Servers specify explicit expiration times using either the Expires + header, or the max-age directive of the Cache-Control header. + + An expiration time cannot be used to force a user agent to refresh + its display or reload a resource; its semantics apply only to caching + mechanisms, and such mechanisms need only check a resource's + expiration status when a new request for that resource is initiated. + See section 13.13 for an explanation of the difference between caches + and history mechanisms. + +13.2.2 Heuristic Expiration + + Since origin servers do not always provide explicit expiration times, + HTTP caches typically assign heuristic expiration times, employing + algorithms that use other header values (such as the Last-Modified + time) to estimate a plausible expiration time. The HTTP/1.1 + specification does not provide specific algorithms, but does impose + worst-case constraints on their results. Since heuristic expiration + times might compromise semantic transparency, they ought to used + cautiously, and we encourage origin servers to provide explicit + expiration times as much as possible. + +13.2.3 Age Calculations + + In order to know if a cached entry is fresh, a cache needs to know if + its age exceeds its freshness lifetime. We discuss how to calculate + the latter in section 13.2.4; this section describes how to calculate + the age of a response or cache entry. + + In this discussion, we use the term "now" to mean "the current value + of the clock at the host performing the calculation." Hosts that use + HTTP, but especially hosts running origin servers and caches, SHOULD + use NTP [28] or some similar protocol to synchronize their clocks to + a globally accurate time standard. + + + +Fielding, et al. Standards Track [Page 80] + +RFC 2616 HTTP/1.1 June 1999 + + + HTTP/1.1 requires origin servers to send a Date header, if possible, + with every response, giving the time at which the response was + generated (see section 14.18). We use the term "date_value" to denote + the value of the Date header, in a form appropriate for arithmetic + operations. + + HTTP/1.1 uses the Age response-header to convey the estimated age of + the response message when obtained from a cache. The Age field value + is the cache's estimate of the amount of time since the response was + generated or revalidated by the origin server. + + In essence, the Age value is the sum of the time that the response + has been resident in each of the caches along the path from the + origin server, plus the amount of time it has been in transit along + network paths. + + We use the term "age_value" to denote the value of the Age header, in + a form appropriate for arithmetic operations. + + A response's age can be calculated in two entirely independent ways: + + 1. now minus date_value, if the local clock is reasonably well + synchronized to the origin server's clock. If the result is + negative, the result is replaced by zero. + + 2. age_value, if all of the caches along the response path + implement HTTP/1.1. + + Given that we have two independent ways to compute the age of a + response when it is received, we can combine these as + + corrected_received_age = max(now - date_value, age_value) + + and as long as we have either nearly synchronized clocks or all- + HTTP/1.1 paths, one gets a reliable (conservative) result. + + Because of network-imposed delays, some significant interval might + pass between the time that a server generates a response and the time + it is received at the next outbound cache or client. If uncorrected, + this delay could result in improperly low ages. + + Because the request that resulted in the returned Age value must have + been initiated prior to that Age value's generation, we can correct + for delays imposed by the network by recording the time at which the + request was initiated. Then, when an Age value is received, it MUST + be interpreted relative to the time the request was initiated, not + + + + + +Fielding, et al. Standards Track [Page 81] + +RFC 2616 HTTP/1.1 June 1999 + + + the time that the response was received. This algorithm results in + conservative behavior no matter how much delay is experienced. So, we + compute: + + corrected_initial_age = corrected_received_age + + (now - request_time) + + where "request_time" is the time (according to the local clock) when + the request that elicited this response was sent. + + Summary of age calculation algorithm, when a cache receives a + response: + + /* + * age_value + * is the value of Age: header received by the cache with + * this response. + * date_value + * is the value of the origin server's Date: header + * request_time + * is the (local) time when the cache made the request + * that resulted in this cached response + * response_time + * is the (local) time when the cache received the + * response + * now + * is the current (local) time + */ + + apparent_age = max(0, response_time - date_value); + corrected_received_age = max(apparent_age, age_value); + response_delay = response_time - request_time; + corrected_initial_age = corrected_received_age + response_delay; + resident_time = now - response_time; + current_age = corrected_initial_age + resident_time; + + The current_age of a cache entry is calculated by adding the amount + of time (in seconds) since the cache entry was last validated by the + origin server to the corrected_initial_age. When a response is + generated from a cache entry, the cache MUST include a single Age + header field in the response with a value equal to the cache entry's + current_age. + + The presence of an Age header field in a response implies that a + response is not first-hand. However, the converse is not true, since + the lack of an Age header field in a response does not imply that the + + + + + +Fielding, et al. Standards Track [Page 82] + +RFC 2616 HTTP/1.1 June 1999 + + + response is first-hand unless all caches along the request path are + compliant with HTTP/1.1 (i.e., older HTTP caches did not implement + the Age header field). + +13.2.4 Expiration Calculations + + In order to decide whether a response is fresh or stale, we need to + compare its freshness lifetime to its age. The age is calculated as + described in section 13.2.3; this section describes how to calculate + the freshness lifetime, and to determine if a response has expired. + In the discussion below, the values can be represented in any form + appropriate for arithmetic operations. + + We use the term "expires_value" to denote the value of the Expires + header. We use the term "max_age_value" to denote an appropriate + value of the number of seconds carried by the "max-age" directive of + the Cache-Control header in a response (see section 14.9.3). + + The max-age directive takes priority over Expires, so if max-age is + present in a response, the calculation is simply: + + freshness_lifetime = max_age_value + + Otherwise, if Expires is present in the response, the calculation is: + + freshness_lifetime = expires_value - date_value + + Note that neither of these calculations is vulnerable to clock skew, + since all of the information comes from the origin server. + + If none of Expires, Cache-Control: max-age, or Cache-Control: s- + maxage (see section 14.9.3) appears in the response, and the response + does not include other restrictions on caching, the cache MAY compute + a freshness lifetime using a heuristic. The cache MUST attach Warning + 113 to any response whose age is more than 24 hours if such warning + has not already been added. + + Also, if the response does have a Last-Modified time, the heuristic + expiration value SHOULD be no more than some fraction of the interval + since that time. A typical setting of this fraction might be 10%. + + The calculation to determine if a response has expired is quite + simple: + + response_is_fresh = (freshness_lifetime > current_age) + + + + + + +Fielding, et al. Standards Track [Page 83] + +RFC 2616 HTTP/1.1 June 1999 + + +13.2.5 Disambiguating Expiration Values + + Because expiration values are assigned optimistically, it is possible + for two caches to contain fresh values for the same resource that are + different. + + If a client performing a retrieval receives a non-first-hand response + for a request that was already fresh in its own cache, and the Date + header in its existing cache entry is newer than the Date on the new + response, then the client MAY ignore the response. If so, it MAY + retry the request with a "Cache-Control: max-age=0" directive (see + section 14.9), to force a check with the origin server. + + If a cache has two fresh responses for the same representation with + different validators, it MUST use the one with the more recent Date + header. This situation might arise because the cache is pooling + responses from other caches, or because a client has asked for a + reload or a revalidation of an apparently fresh cache entry. + +13.2.6 Disambiguating Multiple Responses + + Because a client might be receiving responses via multiple paths, so + that some responses flow through one set of caches and other + responses flow through a different set of caches, a client might + receive responses in an order different from that in which the origin + server sent them. We would like the client to use the most recently + generated response, even if older responses are still apparently + fresh. + + Neither the entity tag nor the expiration value can impose an + ordering on responses, since it is possible that a later response + intentionally carries an earlier expiration time. The Date values are + ordered to a granularity of one second. + + When a client tries to revalidate a cache entry, and the response it + receives contains a Date header that appears to be older than the one + for the existing entry, then the client SHOULD repeat the request + unconditionally, and include + + Cache-Control: max-age=0 + + to force any intermediate caches to validate their copies directly + with the origin server, or + + Cache-Control: no-cache + + to force any intermediate caches to obtain a new copy from the origin + server. + + + +Fielding, et al. Standards Track [Page 84] + +RFC 2616 HTTP/1.1 June 1999 + + + If the Date values are equal, then the client MAY use either response + (or MAY, if it is being extremely prudent, request a new response). + Servers MUST NOT depend on clients being able to choose + deterministically between responses generated during the same second, + if their expiration times overlap. + +13.3 Validation Model + + When a cache has a stale entry that it would like to use as a + response to a client's request, it first has to check with the origin + server (or possibly an intermediate cache with a fresh response) to + see if its cached entry is still usable. We call this "validating" + the cache entry. Since we do not want to have to pay the overhead of + retransmitting the full response if the cached entry is good, and we + do not want to pay the overhead of an extra round trip if the cached + entry is invalid, the HTTP/1.1 protocol supports the use of + conditional methods. + + The key protocol features for supporting conditional methods are + those concerned with "cache validators." When an origin server + generates a full response, it attaches some sort of validator to it, + which is kept with the cache entry. When a client (user agent or + proxy cache) makes a conditional request for a resource for which it + has a cache entry, it includes the associated validator in the + request. + + The server then checks that validator against the current validator + for the entity, and, if they match (see section 13.3.3), it responds + with a special status code (usually, 304 (Not Modified)) and no + entity-body. Otherwise, it returns a full response (including + entity-body). Thus, we avoid transmitting the full response if the + validator matches, and we avoid an extra round trip if it does not + match. + + In HTTP/1.1, a conditional request looks exactly the same as a normal + request for the same resource, except that it carries a special + header (which includes the validator) that implicitly turns the + method (usually, GET) into a conditional. + + The protocol includes both positive and negative senses of cache- + validating conditions. That is, it is possible to request either that + a method be performed if and only if a validator matches or if and + only if no validators match. + + + + + + + + +Fielding, et al. Standards Track [Page 85] + +RFC 2616 HTTP/1.1 June 1999 + + + Note: a response that lacks a validator may still be cached, and + served from cache until it expires, unless this is explicitly + prohibited by a cache-control directive. However, a cache cannot + do a conditional retrieval if it does not have a validator for the + entity, which means it will not be refreshable after it expires. + +13.3.1 Last-Modified Dates + + The Last-Modified entity-header field value is often used as a cache + validator. In simple terms, a cache entry is considered to be valid + if the entity has not been modified since the Last-Modified value. + +13.3.2 Entity Tag Cache Validators + + The ETag response-header field value, an entity tag, provides for an + "opaque" cache validator. This might allow more reliable validation + in situations where it is inconvenient to store modification dates, + where the one-second resolution of HTTP date values is not + sufficient, or where the origin server wishes to avoid certain + paradoxes that might arise from the use of modification dates. + + Entity Tags are described in section 3.11. The headers used with + entity tags are described in sections 14.19, 14.24, 14.26 and 14.44. + +13.3.3 Weak and Strong Validators + + Since both origin servers and caches will compare two validators to + decide if they represent the same or different entities, one normally + would expect that if the entity (the entity-body or any entity- + headers) changes in any way, then the associated validator would + change as well. If this is true, then we call this validator a + "strong validator." + + However, there might be cases when a server prefers to change the + validator only on semantically significant changes, and not when + insignificant aspects of the entity change. A validator that does not + always change when the resource changes is a "weak validator." + + Entity tags are normally "strong validators," but the protocol + provides a mechanism to tag an entity tag as "weak." One can think of + a strong validator as one that changes whenever the bits of an entity + changes, while a weak value changes whenever the meaning of an entity + changes. Alternatively, one can think of a strong validator as part + of an identifier for a specific entity, while a weak validator is + part of an identifier for a set of semantically equivalent entities. + + Note: One example of a strong validator is an integer that is + incremented in stable storage every time an entity is changed. + + + +Fielding, et al. Standards Track [Page 86] + +RFC 2616 HTTP/1.1 June 1999 + + + An entity's modification time, if represented with one-second + resolution, could be a weak validator, since it is possible that + the resource might be modified twice during a single second. + + Support for weak validators is optional. However, weak validators + allow for more efficient caching of equivalent objects; for + example, a hit counter on a site is probably good enough if it is + updated every few days or weeks, and any value during that period + is likely "good enough" to be equivalent. + + A "use" of a validator is either when a client generates a request + and includes the validator in a validating header field, or when a + server compares two validators. + + Strong validators are usable in any context. Weak validators are only + usable in contexts that do not depend on exact equality of an entity. + For example, either kind is usable for a conditional GET of a full + entity. However, only a strong validator is usable for a sub-range + retrieval, since otherwise the client might end up with an internally + inconsistent entity. + + Clients MAY issue simple (non-subrange) GET requests with either weak + validators or strong validators. Clients MUST NOT use weak validators + in other forms of request. + + The only function that the HTTP/1.1 protocol defines on validators is + comparison. There are two validator comparison functions, depending + on whether the comparison context allows the use of weak validators + or not: + + - The strong comparison function: in order to be considered equal, + both validators MUST be identical in every way, and both MUST + NOT be weak. + + - The weak comparison function: in order to be considered equal, + both validators MUST be identical in every way, but either or + both of them MAY be tagged as "weak" without affecting the + result. + + An entity tag is strong unless it is explicitly tagged as weak. + Section 3.11 gives the syntax for entity tags. + + A Last-Modified time, when used as a validator in a request, is + implicitly weak unless it is possible to deduce that it is strong, + using the following rules: + + - The validator is being compared by an origin server to the + actual current validator for the entity and, + + + +Fielding, et al. Standards Track [Page 87] + +RFC 2616 HTTP/1.1 June 1999 + + + - That origin server reliably knows that the associated entity did + not change twice during the second covered by the presented + validator. + + or + + - The validator is about to be used by a client in an If- + Modified-Since or If-Unmodified-Since header, because the client + has a cache entry for the associated entity, and + + - That cache entry includes a Date value, which gives the time + when the origin server sent the original response, and + + - The presented Last-Modified time is at least 60 seconds before + the Date value. + + or + + - The validator is being compared by an intermediate cache to the + validator stored in its cache entry for the entity, and + + - That cache entry includes a Date value, which gives the time + when the origin server sent the original response, and + + - The presented Last-Modified time is at least 60 seconds before + the Date value. + + This method relies on the fact that if two different responses were + sent by the origin server during the same second, but both had the + same Last-Modified time, then at least one of those responses would + have a Date value equal to its Last-Modified time. The arbitrary 60- + second limit guards against the possibility that the Date and Last- + Modified values are generated from different clocks, or at somewhat + different times during the preparation of the response. An + implementation MAY use a value larger than 60 seconds, if it is + believed that 60 seconds is too short. + + If a client wishes to perform a sub-range retrieval on a value for + which it has only a Last-Modified time and no opaque validator, it + MAY do this only if the Last-Modified time is strong in the sense + described here. + + A cache or origin server receiving a conditional request, other than + a full-body GET request, MUST use the strong comparison function to + evaluate the condition. + + These rules allow HTTP/1.1 caches and clients to safely perform sub- + range retrievals on values that have been obtained from HTTP/1.0 + + + +Fielding, et al. Standards Track [Page 88] + +RFC 2616 HTTP/1.1 June 1999 + + + servers. + +13.3.4 Rules for When to Use Entity Tags and Last-Modified Dates + + We adopt a set of rules and recommendations for origin servers, + clients, and caches regarding when various validator types ought to + be used, and for what purposes. + + HTTP/1.1 origin servers: + + - SHOULD send an entity tag validator unless it is not feasible to + generate one. + + - MAY send a weak entity tag instead of a strong entity tag, if + performance considerations support the use of weak entity tags, + or if it is unfeasible to send a strong entity tag. + + - SHOULD send a Last-Modified value if it is feasible to send one, + unless the risk of a breakdown in semantic transparency that + could result from using this date in an If-Modified-Since header + would lead to serious problems. + + In other words, the preferred behavior for an HTTP/1.1 origin server + is to send both a strong entity tag and a Last-Modified value. + + In order to be legal, a strong entity tag MUST change whenever the + associated entity value changes in any way. A weak entity tag SHOULD + change whenever the associated entity changes in a semantically + significant way. + + Note: in order to provide semantically transparent caching, an + origin server must avoid reusing a specific strong entity tag + value for two different entities, or reusing a specific weak + entity tag value for two semantically different entities. Cache + entries might persist for arbitrarily long periods, regardless of + expiration times, so it might be inappropriate to expect that a + cache will never again attempt to validate an entry using a + validator that it obtained at some point in the past. + + HTTP/1.1 clients: + + - If an entity tag has been provided by the origin server, MUST + use that entity tag in any cache-conditional request (using If- + Match or If-None-Match). + + - If only a Last-Modified value has been provided by the origin + server, SHOULD use that value in non-subrange cache-conditional + requests (using If-Modified-Since). + + + +Fielding, et al. Standards Track [Page 89] + +RFC 2616 HTTP/1.1 June 1999 + + + - If only a Last-Modified value has been provided by an HTTP/1.0 + origin server, MAY use that value in subrange cache-conditional + requests (using If-Unmodified-Since:). The user agent SHOULD + provide a way to disable this, in case of difficulty. + + - If both an entity tag and a Last-Modified value have been + provided by the origin server, SHOULD use both validators in + cache-conditional requests. This allows both HTTP/1.0 and + HTTP/1.1 caches to respond appropriately. + + An HTTP/1.1 origin server, upon receiving a conditional request that + includes both a Last-Modified date (e.g., in an If-Modified-Since or + If-Unmodified-Since header field) and one or more entity tags (e.g., + in an If-Match, If-None-Match, or If-Range header field) as cache + validators, MUST NOT return a response status of 304 (Not Modified) + unless doing so is consistent with all of the conditional header + fields in the request. + + An HTTP/1.1 caching proxy, upon receiving a conditional request that + includes both a Last-Modified date and one or more entity tags as + cache validators, MUST NOT return a locally cached response to the + client unless that cached response is consistent with all of the + conditional header fields in the request. + + Note: The general principle behind these rules is that HTTP/1.1 + servers and clients should transmit as much non-redundant + information as is available in their responses and requests. + HTTP/1.1 systems receiving this information will make the most + conservative assumptions about the validators they receive. + + HTTP/1.0 clients and caches will ignore entity tags. Generally, + last-modified values received or used by these systems will + support transparent and efficient caching, and so HTTP/1.1 origin + servers should provide Last-Modified values. In those rare cases + where the use of a Last-Modified value as a validator by an + HTTP/1.0 system could result in a serious problem, then HTTP/1.1 + origin servers should not provide one. + +13.3.5 Non-validating Conditionals + + The principle behind entity tags is that only the service author + knows the semantics of a resource well enough to select an + appropriate cache validation mechanism, and the specification of any + validator comparison function more complex than byte-equality would + open up a can of worms. Thus, comparisons of any other headers + (except Last-Modified, for compatibility with HTTP/1.0) are never + used for purposes of validating a cache entry. + + + + +Fielding, et al. Standards Track [Page 90] + +RFC 2616 HTTP/1.1 June 1999 + + +13.4 Response Cacheability + + Unless specifically constrained by a cache-control (section 14.9) + directive, a caching system MAY always store a successful response + (see section 13.8) as a cache entry, MAY return it without validation + if it is fresh, and MAY return it after successful validation. If + there is neither a cache validator nor an explicit expiration time + associated with a response, we do not expect it to be cached, but + certain caches MAY violate this expectation (for example, when little + or no network connectivity is available). A client can usually detect + that such a response was taken from a cache by comparing the Date + header to the current time. + + Note: some HTTP/1.0 caches are known to violate this expectation + without providing any Warning. + + However, in some cases it might be inappropriate for a cache to + retain an entity, or to return it in response to a subsequent + request. This might be because absolute semantic transparency is + deemed necessary by the service author, or because of security or + privacy considerations. Certain cache-control directives are + therefore provided so that the server can indicate that certain + resource entities, or portions thereof, are not to be cached + regardless of other considerations. + + Note that section 14.8 normally prevents a shared cache from saving + and returning a response to a previous request if that request + included an Authorization header. + + A response received with a status code of 200, 203, 206, 300, 301 or + 410 MAY be stored by a cache and used in reply to a subsequent + request, subject to the expiration mechanism, unless a cache-control + directive prohibits caching. However, a cache that does not support + the Range and Content-Range headers MUST NOT cache 206 (Partial + Content) responses. + + A response received with any other status code (e.g. status codes 302 + and 307) MUST NOT be returned in a reply to a subsequent request + unless there are cache-control directives or another header(s) that + explicitly allow it. For example, these include the following: an + Expires header (section 14.21); a "max-age", "s-maxage", "must- + revalidate", "proxy-revalidate", "public" or "private" cache-control + directive (section 14.9). + + + + + + + + +Fielding, et al. Standards Track [Page 91] + +RFC 2616 HTTP/1.1 June 1999 + + +13.5 Constructing Responses From Caches + + The purpose of an HTTP cache is to store information received in + response to requests for use in responding to future requests. In + many cases, a cache simply returns the appropriate parts of a + response to the requester. However, if the cache holds a cache entry + based on a previous response, it might have to combine parts of a new + response with what is held in the cache entry. + +13.5.1 End-to-end and Hop-by-hop Headers + + For the purpose of defining the behavior of caches and non-caching + proxies, we divide HTTP headers into two categories: + + - End-to-end headers, which are transmitted to the ultimate + recipient of a request or response. End-to-end headers in + responses MUST be stored as part of a cache entry and MUST be + transmitted in any response formed from a cache entry. + + - Hop-by-hop headers, which are meaningful only for a single + transport-level connection, and are not stored by caches or + forwarded by proxies. + + The following HTTP/1.1 headers are hop-by-hop headers: + + - Connection + - Keep-Alive + - Proxy-Authenticate + - Proxy-Authorization + - TE + - Trailers + - Transfer-Encoding + - Upgrade + + All other headers defined by HTTP/1.1 are end-to-end headers. + + Other hop-by-hop headers MUST be listed in a Connection header, + (section 14.10) to be introduced into HTTP/1.1 (or later). + +13.5.2 Non-modifiable Headers + + Some features of the HTTP/1.1 protocol, such as Digest + Authentication, depend on the value of certain end-to-end headers. A + transparent proxy SHOULD NOT modify an end-to-end header unless the + definition of that header requires or specifically allows that. + + + + + + +Fielding, et al. Standards Track [Page 92] + +RFC 2616 HTTP/1.1 June 1999 + + + A transparent proxy MUST NOT modify any of the following fields in a + request or response, and it MUST NOT add any of these fields if not + already present: + + - Content-Location + + - Content-MD5 + + - ETag + + - Last-Modified + + A transparent proxy MUST NOT modify any of the following fields in a + response: + + - Expires + + but it MAY add any of these fields if not already present. If an + Expires header is added, it MUST be given a field-value identical to + that of the Date header in that response. + + A proxy MUST NOT modify or add any of the following fields in a + message that contains the no-transform cache-control directive, or in + any request: + + - Content-Encoding + + - Content-Range + + - Content-Type + + A non-transparent proxy MAY modify or add these fields to a message + that does not include no-transform, but if it does so, it MUST add a + Warning 214 (Transformation applied) if one does not already appear + in the message (see section 14.46). + + Warning: unnecessary modification of end-to-end headers might + cause authentication failures if stronger authentication + mechanisms are introduced in later versions of HTTP. Such + authentication mechanisms MAY rely on the values of header fields + not listed here. + + The Content-Length field of a request or response is added or deleted + according to the rules in section 4.4. A transparent proxy MUST + preserve the entity-length (section 7.2.2) of the entity-body, + although it MAY change the transfer-length (section 4.4). + + + + + +Fielding, et al. Standards Track [Page 93] + +RFC 2616 HTTP/1.1 June 1999 + + +13.5.3 Combining Headers + + When a cache makes a validating request to a server, and the server + provides a 304 (Not Modified) response or a 206 (Partial Content) + response, the cache then constructs a response to send to the + requesting client. + + If the status code is 304 (Not Modified), the cache uses the entity- + body stored in the cache entry as the entity-body of this outgoing + response. If the status code is 206 (Partial Content) and the ETag or + Last-Modified headers match exactly, the cache MAY combine the + contents stored in the cache entry with the new contents received in + the response and use the result as the entity-body of this outgoing + response, (see 13.5.4). + + The end-to-end headers stored in the cache entry are used for the + constructed response, except that + + - any stored Warning headers with warn-code 1xx (see section + 14.46) MUST be deleted from the cache entry and the forwarded + response. + + - any stored Warning headers with warn-code 2xx MUST be retained + in the cache entry and the forwarded response. + + - any end-to-end headers provided in the 304 or 206 response MUST + replace the corresponding headers from the cache entry. + + Unless the cache decides to remove the cache entry, it MUST also + replace the end-to-end headers stored with the cache entry with + corresponding headers received in the incoming response, except for + Warning headers as described immediately above. If a header field- + name in the incoming response matches more than one header in the + cache entry, all such old headers MUST be replaced. + + In other words, the set of end-to-end headers received in the + incoming response overrides all corresponding end-to-end headers + stored with the cache entry (except for stored Warning headers with + warn-code 1xx, which are deleted even if not overridden). + + Note: this rule allows an origin server to use a 304 (Not + Modified) or a 206 (Partial Content) response to update any header + associated with a previous response for the same entity or sub- + ranges thereof, although it might not always be meaningful or + correct to do so. This rule does not allow an origin server to use + a 304 (Not Modified) or a 206 (Partial Content) response to + entirely delete a header that it had provided with a previous + response. + + + +Fielding, et al. Standards Track [Page 94] + +RFC 2616 HTTP/1.1 June 1999 + + +13.5.4 Combining Byte Ranges + + A response might transfer only a subrange of the bytes of an entity- + body, either because the request included one or more Range + specifications, or because a connection was broken prematurely. After + several such transfers, a cache might have received several ranges of + the same entity-body. + + If a cache has a stored non-empty set of subranges for an entity, and + an incoming response transfers another subrange, the cache MAY + combine the new subrange with the existing set if both the following + conditions are met: + + - Both the incoming response and the cache entry have a cache + validator. + + - The two cache validators match using the strong comparison + function (see section 13.3.3). + + If either requirement is not met, the cache MUST use only the most + recent partial response (based on the Date values transmitted with + every response, and using the incoming response if these values are + equal or missing), and MUST discard the other partial information. + +13.6 Caching Negotiated Responses + + Use of server-driven content negotiation (section 12.1), as indicated + by the presence of a Vary header field in a response, alters the + conditions and procedure by which a cache can use the response for + subsequent requests. See section 14.44 for use of the Vary header + field by servers. + + A server SHOULD use the Vary header field to inform a cache of what + request-header fields were used to select among multiple + representations of a cacheable response subject to server-driven + negotiation. The set of header fields named by the Vary field value + is known as the "selecting" request-headers. + + When the cache receives a subsequent request whose Request-URI + specifies one or more cache entries including a Vary header field, + the cache MUST NOT use such a cache entry to construct a response to + the new request unless all of the selecting request-headers present + in the new request match the corresponding stored request-headers in + the original request. + + The selecting request-headers from two requests are defined to match + if and only if the selecting request-headers in the first request can + be transformed to the selecting request-headers in the second request + + + +Fielding, et al. Standards Track [Page 95] + +RFC 2616 HTTP/1.1 June 1999 + + + by adding or removing linear white space (LWS) at places where this + is allowed by the corresponding BNF, and/or combining multiple + message-header fields with the same field name following the rules + about message headers in section 4.2. + + A Vary header field-value of "*" always fails to match and subsequent + requests on that resource can only be properly interpreted by the + origin server. + + If the selecting request header fields for the cached entry do not + match the selecting request header fields of the new request, then + the cache MUST NOT use a cached entry to satisfy the request unless + it first relays the new request to the origin server in a conditional + request and the server responds with 304 (Not Modified), including an + entity tag or Content-Location that indicates the entity to be used. + + If an entity tag was assigned to a cached representation, the + forwarded request SHOULD be conditional and include the entity tags + in an If-None-Match header field from all its cache entries for the + resource. This conveys to the server the set of entities currently + held by the cache, so that if any one of these entities matches the + requested entity, the server can use the ETag header field in its 304 + (Not Modified) response to tell the cache which entry is appropriate. + If the entity-tag of the new response matches that of an existing + entry, the new response SHOULD be used to update the header fields of + the existing entry, and the result MUST be returned to the client. + + If any of the existing cache entries contains only partial content + for the associated entity, its entity-tag SHOULD NOT be included in + the If-None-Match header field unless the request is for a range that + would be fully satisfied by that entry. + + If a cache receives a successful response whose Content-Location + field matches that of an existing cache entry for the same Request- + ]URI, whose entity-tag differs from that of the existing entry, and + whose Date is more recent than that of the existing entry, the + existing entry SHOULD NOT be returned in response to future requests + and SHOULD be deleted from the cache. + +13.7 Shared and Non-Shared Caches + + For reasons of security and privacy, it is necessary to make a + distinction between "shared" and "non-shared" caches. A non-shared + cache is one that is accessible only to a single user. Accessibility + in this case SHOULD be enforced by appropriate security mechanisms. + All other caches are considered to be "shared." Other sections of + + + + + +Fielding, et al. Standards Track [Page 96] + +RFC 2616 HTTP/1.1 June 1999 + + + this specification place certain constraints on the operation of + shared caches in order to prevent loss of privacy or failure of + access controls. + +13.8 Errors or Incomplete Response Cache Behavior + + A cache that receives an incomplete response (for example, with fewer + bytes of data than specified in a Content-Length header) MAY store + the response. However, the cache MUST treat this as a partial + response. Partial responses MAY be combined as described in section + 13.5.4; the result might be a full response or might still be + partial. A cache MUST NOT return a partial response to a client + without explicitly marking it as such, using the 206 (Partial + Content) status code. A cache MUST NOT return a partial response + using a status code of 200 (OK). + + If a cache receives a 5xx response while attempting to revalidate an + entry, it MAY either forward this response to the requesting client, + or act as if the server failed to respond. In the latter case, it MAY + return a previously received response unless the cached entry + includes the "must-revalidate" cache-control directive (see section + 14.9). + +13.9 Side Effects of GET and HEAD + + Unless the origin server explicitly prohibits the caching of their + responses, the application of GET and HEAD methods to any resources + SHOULD NOT have side effects that would lead to erroneous behavior if + these responses are taken from a cache. They MAY still have side + effects, but a cache is not required to consider such side effects in + its caching decisions. Caches are always expected to observe an + origin server's explicit restrictions on caching. + + We note one exception to this rule: since some applications have + traditionally used GETs and HEADs with query URLs (those containing a + "?" in the rel_path part) to perform operations with significant side + effects, caches MUST NOT treat responses to such URIs as fresh unless + the server provides an explicit expiration time. This specifically + means that responses from HTTP/1.0 servers for such URIs SHOULD NOT + be taken from a cache. See section 9.1.1 for related information. + +13.10 Invalidation After Updates or Deletions + + The effect of certain methods performed on a resource at the origin + server might cause one or more existing cache entries to become non- + transparently invalid. That is, although they might continue to be + "fresh," they do not accurately reflect what the origin server would + return for a new request on that resource. + + + +Fielding, et al. Standards Track [Page 97] + +RFC 2616 HTTP/1.1 June 1999 + + + There is no way for the HTTP protocol to guarantee that all such + cache entries are marked invalid. For example, the request that + caused the change at the origin server might not have gone through + the proxy where a cache entry is stored. However, several rules help + reduce the likelihood of erroneous behavior. + + In this section, the phrase "invalidate an entity" means that the + cache will either remove all instances of that entity from its + storage, or will mark these as "invalid" and in need of a mandatory + revalidation before they can be returned in response to a subsequent + request. + + Some HTTP methods MUST cause a cache to invalidate an entity. This is + either the entity referred to by the Request-URI, or by the Location + or Content-Location headers (if present). These methods are: + + - PUT + + - DELETE + + - POST + + In order to prevent denial of service attacks, an invalidation based + on the URI in a Location or Content-Location header MUST only be + performed if the host part is the same as in the Request-URI. + + A cache that passes through requests for methods it does not + understand SHOULD invalidate any entities referred to by the + Request-URI. + +13.11 Write-Through Mandatory + + All methods that might be expected to cause modifications to the + origin server's resources MUST be written through to the origin + server. This currently includes all methods except for GET and HEAD. + A cache MUST NOT reply to such a request from a client before having + transmitted the request to the inbound server, and having received a + corresponding response from the inbound server. This does not prevent + a proxy cache from sending a 100 (Continue) response before the + inbound server has sent its final reply. + + The alternative (known as "write-back" or "copy-back" caching) is not + allowed in HTTP/1.1, due to the difficulty of providing consistent + updates and the problems arising from server, cache, or network + failure prior to write-back. + + + + + + +Fielding, et al. Standards Track [Page 98] + +RFC 2616 HTTP/1.1 June 1999 + + +13.12 Cache Replacement + + If a new cacheable (see sections 14.9.2, 13.2.5, 13.2.6 and 13.8) + response is received from a resource while any existing responses for + the same resource are cached, the cache SHOULD use the new response + to reply to the current request. It MAY insert it into cache storage + and MAY, if it meets all other requirements, use it to respond to any + future requests that would previously have caused the old response to + be returned. If it inserts the new response into cache storage the + rules in section 13.5.3 apply. + + Note: a new response that has an older Date header value than + existing cached responses is not cacheable. + +13.13 History Lists + + User agents often have history mechanisms, such as "Back" buttons and + history lists, which can be used to redisplay an entity retrieved + earlier in a session. + + History mechanisms and caches are different. In particular history + mechanisms SHOULD NOT try to show a semantically transparent view of + the current state of a resource. Rather, a history mechanism is meant + to show exactly what the user saw at the time when the resource was + retrieved. + + By default, an expiration time does not apply to history mechanisms. + If the entity is still in storage, a history mechanism SHOULD display + it even if the entity has expired, unless the user has specifically + configured the agent to refresh expired history documents. + + This is not to be construed to prohibit the history mechanism from + telling the user that a view might be stale. + + Note: if history list mechanisms unnecessarily prevent users from + viewing stale resources, this will tend to force service authors + to avoid using HTTP expiration controls and cache controls when + they would otherwise like to. Service authors may consider it + important that users not be presented with error messages or + warning messages when they use navigation controls (such as BACK) + to view previously fetched resources. Even though sometimes such + resources ought not to cached, or ought to expire quickly, user + interface considerations may force service authors to resort to + other means of preventing caching (e.g. "once-only" URLs) in order + not to suffer the effects of improperly functioning history + mechanisms. + + + + + +Fielding, et al. Standards Track [Page 99] + +RFC 2616 HTTP/1.1 June 1999 + + +14 Header Field Definitions + + This section defines the syntax and semantics of all standard + HTTP/1.1 header fields. For entity-header fields, both sender and + recipient refer to either the client or the server, depending on who + sends and who receives the entity. + +14.1 Accept + + The Accept request-header field can be used to specify certain media + types which are acceptable for the response. Accept headers can be + used to indicate that the request is specifically limited to a small + set of desired types, as in the case of a request for an in-line + image. + + Accept = "Accept" ":" + #( media-range [ accept-params ] ) + + media-range = ( "*/*" + | ( type "/" "*" ) + | ( type "/" subtype ) + ) *( ";" parameter ) + accept-params = ";" "q" "=" qvalue *( accept-extension ) + accept-extension = ";" token [ "=" ( token | quoted-string ) ] + + The asterisk "*" character is used to group media types into ranges, + with "*/*" indicating all media types and "type/*" indicating all + subtypes of that type. The media-range MAY include media type + parameters that are applicable to that range. + + Each media-range MAY be followed by one or more accept-params, + beginning with the "q" parameter for indicating a relative quality + factor. The first "q" parameter (if any) separates the media-range + parameter(s) from the accept-params. Quality factors allow the user + or user agent to indicate the relative degree of preference for that + media-range, using the qvalue scale from 0 to 1 (section 3.9). The + default value is q=1. + + Note: Use of the "q" parameter name to separate media type + parameters from Accept extension parameters is due to historical + practice. Although this prevents any media type parameter named + "q" from being used with a media range, such an event is believed + to be unlikely given the lack of any "q" parameters in the IANA + media type registry and the rare usage of any media type + parameters in Accept. Future media types are discouraged from + registering any parameter named "q". + + + + + +Fielding, et al. Standards Track [Page 100] + +RFC 2616 HTTP/1.1 June 1999 + + + The example + + Accept: audio/*; q=0.2, audio/basic + + SHOULD be interpreted as "I prefer audio/basic, but send me any audio + type if it is the best available after an 80% mark-down in quality." + + If no Accept header field is present, then it is assumed that the + client accepts all media types. If an Accept header field is present, + and if the server cannot send a response which is acceptable + according to the combined Accept field value, then the server SHOULD + send a 406 (not acceptable) response. + + A more elaborate example is + + Accept: text/plain; q=0.5, text/html, + text/x-dvi; q=0.8, text/x-c + + Verbally, this would be interpreted as "text/html and text/x-c are + the preferred media types, but if they do not exist, then send the + text/x-dvi entity, and if that does not exist, send the text/plain + entity." + + Media ranges can be overridden by more specific media ranges or + specific media types. If more than one media range applies to a given + type, the most specific reference has precedence. For example, + + Accept: text/*, text/html, text/html;level=1, */* + + have the following precedence: + + 1) text/html;level=1 + 2) text/html + 3) text/* + 4) */* + + The media type quality factor associated with a given type is + determined by finding the media range with the highest precedence + which matches that type. For example, + + Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1, + text/html;level=2;q=0.4, */*;q=0.5 + + would cause the following values to be associated: + + text/html;level=1 = 1 + text/html = 0.7 + text/plain = 0.3 + + + +Fielding, et al. Standards Track [Page 101] + +RFC 2616 HTTP/1.1 June 1999 + + + image/jpeg = 0.5 + text/html;level=2 = 0.4 + text/html;level=3 = 0.7 + + Note: A user agent might be provided with a default set of quality + values for certain media ranges. However, unless the user agent is + a closed system which cannot interact with other rendering agents, + this default set ought to be configurable by the user. + +14.2 Accept-Charset + + The Accept-Charset request-header field can be used to indicate what + character sets are acceptable for the response. This field allows + clients capable of understanding more comprehensive or special- + purpose character sets to signal that capability to a server which is + capable of representing documents in those character sets. + + Accept-Charset = "Accept-Charset" ":" + 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] ) + + + Character set values are described in section 3.4. Each charset MAY + be given an associated quality value which represents the user's + preference for that charset. The default value is q=1. An example is + + Accept-Charset: iso-8859-5, unicode-1-1;q=0.8 + + The special value "*", if present in the Accept-Charset field, + matches every character set (including ISO-8859-1) which is not + mentioned elsewhere in the Accept-Charset field. If no "*" is present + in an Accept-Charset field, then all character sets not explicitly + mentioned get a quality value of 0, except for ISO-8859-1, which gets + a quality value of 1 if not explicitly mentioned. + + If no Accept-Charset header is present, the default is that any + character set is acceptable. If an Accept-Charset header is present, + and if the server cannot send a response which is acceptable + according to the Accept-Charset header, then the server SHOULD send + an error response with the 406 (not acceptable) status code, though + the sending of an unacceptable response is also allowed. + +14.3 Accept-Encoding + + The Accept-Encoding request-header field is similar to Accept, but + restricts the content-codings (section 3.5) that are acceptable in + the response. + + Accept-Encoding = "Accept-Encoding" ":" + + + +Fielding, et al. Standards Track [Page 102] + +RFC 2616 HTTP/1.1 June 1999 + + + 1#( codings [ ";" "q" "=" qvalue ] ) + codings = ( content-coding | "*" ) + + Examples of its use are: + + Accept-Encoding: compress, gzip + Accept-Encoding: + Accept-Encoding: * + Accept-Encoding: compress;q=0.5, gzip;q=1.0 + Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0 + + A server tests whether a content-coding is acceptable, according to + an Accept-Encoding field, using these rules: + + 1. If the content-coding is one of the content-codings listed in + the Accept-Encoding field, then it is acceptable, unless it is + accompanied by a qvalue of 0. (As defined in section 3.9, a + qvalue of 0 means "not acceptable.") + + 2. The special "*" symbol in an Accept-Encoding field matches any + available content-coding not explicitly listed in the header + field. + + 3. If multiple content-codings are acceptable, then the acceptable + content-coding with the highest non-zero qvalue is preferred. + + 4. The "identity" content-coding is always acceptable, unless + specifically refused because the Accept-Encoding field includes + "identity;q=0", or because the field includes "*;q=0" and does + not explicitly include the "identity" content-coding. If the + Accept-Encoding field-value is empty, then only the "identity" + encoding is acceptable. + + If an Accept-Encoding field is present in a request, and if the + server cannot send a response which is acceptable according to the + Accept-Encoding header, then the server SHOULD send an error response + with the 406 (Not Acceptable) status code. + + If no Accept-Encoding field is present in a request, the server MAY + assume that the client will accept any content coding. In this case, + if "identity" is one of the available content-codings, then the + server SHOULD use the "identity" content-coding, unless it has + additional information that a different content-coding is meaningful + to the client. + + Note: If the request does not include an Accept-Encoding field, + and if the "identity" content-coding is unavailable, then + content-codings commonly understood by HTTP/1.0 clients (i.e., + + + +Fielding, et al. Standards Track [Page 103] + +RFC 2616 HTTP/1.1 June 1999 + + + "gzip" and "compress") are preferred; some older clients + improperly display messages sent with other content-codings. The + server might also make this decision based on information about + the particular user-agent or client. + + Note: Most HTTP/1.0 applications do not recognize or obey qvalues + associated with content-codings. This means that qvalues will not + work and are not permitted with x-gzip or x-compress. + +14.4 Accept-Language + + The Accept-Language request-header field is similar to Accept, but + restricts the set of natural languages that are preferred as a + response to the request. Language tags are defined in section 3.10. + + Accept-Language = "Accept-Language" ":" + 1#( language-range [ ";" "q" "=" qvalue ] ) + language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) + + Each language-range MAY be given an associated quality value which + represents an estimate of the user's preference for the languages + specified by that range. The quality value defaults to "q=1". For + example, + + Accept-Language: da, en-gb;q=0.8, en;q=0.7 + + would mean: "I prefer Danish, but will accept British English and + other types of English." A language-range matches a language-tag if + it exactly equals the tag, or if it exactly equals a prefix of the + tag such that the first tag character following the prefix is "-". + The special range "*", if present in the Accept-Language field, + matches every tag not matched by any other range present in the + Accept-Language field. + + Note: This use of a prefix matching rule does not imply that + language tags are assigned to languages in such a way that it is + always true that if a user understands a language with a certain + tag, then this user will also understand all languages with tags + for which this tag is a prefix. The prefix rule simply allows the + use of prefix tags if this is the case. + + The language quality factor assigned to a language-tag by the + Accept-Language field is the quality value of the longest language- + range in the field that matches the language-tag. If no language- + range in the field matches the tag, the language quality factor + assigned is 0. If no Accept-Language header is present in the + request, the server + + + + +Fielding, et al. Standards Track [Page 104] + +RFC 2616 HTTP/1.1 June 1999 + + + SHOULD assume that all languages are equally acceptable. If an + Accept-Language header is present, then all languages which are + assigned a quality factor greater than 0 are acceptable. + + It might be contrary to the privacy expectations of the user to send + an Accept-Language header with the complete linguistic preferences of + the user in every request. For a discussion of this issue, see + section 15.1.4. + + As intelligibility is highly dependent on the individual user, it is + recommended that client applications make the choice of linguistic + preference available to the user. If the choice is not made + available, then the Accept-Language header field MUST NOT be given in + the request. + + Note: When making the choice of linguistic preference available to + the user, we remind implementors of the fact that users are not + familiar with the details of language matching as described above, + and should provide appropriate guidance. As an example, users + might assume that on selecting "en-gb", they will be served any + kind of English document if British English is not available. A + user agent might suggest in such a case to add "en" to get the + best matching behavior. + +14.5 Accept-Ranges + + The Accept-Ranges response-header field allows the server to + indicate its acceptance of range requests for a resource: + + Accept-Ranges = "Accept-Ranges" ":" acceptable-ranges + acceptable-ranges = 1#range-unit | "none" + + Origin servers that accept byte-range requests MAY send + + Accept-Ranges: bytes + + but are not required to do so. Clients MAY generate byte-range + requests without having received this header for the resource + involved. Range units are defined in section 3.12. + + Servers that do not accept any kind of range request for a + resource MAY send + + Accept-Ranges: none + + to advise the client not to attempt a range request. + + + + + +Fielding, et al. Standards Track [Page 105] + +RFC 2616 HTTP/1.1 June 1999 + + +14.6 Age + + The Age response-header field conveys the sender's estimate of the + amount of time since the response (or its revalidation) was + generated at the origin server. A cached response is "fresh" if + its age does not exceed its freshness lifetime. Age values are + calculated as specified in section 13.2.3. + + Age = "Age" ":" age-value + age-value = delta-seconds + + Age values are non-negative decimal integers, representing time in + seconds. + + If a cache receives a value larger than the largest positive + integer it can represent, or if any of its age calculations + overflows, it MUST transmit an Age header with a value of + 2147483648 (2^31). An HTTP/1.1 server that includes a cache MUST + include an Age header field in every response generated from its + own cache. Caches SHOULD use an arithmetic type of at least 31 + bits of range. + +14.7 Allow + + The Allow entity-header field lists the set of methods supported + by the resource identified by the Request-URI. The purpose of this + field is strictly to inform the recipient of valid methods + associated with the resource. An Allow header field MUST be + present in a 405 (Method Not Allowed) response. + + Allow = "Allow" ":" #Method + + Example of use: + + Allow: GET, HEAD, PUT + + This field cannot prevent a client from trying other methods. + However, the indications given by the Allow header field value + SHOULD be followed. The actual set of allowed methods is defined + by the origin server at the time of each request. + + The Allow header field MAY be provided with a PUT request to + recommend the methods to be supported by the new or modified + resource. The server is not required to support these methods and + SHOULD include an Allow header in the response giving the actual + supported methods. + + + + + +Fielding, et al. Standards Track [Page 106] + +RFC 2616 HTTP/1.1 June 1999 + + + A proxy MUST NOT modify the Allow header field even if it does not + understand all the methods specified, since the user agent might + have other means of communicating with the origin server. + +14.8 Authorization + + A user agent that wishes to authenticate itself with a server-- + usually, but not necessarily, after receiving a 401 response--does + so by including an Authorization request-header field with the + request. The Authorization field value consists of credentials + containing the authentication information of the user agent for + the realm of the resource being requested. + + Authorization = "Authorization" ":" credentials + + HTTP access authentication is described in "HTTP Authentication: + Basic and Digest Access Authentication" [43]. If a request is + authenticated and a realm specified, the same credentials SHOULD + be valid for all other requests within this realm (assuming that + the authentication scheme itself does not require otherwise, such + as credentials that vary according to a challenge value or using + synchronized clocks). + + When a shared cache (see section 13.7) receives a request + containing an Authorization field, it MUST NOT return the + corresponding response as a reply to any other request, unless one + of the following specific exceptions holds: + + 1. If the response includes the "s-maxage" cache-control + directive, the cache MAY use that response in replying to a + subsequent request. But (if the specified maximum age has + passed) a proxy cache MUST first revalidate it with the origin + server, using the request-headers from the new request to allow + the origin server to authenticate the new request. (This is the + defined behavior for s-maxage.) If the response includes "s- + maxage=0", the proxy MUST always revalidate it before re-using + it. + + 2. If the response includes the "must-revalidate" cache-control + directive, the cache MAY use that response in replying to a + subsequent request. But if the response is stale, all caches + MUST first revalidate it with the origin server, using the + request-headers from the new request to allow the origin server + to authenticate the new request. + + 3. If the response includes the "public" cache-control directive, + it MAY be returned in reply to any subsequent request. + + + + +Fielding, et al. Standards Track [Page 107] + +RFC 2616 HTTP/1.1 June 1999 + + +14.9 Cache-Control + + The Cache-Control general-header field is used to specify directives + that MUST be obeyed by all caching mechanisms along the + request/response chain. The directives specify behavior intended to + prevent caches from adversely interfering with the request or + response. These directives typically override the default caching + algorithms. Cache directives are unidirectional in that the presence + of a directive in a request does not imply that the same directive is + to be given in the response. + + Note that HTTP/1.0 caches might not implement Cache-Control and + might only implement Pragma: no-cache (see section 14.32). + + Cache directives MUST be passed through by a proxy or gateway + application, regardless of their significance to that application, + since the directives might be applicable to all recipients along the + request/response chain. It is not possible to specify a cache- + directive for a specific cache. + + Cache-Control = "Cache-Control" ":" 1#cache-directive + + cache-directive = cache-request-directive + | cache-response-directive + + cache-request-directive = + "no-cache" ; Section 14.9.1 + | "no-store" ; Section 14.9.2 + | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4 + | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3 + | "min-fresh" "=" delta-seconds ; Section 14.9.3 + | "no-transform" ; Section 14.9.5 + | "only-if-cached" ; Section 14.9.4 + | cache-extension ; Section 14.9.6 + + cache-response-directive = + "public" ; Section 14.9.1 + | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1 + | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1 + | "no-store" ; Section 14.9.2 + | "no-transform" ; Section 14.9.5 + | "must-revalidate" ; Section 14.9.4 + | "proxy-revalidate" ; Section 14.9.4 + | "max-age" "=" delta-seconds ; Section 14.9.3 + | "s-maxage" "=" delta-seconds ; Section 14.9.3 + | cache-extension ; Section 14.9.6 + + cache-extension = token [ "=" ( token | quoted-string ) ] + + + +Fielding, et al. Standards Track [Page 108] + +RFC 2616 HTTP/1.1 June 1999 + + + When a directive appears without any 1#field-name parameter, the + directive applies to the entire request or response. When such a + directive appears with a 1#field-name parameter, it applies only to + the named field or fields, and not to the rest of the request or + response. This mechanism supports extensibility; implementations of + future versions of the HTTP protocol might apply these directives to + header fields not defined in HTTP/1.1. + + The cache-control directives can be broken down into these general + categories: + + - Restrictions on what are cacheable; these may only be imposed by + the origin server. + + - Restrictions on what may be stored by a cache; these may be + imposed by either the origin server or the user agent. + + - Modifications of the basic expiration mechanism; these may be + imposed by either the origin server or the user agent. + + - Controls over cache revalidation and reload; these may only be + imposed by a user agent. + + - Control over transformation of entities. + + - Extensions to the caching system. + +14.9.1 What is Cacheable + + By default, a response is cacheable if the requirements of the + request method, request header fields, and the response status + indicate that it is cacheable. Section 13.4 summarizes these defaults + for cacheability. The following Cache-Control response directives + allow an origin server to override the default cacheability of a + response: + + public + Indicates that the response MAY be cached by any cache, even if it + would normally be non-cacheable or cacheable only within a non- + shared cache. (See also Authorization, section 14.8, for + additional details.) + + private + Indicates that all or part of the response message is intended for + a single user and MUST NOT be cached by a shared cache. This + allows an origin server to state that the specified parts of the + + + + + +Fielding, et al. Standards Track [Page 109] + +RFC 2616 HTTP/1.1 June 1999 + + + response are intended for only one user and are not a valid + response for requests by other users. A private (non-shared) cache + MAY cache the response. + + Note: This usage of the word private only controls where the + response may be cached, and cannot ensure the privacy of the + message content. + + no-cache + If the no-cache directive does not specify a field-name, then a + cache MUST NOT use the response to satisfy a subsequent request + without successful revalidation with the origin server. This + allows an origin server to prevent caching even by caches that + have been configured to return stale responses to client requests. + + If the no-cache directive does specify one or more field-names, + then a cache MAY use the response to satisfy a subsequent request, + subject to any other restrictions on caching. However, the + specified field-name(s) MUST NOT be sent in the response to a + subsequent request without successful revalidation with the origin + server. This allows an origin server to prevent the re-use of + certain header fields in a response, while still allowing caching + of the rest of the response. + + Note: Most HTTP/1.0 caches will not recognize or obey this + directive. + +14.9.2 What May be Stored by Caches + + no-store + The purpose of the no-store directive is to prevent the + inadvertent release or retention of sensitive information (for + example, on backup tapes). The no-store directive applies to the + entire message, and MAY be sent either in a response or in a + request. If sent in a request, a cache MUST NOT store any part of + either this request or any response to it. If sent in a response, + a cache MUST NOT store any part of either this response or the + request that elicited it. This directive applies to both non- + shared and shared caches. "MUST NOT store" in this context means + that the cache MUST NOT intentionally store the information in + non-volatile storage, and MUST make a best-effort attempt to + remove the information from volatile storage as promptly as + possible after forwarding it. + + Even when this directive is associated with a response, users + might explicitly store such a response outside of the caching + system (e.g., with a "Save As" dialog). History buffers MAY store + such responses as part of their normal operation. + + + +Fielding, et al. Standards Track [Page 110] + +RFC 2616 HTTP/1.1 June 1999 + + + The purpose of this directive is to meet the stated requirements + of certain users and service authors who are concerned about + accidental releases of information via unanticipated accesses to + cache data structures. While the use of this directive might + improve privacy in some cases, we caution that it is NOT in any + way a reliable or sufficient mechanism for ensuring privacy. In + particular, malicious or compromised caches might not recognize or + obey this directive, and communications networks might be + vulnerable to eavesdropping. + +14.9.3 Modifications of the Basic Expiration Mechanism + + The expiration time of an entity MAY be specified by the origin + server using the Expires header (see section 14.21). Alternatively, + it MAY be specified using the max-age directive in a response. When + the max-age cache-control directive is present in a cached response, + the response is stale if its current age is greater than the age + value given (in seconds) at the time of a new request for that + resource. The max-age directive on a response implies that the + response is cacheable (i.e., "public") unless some other, more + restrictive cache directive is also present. + + If a response includes both an Expires header and a max-age + directive, the max-age directive overrides the Expires header, even + if the Expires header is more restrictive. This rule allows an origin + server to provide, for a given response, a longer expiration time to + an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache. This might be + useful if certain HTTP/1.0 caches improperly calculate ages or + expiration times, perhaps due to desynchronized clocks. + + Many HTTP/1.0 cache implementations will treat an Expires value that + is less than or equal to the response Date value as being equivalent + to the Cache-Control response directive "no-cache". If an HTTP/1.1 + cache receives such a response, and the response does not include a + Cache-Control header field, it SHOULD consider the response to be + non-cacheable in order to retain compatibility with HTTP/1.0 servers. + + Note: An origin server might wish to use a relatively new HTTP + cache control feature, such as the "private" directive, on a + network including older caches that do not understand that + feature. The origin server will need to combine the new feature + with an Expires field whose value is less than or equal to the + Date value. This will prevent older caches from improperly + caching the response. + + + + + + + +Fielding, et al. Standards Track [Page 111] + +RFC 2616 HTTP/1.1 June 1999 + + + s-maxage + If a response includes an s-maxage directive, then for a shared + cache (but not for a private cache), the maximum age specified by + this directive overrides the maximum age specified by either the + max-age directive or the Expires header. The s-maxage directive + also implies the semantics of the proxy-revalidate directive (see + section 14.9.4), i.e., that the shared cache must not use the + entry after it becomes stale to respond to a subsequent request + without first revalidating it with the origin server. The s- + maxage directive is always ignored by a private cache. + + Note that most older caches, not compliant with this specification, + do not implement any cache-control directives. An origin server + wishing to use a cache-control directive that restricts, but does not + prevent, caching by an HTTP/1.1-compliant cache MAY exploit the + requirement that the max-age directive overrides the Expires header, + and the fact that pre-HTTP/1.1-compliant caches do not observe the + max-age directive. + + Other directives allow a user agent to modify the basic expiration + mechanism. These directives MAY be specified on a request: + + max-age + Indicates that the client is willing to accept a response whose + age is no greater than the specified time in seconds. Unless max- + stale directive is also included, the client is not willing to + accept a stale response. + + min-fresh + Indicates that the client is willing to accept a response whose + freshness lifetime is no less than its current age plus the + specified time in seconds. That is, the client wants a response + that will still be fresh for at least the specified number of + seconds. + + max-stale + Indicates that the client is willing to accept a response that has + exceeded its expiration time. If max-stale is assigned a value, + then the client is willing to accept a response that has exceeded + its expiration time by no more than the specified number of + seconds. If no value is assigned to max-stale, then the client is + willing to accept a stale response of any age. + + If a cache returns a stale response, either because of a max-stale + directive on a request, or because the cache is configured to + override the expiration time of a response, the cache MUST attach a + Warning header to the stale response, using Warning 110 (Response is + stale). + + + +Fielding, et al. Standards Track [Page 112] + +RFC 2616 HTTP/1.1 June 1999 + + + A cache MAY be configured to return stale responses without + validation, but only if this does not conflict with any "MUST"-level + requirements concerning cache validation (e.g., a "must-revalidate" + cache-control directive). + + If both the new request and the cached entry include "max-age" + directives, then the lesser of the two values is used for determining + the freshness of the cached entry for that request. + +14.9.4 Cache Revalidation and Reload Controls + + Sometimes a user agent might want or need to insist that a cache + revalidate its cache entry with the origin server (and not just with + the next cache along the path to the origin server), or to reload its + cache entry from the origin server. End-to-end revalidation might be + necessary if either the cache or the origin server has overestimated + the expiration time of the cached response. End-to-end reload may be + necessary if the cache entry has become corrupted for some reason. + + End-to-end revalidation may be requested either when the client does + not have its own local cached copy, in which case we call it + "unspecified end-to-end revalidation", or when the client does have a + local cached copy, in which case we call it "specific end-to-end + revalidation." + + The client can specify these three kinds of action using Cache- + Control request directives: + + End-to-end reload + The request includes a "no-cache" cache-control directive or, for + compatibility with HTTP/1.0 clients, "Pragma: no-cache". Field + names MUST NOT be included with the no-cache directive in a + request. The server MUST NOT use a cached copy when responding to + such a request. + + Specific end-to-end revalidation + The request includes a "max-age=0" cache-control directive, which + forces each cache along the path to the origin server to + revalidate its own entry, if any, with the next cache or server. + The initial request includes a cache-validating conditional with + the client's current validator. + + Unspecified end-to-end revalidation + The request includes "max-age=0" cache-control directive, which + forces each cache along the path to the origin server to + revalidate its own entry, if any, with the next cache or server. + The initial request does not include a cache-validating + + + + +Fielding, et al. Standards Track [Page 113] + +RFC 2616 HTTP/1.1 June 1999 + + + conditional; the first cache along the path (if any) that holds a + cache entry for this resource includes a cache-validating + conditional with its current validator. + + max-age + When an intermediate cache is forced, by means of a max-age=0 + directive, to revalidate its own cache entry, and the client has + supplied its own validator in the request, the supplied validator + might differ from the validator currently stored with the cache + entry. In this case, the cache MAY use either validator in making + its own request without affecting semantic transparency. + + However, the choice of validator might affect performance. The + best approach is for the intermediate cache to use its own + validator when making its request. If the server replies with 304 + (Not Modified), then the cache can return its now validated copy + to the client with a 200 (OK) response. If the server replies with + a new entity and cache validator, however, the intermediate cache + can compare the returned validator with the one provided in the + client's request, using the strong comparison function. If the + client's validator is equal to the origin server's, then the + intermediate cache simply returns 304 (Not Modified). Otherwise, + it returns the new entity with a 200 (OK) response. + + If a request includes the no-cache directive, it SHOULD NOT + include min-fresh, max-stale, or max-age. + + only-if-cached + In some cases, such as times of extremely poor network + connectivity, a client may want a cache to return only those + responses that it currently has stored, and not to reload or + revalidate with the origin server. To do this, the client may + include the only-if-cached directive in a request. If it receives + this directive, a cache SHOULD either respond using a cached entry + that is consistent with the other constraints of the request, or + respond with a 504 (Gateway Timeout) status. However, if a group + of caches is being operated as a unified system with good internal + connectivity, such a request MAY be forwarded within that group of + caches. + + must-revalidate + Because a cache MAY be configured to ignore a server's specified + expiration time, and because a client request MAY include a max- + stale directive (which has a similar effect), the protocol also + includes a mechanism for the origin server to require revalidation + of a cache entry on any subsequent use. When the must-revalidate + directive is present in a response received by a cache, that cache + MUST NOT use the entry after it becomes stale to respond to a + + + +Fielding, et al. Standards Track [Page 114] + +RFC 2616 HTTP/1.1 June 1999 + + + subsequent request without first revalidating it with the origin + server. (I.e., the cache MUST do an end-to-end revalidation every + time, if, based solely on the origin server's Expires or max-age + value, the cached response is stale.) + + The must-revalidate directive is necessary to support reliable + operation for certain protocol features. In all circumstances an + HTTP/1.1 cache MUST obey the must-revalidate directive; in + particular, if the cache cannot reach the origin server for any + reason, it MUST generate a 504 (Gateway Timeout) response. + + Servers SHOULD send the must-revalidate directive if and only if + failure to revalidate a request on the entity could result in + incorrect operation, such as a silently unexecuted financial + transaction. Recipients MUST NOT take any automated action that + violates this directive, and MUST NOT automatically provide an + unvalidated copy of the entity if revalidation fails. + + Although this is not recommended, user agents operating under + severe connectivity constraints MAY violate this directive but, if + so, MUST explicitly warn the user that an unvalidated response has + been provided. The warning MUST be provided on each unvalidated + access, and SHOULD require explicit user confirmation. + + proxy-revalidate + The proxy-revalidate directive has the same meaning as the must- + revalidate directive, except that it does not apply to non-shared + user agent caches. It can be used on a response to an + authenticated request to permit the user's cache to store and + later return the response without needing to revalidate it (since + it has already been authenticated once by that user), while still + requiring proxies that service many users to revalidate each time + (in order to make sure that each user has been authenticated). + Note that such authenticated responses also need the public cache + control directive in order to allow them to be cached at all. + +14.9.5 No-Transform Directive + + no-transform + Implementors of intermediate caches (proxies) have found it useful + to convert the media type of certain entity bodies. A non- + transparent proxy might, for example, convert between image + formats in order to save cache space or to reduce the amount of + traffic on a slow link. + + Serious operational problems occur, however, when these + transformations are applied to entity bodies intended for certain + kinds of applications. For example, applications for medical + + + +Fielding, et al. Standards Track [Page 115] + +RFC 2616 HTTP/1.1 June 1999 + + + imaging, scientific data analysis and those using end-to-end + authentication, all depend on receiving an entity body that is bit + for bit identical to the original entity-body. + + Therefore, if a message includes the no-transform directive, an + intermediate cache or proxy MUST NOT change those headers that are + listed in section 13.5.2 as being subject to the no-transform + directive. This implies that the cache or proxy MUST NOT change + any aspect of the entity-body that is specified by these headers, + including the value of the entity-body itself. + +14.9.6 Cache Control Extensions + + The Cache-Control header field can be extended through the use of one + or more cache-extension tokens, each with an optional assigned value. + Informational extensions (those which do not require a change in + cache behavior) MAY be added without changing the semantics of other + directives. Behavioral extensions are designed to work by acting as + modifiers to the existing base of cache directives. Both the new + directive and the standard directive are supplied, such that + applications which do not understand the new directive will default + to the behavior specified by the standard directive, and those that + understand the new directive will recognize it as modifying the + requirements associated with the standard directive. In this way, + extensions to the cache-control directives can be made without + requiring changes to the base protocol. + + This extension mechanism depends on an HTTP cache obeying all of the + cache-control directives defined for its native HTTP-version, obeying + certain extensions, and ignoring all directives that it does not + understand. + + For example, consider a hypothetical new response directive called + community which acts as a modifier to the private directive. We + define this new directive to mean that, in addition to any non-shared + cache, any cache which is shared only by members of the community + named within its value may cache the response. An origin server + wishing to allow the UCI community to use an otherwise private + response in their shared cache(s) could do so by including + + Cache-Control: private, community="UCI" + + A cache seeing this header field will act correctly even if the cache + does not understand the community cache-extension, since it will also + see and understand the private directive and thus default to the safe + behavior. + + + + + +Fielding, et al. Standards Track [Page 116] + +RFC 2616 HTTP/1.1 June 1999 + + + Unrecognized cache-directives MUST be ignored; it is assumed that any + cache-directive likely to be unrecognized by an HTTP/1.1 cache will + be combined with standard directives (or the response's default + cacheability) such that the cache behavior will remain minimally + correct even if the cache does not understand the extension(s). + +14.10 Connection + + The Connection general-header field allows the sender to specify + options that are desired for that particular connection and MUST NOT + be communicated by proxies over further connections. + + The Connection header has the following grammar: + + Connection = "Connection" ":" 1#(connection-token) + connection-token = token + + HTTP/1.1 proxies MUST parse the Connection header field before a + message is forwarded and, for each connection-token in this field, + remove any header field(s) from the message with the same name as the + connection-token. Connection options are signaled by the presence of + a connection-token in the Connection header field, not by any + corresponding additional header field(s), since the additional header + field may not be sent if there are no parameters associated with that + connection option. + + Message headers listed in the Connection header MUST NOT include + end-to-end headers, such as Cache-Control. + + HTTP/1.1 defines the "close" connection option for the sender to + signal that the connection will be closed after completion of the + response. For example, + + Connection: close + + in either the request or the response header fields indicates that + the connection SHOULD NOT be considered `persistent' (section 8.1) + after the current request/response is complete. + + HTTP/1.1 applications that do not support persistent connections MUST + include the "close" connection option in every message. + + A system receiving an HTTP/1.0 (or lower-version) message that + includes a Connection header MUST, for each connection-token in this + field, remove and ignore any header field(s) from the message with + the same name as the connection-token. This protects against mistaken + forwarding of such header fields by pre-HTTP/1.1 proxies. See section + 19.6.2. + + + +Fielding, et al. Standards Track [Page 117] + +RFC 2616 HTTP/1.1 June 1999 + + +14.11 Content-Encoding + + The Content-Encoding entity-header field is used as a modifier to the + media-type. When present, its value indicates what additional content + codings have been applied to the entity-body, and thus what decoding + mechanisms must be applied in order to obtain the media-type + referenced by the Content-Type header field. Content-Encoding is + primarily used to allow a document to be compressed without losing + the identity of its underlying media type. + + Content-Encoding = "Content-Encoding" ":" 1#content-coding + + Content codings are defined in section 3.5. An example of its use is + + Content-Encoding: gzip + + The content-coding is a characteristic of the entity identified by + the Request-URI. Typically, the entity-body is stored with this + encoding and is only decoded before rendering or analogous usage. + However, a non-transparent proxy MAY modify the content-coding if the + new coding is known to be acceptable to the recipient, unless the + "no-transform" cache-control directive is present in the message. + + If the content-coding of an entity is not "identity", then the + response MUST include a Content-Encoding entity-header (section + 14.11) that lists the non-identity content-coding(s) used. + + If the content-coding of an entity in a request message is not + acceptable to the origin server, the server SHOULD respond with a + status code of 415 (Unsupported Media Type). + + If multiple encodings have been applied to an entity, the content + codings MUST be listed in the order in which they were applied. + Additional information about the encoding parameters MAY be provided + by other entity-header fields not defined by this specification. + +14.12 Content-Language + + The Content-Language entity-header field describes the natural + language(s) of the intended audience for the enclosed entity. Note + that this might not be equivalent to all the languages used within + the entity-body. + + Content-Language = "Content-Language" ":" 1#language-tag + + + + + + + +Fielding, et al. Standards Track [Page 118] + +RFC 2616 HTTP/1.1 June 1999 + + + Language tags are defined in section 3.10. The primary purpose of + Content-Language is to allow a user to identify and differentiate + entities according to the user's own preferred language. Thus, if the + body content is intended only for a Danish-literate audience, the + appropriate field is + + Content-Language: da + + If no Content-Language is specified, the default is that the content + is intended for all language audiences. This might mean that the + sender does not consider it to be specific to any natural language, + or that the sender does not know for which language it is intended. + + Multiple languages MAY be listed for content that is intended for + multiple audiences. For example, a rendition of the "Treaty of + Waitangi," presented simultaneously in the original Maori and English + versions, would call for + + Content-Language: mi, en + + However, just because multiple languages are present within an entity + does not mean that it is intended for multiple linguistic audiences. + An example would be a beginner's language primer, such as "A First + Lesson in Latin," which is clearly intended to be used by an + English-literate audience. In this case, the Content-Language would + properly only include "en". + + Content-Language MAY be applied to any media type -- it is not + limited to textual documents. + +14.13 Content-Length + + The Content-Length entity-header field indicates the size of the + entity-body, in decimal number of OCTETs, sent to the recipient or, + in the case of the HEAD method, the size of the entity-body that + would have been sent had the request been a GET. + + Content-Length = "Content-Length" ":" 1*DIGIT + + An example is + + Content-Length: 3495 + + Applications SHOULD use this field to indicate the transfer-length of + the message-body, unless this is prohibited by the rules in section + 4.4. + + + + + +Fielding, et al. Standards Track [Page 119] + +RFC 2616 HTTP/1.1 June 1999 + + + Any Content-Length greater than or equal to zero is a valid value. + Section 4.4 describes how to determine the length of a message-body + if a Content-Length is not given. + + Note that the meaning of this field is significantly different from + the corresponding definition in MIME, where it is an optional field + used within the "message/external-body" content-type. In HTTP, it + SHOULD be sent whenever the message's length can be determined prior + to being transferred, unless this is prohibited by the rules in + section 4.4. + +14.14 Content-Location + + The Content-Location entity-header field MAY be used to supply the + resource location for the entity enclosed in the message when that + entity is accessible from a location separate from the requested + resource's URI. A server SHOULD provide a Content-Location for the + variant corresponding to the response entity; especially in the case + where a resource has multiple entities associated with it, and those + entities actually have separate locations by which they might be + individually accessed, the server SHOULD provide a Content-Location + for the particular variant which is returned. + + Content-Location = "Content-Location" ":" + ( absoluteURI | relativeURI ) + + The value of Content-Location also defines the base URI for the + entity. + + The Content-Location value is not a replacement for the original + requested URI; it is only a statement of the location of the resource + corresponding to this particular entity at the time of the request. + Future requests MAY specify the Content-Location URI as the request- + URI if the desire is to identify the source of that particular + entity. + + A cache cannot assume that an entity with a Content-Location + different from the URI used to retrieve it can be used to respond to + later requests on that Content-Location URI. However, the Content- + Location can be used to differentiate between multiple entities + retrieved from a single requested resource, as described in section + 13.6. + + If the Content-Location is a relative URI, the relative URI is + interpreted relative to the Request-URI. + + The meaning of the Content-Location header in PUT or POST requests is + undefined; servers are free to ignore it in those cases. + + + +Fielding, et al. Standards Track [Page 120] + +RFC 2616 HTTP/1.1 June 1999 + + +14.15 Content-MD5 + + The Content-MD5 entity-header field, as defined in RFC 1864 [23], is + an MD5 digest of the entity-body for the purpose of providing an + end-to-end message integrity check (MIC) of the entity-body. (Note: a + MIC is good for detecting accidental modification of the entity-body + in transit, but is not proof against malicious attacks.) + + Content-MD5 = "Content-MD5" ":" md5-digest + md5-digest = + + The Content-MD5 header field MAY be generated by an origin server or + client to function as an integrity check of the entity-body. Only + origin servers or clients MAY generate the Content-MD5 header field; + proxies and gateways MUST NOT generate it, as this would defeat its + value as an end-to-end integrity check. Any recipient of the entity- + body, including gateways and proxies, MAY check that the digest value + in this header field matches that of the entity-body as received. + + The MD5 digest is computed based on the content of the entity-body, + including any content-coding that has been applied, but not including + any transfer-encoding applied to the message-body. If the message is + received with a transfer-encoding, that encoding MUST be removed + prior to checking the Content-MD5 value against the received entity. + + This has the result that the digest is computed on the octets of the + entity-body exactly as, and in the order that, they would be sent if + no transfer-encoding were being applied. + + HTTP extends RFC 1864 to permit the digest to be computed for MIME + composite media-types (e.g., multipart/* and message/rfc822), but + this does not change how the digest is computed as defined in the + preceding paragraph. + + There are several consequences of this. The entity-body for composite + types MAY contain many body-parts, each with its own MIME and HTTP + headers (including Content-MD5, Content-Transfer-Encoding, and + Content-Encoding headers). If a body-part has a Content-Transfer- + Encoding or Content-Encoding header, it is assumed that the content + of the body-part has had the encoding applied, and the body-part is + included in the Content-MD5 digest as is -- i.e., after the + application. The Transfer-Encoding header field is not allowed within + body-parts. + + Conversion of all line breaks to CRLF MUST NOT be done before + computing or checking the digest: the line break convention used in + the text actually transmitted MUST be left unaltered when computing + the digest. + + + +Fielding, et al. Standards Track [Page 121] + +RFC 2616 HTTP/1.1 June 1999 + + + Note: while the definition of Content-MD5 is exactly the same for + HTTP as in RFC 1864 for MIME entity-bodies, there are several ways + in which the application of Content-MD5 to HTTP entity-bodies + differs from its application to MIME entity-bodies. One is that + HTTP, unlike MIME, does not use Content-Transfer-Encoding, and + does use Transfer-Encoding and Content-Encoding. Another is that + HTTP more frequently uses binary content types than MIME, so it is + worth noting that, in such cases, the byte order used to compute + the digest is the transmission byte order defined for the type. + Lastly, HTTP allows transmission of text types with any of several + line break conventions and not just the canonical form using CRLF. + +14.16 Content-Range + + The Content-Range entity-header is sent with a partial entity-body to + specify where in the full entity-body the partial body should be + applied. Range units are defined in section 3.12. + + Content-Range = "Content-Range" ":" content-range-spec + + content-range-spec = byte-content-range-spec + byte-content-range-spec = bytes-unit SP + byte-range-resp-spec "/" + ( instance-length | "*" ) + + byte-range-resp-spec = (first-byte-pos "-" last-byte-pos) + | "*" + instance-length = 1*DIGIT + + The header SHOULD indicate the total length of the full entity-body, + unless this length is unknown or difficult to determine. The asterisk + "*" character means that the instance-length is unknown at the time + when the response was generated. + + Unlike byte-ranges-specifier values (see section 14.35.1), a byte- + range-resp-spec MUST only specify one range, and MUST contain + absolute byte positions for both the first and last byte of the + range. + + A byte-content-range-spec with a byte-range-resp-spec whose last- + byte-pos value is less than its first-byte-pos value, or whose + instance-length value is less than or equal to its last-byte-pos + value, is invalid. The recipient of an invalid byte-content-range- + spec MUST ignore it and any content transferred along with it. + + A server sending a response with status code 416 (Requested range not + satisfiable) SHOULD include a Content-Range field with a byte-range- + resp-spec of "*". The instance-length specifies the current length of + + + +Fielding, et al. Standards Track [Page 122] + +RFC 2616 HTTP/1.1 June 1999 + + + the selected resource. A response with status code 206 (Partial + Content) MUST NOT include a Content-Range field with a byte-range- + resp-spec of "*". + + Examples of byte-content-range-spec values, assuming that the entity + contains a total of 1234 bytes: + + . The first 500 bytes: + bytes 0-499/1234 + + . The second 500 bytes: + bytes 500-999/1234 + + . All except for the first 500 bytes: + bytes 500-1233/1234 + + . The last 500 bytes: + bytes 734-1233/1234 + + When an HTTP message includes the content of a single range (for + example, a response to a request for a single range, or to a request + for a set of ranges that overlap without any holes), this content is + transmitted with a Content-Range header, and a Content-Length header + showing the number of bytes actually transferred. For example, + + HTTP/1.1 206 Partial content + Date: Wed, 15 Nov 1995 06:25:24 GMT + Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT + Content-Range: bytes 21010-47021/47022 + Content-Length: 26012 + Content-Type: image/gif + + When an HTTP message includes the content of multiple ranges (for + example, a response to a request for multiple non-overlapping + ranges), these are transmitted as a multipart message. The multipart + media type used for this purpose is "multipart/byteranges" as defined + in appendix 19.2. See appendix 19.6.3 for a compatibility issue. + + A response to a request for a single range MUST NOT be sent using the + multipart/byteranges media type. A response to a request for + multiple ranges, whose result is a single range, MAY be sent as a + multipart/byteranges media type with one part. A client that cannot + decode a multipart/byteranges message MUST NOT ask for multiple + byte-ranges in a single request. + + When a client requests multiple byte-ranges in one request, the + server SHOULD return them in the order that they appeared in the + request. + + + +Fielding, et al. Standards Track [Page 123] + +RFC 2616 HTTP/1.1 June 1999 + + + If the server ignores a byte-range-spec because it is syntactically + invalid, the server SHOULD treat the request as if the invalid Range + header field did not exist. (Normally, this means return a 200 + response containing the full entity). + + If the server receives a request (other than one including an If- + Range request-header field) with an unsatisfiable Range request- + header field (that is, all of whose byte-range-spec values have a + first-byte-pos value greater than the current length of the selected + resource), it SHOULD return a response code of 416 (Requested range + not satisfiable) (section 10.4.17). + + Note: clients cannot depend on servers to send a 416 (Requested + range not satisfiable) response instead of a 200 (OK) response for + an unsatisfiable Range request-header, since not all servers + implement this request-header. + +14.17 Content-Type + + The Content-Type entity-header field indicates the media type of the + entity-body sent to the recipient or, in the case of the HEAD method, + the media type that would have been sent had the request been a GET. + + Content-Type = "Content-Type" ":" media-type + + Media types are defined in section 3.7. An example of the field is + + Content-Type: text/html; charset=ISO-8859-4 + + Further discussion of methods for identifying the media type of an + entity is provided in section 7.2.1. + +14.18 Date + + The Date general-header field represents the date and time at which + the message was originated, having the same semantics as orig-date in + RFC 822. The field value is an HTTP-date, as described in section + 3.3.1; it MUST be sent in RFC 1123 [8]-date format. + + Date = "Date" ":" HTTP-date + + An example is + + Date: Tue, 15 Nov 1994 08:12:31 GMT + + Origin servers MUST include a Date header field in all responses, + except in these cases: + + + + +Fielding, et al. Standards Track [Page 124] + +RFC 2616 HTTP/1.1 June 1999 + + + 1. If the response status code is 100 (Continue) or 101 (Switching + Protocols), the response MAY include a Date header field, at + the server's option. + + 2. If the response status code conveys a server error, e.g. 500 + (Internal Server Error) or 503 (Service Unavailable), and it is + inconvenient or impossible to generate a valid Date. + + 3. If the server does not have a clock that can provide a + reasonable approximation of the current time, its responses + MUST NOT include a Date header field. In this case, the rules + in section 14.18.1 MUST be followed. + + A received message that does not have a Date header field MUST be + assigned one by the recipient if the message will be cached by that + recipient or gatewayed via a protocol which requires a Date. An HTTP + implementation without a clock MUST NOT cache responses without + revalidating them on every use. An HTTP cache, especially a shared + cache, SHOULD use a mechanism, such as NTP [28], to synchronize its + clock with a reliable external standard. + + Clients SHOULD only send a Date header field in messages that include + an entity-body, as in the case of the PUT and POST requests, and even + then it is optional. A client without a clock MUST NOT send a Date + header field in a request. + + The HTTP-date sent in a Date header SHOULD NOT represent a date and + time subsequent to the generation of the message. It SHOULD represent + the best available approximation of the date and time of message + generation, unless the implementation has no means of generating a + reasonably accurate date and time. In theory, the date ought to + represent the moment just before the entity is generated. In + practice, the date can be generated at any time during the message + origination without affecting its semantic value. + +14.18.1 Clockless Origin Server Operation + + Some origin server implementations might not have a clock available. + An origin server without a clock MUST NOT assign Expires or Last- + Modified values to a response, unless these values were associated + with the resource by a system or user with a reliable clock. It MAY + assign an Expires value that is known, at or before server + configuration time, to be in the past (this allows "pre-expiration" + of responses without storing separate Expires values for each + resource). + + + + + + +Fielding, et al. Standards Track [Page 125] + +RFC 2616 HTTP/1.1 June 1999 + + +14.19 ETag + + The ETag response-header field provides the current value of the + entity tag for the requested variant. The headers used with entity + tags are described in sections 14.24, 14.26 and 14.44. The entity tag + MAY be used for comparison with other entities from the same resource + (see section 13.3.3). + + ETag = "ETag" ":" entity-tag + + Examples: + + ETag: "xyzzy" + ETag: W/"xyzzy" + ETag: "" + +14.20 Expect + + The Expect request-header field is used to indicate that particular + server behaviors are required by the client. + + Expect = "Expect" ":" 1#expectation + + expectation = "100-continue" | expectation-extension + expectation-extension = token [ "=" ( token | quoted-string ) + *expect-params ] + expect-params = ";" token [ "=" ( token | quoted-string ) ] + + + A server that does not understand or is unable to comply with any of + the expectation values in the Expect field of a request MUST respond + with appropriate error status. The server MUST respond with a 417 + (Expectation Failed) status if any of the expectations cannot be met + or, if there are other problems with the request, some other 4xx + status. + + This header field is defined with extensible syntax to allow for + future extensions. If a server receives a request containing an + Expect field that includes an expectation-extension that it does not + support, it MUST respond with a 417 (Expectation Failed) status. + + Comparison of expectation values is case-insensitive for unquoted + tokens (including the 100-continue token), and is case-sensitive for + quoted-string expectation-extensions. + + + + + + + +Fielding, et al. Standards Track [Page 126] + +RFC 2616 HTTP/1.1 June 1999 + + + The Expect mechanism is hop-by-hop: that is, an HTTP/1.1 proxy MUST + return a 417 (Expectation Failed) status if it receives a request + with an expectation that it cannot meet. However, the Expect + request-header itself is end-to-end; it MUST be forwarded if the + request is forwarded. + + Many older HTTP/1.0 and HTTP/1.1 applications do not understand the + Expect header. + + See section 8.2.3 for the use of the 100 (continue) status. + +14.21 Expires + + The Expires entity-header field gives the date/time after which the + response is considered stale. A stale cache entry may not normally be + returned by a cache (either a proxy cache or a user agent cache) + unless it is first validated with the origin server (or with an + intermediate cache that has a fresh copy of the entity). See section + 13.2 for further discussion of the expiration model. + + The presence of an Expires field does not imply that the original + resource will change or cease to exist at, before, or after that + time. + + The format is an absolute date and time as defined by HTTP-date in + section 3.3.1; it MUST be in RFC 1123 date format: + + Expires = "Expires" ":" HTTP-date + + An example of its use is + + Expires: Thu, 01 Dec 1994 16:00:00 GMT + + Note: if a response includes a Cache-Control field with the max- + age directive (see section 14.9.3), that directive overrides the + Expires field. + + HTTP/1.1 clients and caches MUST treat other invalid date formats, + especially including the value "0", as in the past (i.e., "already + expired"). + + To mark a response as "already expired," an origin server sends an + Expires date that is equal to the Date header value. (See the rules + for expiration calculations in section 13.2.4.) + + + + + + + +Fielding, et al. Standards Track [Page 127] + +RFC 2616 HTTP/1.1 June 1999 + + + To mark a response as "never expires," an origin server sends an + Expires date approximately one year from the time the response is + sent. HTTP/1.1 servers SHOULD NOT send Expires dates more than one + year in the future. + + The presence of an Expires header field with a date value of some + time in the future on a response that otherwise would by default be + non-cacheable indicates that the response is cacheable, unless + indicated otherwise by a Cache-Control header field (section 14.9). + +14.22 From + + The From request-header field, if given, SHOULD contain an Internet + e-mail address for the human user who controls the requesting user + agent. The address SHOULD be machine-usable, as defined by "mailbox" + in RFC 822 [9] as updated by RFC 1123 [8]: + + From = "From" ":" mailbox + + An example is: + + From: webmaster@w3.org + + This header field MAY be used for logging purposes and as a means for + identifying the source of invalid or unwanted requests. It SHOULD NOT + be used as an insecure form of access protection. The interpretation + of this field is that the request is being performed on behalf of the + person given, who accepts responsibility for the method performed. In + particular, robot agents SHOULD include this header so that the + person responsible for running the robot can be contacted if problems + occur on the receiving end. + + The Internet e-mail address in this field MAY be separate from the + Internet host which issued the request. For example, when a request + is passed through a proxy the original issuer's address SHOULD be + used. + + The client SHOULD NOT send the From header field without the user's + approval, as it might conflict with the user's privacy interests or + their site's security policy. It is strongly recommended that the + user be able to disable, enable, and modify the value of this field + at any time prior to a request. + +14.23 Host + + The Host request-header field specifies the Internet host and port + number of the resource being requested, as obtained from the original + URI given by the user or referring resource (generally an HTTP URL, + + + +Fielding, et al. Standards Track [Page 128] + +RFC 2616 HTTP/1.1 June 1999 + + + as described in section 3.2.2). The Host field value MUST represent + the naming authority of the origin server or gateway given by the + original URL. This allows the origin server or gateway to + differentiate between internally-ambiguous URLs, such as the root "/" + URL of a server for multiple host names on a single IP address. + + Host = "Host" ":" host [ ":" port ] ; Section 3.2.2 + + A "host" without any trailing port information implies the default + port for the service requested (e.g., "80" for an HTTP URL). For + example, a request on the origin server for + would properly include: + + GET /pub/WWW/ HTTP/1.1 + Host: www.w3.org + + A client MUST include a Host header field in all HTTP/1.1 request + messages . If the requested URI does not include an Internet host + name for the service being requested, then the Host header field MUST + be given with an empty value. An HTTP/1.1 proxy MUST ensure that any + request message it forwards does contain an appropriate Host header + field that identifies the service being requested by the proxy. All + Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request) + status code to any HTTP/1.1 request message which lacks a Host header + field. + + See sections 5.2 and 19.6.1.1 for other requirements relating to + Host. + +14.24 If-Match + + The If-Match request-header field is used with a method to make it + conditional. A client that has one or more entities previously + obtained from the resource can verify that one of those entities is + current by including a list of their associated entity tags in the + If-Match header field. Entity tags are defined in section 3.11. The + purpose of this feature is to allow efficient updates of cached + information with a minimum amount of transaction overhead. It is also + used, on updating requests, to prevent inadvertent modification of + the wrong version of a resource. As a special case, the value "*" + matches any current entity of the resource. + + If-Match = "If-Match" ":" ( "*" | 1#entity-tag ) + + If any of the entity tags match the entity tag of the entity that + would have been returned in the response to a similar GET request + (without the If-Match header) on that resource, or if "*" is given + + + + +Fielding, et al. Standards Track [Page 129] + +RFC 2616 HTTP/1.1 June 1999 + + + and any current entity exists for that resource, then the server MAY + perform the requested method as if the If-Match header field did not + exist. + + A server MUST use the strong comparison function (see section 13.3.3) + to compare the entity tags in If-Match. + + If none of the entity tags match, or if "*" is given and no current + entity exists, the server MUST NOT perform the requested method, and + MUST return a 412 (Precondition Failed) response. This behavior is + most useful when the client wants to prevent an updating method, such + as PUT, from modifying a resource that has changed since the client + last retrieved it. + + If the request would, without the If-Match header field, result in + anything other than a 2xx or 412 status, then the If-Match header + MUST be ignored. + + The meaning of "If-Match: *" is that the method SHOULD be performed + if the representation selected by the origin server (or by a cache, + possibly using the Vary mechanism, see section 14.44) exists, and + MUST NOT be performed if the representation does not exist. + + A request intended to update a resource (e.g., a PUT) MAY include an + If-Match header field to signal that the request method MUST NOT be + applied if the entity corresponding to the If-Match value (a single + entity tag) is no longer a representation of that resource. This + allows the user to indicate that they do not wish the request to be + successful if the resource has been changed without their knowledge. + Examples: + + If-Match: "xyzzy" + If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz" + If-Match: * + + The result of a request having both an If-Match header field and + either an If-None-Match or an If-Modified-Since header fields is + undefined by this specification. + +14.25 If-Modified-Since + + The If-Modified-Since request-header field is used with a method to + make it conditional: if the requested variant has not been modified + since the time specified in this field, an entity will not be + returned from the server; instead, a 304 (not modified) response will + be returned without any message-body. + + If-Modified-Since = "If-Modified-Since" ":" HTTP-date + + + +Fielding, et al. Standards Track [Page 130] + +RFC 2616 HTTP/1.1 June 1999 + + + An example of the field is: + + If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT + + A GET method with an If-Modified-Since header and no Range header + requests that the identified entity be transferred only if it has + been modified since the date given by the If-Modified-Since header. + The algorithm for determining this includes the following cases: + + a) If the request would normally result in anything other than a + 200 (OK) status, or if the passed If-Modified-Since date is + invalid, the response is exactly the same as for a normal GET. + A date which is later than the server's current time is + invalid. + + b) If the variant has been modified since the If-Modified-Since + date, the response is exactly the same as for a normal GET. + + c) If the variant has not been modified since a valid If- + Modified-Since date, the server SHOULD return a 304 (Not + Modified) response. + + The purpose of this feature is to allow efficient updates of cached + information with a minimum amount of transaction overhead. + + Note: The Range request-header field modifies the meaning of If- + Modified-Since; see section 14.35 for full details. + + Note: If-Modified-Since times are interpreted by the server, whose + clock might not be synchronized with the client. + + Note: When handling an If-Modified-Since header field, some + servers will use an exact date comparison function, rather than a + less-than function, for deciding whether to send a 304 (Not + Modified) response. To get best results when sending an If- + Modified-Since header field for cache validation, clients are + advised to use the exact date string received in a previous Last- + Modified header field whenever possible. + + Note: If a client uses an arbitrary date in the If-Modified-Since + header instead of a date taken from the Last-Modified header for + the same request, the client should be aware of the fact that this + date is interpreted in the server's understanding of time. The + client should consider unsynchronized clocks and rounding problems + due to the different encodings of time between the client and + server. This includes the possibility of race conditions if the + document has changed between the time it was first requested and + the If-Modified-Since date of a subsequent request, and the + + + +Fielding, et al. Standards Track [Page 131] + +RFC 2616 HTTP/1.1 June 1999 + + + possibility of clock-skew-related problems if the If-Modified- + Since date is derived from the client's clock without correction + to the server's clock. Corrections for different time bases + between client and server are at best approximate due to network + latency. + + The result of a request having both an If-Modified-Since header field + and either an If-Match or an If-Unmodified-Since header fields is + undefined by this specification. + +14.26 If-None-Match + + The If-None-Match request-header field is used with a method to make + it conditional. A client that has one or more entities previously + obtained from the resource can verify that none of those entities is + current by including a list of their associated entity tags in the + If-None-Match header field. The purpose of this feature is to allow + efficient updates of cached information with a minimum amount of + transaction overhead. It is also used to prevent a method (e.g. PUT) + from inadvertently modifying an existing resource when the client + believes that the resource does not exist. + + As a special case, the value "*" matches any current entity of the + resource. + + If-None-Match = "If-None-Match" ":" ( "*" | 1#entity-tag ) + + If any of the entity tags match the entity tag of the entity that + would have been returned in the response to a similar GET request + (without the If-None-Match header) on that resource, or if "*" is + given and any current entity exists for that resource, then the + server MUST NOT perform the requested method, unless required to do + so because the resource's modification date fails to match that + supplied in an If-Modified-Since header field in the request. + Instead, if the request method was GET or HEAD, the server SHOULD + respond with a 304 (Not Modified) response, including the cache- + related header fields (particularly ETag) of one of the entities that + matched. For all other request methods, the server MUST respond with + a status of 412 (Precondition Failed). + + See section 13.3.3 for rules on how to determine if two entities tags + match. The weak comparison function can only be used with GET or HEAD + requests. + + + + + + + + +Fielding, et al. Standards Track [Page 132] + +RFC 2616 HTTP/1.1 June 1999 + + + If none of the entity tags match, then the server MAY perform the + requested method as if the If-None-Match header field did not exist, + but MUST also ignore any If-Modified-Since header field(s) in the + request. That is, if no entity tags match, then the server MUST NOT + return a 304 (Not Modified) response. + + If the request would, without the If-None-Match header field, result + in anything other than a 2xx or 304 status, then the If-None-Match + header MUST be ignored. (See section 13.3.4 for a discussion of + server behavior when both If-Modified-Since and If-None-Match appear + in the same request.) + + The meaning of "If-None-Match: *" is that the method MUST NOT be + performed if the representation selected by the origin server (or by + a cache, possibly using the Vary mechanism, see section 14.44) + exists, and SHOULD be performed if the representation does not exist. + This feature is intended to be useful in preventing races between PUT + operations. + + Examples: + + If-None-Match: "xyzzy" + If-None-Match: W/"xyzzy" + If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz" + If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz" + If-None-Match: * + + The result of a request having both an If-None-Match header field and + either an If-Match or an If-Unmodified-Since header fields is + undefined by this specification. + +14.27 If-Range + + If a client has a partial copy of an entity in its cache, and wishes + to have an up-to-date copy of the entire entity in its cache, it + could use the Range request-header with a conditional GET (using + either or both of If-Unmodified-Since and If-Match.) However, if the + condition fails because the entity has been modified, the client + would then have to make a second request to obtain the entire current + entity-body. + + The If-Range header allows a client to "short-circuit" the second + request. Informally, its meaning is `if the entity is unchanged, send + me the part(s) that I am missing; otherwise, send me the entire new + entity'. + + If-Range = "If-Range" ":" ( entity-tag | HTTP-date ) + + + + +Fielding, et al. Standards Track [Page 133] + +RFC 2616 HTTP/1.1 June 1999 + + + If the client has no entity tag for an entity, but does have a Last- + Modified date, it MAY use that date in an If-Range header. (The + server can distinguish between a valid HTTP-date and any form of + entity-tag by examining no more than two characters.) The If-Range + header SHOULD only be used together with a Range header, and MUST be + ignored if the request does not include a Range header, or if the + server does not support the sub-range operation. + + If the entity tag given in the If-Range header matches the current + entity tag for the entity, then the server SHOULD provide the + specified sub-range of the entity using a 206 (Partial content) + response. If the entity tag does not match, then the server SHOULD + return the entire entity using a 200 (OK) response. + +14.28 If-Unmodified-Since + + The If-Unmodified-Since request-header field is used with a method to + make it conditional. If the requested resource has not been modified + since the time specified in this field, the server SHOULD perform the + requested operation as if the If-Unmodified-Since header were not + present. + + If the requested variant has been modified since the specified time, + the server MUST NOT perform the requested operation, and MUST return + a 412 (Precondition Failed). + + If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date + + An example of the field is: + + If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT + + If the request normally (i.e., without the If-Unmodified-Since + header) would result in anything other than a 2xx or 412 status, the + If-Unmodified-Since header SHOULD be ignored. + + If the specified date is invalid, the header is ignored. + + The result of a request having both an If-Unmodified-Since header + field and either an If-None-Match or an If-Modified-Since header + fields is undefined by this specification. + +14.29 Last-Modified + + The Last-Modified entity-header field indicates the date and time at + which the origin server believes the variant was last modified. + + Last-Modified = "Last-Modified" ":" HTTP-date + + + +Fielding, et al. Standards Track [Page 134] + +RFC 2616 HTTP/1.1 June 1999 + + + An example of its use is + + Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT + + The exact meaning of this header field depends on the implementation + of the origin server and the nature of the original resource. For + files, it may be just the file system last-modified time. For + entities with dynamically included parts, it may be the most recent + of the set of last-modify times for its component parts. For database + gateways, it may be the last-update time stamp of the record. For + virtual objects, it may be the last time the internal state changed. + + An origin server MUST NOT send a Last-Modified date which is later + than the server's time of message origination. In such cases, where + the resource's last modification would indicate some time in the + future, the server MUST replace that date with the message + origination date. + + An origin server SHOULD obtain the Last-Modified value of the entity + as close as possible to the time that it generates the Date value of + its response. This allows a recipient to make an accurate assessment + of the entity's modification time, especially if the entity changes + near the time that the response is generated. + + HTTP/1.1 servers SHOULD send Last-Modified whenever feasible. + +14.30 Location + + The Location response-header field is used to redirect the recipient + to a location other than the Request-URI for completion of the + request or identification of a new resource. For 201 (Created) + responses, the Location is that of the new resource which was created + by the request. For 3xx responses, the location SHOULD indicate the + server's preferred URI for automatic redirection to the resource. The + field value consists of a single absolute URI. + + Location = "Location" ":" absoluteURI + + An example is: + + Location: http://www.w3.org/pub/WWW/People.html + + Note: The Content-Location header field (section 14.14) differs + from Location in that the Content-Location identifies the original + location of the entity enclosed in the request. It is therefore + possible for a response to contain header fields for both Location + and Content-Location. Also see section 13.10 for cache + requirements of some methods. + + + +Fielding, et al. Standards Track [Page 135] + +RFC 2616 HTTP/1.1 June 1999 + + +14.31 Max-Forwards + + The Max-Forwards request-header field provides a mechanism with the + TRACE (section 9.8) and OPTIONS (section 9.2) methods to limit the + number of proxies or gateways that can forward the request to the + next inbound server. This can be useful when the client is attempting + to trace a request chain which appears to be failing or looping in + mid-chain. + + Max-Forwards = "Max-Forwards" ":" 1*DIGIT + + The Max-Forwards value is a decimal integer indicating the remaining + number of times this request message may be forwarded. + + Each proxy or gateway recipient of a TRACE or OPTIONS request + containing a Max-Forwards header field MUST check and update its + value prior to forwarding the request. If the received value is zero + (0), the recipient MUST NOT forward the request; instead, it MUST + respond as the final recipient. If the received Max-Forwards value is + greater than zero, then the forwarded message MUST contain an updated + Max-Forwards field with a value decremented by one (1). + + The Max-Forwards header field MAY be ignored for all other methods + defined by this specification and for any extension methods for which + it is not explicitly referred to as part of that method definition. + +14.32 Pragma + + The Pragma general-header field is used to include implementation- + specific directives that might apply to any recipient along the + request/response chain. All pragma directives specify optional + behavior from the viewpoint of the protocol; however, some systems + MAY require that behavior be consistent with the directives. + + Pragma = "Pragma" ":" 1#pragma-directive + pragma-directive = "no-cache" | extension-pragma + extension-pragma = token [ "=" ( token | quoted-string ) ] + + When the no-cache directive is present in a request message, an + application SHOULD forward the request toward the origin server even + if it has a cached copy of what is being requested. This pragma + directive has the same semantics as the no-cache cache-directive (see + section 14.9) and is defined here for backward compatibility with + HTTP/1.0. Clients SHOULD include both header fields when a no-cache + request is sent to a server not known to be HTTP/1.1 compliant. + + + + + + +Fielding, et al. Standards Track [Page 136] + +RFC 2616 HTTP/1.1 June 1999 + + + Pragma directives MUST be passed through by a proxy or gateway + application, regardless of their significance to that application, + since the directives might be applicable to all recipients along the + request/response chain. It is not possible to specify a pragma for a + specific recipient; however, any pragma directive not relevant to a + recipient SHOULD be ignored by that recipient. + + HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client had + sent "Cache-Control: no-cache". No new Pragma directives will be + defined in HTTP. + + Note: because the meaning of "Pragma: no-cache as a response + header field is not actually specified, it does not provide a + reliable replacement for "Cache-Control: no-cache" in a response + +14.33 Proxy-Authenticate + + The Proxy-Authenticate response-header field MUST be included as part + of a 407 (Proxy Authentication Required) response. The field value + consists of a challenge that indicates the authentication scheme and + parameters applicable to the proxy for this Request-URI. + + Proxy-Authenticate = "Proxy-Authenticate" ":" 1#challenge + + The HTTP access authentication process is described in "HTTP + Authentication: Basic and Digest Access Authentication" [43]. Unlike + WWW-Authenticate, the Proxy-Authenticate header field applies only to + the current connection and SHOULD NOT be passed on to downstream + clients. However, an intermediate proxy might need to obtain its own + credentials by requesting them from the downstream client, which in + some circumstances will appear as if the proxy is forwarding the + Proxy-Authenticate header field. + +14.34 Proxy-Authorization + + The Proxy-Authorization request-header field allows the client to + identify itself (or its user) to a proxy which requires + authentication. The Proxy-Authorization field value consists of + credentials containing the authentication information of the user + agent for the proxy and/or realm of the resource being requested. + + Proxy-Authorization = "Proxy-Authorization" ":" credentials + + The HTTP access authentication process is described in "HTTP + Authentication: Basic and Digest Access Authentication" [43] . Unlike + Authorization, the Proxy-Authorization header field applies only to + the next outbound proxy that demanded authentication using the Proxy- + Authenticate field. When multiple proxies are used in a chain, the + + + +Fielding, et al. Standards Track [Page 137] + +RFC 2616 HTTP/1.1 June 1999 + + + Proxy-Authorization header field is consumed by the first outbound + proxy that was expecting to receive credentials. A proxy MAY relay + the credentials from the client request to the next proxy if that is + the mechanism by which the proxies cooperatively authenticate a given + request. + +14.35 Range + +14.35.1 Byte Ranges + + Since all HTTP entities are represented in HTTP messages as sequences + of bytes, the concept of a byte range is meaningful for any HTTP + entity. (However, not all clients and servers need to support byte- + range operations.) + + Byte range specifications in HTTP apply to the sequence of bytes in + the entity-body (not necessarily the same as the message-body). + + A byte range operation MAY specify a single range of bytes, or a set + of ranges within a single entity. + + ranges-specifier = byte-ranges-specifier + byte-ranges-specifier = bytes-unit "=" byte-range-set + byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec ) + byte-range-spec = first-byte-pos "-" [last-byte-pos] + first-byte-pos = 1*DIGIT + last-byte-pos = 1*DIGIT + + The first-byte-pos value in a byte-range-spec gives the byte-offset + of the first byte in a range. The last-byte-pos value gives the + byte-offset of the last byte in the range; that is, the byte + positions specified are inclusive. Byte offsets start at zero. + + If the last-byte-pos value is present, it MUST be greater than or + equal to the first-byte-pos in that byte-range-spec, or the byte- + range-spec is syntactically invalid. The recipient of a byte-range- + set that includes one or more syntactically invalid byte-range-spec + values MUST ignore the header field that includes that byte-range- + set. + + If the last-byte-pos value is absent, or if the value is greater than + or equal to the current length of the entity-body, last-byte-pos is + taken to be equal to one less than the current length of the entity- + body in bytes. + + By its choice of last-byte-pos, a client can limit the number of + bytes retrieved without knowing the size of the entity. + + + + +Fielding, et al. Standards Track [Page 138] + +RFC 2616 HTTP/1.1 June 1999 + + + suffix-byte-range-spec = "-" suffix-length + suffix-length = 1*DIGIT + + A suffix-byte-range-spec is used to specify the suffix of the + entity-body, of a length given by the suffix-length value. (That is, + this form specifies the last N bytes of an entity-body.) If the + entity is shorter than the specified suffix-length, the entire + entity-body is used. + + If a syntactically valid byte-range-set includes at least one byte- + range-spec whose first-byte-pos is less than the current length of + the entity-body, or at least one suffix-byte-range-spec with a non- + zero suffix-length, then the byte-range-set is satisfiable. + Otherwise, the byte-range-set is unsatisfiable. If the byte-range-set + is unsatisfiable, the server SHOULD return a response with a status + of 416 (Requested range not satisfiable). Otherwise, the server + SHOULD return a response with a status of 206 (Partial Content) + containing the satisfiable ranges of the entity-body. + + Examples of byte-ranges-specifier values (assuming an entity-body of + length 10000): + + - The first 500 bytes (byte offsets 0-499, inclusive): bytes=0- + 499 + + - The second 500 bytes (byte offsets 500-999, inclusive): + bytes=500-999 + + - The final 500 bytes (byte offsets 9500-9999, inclusive): + bytes=-500 + + - Or bytes=9500- + + - The first and last bytes only (bytes 0 and 9999): bytes=0-0,-1 + + - Several legal but not canonical specifications of the second 500 + bytes (byte offsets 500-999, inclusive): + bytes=500-600,601-999 + bytes=500-700,601-999 + +14.35.2 Range Retrieval Requests + + HTTP retrieval requests using conditional or unconditional GET + methods MAY request one or more sub-ranges of the entity, instead of + the entire entity, using the Range request header, which applies to + the entity returned as the result of the request: + + Range = "Range" ":" ranges-specifier + + + +Fielding, et al. Standards Track [Page 139] + +RFC 2616 HTTP/1.1 June 1999 + + + A server MAY ignore the Range header. However, HTTP/1.1 origin + servers and intermediate caches ought to support byte ranges when + possible, since Range supports efficient recovery from partially + failed transfers, and supports efficient partial retrieval of large + entities. + + If the server supports the Range header and the specified range or + ranges are appropriate for the entity: + + - The presence of a Range header in an unconditional GET modifies + what is returned if the GET is otherwise successful. In other + words, the response carries a status code of 206 (Partial + Content) instead of 200 (OK). + + - The presence of a Range header in a conditional GET (a request + using one or both of If-Modified-Since and If-None-Match, or + one or both of If-Unmodified-Since and If-Match) modifies what + is returned if the GET is otherwise successful and the + condition is true. It does not affect the 304 (Not Modified) + response returned if the conditional is false. + + In some cases, it might be more appropriate to use the If-Range + header (see section 14.27) in addition to the Range header. + + If a proxy that supports ranges receives a Range request, forwards + the request to an inbound server, and receives an entire entity in + reply, it SHOULD only return the requested range to its client. It + SHOULD store the entire received response in its cache if that is + consistent with its cache allocation policies. + +14.36 Referer + + The Referer[sic] request-header field allows the client to specify, + for the server's benefit, the address (URI) of the resource from + which the Request-URI was obtained (the "referrer", although the + header field is misspelled.) The Referer request-header allows a + server to generate lists of back-links to resources for interest, + logging, optimized caching, etc. It also allows obsolete or mistyped + links to be traced for maintenance. The Referer field MUST NOT be + sent if the Request-URI was obtained from a source that does not have + its own URI, such as input from the user keyboard. + + Referer = "Referer" ":" ( absoluteURI | relativeURI ) + + Example: + + Referer: http://www.w3.org/hypertext/DataSources/Overview.html + + + + +Fielding, et al. Standards Track [Page 140] + +RFC 2616 HTTP/1.1 June 1999 + + + If the field value is a relative URI, it SHOULD be interpreted + relative to the Request-URI. The URI MUST NOT include a fragment. See + section 15.1.3 for security considerations. + +14.37 Retry-After + + The Retry-After response-header field can be used with a 503 (Service + Unavailable) response to indicate how long the service is expected to + be unavailable to the requesting client. This field MAY also be used + with any 3xx (Redirection) response to indicate the minimum time the + user-agent is asked wait before issuing the redirected request. The + value of this field can be either an HTTP-date or an integer number + of seconds (in decimal) after the time of the response. + + Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) + + Two examples of its use are + + Retry-After: Fri, 31 Dec 1999 23:59:59 GMT + Retry-After: 120 + + In the latter example, the delay is 2 minutes. + +14.38 Server + + The Server response-header field contains information about the + software used by the origin server to handle the request. The field + can contain multiple product tokens (section 3.8) and comments + identifying the server and any significant subproducts. The product + tokens are listed in order of their significance for identifying the + application. + + Server = "Server" ":" 1*( product | comment ) + + Example: + + Server: CERN/3.0 libwww/2.17 + + If the response is being forwarded through a proxy, the proxy + application MUST NOT modify the Server response-header. Instead, it + SHOULD include a Via field (as described in section 14.45). + + Note: Revealing the specific software version of the server might + allow the server machine to become more vulnerable to attacks + against software that is known to contain security holes. Server + implementors are encouraged to make this field a configurable + option. + + + + +Fielding, et al. Standards Track [Page 141] + +RFC 2616 HTTP/1.1 June 1999 + + +14.39 TE + + The TE request-header field indicates what extension transfer-codings + it is willing to accept in the response and whether or not it is + willing to accept trailer fields in a chunked transfer-coding. Its + value may consist of the keyword "trailers" and/or a comma-separated + list of extension transfer-coding names with optional accept + parameters (as described in section 3.6). + + TE = "TE" ":" #( t-codings ) + t-codings = "trailers" | ( transfer-extension [ accept-params ] ) + + The presence of the keyword "trailers" indicates that the client is + willing to accept trailer fields in a chunked transfer-coding, as + defined in section 3.6.1. This keyword is reserved for use with + transfer-coding values even though it does not itself represent a + transfer-coding. + + Examples of its use are: + + TE: deflate + TE: + TE: trailers, deflate;q=0.5 + + The TE header field only applies to the immediate connection. + Therefore, the keyword MUST be supplied within a Connection header + field (section 14.10) whenever TE is present in an HTTP/1.1 message. + + A server tests whether a transfer-coding is acceptable, according to + a TE field, using these rules: + + 1. The "chunked" transfer-coding is always acceptable. If the + keyword "trailers" is listed, the client indicates that it is + willing to accept trailer fields in the chunked response on + behalf of itself and any downstream clients. The implication is + that, if given, the client is stating that either all + downstream clients are willing to accept trailer fields in the + forwarded response, or that it will attempt to buffer the + response on behalf of downstream recipients. + + Note: HTTP/1.1 does not define any means to limit the size of a + chunked response such that a client can be assured of buffering + the entire response. + + 2. If the transfer-coding being tested is one of the transfer- + codings listed in the TE field, then it is acceptable unless it + is accompanied by a qvalue of 0. (As defined in section 3.9, a + qvalue of 0 means "not acceptable.") + + + +Fielding, et al. Standards Track [Page 142] + +RFC 2616 HTTP/1.1 June 1999 + + + 3. If multiple transfer-codings are acceptable, then the + acceptable transfer-coding with the highest non-zero qvalue is + preferred. The "chunked" transfer-coding always has a qvalue + of 1. + + If the TE field-value is empty or if no TE field is present, the only + transfer-coding is "chunked". A message with no transfer-coding is + always acceptable. + +14.40 Trailer + + The Trailer general field value indicates that the given set of + header fields is present in the trailer of a message encoded with + chunked transfer-coding. + + Trailer = "Trailer" ":" 1#field-name + + An HTTP/1.1 message SHOULD include a Trailer header field in a + message using chunked transfer-coding with a non-empty trailer. Doing + so allows the recipient to know which header fields to expect in the + trailer. + + If no Trailer header field is present, the trailer SHOULD NOT include + any header fields. See section 3.6.1 for restrictions on the use of + trailer fields in a "chunked" transfer-coding. + + Message header fields listed in the Trailer header field MUST NOT + include the following header fields: + + . Transfer-Encoding + + . Content-Length + + . Trailer + +14.41 Transfer-Encoding + + The Transfer-Encoding general-header field indicates what (if any) + type of transformation has been applied to the message body in order + to safely transfer it between the sender and the recipient. This + differs from the content-coding in that the transfer-coding is a + property of the message, not of the entity. + + Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding + + Transfer-codings are defined in section 3.6. An example is: + + Transfer-Encoding: chunked + + + +Fielding, et al. Standards Track [Page 143] + +RFC 2616 HTTP/1.1 June 1999 + + + If multiple encodings have been applied to an entity, the transfer- + codings MUST be listed in the order in which they were applied. + Additional information about the encoding parameters MAY be provided + by other entity-header fields not defined by this specification. + + Many older HTTP/1.0 applications do not understand the Transfer- + Encoding header. + +14.42 Upgrade + + The Upgrade general-header allows the client to specify what + additional communication protocols it supports and would like to use + if the server finds it appropriate to switch protocols. The server + MUST use the Upgrade header field within a 101 (Switching Protocols) + response to indicate which protocol(s) are being switched. + + Upgrade = "Upgrade" ":" 1#product + + For example, + + Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 + + The Upgrade header field is intended to provide a simple mechanism + for transition from HTTP/1.1 to some other, incompatible protocol. It + does so by allowing the client to advertise its desire to use another + protocol, such as a later version of HTTP with a higher major version + number, even though the current request has been made using HTTP/1.1. + This eases the difficult transition between incompatible protocols by + allowing the client to initiate a request in the more commonly + supported protocol while indicating to the server that it would like + to use a "better" protocol if available (where "better" is determined + by the server, possibly according to the nature of the method and/or + resource being requested). + + The Upgrade header field only applies to switching application-layer + protocols upon the existing transport-layer connection. Upgrade + cannot be used to insist on a protocol change; its acceptance and use + by the server is optional. The capabilities and nature of the + application-layer communication after the protocol change is entirely + dependent upon the new protocol chosen, although the first action + after changing the protocol MUST be a response to the initial HTTP + request containing the Upgrade header field. + + The Upgrade header field only applies to the immediate connection. + Therefore, the upgrade keyword MUST be supplied within a Connection + header field (section 14.10) whenever Upgrade is present in an + HTTP/1.1 message. + + + + +Fielding, et al. Standards Track [Page 144] + +RFC 2616 HTTP/1.1 June 1999 + + + The Upgrade header field cannot be used to indicate a switch to a + protocol on a different connection. For that purpose, it is more + appropriate to use a 301, 302, 303, or 305 redirection response. + + This specification only defines the protocol name "HTTP" for use by + the family of Hypertext Transfer Protocols, as defined by the HTTP + version rules of section 3.1 and future updates to this + specification. Any token can be used as a protocol name; however, it + will only be useful if both the client and server associate the name + with the same protocol. + +14.43 User-Agent + + The User-Agent request-header field contains information about the + user agent originating the request. This is for statistical purposes, + the tracing of protocol violations, and automated recognition of user + agents for the sake of tailoring responses to avoid particular user + agent limitations. User agents SHOULD include this field with + requests. The field can contain multiple product tokens (section 3.8) + and comments identifying the agent and any subproducts which form a + significant part of the user agent. By convention, the product tokens + are listed in order of their significance for identifying the + application. + + User-Agent = "User-Agent" ":" 1*( product | comment ) + + Example: + + User-Agent: CERN-LineMode/2.15 libwww/2.17b3 + +14.44 Vary + + The Vary field value indicates the set of request-header fields that + fully determines, while the response is fresh, whether a cache is + permitted to use the response to reply to a subsequent request + without revalidation. For uncacheable or stale responses, the Vary + field value advises the user agent about the criteria that were used + to select the representation. A Vary field value of "*" implies that + a cache cannot determine from the request headers of a subsequent + request whether this response is the appropriate representation. See + section 13.6 for use of the Vary header field by caches. + + Vary = "Vary" ":" ( "*" | 1#field-name ) + + An HTTP/1.1 server SHOULD include a Vary header field with any + cacheable response that is subject to server-driven negotiation. + Doing so allows a cache to properly interpret future requests on that + resource and informs the user agent about the presence of negotiation + + + +Fielding, et al. Standards Track [Page 145] + +RFC 2616 HTTP/1.1 June 1999 + + + on that resource. A server MAY include a Vary header field with a + non-cacheable response that is subject to server-driven negotiation, + since this might provide the user agent with useful information about + the dimensions over which the response varies at the time of the + response. + + A Vary field value consisting of a list of field-names signals that + the representation selected for the response is based on a selection + algorithm which considers ONLY the listed request-header field values + in selecting the most appropriate representation. A cache MAY assume + that the same selection will be made for future requests with the + same values for the listed field names, for the duration of time for + which the response is fresh. + + The field-names given are not limited to the set of standard + request-header fields defined by this specification. Field names are + case-insensitive. + + A Vary field value of "*" signals that unspecified parameters not + limited to the request-headers (e.g., the network address of the + client), play a role in the selection of the response representation. + The "*" value MUST NOT be generated by a proxy server; it may only be + generated by an origin server. + +14.45 Via + + The Via general-header field MUST be used by gateways and proxies to + indicate the intermediate protocols and recipients between the user + agent and the server on requests, and between the origin server and + the client on responses. It is analogous to the "Received" field of + RFC 822 [9] and is intended to be used for tracking message forwards, + avoiding request loops, and identifying the protocol capabilities of + all senders along the request/response chain. + + Via = "Via" ":" 1#( received-protocol received-by [ comment ] ) + received-protocol = [ protocol-name "/" ] protocol-version + protocol-name = token + protocol-version = token + received-by = ( host [ ":" port ] ) | pseudonym + pseudonym = token + + The received-protocol indicates the protocol version of the message + received by the server or client along each segment of the + request/response chain. The received-protocol version is appended to + the Via field value when the message is forwarded so that information + about the protocol capabilities of upstream applications remains + visible to all recipients. + + + + +Fielding, et al. Standards Track [Page 146] + +RFC 2616 HTTP/1.1 June 1999 + + + The protocol-name is optional if and only if it would be "HTTP". The + received-by field is normally the host and optional port number of a + recipient server or client that subsequently forwarded the message. + However, if the real host is considered to be sensitive information, + it MAY be replaced by a pseudonym. If the port is not given, it MAY + be assumed to be the default port of the received-protocol. + + Multiple Via field values represents each proxy or gateway that has + forwarded the message. Each recipient MUST append its information + such that the end result is ordered according to the sequence of + forwarding applications. + + Comments MAY be used in the Via header field to identify the software + of the recipient proxy or gateway, analogous to the User-Agent and + Server header fields. However, all comments in the Via field are + optional and MAY be removed by any recipient prior to forwarding the + message. + + For example, a request message could be sent from an HTTP/1.0 user + agent to an internal proxy code-named "fred", which uses HTTP/1.1 to + forward the request to a public proxy at nowhere.com, which completes + the request by forwarding it to the origin server at www.ics.uci.edu. + The request received by www.ics.uci.edu would then have the following + Via header field: + + Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) + + Proxies and gateways used as a portal through a network firewall + SHOULD NOT, by default, forward the names and ports of hosts within + the firewall region. This information SHOULD only be propagated if + explicitly enabled. If not enabled, the received-by host of any host + behind the firewall SHOULD be replaced by an appropriate pseudonym + for that host. + + For organizations that have strong privacy requirements for hiding + internal structures, a proxy MAY combine an ordered subsequence of + Via header field entries with identical received-protocol values into + a single such entry. For example, + + Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy + + could be collapsed to + + Via: 1.0 ricky, 1.1 mertz, 1.0 lucy + + + + + + + +Fielding, et al. Standards Track [Page 147] + +RFC 2616 HTTP/1.1 June 1999 + + + Applications SHOULD NOT combine multiple entries unless they are all + under the same organizational control and the hosts have already been + replaced by pseudonyms. Applications MUST NOT combine entries which + have different received-protocol values. + +14.46 Warning + + The Warning general-header field is used to carry additional + information about the status or transformation of a message which + might not be reflected in the message. This information is typically + used to warn about a possible lack of semantic transparency from + caching operations or transformations applied to the entity body of + the message. + + Warning headers are sent with responses using: + + Warning = "Warning" ":" 1#warning-value + + warning-value = warn-code SP warn-agent SP warn-text + [SP warn-date] + + warn-code = 3DIGIT + warn-agent = ( host [ ":" port ] ) | pseudonym + ; the name or pseudonym of the server adding + ; the Warning header, for use in debugging + warn-text = quoted-string + warn-date = <"> HTTP-date <"> + + A response MAY carry more than one Warning header. + + The warn-text SHOULD be in a natural language and character set that + is most likely to be intelligible to the human user receiving the + response. This decision MAY be based on any available knowledge, such + as the location of the cache or user, the Accept-Language field in a + request, the Content-Language field in a response, etc. The default + language is English and the default character set is ISO-8859-1. + + If a character set other than ISO-8859-1 is used, it MUST be encoded + in the warn-text using the method described in RFC 2047 [14]. + + Warning headers can in general be applied to any message, however + some specific warn-codes are specific to caches and can only be + applied to response messages. New Warning headers SHOULD be added + after any existing Warning headers. A cache MUST NOT delete any + Warning header that it received with a message. However, if a cache + successfully validates a cache entry, it SHOULD remove any Warning + headers previously attached to that entry except as specified for + + + + +Fielding, et al. Standards Track [Page 148] + +RFC 2616 HTTP/1.1 June 1999 + + + specific Warning codes. It MUST then add any Warning headers received + in the validating response. In other words, Warning headers are those + that would be attached to the most recent relevant response. + + When multiple Warning headers are attached to a response, the user + agent ought to inform the user of as many of them as possible, in the + order that they appear in the response. If it is not possible to + inform the user of all of the warnings, the user agent SHOULD follow + these heuristics: + + - Warnings that appear early in the response take priority over + those appearing later in the response. + + - Warnings in the user's preferred character set take priority + over warnings in other character sets but with identical warn- + codes and warn-agents. + + Systems that generate multiple Warning headers SHOULD order them with + this user agent behavior in mind. + + Requirements for the behavior of caches with respect to Warnings are + stated in section 13.1.2. + + This is a list of the currently-defined warn-codes, each with a + recommended warn-text in English, and a description of its meaning. + + 110 Response is stale + MUST be included whenever the returned response is stale. + + 111 Revalidation failed + MUST be included if a cache returns a stale response because an + attempt to revalidate the response failed, due to an inability to + reach the server. + + 112 Disconnected operation + SHOULD be included if the cache is intentionally disconnected from + the rest of the network for a period of time. + + 113 Heuristic expiration + MUST be included if the cache heuristically chose a freshness + lifetime greater than 24 hours and the response's age is greater + than 24 hours. + + 199 Miscellaneous warning + The warning text MAY include arbitrary information to be presented + to a human user, or logged. A system receiving this warning MUST + NOT take any automated action, besides presenting the warning to + the user. + + + +Fielding, et al. Standards Track [Page 149] + +RFC 2616 HTTP/1.1 June 1999 + + + 214 Transformation applied + MUST be added by an intermediate cache or proxy if it applies any + transformation changing the content-coding (as specified in the + Content-Encoding header) or media-type (as specified in the + Content-Type header) of the response, or the entity-body of the + response, unless this Warning code already appears in the response. + + 299 Miscellaneous persistent warning + The warning text MAY include arbitrary information to be presented + to a human user, or logged. A system receiving this warning MUST + NOT take any automated action. + + If an implementation sends a message with one or more Warning headers + whose version is HTTP/1.0 or lower, then the sender MUST include in + each warning-value a warn-date that matches the date in the response. + + If an implementation receives a message with a warning-value that + includes a warn-date, and that warn-date is different from the Date + value in the response, then that warning-value MUST be deleted from + the message before storing, forwarding, or using it. (This prevents + bad consequences of naive caching of Warning header fields.) If all + of the warning-values are deleted for this reason, the Warning header + MUST be deleted as well. + +14.47 WWW-Authenticate + + The WWW-Authenticate response-header field MUST be included in 401 + (Unauthorized) response messages. The field value consists of at + least one challenge that indicates the authentication scheme(s) and + parameters applicable to the Request-URI. + + WWW-Authenticate = "WWW-Authenticate" ":" 1#challenge + + The HTTP access authentication process is described in "HTTP + Authentication: Basic and Digest Access Authentication" [43]. User + agents are advised to take special care in parsing the WWW- + Authenticate field value as it might contain more than one challenge, + or if more than one WWW-Authenticate header field is provided, the + contents of a challenge itself can contain a comma-separated list of + authentication parameters. + +15 Security Considerations + + This section is meant to inform application developers, information + providers, and users of the security limitations in HTTP/1.1 as + described by this document. The discussion does not include + definitive solutions to the problems revealed, though it does make + some suggestions for reducing security risks. + + + +Fielding, et al. Standards Track [Page 150] + +RFC 2616 HTTP/1.1 June 1999 + + +15.1 Personal Information + + HTTP clients are often privy to large amounts of personal information + (e.g. the user's name, location, mail address, passwords, encryption + keys, etc.), and SHOULD be very careful to prevent unintentional + leakage of this information via the HTTP protocol to other sources. + We very strongly recommend that a convenient interface be provided + for the user to control dissemination of such information, and that + designers and implementors be particularly careful in this area. + History shows that errors in this area often create serious security + and/or privacy problems and generate highly adverse publicity for the + implementor's company. + +15.1.1 Abuse of Server Log Information + + A server is in the position to save personal data about a user's + requests which might identify their reading patterns or subjects of + interest. This information is clearly confidential in nature and its + handling can be constrained by law in certain countries. People using + the HTTP protocol to provide data are responsible for ensuring that + such material is not distributed without the permission of any + individuals that are identifiable by the published results. + +15.1.2 Transfer of Sensitive Information + + Like any generic data transfer protocol, HTTP cannot regulate the + content of the data that is transferred, nor is there any a priori + method of determining the sensitivity of any particular piece of + information within the context of any given request. Therefore, + applications SHOULD supply as much control over this information as + possible to the provider of that information. Four header fields are + worth special mention in this context: Server, Via, Referer and From. + + Revealing the specific software version of the server might allow the + server machine to become more vulnerable to attacks against software + that is known to contain security holes. Implementors SHOULD make the + Server header field a configurable option. + + Proxies which serve as a portal through a network firewall SHOULD + take special precautions regarding the transfer of header information + that identifies the hosts behind the firewall. In particular, they + SHOULD remove, or replace with sanitized versions, any Via fields + generated behind the firewall. + + The Referer header allows reading patterns to be studied and reverse + links drawn. Although it can be very useful, its power can be abused + if user details are not separated from the information contained in + + + + +Fielding, et al. Standards Track [Page 151] + +RFC 2616 HTTP/1.1 June 1999 + + + the Referer. Even when the personal information has been removed, the + Referer header might indicate a private document's URI whose + publication would be inappropriate. + + The information sent in the From field might conflict with the user's + privacy interests or their site's security policy, and hence it + SHOULD NOT be transmitted without the user being able to disable, + enable, and modify the contents of the field. The user MUST be able + to set the contents of this field within a user preference or + application defaults configuration. + + We suggest, though do not require, that a convenient toggle interface + be provided for the user to enable or disable the sending of From and + Referer information. + + The User-Agent (section 14.43) or Server (section 14.38) header + fields can sometimes be used to determine that a specific client or + server have a particular security hole which might be exploited. + Unfortunately, this same information is often used for other valuable + purposes for which HTTP currently has no better mechanism. + +15.1.3 Encoding Sensitive Information in URI's + + Because the source of a link might be private information or might + reveal an otherwise private information source, it is strongly + recommended that the user be able to select whether or not the + Referer field is sent. For example, a browser client could have a + toggle switch for browsing openly/anonymously, which would + respectively enable/disable the sending of Referer and From + information. + + Clients SHOULD NOT include a Referer header field in a (non-secure) + HTTP request if the referring page was transferred with a secure + protocol. + + Authors of services which use the HTTP protocol SHOULD NOT use GET + based forms for the submission of sensitive data, because this will + cause this data to be encoded in the Request-URI. Many existing + servers, proxies, and user agents will log the request URI in some + place where it might be visible to third parties. Servers can use + POST-based form submission instead + +15.1.4 Privacy Issues Connected to Accept Headers + + Accept request-headers can reveal information about the user to all + servers which are accessed. The Accept-Language header in particular + can reveal information the user would consider to be of a private + nature, because the understanding of particular languages is often + + + +Fielding, et al. Standards Track [Page 152] + +RFC 2616 HTTP/1.1 June 1999 + + + strongly correlated to the membership of a particular ethnic group. + User agents which offer the option to configure the contents of an + Accept-Language header to be sent in every request are strongly + encouraged to let the configuration process include a message which + makes the user aware of the loss of privacy involved. + + An approach that limits the loss of privacy would be for a user agent + to omit the sending of Accept-Language headers by default, and to ask + the user whether or not to start sending Accept-Language headers to a + server if it detects, by looking for any Vary response-header fields + generated by the server, that such sending could improve the quality + of service. + + Elaborate user-customized accept header fields sent in every request, + in particular if these include quality values, can be used by servers + as relatively reliable and long-lived user identifiers. Such user + identifiers would allow content providers to do click-trail tracking, + and would allow collaborating content providers to match cross-server + click-trails or form submissions of individual users. Note that for + many users not behind a proxy, the network address of the host + running the user agent will also serve as a long-lived user + identifier. In environments where proxies are used to enhance + privacy, user agents ought to be conservative in offering accept + header configuration options to end users. As an extreme privacy + measure, proxies could filter the accept headers in relayed requests. + General purpose user agents which provide a high degree of header + configurability SHOULD warn users about the loss of privacy which can + be involved. + +15.2 Attacks Based On File and Path Names + + Implementations of HTTP origin servers SHOULD be careful to restrict + the documents returned by HTTP requests to be only those that were + intended by the server administrators. If an HTTP server translates + HTTP URIs directly into file system calls, the server MUST take + special care not to serve files that were not intended to be + delivered to HTTP clients. For example, UNIX, Microsoft Windows, and + other operating systems use ".." as a path component to indicate a + directory level above the current one. On such a system, an HTTP + server MUST disallow any such construct in the Request-URI if it + would otherwise allow access to a resource outside those intended to + be accessible via the HTTP server. Similarly, files intended for + reference only internally to the server (such as access control + files, configuration files, and script code) MUST be protected from + inappropriate retrieval, since they might contain sensitive + information. Experience has shown that minor bugs in such HTTP server + implementations have turned into security risks. + + + + +Fielding, et al. Standards Track [Page 153] + +RFC 2616 HTTP/1.1 June 1999 + + +15.3 DNS Spoofing + + Clients using HTTP rely heavily on the Domain Name Service, and are + thus generally prone to security attacks based on the deliberate + mis-association of IP addresses and DNS names. Clients need to be + cautious in assuming the continuing validity of an IP number/DNS name + association. + + In particular, HTTP clients SHOULD rely on their name resolver for + confirmation of an IP number/DNS name association, rather than + caching the result of previous host name lookups. Many platforms + already can cache host name lookups locally when appropriate, and + they SHOULD be configured to do so. It is proper for these lookups to + be cached, however, only when the TTL (Time To Live) information + reported by the name server makes it likely that the cached + information will remain useful. + + If HTTP clients cache the results of host name lookups in order to + achieve a performance improvement, they MUST observe the TTL + information reported by DNS. + + If HTTP clients do not observe this rule, they could be spoofed when + a previously-accessed server's IP address changes. As network + renumbering is expected to become increasingly common [24], the + possibility of this form of attack will grow. Observing this + requirement thus reduces this potential security vulnerability. + + This requirement also improves the load-balancing behavior of clients + for replicated servers using the same DNS name and reduces the + likelihood of a user's experiencing failure in accessing sites which + use that strategy. + +15.4 Location Headers and Spoofing + + If a single server supports multiple organizations that do not trust + one another, then it MUST check the values of Location and Content- + Location headers in responses that are generated under control of + said organizations to make sure that they do not attempt to + invalidate resources over which they have no authority. + +15.5 Content-Disposition Issues + + RFC 1806 [35], from which the often implemented Content-Disposition + (see section 19.5.1) header in HTTP is derived, has a number of very + serious security considerations. Content-Disposition is not part of + the HTTP standard, but since it is widely implemented, we are + documenting its use and risks for implementors. See RFC 2183 [49] + (which updates RFC 1806) for details. + + + +Fielding, et al. Standards Track [Page 154] + +RFC 2616 HTTP/1.1 June 1999 + + +15.6 Authentication Credentials and Idle Clients + + Existing HTTP clients and user agents typically retain authentication + information indefinitely. HTTP/1.1. does not provide a method for a + server to direct clients to discard these cached credentials. This is + a significant defect that requires further extensions to HTTP. + Circumstances under which credential caching can interfere with the + application's security model include but are not limited to: + + - Clients which have been idle for an extended period following + which the server might wish to cause the client to reprompt the + user for credentials. + + - Applications which include a session termination indication + (such as a `logout' or `commit' button on a page) after which + the server side of the application `knows' that there is no + further reason for the client to retain the credentials. + + This is currently under separate study. There are a number of work- + arounds to parts of this problem, and we encourage the use of + password protection in screen savers, idle time-outs, and other + methods which mitigate the security problems inherent in this + problem. In particular, user agents which cache credentials are + encouraged to provide a readily accessible mechanism for discarding + cached credentials under user control. + +15.7 Proxies and Caching + + By their very nature, HTTP proxies are men-in-the-middle, and + represent an opportunity for man-in-the-middle attacks. Compromise of + the systems on which the proxies run can result in serious security + and privacy problems. Proxies have access to security-related + information, personal information about individual users and + organizations, and proprietary information belonging to users and + content providers. A compromised proxy, or a proxy implemented or + configured without regard to security and privacy considerations, + might be used in the commission of a wide range of potential attacks. + + Proxy operators should protect the systems on which proxies run as + they would protect any system that contains or transports sensitive + information. In particular, log information gathered at proxies often + contains highly sensitive personal information, and/or information + about organizations. Log information should be carefully guarded, and + appropriate guidelines for use developed and followed. (Section + 15.1.1). + + + + + + +Fielding, et al. Standards Track [Page 155] + +RFC 2616 HTTP/1.1 June 1999 + + + Caching proxies provide additional potential vulnerabilities, since + the contents of the cache represent an attractive target for + malicious exploitation. Because cache contents persist after an HTTP + request is complete, an attack on the cache can reveal information + long after a user believes that the information has been removed from + the network. Therefore, cache contents should be protected as + sensitive information. + + Proxy implementors should consider the privacy and security + implications of their design and coding decisions, and of the + configuration options they provide to proxy operators (especially the + default configuration). + + Users of a proxy need to be aware that they are no trustworthier than + the people who run the proxy; HTTP itself cannot solve this problem. + + The judicious use of cryptography, when appropriate, may suffice to + protect against a broad range of security and privacy attacks. Such + cryptography is beyond the scope of the HTTP/1.1 specification. + +15.7.1 Denial of Service Attacks on Proxies + + They exist. They are hard to defend against. Research continues. + Beware. + +16 Acknowledgments + + This specification makes heavy use of the augmented BNF and generic + constructs defined by David H. Crocker for RFC 822 [9]. Similarly, it + reuses many of the definitions provided by Nathaniel Borenstein and + Ned Freed for MIME [7]. We hope that their inclusion in this + specification will help reduce past confusion over the relationship + between HTTP and Internet mail message formats. + + The HTTP protocol has evolved considerably over the years. It has + benefited from a large and active developer community--the many + people who have participated on the www-talk mailing list--and it is + that community which has been most responsible for the success of + HTTP and of the World-Wide Web in general. Marc Andreessen, Robert + Cailliau, Daniel W. Connolly, Bob Denny, John Franks, Jean-Francois + Groff, Phillip M. Hallam-Baker, Hakon W. Lie, Ari Luotonen, Rob + McCool, Lou Montulli, Dave Raggett, Tony Sanders, and Marc + VanHeyningen deserve special recognition for their efforts in + defining early aspects of the protocol. + + This document has benefited greatly from the comments of all those + participating in the HTTP-WG. In addition to those already mentioned, + the following individuals have contributed to this specification: + + + +Fielding, et al. Standards Track [Page 156] + +RFC 2616 HTTP/1.1 June 1999 + + + Gary Adams Ross Patterson + Harald Tveit Alvestrand Albert Lunde + Keith Ball John C. Mallery + Brian Behlendorf Jean-Philippe Martin-Flatin + Paul Burchard Mitra + Maurizio Codogno David Morris + Mike Cowlishaw Gavin Nicol + Roman Czyborra Bill Perry + Michael A. Dolan Jeffrey Perry + David J. Fiander Scott Powers + Alan Freier Owen Rees + Marc Hedlund Luigi Rizzo + Greg Herlihy David Robinson + Koen Holtman Marc Salomon + Alex Hopmann Rich Salz + Bob Jernigan Allan M. Schiffman + Shel Kaphan Jim Seidman + Rohit Khare Chuck Shotton + John Klensin Eric W. Sink + Martijn Koster Simon E. Spero + Alexei Kosut Richard N. Taylor + David M. Kristol Robert S. Thau + Daniel LaLiberte Bill (BearHeart) Weinman + Ben Laurie Francois Yergeau + Paul J. Leach Mary Ellen Zurko + Daniel DuBois Josh Cohen + + + Much of the content and presentation of the caching design is due to + suggestions and comments from individuals including: Shel Kaphan, + Paul Leach, Koen Holtman, David Morris, and Larry Masinter. + + Most of the specification of ranges is based on work originally done + by Ari Luotonen and John Franks, with additional input from Steve + Zilles. + + Thanks to the "cave men" of Palo Alto. You know who you are. + + Jim Gettys (the current editor of this document) wishes particularly + to thank Roy Fielding, the previous editor of this document, along + with John Klensin, Jeff Mogul, Paul Leach, Dave Kristol, Koen + Holtman, John Franks, Josh Cohen, Alex Hopmann, Scott Lawrence, and + Larry Masinter for their help. And thanks go particularly to Jeff + Mogul and Scott Lawrence for performing the "MUST/MAY/SHOULD" audit. + + + + + + + +Fielding, et al. Standards Track [Page 157] + +RFC 2616 HTTP/1.1 June 1999 + + + The Apache Group, Anselm Baird-Smith, author of Jigsaw, and Henrik + Frystyk implemented RFC 2068 early, and we wish to thank them for the + discovery of many of the problems that this document attempts to + rectify. + +17 References + + [1] Alvestrand, H., "Tags for the Identification of Languages", RFC + 1766, March 1995. + + [2] Anklesaria, F., McCahill, M., Lindner, P., Johnson, D., Torrey, + D. and B. Alberti, "The Internet Gopher Protocol (a distributed + document search and retrieval protocol)", RFC 1436, March 1993. + + [3] Berners-Lee, T., "Universal Resource Identifiers in WWW", RFC + 1630, June 1994. + + [4] Berners-Lee, T., Masinter, L. and M. McCahill, "Uniform Resource + Locators (URL)", RFC 1738, December 1994. + + [5] Berners-Lee, T. and D. Connolly, "Hypertext Markup Language - + 2.0", RFC 1866, November 1995. + + [6] Berners-Lee, T., Fielding, R. and H. Frystyk, "Hypertext Transfer + Protocol -- HTTP/1.0", RFC 1945, May 1996. + + [7] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message Bodies", + RFC 2045, November 1996. + + [8] Braden, R., "Requirements for Internet Hosts -- Communication + Layers", STD 3, RFC 1123, October 1989. + + [9] Crocker, D., "Standard for The Format of ARPA Internet Text + Messages", STD 11, RFC 822, August 1982. + + [10] Davis, F., Kahle, B., Morris, H., Salem, J., Shen, T., Wang, R., + Sui, J., and M. Grinbaum, "WAIS Interface Protocol Prototype + Functional Specification," (v1.5), Thinking Machines + Corporation, April 1990. + + [11] Fielding, R., "Relative Uniform Resource Locators", RFC 1808, + June 1995. + + [12] Horton, M. and R. Adams, "Standard for Interchange of USENET + Messages", RFC 1036, December 1987. + + + + + +Fielding, et al. Standards Track [Page 158] + +RFC 2616 HTTP/1.1 June 1999 + + + [13] Kantor, B. and P. Lapsley, "Network News Transfer Protocol", RFC + 977, February 1986. + + [14] Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part + Three: Message Header Extensions for Non-ASCII Text", RFC 2047, + November 1996. + + [15] Nebel, E. and L. Masinter, "Form-based File Upload in HTML", RFC + 1867, November 1995. + + [16] Postel, J., "Simple Mail Transfer Protocol", STD 10, RFC 821, + August 1982. + + [17] Postel, J., "Media Type Registration Procedure", RFC 1590, + November 1996. + + [18] Postel, J. and J. Reynolds, "File Transfer Protocol", STD 9, RFC + 959, October 1985. + + [19] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC 1700, + October 1994. + + [20] Sollins, K. and L. Masinter, "Functional Requirements for + Uniform Resource Names", RFC 1737, December 1994. + + [21] US-ASCII. Coded Character Set - 7-Bit American Standard Code for + Information Interchange. Standard ANSI X3.4-1986, ANSI, 1986. + + [22] ISO-8859. International Standard -- Information Processing -- + 8-bit Single-Byte Coded Graphic Character Sets -- + Part 1: Latin alphabet No. 1, ISO-8859-1:1987. + Part 2: Latin alphabet No. 2, ISO-8859-2, 1987. + Part 3: Latin alphabet No. 3, ISO-8859-3, 1988. + Part 4: Latin alphabet No. 4, ISO-8859-4, 1988. + Part 5: Latin/Cyrillic alphabet, ISO-8859-5, 1988. + Part 6: Latin/Arabic alphabet, ISO-8859-6, 1987. + Part 7: Latin/Greek alphabet, ISO-8859-7, 1987. + Part 8: Latin/Hebrew alphabet, ISO-8859-8, 1988. + Part 9: Latin alphabet No. 5, ISO-8859-9, 1990. + + [23] Meyers, J. and M. Rose, "The Content-MD5 Header Field", RFC + 1864, October 1995. + + [24] Carpenter, B. and Y. Rekhter, "Renumbering Needs Work", RFC + 1900, February 1996. + + [25] Deutsch, P., "GZIP file format specification version 4.3", RFC + 1952, May 1996. + + + +Fielding, et al. Standards Track [Page 159] + +RFC 2616 HTTP/1.1 June 1999 + + + [26] Venkata N. Padmanabhan, and Jeffrey C. Mogul. "Improving HTTP + Latency", Computer Networks and ISDN Systems, v. 28, pp. 25-35, + Dec. 1995. Slightly revised version of paper in Proc. 2nd + International WWW Conference '94: Mosaic and the Web, Oct. 1994, + which is available at + http://www.ncsa.uiuc.edu/SDG/IT94/Proceedings/DDay/mogul/HTTPLat + ency.html. + + [27] Joe Touch, John Heidemann, and Katia Obraczka. "Analysis of HTTP + Performance", , + ISI Research Report ISI/RR-98-463, (original report dated Aug. + 1996), USC/Information Sciences Institute, August 1998. + + [28] Mills, D., "Network Time Protocol (Version 3) Specification, + Implementation and Analysis", RFC 1305, March 1992. + + [29] Deutsch, P., "DEFLATE Compressed Data Format Specification + version 1.3", RFC 1951, May 1996. + + [30] S. Spero, "Analysis of HTTP Performance Problems," + http://sunsite.unc.edu/mdma-release/http-prob.html. + + [31] Deutsch, P. and J. Gailly, "ZLIB Compressed Data Format + Specification version 3.3", RFC 1950, May 1996. + + [32] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P., + Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP: + Digest Access Authentication", RFC 2069, January 1997. + + [33] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T. + Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC + 2068, January 1997. + + [34] Bradner, S., "Key words for use in RFCs to Indicate Requirement + Levels", BCP 14, RFC 2119, March 1997. + + [35] Troost, R. and Dorner, S., "Communicating Presentation + Information in Internet Messages: The Content-Disposition + Header", RFC 1806, June 1995. + + [36] Mogul, J., Fielding, R., Gettys, J. and H. Frystyk, "Use and + Interpretation of HTTP Version Numbers", RFC 2145, May 1997. + [jg639] + + [37] Palme, J., "Common Internet Message Headers", RFC 2076, February + 1997. [jg640] + + + + + +Fielding, et al. Standards Track [Page 160] + +RFC 2616 HTTP/1.1 June 1999 + + + [38] Yergeau, F., "UTF-8, a transformation format of Unicode and + ISO-10646", RFC 2279, January 1998. [jg641] + + [39] Nielsen, H.F., Gettys, J., Baird-Smith, A., Prud'hommeaux, E., + Lie, H., and C. Lilley. "Network Performance Effects of + HTTP/1.1, CSS1, and PNG," Proceedings of ACM SIGCOMM '97, Cannes + France, September 1997.[jg642] + + [40] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part Two: Media Types", RFC 2046, November + 1996. [jg643] + + [41] Alvestrand, H., "IETF Policy on Character Sets and Languages", + BCP 18, RFC 2277, January 1998. [jg644] + + [42] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform Resource + Identifiers (URI): Generic Syntax and Semantics", RFC 2396, + August 1998. [jg645] + + [43] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., + Leach, P., Luotonen, A., Sink, E. and L. Stewart, "HTTP + Authentication: Basic and Digest Access Authentication", RFC + 2617, June 1999. [jg646] + + [44] Luotonen, A., "Tunneling TCP based protocols through Web proxy + servers," Work in Progress. [jg647] + + [45] Palme, J. and A. Hopmann, "MIME E-mail Encapsulation of + Aggregate Documents, such as HTML (MHTML)", RFC 2110, March + 1997. + + [46] Bradner, S., "The Internet Standards Process -- Revision 3", BCP + 9, RFC 2026, October 1996. + + [47] Masinter, L., "Hyper Text Coffee Pot Control Protocol + (HTCPCP/1.0)", RFC 2324, 1 April 1998. + + [48] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part Five: Conformance Criteria and Examples", + RFC 2049, November 1996. + + [49] Troost, R., Dorner, S. and K. Moore, "Communicating Presentation + Information in Internet Messages: The Content-Disposition Header + Field", RFC 2183, August 1997. + + + + + + + +Fielding, et al. Standards Track [Page 161] + +RFC 2616 HTTP/1.1 June 1999 + + +18 Authors' Addresses + + Roy T. Fielding + Information and Computer Science + University of California, Irvine + Irvine, CA 92697-3425, USA + + Fax: +1 (949) 824-1715 + EMail: fielding@ics.uci.edu + + + James Gettys + World Wide Web Consortium + MIT Laboratory for Computer Science + 545 Technology Square + Cambridge, MA 02139, USA + + Fax: +1 (617) 258 8682 + EMail: jg@w3.org + + + Jeffrey C. Mogul + Western Research Laboratory + Compaq Computer Corporation + 250 University Avenue + Palo Alto, California, 94305, USA + + EMail: mogul@wrl.dec.com + + + Henrik Frystyk Nielsen + World Wide Web Consortium + MIT Laboratory for Computer Science + 545 Technology Square + Cambridge, MA 02139, USA + + Fax: +1 (617) 258 8682 + EMail: frystyk@w3.org + + + Larry Masinter + Xerox Corporation + 3333 Coyote Hill Road + Palo Alto, CA 94034, USA + + EMail: masinter@parc.xerox.com + + + + + +Fielding, et al. Standards Track [Page 162] + +RFC 2616 HTTP/1.1 June 1999 + + + Paul J. Leach + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052, USA + + EMail: paulle@microsoft.com + + + Tim Berners-Lee + Director, World Wide Web Consortium + MIT Laboratory for Computer Science + 545 Technology Square + Cambridge, MA 02139, USA + + Fax: +1 (617) 258 8682 + EMail: timbl@w3.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fielding, et al. Standards Track [Page 163] + +RFC 2616 HTTP/1.1 June 1999 + + +19 Appendices + +19.1 Internet Media Type message/http and application/http + + In addition to defining the HTTP/1.1 protocol, this document serves + as the specification for the Internet media type "message/http" and + "application/http". The message/http type can be used to enclose a + single HTTP request or response message, provided that it obeys the + MIME restrictions for all "message" types regarding line length and + encodings. The application/http type can be used to enclose a + pipeline of one or more HTTP request or response messages (not + intermixed). The following is to be registered with IANA [17]. + + Media Type name: message + Media subtype name: http + Required parameters: none + Optional parameters: version, msgtype + version: The HTTP-Version number of the enclosed message + (e.g., "1.1"). If not present, the version can be + determined from the first line of the body. + msgtype: The message type -- "request" or "response". If not + present, the type can be determined from the first + line of the body. + Encoding considerations: only "7bit", "8bit", or "binary" are + permitted + Security considerations: none + + Media Type name: application + Media subtype name: http + Required parameters: none + Optional parameters: version, msgtype + version: The HTTP-Version number of the enclosed messages + (e.g., "1.1"). If not present, the version can be + determined from the first line of the body. + msgtype: The message type -- "request" or "response". If not + present, the type can be determined from the first + line of the body. + Encoding considerations: HTTP messages enclosed by this type + are in "binary" format; use of an appropriate + Content-Transfer-Encoding is required when + transmitted via E-mail. + Security considerations: none + + + + + + + + + +Fielding, et al. Standards Track [Page 164] + +RFC 2616 HTTP/1.1 June 1999 + + +19.2 Internet Media Type multipart/byteranges + + When an HTTP 206 (Partial Content) response message includes the + content of multiple ranges (a response to a request for multiple + non-overlapping ranges), these are transmitted as a multipart + message-body. The media type for this purpose is called + "multipart/byteranges". + + The multipart/byteranges media type includes two or more parts, each + with its own Content-Type and Content-Range fields. The required + boundary parameter specifies the boundary string used to separate + each body-part. + + Media Type name: multipart + Media subtype name: byteranges + Required parameters: boundary + Optional parameters: none + Encoding considerations: only "7bit", "8bit", or "binary" are + permitted + Security considerations: none + + + For example: + + HTTP/1.1 206 Partial Content + Date: Wed, 15 Nov 1995 06:25:24 GMT + Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT + Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES + + --THIS_STRING_SEPARATES + Content-type: application/pdf + Content-range: bytes 500-999/8000 + + ...the first range... + --THIS_STRING_SEPARATES + Content-type: application/pdf + Content-range: bytes 7000-7999/8000 + + ...the second range + --THIS_STRING_SEPARATES-- + + Notes: + + 1) Additional CRLFs may precede the first boundary string in the + entity. + + + + + + +Fielding, et al. Standards Track [Page 165] + +RFC 2616 HTTP/1.1 June 1999 + + + 2) Although RFC 2046 [40] permits the boundary string to be + quoted, some existing implementations handle a quoted boundary + string incorrectly. + + 3) A number of browsers and servers were coded to an early draft + of the byteranges specification to use a media type of + multipart/x-byteranges, which is almost, but not quite + compatible with the version documented in HTTP/1.1. + +19.3 Tolerant Applications + + Although this document specifies the requirements for the generation + of HTTP/1.1 messages, not all applications will be correct in their + implementation. We therefore recommend that operational applications + be tolerant of deviations whenever those deviations can be + interpreted unambiguously. + + Clients SHOULD be tolerant in parsing the Status-Line and servers + tolerant when parsing the Request-Line. In particular, they SHOULD + accept any amount of SP or HT characters between fields, even though + only a single SP is required. + + The line terminator for message-header fields is the sequence CRLF. + However, we recommend that applications, when parsing such headers, + recognize a single LF as a line terminator and ignore the leading CR. + + The character set of an entity-body SHOULD be labeled as the lowest + common denominator of the character codes used within that body, with + the exception that not labeling the entity is preferred over labeling + the entity with the labels US-ASCII or ISO-8859-1. See section 3.7.1 + and 3.4.1. + + Additional rules for requirements on parsing and encoding of dates + and other potential problems with date encodings include: + + - HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date + which appears to be more than 50 years in the future is in fact + in the past (this helps solve the "year 2000" problem). + + - An HTTP/1.1 implementation MAY internally represent a parsed + Expires date as earlier than the proper value, but MUST NOT + internally represent a parsed Expires date as later than the + proper value. + + - All expiration-related calculations MUST be done in GMT. The + local time zone MUST NOT influence the calculation or comparison + of an age or expiration time. + + + + +Fielding, et al. Standards Track [Page 166] + +RFC 2616 HTTP/1.1 June 1999 + + + - If an HTTP header incorrectly carries a date value with a time + zone other than GMT, it MUST be converted into GMT using the + most conservative possible conversion. + +19.4 Differences Between HTTP Entities and RFC 2045 Entities + + HTTP/1.1 uses many of the constructs defined for Internet Mail (RFC + 822 [9]) and the Multipurpose Internet Mail Extensions (MIME [7]) to + allow entities to be transmitted in an open variety of + representations and with extensible mechanisms. However, RFC 2045 + discusses mail, and HTTP has a few features that are different from + those described in RFC 2045. These differences were carefully chosen + to optimize performance over binary connections, to allow greater + freedom in the use of new media types, to make date comparisons + easier, and to acknowledge the practice of some early HTTP servers + and clients. + + This appendix describes specific areas where HTTP differs from RFC + 2045. Proxies and gateways to strict MIME environments SHOULD be + aware of these differences and provide the appropriate conversions + where necessary. Proxies and gateways from MIME environments to HTTP + also need to be aware of the differences because some conversions + might be required. + +19.4.1 MIME-Version + + HTTP is not a MIME-compliant protocol. However, HTTP/1.1 messages MAY + include a single MIME-Version general-header field to indicate what + version of the MIME protocol was used to construct the message. Use + of the MIME-Version header field indicates that the message is in + full compliance with the MIME protocol (as defined in RFC 2045[7]). + Proxies/gateways are responsible for ensuring full compliance (where + possible) when exporting HTTP messages to strict MIME environments. + + MIME-Version = "MIME-Version" ":" 1*DIGIT "." 1*DIGIT + + MIME version "1.0" is the default for use in HTTP/1.1. However, + HTTP/1.1 message parsing and semantics are defined by this document + and not the MIME specification. + +19.4.2 Conversion to Canonical Form + + RFC 2045 [7] requires that an Internet mail entity be converted to + canonical form prior to being transferred, as described in section 4 + of RFC 2049 [48]. Section 3.7.1 of this document describes the forms + allowed for subtypes of the "text" media type when transmitted over + HTTP. RFC 2046 requires that content with a type of "text" represent + line breaks as CRLF and forbids the use of CR or LF outside of line + + + +Fielding, et al. Standards Track [Page 167] + +RFC 2616 HTTP/1.1 June 1999 + + + break sequences. HTTP allows CRLF, bare CR, and bare LF to indicate a + line break within text content when a message is transmitted over + HTTP. + + Where it is possible, a proxy or gateway from HTTP to a strict MIME + environment SHOULD translate all line breaks within the text media + types described in section 3.7.1 of this document to the RFC 2049 + canonical form of CRLF. Note, however, that this might be complicated + by the presence of a Content-Encoding and by the fact that HTTP + allows the use of some character sets which do not use octets 13 and + 10 to represent CR and LF, as is the case for some multi-byte + character sets. + + Implementors should note that conversion will break any cryptographic + checksums applied to the original content unless the original content + is already in canonical form. Therefore, the canonical form is + recommended for any content that uses such checksums in HTTP. + +19.4.3 Conversion of Date Formats + + HTTP/1.1 uses a restricted set of date formats (section 3.3.1) to + simplify the process of date comparison. Proxies and gateways from + other protocols SHOULD ensure that any Date header field present in a + message conforms to one of the HTTP/1.1 formats and rewrite the date + if necessary. + +19.4.4 Introduction of Content-Encoding + + RFC 2045 does not include any concept equivalent to HTTP/1.1's + Content-Encoding header field. Since this acts as a modifier on the + media type, proxies and gateways from HTTP to MIME-compliant + protocols MUST either change the value of the Content-Type header + field or decode the entity-body before forwarding the message. (Some + experimental applications of Content-Type for Internet mail have used + a media-type parameter of ";conversions=" to perform + a function equivalent to Content-Encoding. However, this parameter is + not part of RFC 2045.) + +19.4.5 No Content-Transfer-Encoding + + HTTP does not use the Content-Transfer-Encoding (CTE) field of RFC + 2045. Proxies and gateways from MIME-compliant protocols to HTTP MUST + remove any non-identity CTE ("quoted-printable" or "base64") encoding + prior to delivering the response message to an HTTP client. + + Proxies and gateways from HTTP to MIME-compliant protocols are + responsible for ensuring that the message is in the correct format + and encoding for safe transport on that protocol, where "safe + + + +Fielding, et al. Standards Track [Page 168] + +RFC 2616 HTTP/1.1 June 1999 + + + transport" is defined by the limitations of the protocol being used. + Such a proxy or gateway SHOULD label the data with an appropriate + Content-Transfer-Encoding if doing so will improve the likelihood of + safe transport over the destination protocol. + +19.4.6 Introduction of Transfer-Encoding + + HTTP/1.1 introduces the Transfer-Encoding header field (section + 14.41). Proxies/gateways MUST remove any transfer-coding prior to + forwarding a message via a MIME-compliant protocol. + + A process for decoding the "chunked" transfer-coding (section 3.6) + can be represented in pseudo-code as: + + length := 0 + read chunk-size, chunk-extension (if any) and CRLF + while (chunk-size > 0) { + read chunk-data and CRLF + append chunk-data to entity-body + length := length + chunk-size + read chunk-size and CRLF + } + read entity-header + while (entity-header not empty) { + append entity-header to existing header fields + read entity-header + } + Content-Length := length + Remove "chunked" from Transfer-Encoding + +19.4.7 MHTML and Line Length Limitations + + HTTP implementations which share code with MHTML [45] implementations + need to be aware of MIME line length limitations. Since HTTP does not + have this limitation, HTTP does not fold long lines. MHTML messages + being transported by HTTP follow all conventions of MHTML, including + line length limitations and folding, canonicalization, etc., since + HTTP transports all message-bodies as payload (see section 3.7.2) and + does not interpret the content or any MIME header lines that might be + contained therein. + +19.5 Additional Features + + RFC 1945 and RFC 2068 document protocol elements used by some + existing HTTP implementations, but not consistently and correctly + across most HTTP/1.1 applications. Implementors are advised to be + aware of these features, but cannot rely upon their presence in, or + interoperability with, other HTTP/1.1 applications. Some of these + + + +Fielding, et al. Standards Track [Page 169] + +RFC 2616 HTTP/1.1 June 1999 + + + describe proposed experimental features, and some describe features + that experimental deployment found lacking that are now addressed in + the base HTTP/1.1 specification. + + A number of other headers, such as Content-Disposition and Title, + from SMTP and MIME are also often implemented (see RFC 2076 [37]). + +19.5.1 Content-Disposition + + The Content-Disposition response-header field has been proposed as a + means for the origin server to suggest a default filename if the user + requests that the content is saved to a file. This usage is derived + from the definition of Content-Disposition in RFC 1806 [35]. + + content-disposition = "Content-Disposition" ":" + disposition-type *( ";" disposition-parm ) + disposition-type = "attachment" | disp-extension-token + disposition-parm = filename-parm | disp-extension-parm + filename-parm = "filename" "=" quoted-string + disp-extension-token = token + disp-extension-parm = token "=" ( token | quoted-string ) + + An example is + + Content-Disposition: attachment; filename="fname.ext" + + The receiving user agent SHOULD NOT respect any directory path + information present in the filename-parm parameter, which is the only + parameter believed to apply to HTTP implementations at this time. The + filename SHOULD be treated as a terminal component only. + + If this header is used in a response with the application/octet- + stream content-type, the implied suggestion is that the user agent + should not display the response, but directly enter a `save response + as...' dialog. + + See section 15.5 for Content-Disposition security issues. + +19.6 Compatibility with Previous Versions + + It is beyond the scope of a protocol specification to mandate + compliance with previous versions. HTTP/1.1 was deliberately + designed, however, to make supporting previous versions easy. It is + worth noting that, at the time of composing this specification + (1996), we would expect commercial HTTP/1.1 servers to: + + - recognize the format of the Request-Line for HTTP/0.9, 1.0, and + 1.1 requests; + + + +Fielding, et al. Standards Track [Page 170] + +RFC 2616 HTTP/1.1 June 1999 + + + - understand any valid request in the format of HTTP/0.9, 1.0, or + 1.1; + + - respond appropriately with a message in the same major version + used by the client. + + And we would expect HTTP/1.1 clients to: + + - recognize the format of the Status-Line for HTTP/1.0 and 1.1 + responses; + + - understand any valid response in the format of HTTP/0.9, 1.0, or + 1.1. + + For most implementations of HTTP/1.0, each connection is established + by the client prior to the request and closed by the server after + sending the response. Some implementations implement the Keep-Alive + version of persistent connections described in section 19.7.1 of RFC + 2068 [33]. + +19.6.1 Changes from HTTP/1.0 + + This section summarizes major differences between versions HTTP/1.0 + and HTTP/1.1. + +19.6.1.1 Changes to Simplify Multi-homed Web Servers and Conserve IP + Addresses + + The requirements that clients and servers support the Host request- + header, report an error if the Host request-header (section 14.23) is + missing from an HTTP/1.1 request, and accept absolute URIs (section + 5.1.2) are among the most important changes defined by this + specification. + + Older HTTP/1.0 clients assumed a one-to-one relationship of IP + addresses and servers; there was no other established mechanism for + distinguishing the intended server of a request than the IP address + to which that request was directed. The changes outlined above will + allow the Internet, once older HTTP clients are no longer common, to + support multiple Web sites from a single IP address, greatly + simplifying large operational Web servers, where allocation of many + IP addresses to a single host has created serious problems. The + Internet will also be able to recover the IP addresses that have been + allocated for the sole purpose of allowing special-purpose domain + names to be used in root-level HTTP URLs. Given the rate of growth of + the Web, and the number of servers already deployed, it is extremely + + + + + +Fielding, et al. Standards Track [Page 171] + +RFC 2616 HTTP/1.1 June 1999 + + + important that all implementations of HTTP (including updates to + existing HTTP/1.0 applications) correctly implement these + requirements: + + - Both clients and servers MUST support the Host request-header. + + - A client that sends an HTTP/1.1 request MUST send a Host header. + + - Servers MUST report a 400 (Bad Request) error if an HTTP/1.1 + request does not include a Host request-header. + + - Servers MUST accept absolute URIs. + +19.6.2 Compatibility with HTTP/1.0 Persistent Connections + + Some clients and servers might wish to be compatible with some + previous implementations of persistent connections in HTTP/1.0 + clients and servers. Persistent connections in HTTP/1.0 are + explicitly negotiated as they are not the default behavior. HTTP/1.0 + experimental implementations of persistent connections are faulty, + and the new facilities in HTTP/1.1 are designed to rectify these + problems. The problem was that some existing 1.0 clients may be + sending Keep-Alive to a proxy server that doesn't understand + Connection, which would then erroneously forward it to the next + inbound server, which would establish the Keep-Alive connection and + result in a hung HTTP/1.0 proxy waiting for the close on the + response. The result is that HTTP/1.0 clients must be prevented from + using Keep-Alive when talking to proxies. + + However, talking to proxies is the most important use of persistent + connections, so that prohibition is clearly unacceptable. Therefore, + we need some other mechanism for indicating a persistent connection + is desired, which is safe to use even when talking to an old proxy + that ignores Connection. Persistent connections are the default for + HTTP/1.1 messages; we introduce a new keyword (Connection: close) for + declaring non-persistence. See section 14.10. + + The original HTTP/1.0 form of persistent connections (the Connection: + Keep-Alive and Keep-Alive header) is documented in RFC 2068. [33] + +19.6.3 Changes from RFC 2068 + + This specification has been carefully audited to correct and + disambiguate key word usage; RFC 2068 had many problems in respect to + the conventions laid out in RFC 2119 [34]. + + Clarified which error code should be used for inbound server failures + (e.g. DNS failures). (Section 10.5.5). + + + +Fielding, et al. Standards Track [Page 172] + +RFC 2616 HTTP/1.1 June 1999 + + + CREATE had a race that required an Etag be sent when a resource is + first created. (Section 10.2.2). + + Content-Base was deleted from the specification: it was not + implemented widely, and there is no simple, safe way to introduce it + without a robust extension mechanism. In addition, it is used in a + similar, but not identical fashion in MHTML [45]. + + Transfer-coding and message lengths all interact in ways that + required fixing exactly when chunked encoding is used (to allow for + transfer encoding that may not be self delimiting); it was important + to straighten out exactly how message lengths are computed. (Sections + 3.6, 4.4, 7.2.2, 13.5.2, 14.13, 14.16) + + A content-coding of "identity" was introduced, to solve problems + discovered in caching. (section 3.5) + + Quality Values of zero should indicate that "I don't want something" + to allow clients to refuse a representation. (Section 3.9) + + The use and interpretation of HTTP version numbers has been clarified + by RFC 2145. Require proxies to upgrade requests to highest protocol + version they support to deal with problems discovered in HTTP/1.0 + implementations (Section 3.1) + + Charset wildcarding is introduced to avoid explosion of character set + names in accept headers. (Section 14.2) + + A case was missed in the Cache-Control model of HTTP/1.1; s-maxage + was introduced to add this missing case. (Sections 13.4, 14.8, 14.9, + 14.9.3) + + The Cache-Control: max-age directive was not properly defined for + responses. (Section 14.9.3) + + There are situations where a server (especially a proxy) does not + know the full length of a response but is capable of serving a + byterange request. We therefore need a mechanism to allow byteranges + with a content-range not indicating the full length of the message. + (Section 14.16) + + Range request responses would become very verbose if all meta-data + were always returned; by allowing the server to only send needed + headers in a 206 response, this problem can be avoided. (Section + 10.2.7, 13.5.3, and 14.27) + + + + + + +Fielding, et al. Standards Track [Page 173] + +RFC 2616 HTTP/1.1 June 1999 + + + Fix problem with unsatisfiable range requests; there are two cases: + syntactic problems, and range doesn't exist in the document. The 416 + status code was needed to resolve this ambiguity needed to indicate + an error for a byte range request that falls outside of the actual + contents of a document. (Section 10.4.17, 14.16) + + Rewrite of message transmission requirements to make it much harder + for implementors to get it wrong, as the consequences of errors here + can have significant impact on the Internet, and to deal with the + following problems: + + 1. Changing "HTTP/1.1 or later" to "HTTP/1.1", in contexts where + this was incorrectly placing a requirement on the behavior of + an implementation of a future version of HTTP/1.x + + 2. Made it clear that user-agents should retry requests, not + "clients" in general. + + 3. Converted requirements for clients to ignore unexpected 100 + (Continue) responses, and for proxies to forward 100 responses, + into a general requirement for 1xx responses. + + 4. Modified some TCP-specific language, to make it clearer that + non-TCP transports are possible for HTTP. + + 5. Require that the origin server MUST NOT wait for the request + body before it sends a required 100 (Continue) response. + + 6. Allow, rather than require, a server to omit 100 (Continue) if + it has already seen some of the request body. + + 7. Allow servers to defend against denial-of-service attacks and + broken clients. + + This change adds the Expect header and 417 status code. The message + transmission requirements fixes are in sections 8.2, 10.4.18, + 8.1.2.2, 13.11, and 14.20. + + Proxies should be able to add Content-Length when appropriate. + (Section 13.5.2) + + Clean up confusion between 403 and 404 responses. (Section 10.4.4, + 10.4.5, and 10.4.11) + + Warnings could be cached incorrectly, or not updated appropriately. + (Section 13.1.2, 13.2.4, 13.5.2, 13.5.3, 14.9.3, and 14.46) Warning + also needed to be a general header, as PUT or other methods may have + need for it in requests. + + + +Fielding, et al. Standards Track [Page 174] + +RFC 2616 HTTP/1.1 June 1999 + + + Transfer-coding had significant problems, particularly with + interactions with chunked encoding. The solution is that transfer- + codings become as full fledged as content-codings. This involves + adding an IANA registry for transfer-codings (separate from content + codings), a new header field (TE) and enabling trailer headers in the + future. Transfer encoding is a major performance benefit, so it was + worth fixing [39]. TE also solves another, obscure, downward + interoperability problem that could have occurred due to interactions + between authentication trailers, chunked encoding and HTTP/1.0 + clients.(Section 3.6, 3.6.1, and 14.39) + + The PATCH, LINK, UNLINK methods were defined but not commonly + implemented in previous versions of this specification. See RFC 2068 + [33]. + + The Alternates, Content-Version, Derived-From, Link, URI, Public and + Content-Base header fields were defined in previous versions of this + specification, but not commonly implemented. See RFC 2068 [33]. + +20 Index + + Please see the PostScript version of this RFC for the INDEX. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Fielding, et al. Standards Track [Page 175] + +RFC 2616 HTTP/1.1 June 1999 + + +21. Full Copyright Statement + + Copyright (C) The Internet Society (1999). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Fielding, et al. Standards Track [Page 176] + diff --git a/dav/SabreDAV/docs/rfc2617.txt b/dav/SabreDAV/docs/rfc2617.txt new file mode 100644 index 000000000..771aa924a --- /dev/null +++ b/dav/SabreDAV/docs/rfc2617.txt @@ -0,0 +1,1907 @@ + + + + + + +Network Working Group J. Franks +Request for Comments: 2617 Northwestern University +Obsoletes: 2069 P. Hallam-Baker +Category: Standards Track Verisign, Inc. + J. Hostetler + AbiSource, Inc. + S. Lawrence + Agranat Systems, Inc. + P. Leach + Microsoft Corporation + A. Luotonen + Netscape Communications Corporation + L. Stewart + Open Market, Inc. + June 1999 + + + HTTP Authentication: Basic and Digest Access Authentication + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1999). All Rights Reserved. + +Abstract + + "HTTP/1.0", includes the specification for a Basic Access + Authentication scheme. This scheme is not considered to be a secure + method of user authentication (unless used in conjunction with some + external secure system such as SSL [5]), as the user name and + password are passed over the network as cleartext. + + This document also provides the specification for HTTP's + authentication framework, the original Basic authentication scheme + and a scheme based on cryptographic hashes, referred to as "Digest + Access Authentication". It is therefore also intended to serve as a + replacement for RFC 2069 [6]. Some optional elements specified by + RFC 2069 have been removed from this specification due to problems + found since its publication; other new elements have been added for + compatibility, those new elements have been made optional, but are + strongly recommended. + + + +Franks, et al. Standards Track [Page 1] + +RFC 2617 HTTP Authentication June 1999 + + + Like Basic, Digest access authentication verifies that both parties + to a communication know a shared secret (a password); unlike Basic, + this verification can be done without sending the password in the + clear, which is Basic's biggest weakness. As with most other + authentication protocols, the greatest sources of risks are usually + found not in the core protocol itself but in policies and procedures + surrounding its use. + +Table of Contents + + 1 Access Authentication................................ 3 + 1.1 Reliance on the HTTP/1.1 Specification............ 3 + 1.2 Access Authentication Framework................... 3 + 2 Basic Authentication Scheme.......................... 5 + 3 Digest Access Authentication Scheme.................. 6 + 3.1 Introduction...................................... 6 + 3.1.1 Purpose......................................... 6 + 3.1.2 Overall Operation............................... 6 + 3.1.3 Representation of digest values................. 7 + 3.1.4 Limitations..................................... 7 + 3.2 Specification of Digest Headers................... 7 + 3.2.1 The WWW-Authenticate Response Header............ 8 + 3.2.2 The Authorization Request Header................ 11 + 3.2.3 The Authentication-Info Header.................. 15 + 3.3 Digest Operation.................................. 17 + 3.4 Security Protocol Negotiation..................... 18 + 3.5 Example........................................... 18 + 3.6 Proxy-Authentication and Proxy-Authorization...... 19 + 4 Security Considerations.............................. 19 + 4.1 Authentication of Clients using Basic + Authentication.................................... 19 + 4.2 Authentication of Clients using Digest + Authentication.................................... 20 + 4.3 Limited Use Nonce Values.......................... 21 + 4.4 Comparison of Digest with Basic Authentication.... 22 + 4.5 Replay Attacks.................................... 22 + 4.6 Weakness Created by Multiple Authentication + Schemes........................................... 23 + 4.7 Online dictionary attacks......................... 23 + 4.8 Man in the Middle................................. 24 + 4.9 Chosen plaintext attacks.......................... 24 + 4.10 Precomputed dictionary attacks.................... 25 + 4.11 Batch brute force attacks......................... 25 + 4.12 Spoofing by Counterfeit Servers................... 25 + 4.13 Storing passwords................................. 26 + 4.14 Summary........................................... 26 + 5 Sample implementation................................ 27 + 6 Acknowledgments...................................... 31 + + + +Franks, et al. Standards Track [Page 2] + +RFC 2617 HTTP Authentication June 1999 + + + 7 References........................................... 31 + 8 Authors' Addresses................................... 32 + 9 Full Copyright Statement............................. 34 + +1 Access Authentication + +1.1 Reliance on the HTTP/1.1 Specification + + This specification is a companion to the HTTP/1.1 specification [2]. + It uses the augmented BNF section 2.1 of that document, and relies on + both the non-terminals defined in that document and other aspects of + the HTTP/1.1 specification. + +1.2 Access Authentication Framework + + HTTP provides a simple challenge-response authentication mechanism + that MAY be used by a server to challenge a client request and by a + client to provide authentication information. It uses an extensible, + case-insensitive token to identify the authentication scheme, + followed by a comma-separated list of attribute-value pairs which + carry the parameters necessary for achieving authentication via that + scheme. + + auth-scheme = token + auth-param = token "=" ( token | quoted-string ) + + The 401 (Unauthorized) response message is used by an origin server + to challenge the authorization of a user agent. This response MUST + include a WWW-Authenticate header field containing at least one + challenge applicable to the requested resource. The 407 (Proxy + Authentication Required) response message is used by a proxy to + challenge the authorization of a client and MUST include a Proxy- + Authenticate header field containing at least one challenge + applicable to the proxy for the requested resource. + + challenge = auth-scheme 1*SP 1#auth-param + + Note: User agents will need to take special care in parsing the WWW- + Authenticate or Proxy-Authenticate header field value if it contains + more than one challenge, or if more than one WWW-Authenticate header + field is provided, since the contents of a challenge may itself + contain a comma-separated list of authentication parameters. + + The authentication parameter realm is defined for all authentication + schemes: + + realm = "realm" "=" realm-value + realm-value = quoted-string + + + +Franks, et al. Standards Track [Page 3] + +RFC 2617 HTTP Authentication June 1999 + + + The realm directive (case-insensitive) is required for all + authentication schemes that issue a challenge. The realm value + (case-sensitive), in combination with the canonical root URL (the + absoluteURI for the server whose abs_path is empty; see section 5.1.2 + of [2]) of the server being accessed, defines the protection space. + These realms allow the protected resources on a server to be + partitioned into a set of protection spaces, each with its own + authentication scheme and/or authorization database. The realm value + is a string, generally assigned by the origin server, which may have + additional semantics specific to the authentication scheme. Note that + there may be multiple challenges with the same auth-scheme but + different realms. + + A user agent that wishes to authenticate itself with an origin + server--usually, but not necessarily, after receiving a 401 + (Unauthorized)--MAY do so by including an Authorization header field + with the request. A client that wishes to authenticate itself with a + proxy--usually, but not necessarily, after receiving a 407 (Proxy + Authentication Required)--MAY do so by including a Proxy- + Authorization header field with the request. Both the Authorization + field value and the Proxy-Authorization field value consist of + credentials containing the authentication information of the client + for the realm of the resource being requested. The user agent MUST + choose to use one of the challenges with the strongest auth-scheme it + understands and request credentials from the user based upon that + challenge. + + credentials = auth-scheme #auth-param + + Note that many browsers will only recognize Basic and will require + that it be the first auth-scheme presented. Servers should only + include Basic if it is minimally acceptable. + + The protection space determines the domain over which credentials can + be automatically applied. If a prior request has been authorized, the + same credentials MAY be reused for all other requests within that + protection space for a period of time determined by the + authentication scheme, parameters, and/or user preference. Unless + otherwise defined by the authentication scheme, a single protection + space cannot extend outside the scope of its server. + + If the origin server does not wish to accept the credentials sent + with a request, it SHOULD return a 401 (Unauthorized) response. The + response MUST include a WWW-Authenticate header field containing at + least one (possibly new) challenge applicable to the requested + resource. If a proxy does not accept the credentials sent with a + request, it SHOULD return a 407 (Proxy Authentication Required). The + response MUST include a Proxy-Authenticate header field containing a + + + +Franks, et al. Standards Track [Page 4] + +RFC 2617 HTTP Authentication June 1999 + + + (possibly new) challenge applicable to the proxy for the requested + resource. + + The HTTP protocol does not restrict applications to this simple + challenge-response mechanism for access authentication. Additional + mechanisms MAY be used, such as encryption at the transport level or + via message encapsulation, and with additional header fields + specifying authentication information. However, these additional + mechanisms are not defined by this specification. + + Proxies MUST be completely transparent regarding user agent + authentication by origin servers. That is, they must forward the + WWW-Authenticate and Authorization headers untouched, and follow the + rules found in section 14.8 of [2]. Both the Proxy-Authenticate and + the Proxy-Authorization header fields are hop-by-hop headers (see + section 13.5.1 of [2]). + +2 Basic Authentication Scheme + + The "basic" authentication scheme is based on the model that the + client must authenticate itself with a user-ID and a password for + each realm. The realm value should be considered an opaque string + which can only be compared for equality with other realms on that + server. The server will service the request only if it can validate + the user-ID and password for the protection space of the Request-URI. + There are no optional authentication parameters. + + For Basic, the framework above is utilized as follows: + + challenge = "Basic" realm + credentials = "Basic" basic-credentials + + Upon receipt of an unauthorized request for a URI within the + protection space, the origin server MAY respond with a challenge like + the following: + + WWW-Authenticate: Basic realm="WallyWorld" + + where "WallyWorld" is the string assigned by the server to identify + the protection space of the Request-URI. A proxy may respond with the + same challenge using the Proxy-Authenticate header field. + + To receive authorization, the client sends the userid and password, + separated by a single colon (":") character, within a base64 [7] + encoded string in the credentials. + + basic-credentials = base64-user-pass + base64-user-pass = + user-pass = userid ":" password + userid = * + password = *TEXT + + Userids might be case sensitive. + + If the user agent wishes to send the userid "Aladdin" and password + "open sesame", it would use the following header field: + + Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + + A client SHOULD assume that all paths at or deeper than the depth of + the last symbolic element in the path field of the Request-URI also + are within the protection space specified by the Basic realm value of + the current challenge. A client MAY preemptively send the + corresponding Authorization header with requests for resources in + that space without receipt of another challenge from the server. + Similarly, when a client sends a request to a proxy, it may reuse a + userid and password in the Proxy-Authorization header field without + receiving another challenge from the proxy server. See section 4 for + security considerations associated with Basic authentication. + +3 Digest Access Authentication Scheme + +3.1 Introduction + +3.1.1 Purpose + + The protocol referred to as "HTTP/1.0" includes the specification for + a Basic Access Authentication scheme[1]. That scheme is not + considered to be a secure method of user authentication, as the user + name and password are passed over the network in an unencrypted form. + This section provides the specification for a scheme that does not + send the password in cleartext, referred to as "Digest Access + Authentication". + + The Digest Access Authentication scheme is not intended to be a + complete answer to the need for security in the World Wide Web. This + scheme provides no encryption of message content. The intent is + simply to create an access authentication method that avoids the most + serious flaws of Basic authentication. + +3.1.2 Overall Operation + + Like Basic Access Authentication, the Digest scheme is based on a + simple challenge-response paradigm. The Digest scheme challenges + using a nonce value. A valid response contains a checksum (by + + + +Franks, et al. Standards Track [Page 6] + +RFC 2617 HTTP Authentication June 1999 + + + default, the MD5 checksum) of the username, the password, the given + nonce value, the HTTP method, and the requested URI. In this way, the + password is never sent in the clear. Just as with the Basic scheme, + the username and password must be prearranged in some fashion not + addressed by this document. + +3.1.3 Representation of digest values + + An optional header allows the server to specify the algorithm used to + create the checksum or digest. By default the MD5 algorithm is used + and that is the only algorithm described in this document. + + For the purposes of this document, an MD5 digest of 128 bits is + represented as 32 ASCII printable characters. The bits in the 128 bit + digest are converted from most significant to least significant bit, + four bits at a time to their ASCII presentation as follows. Each four + bits is represented by its familiar hexadecimal notation from the + characters 0123456789abcdef. That is, binary 0000 gets represented by + the character '0', 0001, by '1', and so on up to the representation + of 1111 as 'f'. + +3.1.4 Limitations + + The Digest authentication scheme described in this document suffers + from many known limitations. It is intended as a replacement for + Basic authentication and nothing more. It is a password-based system + and (on the server side) suffers from all the same problems of any + password system. In particular, no provision is made in this protocol + for the initial secure arrangement between user and server to + establish the user's password. + + Users and implementors should be aware that this protocol is not as + secure as Kerberos, and not as secure as any client-side private-key + scheme. Nevertheless it is better than nothing, better than what is + commonly used with telnet and ftp, and better than Basic + authentication. + +3.2 Specification of Digest Headers + + The Digest Access Authentication scheme is conceptually similar to + the Basic scheme. The formats of the modified WWW-Authenticate header + line and the Authorization header line are specified below. In + addition, a new header, Authentication-Info, is specified. + + + + + + + + +Franks, et al. Standards Track [Page 7] + +RFC 2617 HTTP Authentication June 1999 + + +3.2.1 The WWW-Authenticate Response Header + + If a server receives a request for an access-protected object, and an + acceptable Authorization header is not sent, the server responds with + a "401 Unauthorized" status code, and a WWW-Authenticate header as + per the framework defined above, which for the digest scheme is + utilized as follows: + + challenge = "Digest" digest-challenge + + digest-challenge = 1#( realm | [ domain ] | nonce | + [ opaque ] |[ stale ] | [ algorithm ] | + [ qop-options ] | [auth-param] ) + + + domain = "domain" "=" <"> URI ( 1*SP URI ) <"> + URI = absoluteURI | abs_path + nonce = "nonce" "=" nonce-value + nonce-value = quoted-string + opaque = "opaque" "=" quoted-string + stale = "stale" "=" ( "true" | "false" ) + algorithm = "algorithm" "=" ( "MD5" | "MD5-sess" | + token ) + qop-options = "qop" "=" <"> 1#qop-value <"> + qop-value = "auth" | "auth-int" | token + + The meanings of the values of the directives used above are as + follows: + + realm + A string to be displayed to users so they know which username and + password to use. This string should contain at least the name of + the host performing the authentication and might additionally + indicate the collection of users who might have access. An example + might be "registered_users@gotham.news.com". + + domain + A quoted, space-separated list of URIs, as specified in RFC XURI + [7], that define the protection space. If a URI is an abs_path, it + is relative to the canonical root URL (see section 1.2 above) of + the server being accessed. An absoluteURI in this list may refer to + a different server than the one being accessed. The client can use + this list to determine the set of URIs for which the same + authentication information may be sent: any URI that has a URI in + this list as a prefix (after both have been made absolute) may be + assumed to be in the same protection space. If this directive is + omitted or its value is empty, the client should assume that the + protection space consists of all URIs on the responding server. + + + +Franks, et al. Standards Track [Page 8] + +RFC 2617 HTTP Authentication June 1999 + + + This directive is not meaningful in Proxy-Authenticate headers, for + which the protection space is always the entire proxy; if present + it should be ignored. + + nonce + A server-specified data string which should be uniquely generated + each time a 401 response is made. It is recommended that this + string be base64 or hexadecimal data. Specifically, since the + string is passed in the header lines as a quoted string, the + double-quote character is not allowed. + + The contents of the nonce are implementation dependent. The quality + of the implementation depends on a good choice. A nonce might, for + example, be constructed as the base 64 encoding of + + time-stamp H(time-stamp ":" ETag ":" private-key) + + where time-stamp is a server-generated time or other non-repeating + value, ETag is the value of the HTTP ETag header associated with + the requested entity, and private-key is data known only to the + server. With a nonce of this form a server would recalculate the + hash portion after receiving the client authentication header and + reject the request if it did not match the nonce from that header + or if the time-stamp value is not recent enough. In this way the + server can limit the time of the nonce's validity. The inclusion of + the ETag prevents a replay request for an updated version of the + resource. (Note: including the IP address of the client in the + nonce would appear to offer the server the ability to limit the + reuse of the nonce to the same client that originally got it. + However, that would break proxy farms, where requests from a single + user often go through different proxies in the farm. Also, IP + address spoofing is not that hard.) + + An implementation might choose not to accept a previously used + nonce or a previously used digest, in order to protect against a + replay attack. Or, an implementation might choose to use one-time + nonces or digests for POST or PUT requests and a time-stamp for GET + requests. For more details on the issues involved see section 4. + of this document. + + The nonce is opaque to the client. + + opaque + A string of data, specified by the server, which should be returned + by the client unchanged in the Authorization header of subsequent + requests with URIs in the same protection space. It is recommended + that this string be base64 or hexadecimal data. + + + + +Franks, et al. Standards Track [Page 9] + +RFC 2617 HTTP Authentication June 1999 + + + stale + A flag, indicating that the previous request from the client was + rejected because the nonce value was stale. If stale is TRUE + (case-insensitive), the client may wish to simply retry the request + with a new encrypted response, without reprompting the user for a + new username and password. The server should only set stale to TRUE + if it receives a request for which the nonce is invalid but with a + valid digest for that nonce (indicating that the client knows the + correct username/password). If stale is FALSE, or anything other + than TRUE, or the stale directive is not present, the username + and/or password are invalid, and new values must be obtained. + + algorithm + A string indicating a pair of algorithms used to produce the digest + and a checksum. If this is not present it is assumed to be "MD5". + If the algorithm is not understood, the challenge should be ignored + (and a different one used, if there is more than one). + + In this document the string obtained by applying the digest + algorithm to the data "data" with secret "secret" will be denoted + by KD(secret, data), and the string obtained by applying the + checksum algorithm to the data "data" will be denoted H(data). The + notation unq(X) means the value of the quoted-string X without the + surrounding quotes. + + For the "MD5" and "MD5-sess" algorithms + + H(data) = MD5(data) + + and + + KD(secret, data) = H(concat(secret, ":", data)) + + i.e., the digest is the MD5 of the secret concatenated with a colon + concatenated with the data. The "MD5-sess" algorithm is intended to + allow efficient 3rd party authentication servers; for the + difference in usage, see the description in section 3.2.2.2. + + qop-options + This directive is optional, but is made so only for backward + compatibility with RFC 2069 [6]; it SHOULD be used by all + implementations compliant with this version of the Digest scheme. + If present, it is a quoted string of one or more tokens indicating + the "quality of protection" values supported by the server. The + value "auth" indicates authentication; the value "auth-int" + indicates authentication with integrity protection; see the + + + + + +Franks, et al. Standards Track [Page 10] + +RFC 2617 HTTP Authentication June 1999 + + + descriptions below for calculating the response directive value for + the application of this choice. Unrecognized options MUST be + ignored. + + auth-param + This directive allows for future extensions. Any unrecognized + directive MUST be ignored. + +3.2.2 The Authorization Request Header + + The client is expected to retry the request, passing an Authorization + header line, which is defined according to the framework above, + utilized as follows. + + credentials = "Digest" digest-response + digest-response = 1#( username | realm | nonce | digest-uri + | response | [ algorithm ] | [cnonce] | + [opaque] | [message-qop] | + [nonce-count] | [auth-param] ) + + username = "username" "=" username-value + username-value = quoted-string + digest-uri = "uri" "=" digest-uri-value + digest-uri-value = request-uri ; As specified by HTTP/1.1 + message-qop = "qop" "=" qop-value + cnonce = "cnonce" "=" cnonce-value + cnonce-value = nonce-value + nonce-count = "nc" "=" nc-value + nc-value = 8LHEX + response = "response" "=" request-digest + request-digest = <"> 32LHEX <"> + LHEX = "0" | "1" | "2" | "3" | + "4" | "5" | "6" | "7" | + "8" | "9" | "a" | "b" | + "c" | "d" | "e" | "f" + + The values of the opaque and algorithm fields must be those supplied + in the WWW-Authenticate response header for the entity being + requested. + + response + A string of 32 hex digits computed as defined below, which proves + that the user knows a password + + username + The user's name in the specified realm. + + + + + +Franks, et al. Standards Track [Page 11] + +RFC 2617 HTTP Authentication June 1999 + + + digest-uri + The URI from Request-URI of the Request-Line; duplicated here + because proxies are allowed to change the Request-Line in transit. + + qop + Indicates what "quality of protection" the client has applied to + the message. If present, its value MUST be one of the alternatives + the server indicated it supports in the WWW-Authenticate header. + These values affect the computation of the request-digest. Note + that this is a single token, not a quoted list of alternatives as + in WWW- Authenticate. This directive is optional in order to + preserve backward compatibility with a minimal implementation of + RFC 2069 [6], but SHOULD be used if the server indicated that qop + is supported by providing a qop directive in the WWW-Authenticate + header field. + + cnonce + This MUST be specified if a qop directive is sent (see above), and + MUST NOT be specified if the server did not send a qop directive in + the WWW-Authenticate header field. The cnonce-value is an opaque + quoted string value provided by the client and used by both client + and server to avoid chosen plaintext attacks, to provide mutual + authentication, and to provide some message integrity protection. + See the descriptions below of the calculation of the response- + digest and request-digest values. + + nonce-count + This MUST be specified if a qop directive is sent (see above), and + MUST NOT be specified if the server did not send a qop directive in + the WWW-Authenticate header field. The nc-value is the hexadecimal + count of the number of requests (including the current request) + that the client has sent with the nonce value in this request. For + example, in the first request sent in response to a given nonce + value, the client sends "nc=00000001". The purpose of this + directive is to allow the server to detect request replays by + maintaining its own copy of this count - if the same nc-value is + seen twice, then the request is a replay. See the description + below of the construction of the request-digest value. + + auth-param + This directive allows for future extensions. Any unrecognized + directive MUST be ignored. + + If a directive or its value is improper, or required directives are + missing, the proper response is 400 Bad Request. If the request- + digest is invalid, then a login failure should be logged, since + repeated login failures from a single client may indicate an attacker + attempting to guess passwords. + + + +Franks, et al. Standards Track [Page 12] + +RFC 2617 HTTP Authentication June 1999 + + + The definition of request-digest above indicates the encoding for its + value. The following definitions show how the value is computed. + +3.2.2.1 Request-Digest + + If the "qop" value is "auth" or "auth-int": + + request-digest = <"> < KD ( H(A1), unq(nonce-value) + ":" nc-value + ":" unq(cnonce-value) + ":" unq(qop-value) + ":" H(A2) + ) <"> + + If the "qop" directive is not present (this construction is for + compatibility with RFC 2069): + + request-digest = + <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > + <"> + + See below for the definitions for A1 and A2. + +3.2.2.2 A1 + + If the "algorithm" directive's value is "MD5" or is unspecified, then + A1 is: + + A1 = unq(username-value) ":" unq(realm-value) ":" passwd + + where + + passwd = < user's password > + + If the "algorithm" directive's value is "MD5-sess", then A1 is + calculated only once - on the first request by the client following + receipt of a WWW-Authenticate challenge from the server. It uses the + server nonce from that challenge, and the first client nonce value to + construct A1 as follows: + + A1 = H( unq(username-value) ":" unq(realm-value) + ":" passwd ) + ":" unq(nonce-value) ":" unq(cnonce-value) + + This creates a 'session key' for the authentication of subsequent + requests and responses which is different for each "authentication + session", thus limiting the amount of material hashed with any one + key. (Note: see further discussion of the authentication session in + + + +Franks, et al. Standards Track [Page 13] + +RFC 2617 HTTP Authentication June 1999 + + + section 3.3.) Because the server need only use the hash of the user + credentials in order to create the A1 value, this construction could + be used in conjunction with a third party authentication service so + that the web server would not need the actual password value. The + specification of such a protocol is beyond the scope of this + specification. + +3.2.2.3 A2 + + If the "qop" directive's value is "auth" or is unspecified, then A2 + is: + + A2 = Method ":" digest-uri-value + + If the "qop" value is "auth-int", then A2 is: + + A2 = Method ":" digest-uri-value ":" H(entity-body) + +3.2.2.4 Directive values and quoted-string + + Note that the value of many of the directives, such as "username- + value", are defined as a "quoted-string". However, the "unq" notation + indicates that surrounding quotation marks are removed in forming the + string A1. Thus if the Authorization header includes the fields + + username="Mufasa", realm=myhost@testrealm.com + + and the user Mufasa has password "Circle Of Life" then H(A1) would be + H(Mufasa:myhost@testrealm.com:Circle Of Life) with no quotation marks + in the digested string. + + No white space is allowed in any of the strings to which the digest + function H() is applied unless that white space exists in the quoted + strings or entity body whose contents make up the string to be + digested. For example, the string A1 illustrated above must be + + Mufasa:myhost@testrealm.com:Circle Of Life + + with no white space on either side of the colons, but with the white + space between the words used in the password value. Likewise, the + other strings digested by H() must not have white space on either + side of the colons which delimit their fields unless that white space + was in the quoted strings or entity body being digested. + + Also note that if integrity protection is applied (qop=auth-int), the + H(entity-body) is the hash of the entity body, not the message body - + it is computed before any transfer encoding is applied by the sender + + + + +Franks, et al. Standards Track [Page 14] + +RFC 2617 HTTP Authentication June 1999 + + + and after it has been removed by the recipient. Note that this + includes multipart boundaries and embedded headers in each part of + any multipart content-type. + +3.2.2.5 Various considerations + + The "Method" value is the HTTP request method as specified in section + 5.1.1 of [2]. The "request-uri" value is the Request-URI from the + request line as specified in section 5.1.2 of [2]. This may be "*", + an "absoluteURL" or an "abs_path" as specified in section 5.1.2 of + [2], but it MUST agree with the Request-URI. In particular, it MUST + be an "absoluteURL" if the Request-URI is an "absoluteURL". The + "cnonce-value" is an optional client-chosen value whose purpose is + to foil chosen plaintext attacks. + + The authenticating server must assure that the resource designated by + the "uri" directive is the same as the resource specified in the + Request-Line; if they are not, the server SHOULD return a 400 Bad + Request error. (Since this may be a symptom of an attack, server + implementers may want to consider logging such errors.) The purpose + of duplicating information from the request URL in this field is to + deal with the possibility that an intermediate proxy may alter the + client's Request-Line. This altered (but presumably semantically + equivalent) request would not result in the same digest as that + calculated by the client. + + Implementers should be aware of how authenticated transactions + interact with shared caches. The HTTP/1.1 protocol specifies that + when a shared cache (see section 13.7 of [2]) has received a request + containing an Authorization header and a response from relaying that + request, it MUST NOT return that response as a reply to any other + request, unless one of two Cache-Control (see section 14.9 of [2]) + directives was present in the response. If the original response + included the "must-revalidate" Cache-Control directive, the cache MAY + use the entity of that response in replying to a subsequent request, + but MUST first revalidate it with the origin server, using the + request headers from the new request to allow the origin server to + authenticate the new request. Alternatively, if the original response + included the "public" Cache-Control directive, the response entity + MAY be returned in reply to any subsequent request. + +3.2.3 The Authentication-Info Header + + The Authentication-Info header is used by the server to communicate + some information regarding the successful authentication in the + response. + + + + + +Franks, et al. Standards Track [Page 15] + +RFC 2617 HTTP Authentication June 1999 + + + AuthenticationInfo = "Authentication-Info" ":" auth-info + auth-info = 1#(nextnonce | [ message-qop ] + | [ response-auth ] | [ cnonce ] + | [nonce-count] ) + nextnonce = "nextnonce" "=" nonce-value + response-auth = "rspauth" "=" response-digest + response-digest = <"> *LHEX <"> + + The value of the nextnonce directive is the nonce the server wishes + the client to use for a future authentication response. The server + may send the Authentication-Info header with a nextnonce field as a + means of implementing one-time or otherwise changing nonces. If the + nextnonce field is present the client SHOULD use it when constructing + the Authorization header for its next request. Failure of the client + to do so may result in a request to re-authenticate from the server + with the "stale=TRUE". + + Server implementations should carefully consider the performance + implications of the use of this mechanism; pipelined requests will + not be possible if every response includes a nextnonce directive + that must be used on the next request received by the server. + Consideration should be given to the performance vs. security + tradeoffs of allowing an old nonce value to be used for a limited + time to permit request pipelining. Use of the nonce-count can + retain most of the security advantages of a new server nonce + without the deleterious affects on pipelining. + + message-qop + Indicates the "quality of protection" options applied to the + response by the server. The value "auth" indicates authentication; + the value "auth-int" indicates authentication with integrity + protection. The server SHOULD use the same value for the message- + qop directive in the response as was sent by the client in the + corresponding request. + + The optional response digest in the "response-auth" directive + supports mutual authentication -- the server proves that it knows the + user's secret, and with qop=auth-int also provides limited integrity + protection of the response. The "response-digest" value is calculated + as for the "request-digest" in the Authorization header, except that + if "qop=auth" or is not specified in the Authorization header for the + request, A2 is + + A2 = ":" digest-uri-value + + and if "qop=auth-int", then A2 is + + A2 = ":" digest-uri-value ":" H(entity-body) + + + +Franks, et al. Standards Track [Page 16] + +RFC 2617 HTTP Authentication June 1999 + + + where "digest-uri-value" is the value of the "uri" directive on the + Authorization header in the request. The "cnonce-value" and "nc- + value" MUST be the ones for the client request to which this message + is the response. The "response-auth", "cnonce", and "nonce-count" + directives MUST BE present if "qop=auth" or "qop=auth-int" is + specified. + + The Authentication-Info header is allowed in the trailer of an HTTP + message transferred via chunked transfer-coding. + +3.3 Digest Operation + + Upon receiving the Authorization header, the server may check its + validity by looking up the password that corresponds to the submitted + username. Then, the server must perform the same digest operation + (e.g., MD5) performed by the client, and compare the result to the + given request-digest value. + + Note that the HTTP server does not actually need to know the user's + cleartext password. As long as H(A1) is available to the server, the + validity of an Authorization header may be verified. + + The client response to a WWW-Authenticate challenge for a protection + space starts an authentication session with that protection space. + The authentication session lasts until the client receives another + WWW-Authenticate challenge from any server in the protection space. A + client should remember the username, password, nonce, nonce count and + opaque values associated with an authentication session to use to + construct the Authorization header in future requests within that + protection space. The Authorization header may be included + preemptively; doing so improves server efficiency and avoids extra + round trips for authentication challenges. The server may choose to + accept the old Authorization header information, even though the + nonce value included might not be fresh. Alternatively, the server + may return a 401 response with a new nonce value, causing the client + to retry the request; by specifying stale=TRUE with this response, + the server tells the client to retry with the new nonce, but without + prompting for a new username and password. + + Because the client is required to return the value of the opaque + directive given to it by the server for the duration of a session, + the opaque data may be used to transport authentication session state + information. (Note that any such use can also be accomplished more + easily and safely by including the state in the nonce.) For example, + a server could be responsible for authenticating content that + actually sits on another server. It would achieve this by having the + first 401 response include a domain directive whose value includes a + URI on the second server, and an opaque directive whose value + + + +Franks, et al. Standards Track [Page 17] + +RFC 2617 HTTP Authentication June 1999 + + + contains the state information. The client will retry the request, at + which time the server might respond with a 301/302 redirection, + pointing to the URI on the second server. The client will follow the + redirection, and pass an Authorization header , including the + data. + + As with the basic scheme, proxies must be completely transparent in + the Digest access authentication scheme. That is, they must forward + the WWW-Authenticate, Authentication-Info and Authorization headers + untouched. If a proxy wants to authenticate a client before a request + is forwarded to the server, it can be done using the Proxy- + Authenticate and Proxy-Authorization headers described in section 3.6 + below. + +3.4 Security Protocol Negotiation + + It is useful for a server to be able to know which security schemes a + client is capable of handling. + + It is possible that a server may want to require Digest as its + authentication method, even if the server does not know that the + client supports it. A client is encouraged to fail gracefully if the + server specifies only authentication schemes it cannot handle. + +3.5 Example + + The following example assumes that an access-protected document is + being requested from the server via a GET request. The URI of the + document is "http://www.nowhere.org/dir/index.html". Both client and + server know that the username for this document is "Mufasa", and the + password is "Circle Of Life" (with one space between each of the + three words). + + The first time the client requests the document, no Authorization + header is sent, so the server responds with: + + HTTP/1.1 401 Unauthorized + WWW-Authenticate: Digest + realm="testrealm@host.com", + qop="auth,auth-int", + nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", + opaque="5ccc069c403ebaf9f0171e9517f40e41" + + The client may prompt the user for the username and password, after + which it will respond with a new request, including the following + Authorization header: + + + + + +Franks, et al. Standards Track [Page 18] + +RFC 2617 HTTP Authentication June 1999 + + + Authorization: Digest username="Mufasa", + realm="testrealm@host.com", + nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", + uri="/dir/index.html", + qop=auth, + nc=00000001, + cnonce="0a4f113b", + response="6629fae49393a05397450978507c4ef1", + opaque="5ccc069c403ebaf9f0171e9517f40e41" + +3.6 Proxy-Authentication and Proxy-Authorization + + The digest authentication scheme may also be used for authenticating + users to proxies, proxies to proxies, or proxies to origin servers by + use of the Proxy-Authenticate and Proxy-Authorization headers. These + headers are instances of the Proxy-Authenticate and Proxy- + Authorization headers specified in sections 10.33 and 10.34 of the + HTTP/1.1 specification [2] and their behavior is subject to + restrictions described there. The transactions for proxy + authentication are very similar to those already described. Upon + receiving a request which requires authentication, the proxy/server + must issue the "407 Proxy Authentication Required" response with a + "Proxy-Authenticate" header. The digest-challenge used in the + Proxy-Authenticate header is the same as that for the WWW- + Authenticate header as defined above in section 3.2.1. + + The client/proxy must then re-issue the request with a Proxy- + Authorization header, with directives as specified for the + Authorization header in section 3.2.2 above. + + On subsequent responses, the server sends Proxy-Authentication-Info + with directives the same as those for the Authentication-Info header + field. + + Note that in principle a client could be asked to authenticate itself + to both a proxy and an end-server, but never in the same response. + +4 Security Considerations + +4.1 Authentication of Clients using Basic Authentication + + The Basic authentication scheme is not a secure method of user + authentication, nor does it in any way protect the entity, which is + transmitted in cleartext across the physical network used as the + carrier. HTTP does not prevent additional authentication schemes and + encryption mechanisms from being employed to increase security or the + addition of enhancements (such as schemes to use one-time passwords) + to Basic authentication. + + + +Franks, et al. Standards Track [Page 19] + +RFC 2617 HTTP Authentication June 1999 + + + The most serious flaw in Basic authentication is that it results in + the essentially cleartext transmission of the user's password over + the physical network. It is this problem which Digest Authentication + attempts to address. + + Because Basic authentication involves the cleartext transmission of + passwords it SHOULD NOT be used (without enhancements) to protect + sensitive or valuable information. + + A common use of Basic authentication is for identification purposes + -- requiring the user to provide a user name and password as a means + of identification, for example, for purposes of gathering accurate + usage statistics on a server. When used in this way it is tempting to + think that there is no danger in its use if illicit access to the + protected documents is not a major concern. This is only correct if + the server issues both user name and password to the users and in + particular does not allow the user to choose his or her own password. + The danger arises because naive users frequently reuse a single + password to avoid the task of maintaining multiple passwords. + + If a server permits users to select their own passwords, then the + threat is not only unauthorized access to documents on the server but + also unauthorized access to any other resources on other systems that + the user protects with the same password. Furthermore, in the + server's password database, many of the passwords may also be users' + passwords for other sites. The owner or administrator of such a + system could therefore expose all users of the system to the risk of + unauthorized access to all those sites if this information is not + maintained in a secure fashion. + + Basic Authentication is also vulnerable to spoofing by counterfeit + servers. If a user can be led to believe that he is connecting to a + host containing information protected by Basic authentication when, + in fact, he is connecting to a hostile server or gateway, then the + attacker can request a password, store it for later use, and feign an + error. This type of attack is not possible with Digest + Authentication. Server implementers SHOULD guard against the + possibility of this sort of counterfeiting by gateways or CGI + scripts. In particular it is very dangerous for a server to simply + turn over a connection to a gateway. That gateway can then use the + persistent connection mechanism to engage in multiple transactions + with the client while impersonating the original server in a way that + is not detectable by the client. + +4.2 Authentication of Clients using Digest Authentication + + Digest Authentication does not provide a strong authentication + mechanism, when compared to public key based mechanisms, for example. + + + +Franks, et al. Standards Track [Page 20] + +RFC 2617 HTTP Authentication June 1999 + + + However, it is significantly stronger than (e.g.) CRAM-MD5, which has + been proposed for use with LDAP [10], POP and IMAP (see RFC 2195 + [9]). It is intended to replace the much weaker and even more + dangerous Basic mechanism. + + Digest Authentication offers no confidentiality protection beyond + protecting the actual password. All of the rest of the request and + response are available to an eavesdropper. + + Digest Authentication offers only limited integrity protection for + the messages in either direction. If qop=auth-int mechanism is used, + those parts of the message used in the calculation of the WWW- + Authenticate and Authorization header field response directive values + (see section 3.2 above) are protected. Most header fields and their + values could be modified as a part of a man-in-the-middle attack. + + Many needs for secure HTTP transactions cannot be met by Digest + Authentication. For those needs TLS or SHTTP are more appropriate + protocols. In particular Digest authentication cannot be used for any + transaction requiring confidentiality protection. Nevertheless many + functions remain for which Digest authentication is both useful and + appropriate. Any service in present use that uses Basic should be + switched to Digest as soon as practical. + +4.3 Limited Use Nonce Values + + The Digest scheme uses a server-specified nonce to seed the + generation of the request-digest value (as specified in section + 3.2.2.1 above). As shown in the example nonce in section 3.2.1, the + server is free to construct the nonce such that it may only be used + from a particular client, for a particular resource, for a limited + period of time or number of uses, or any other restrictions. Doing + so strengthens the protection provided against, for example, replay + attacks (see 4.5). However, it should be noted that the method + chosen for generating and checking the nonce also has performance and + resource implications. For example, a server may choose to allow + each nonce value to be used only once by maintaining a record of + whether or not each recently issued nonce has been returned and + sending a next-nonce directive in the Authentication-Info header + field of every response. This protects against even an immediate + replay attack, but has a high cost checking nonce values, and perhaps + more important will cause authentication failures for any pipelined + requests (presumably returning a stale nonce indication). Similarly, + incorporating a request-specific element such as the Etag value for a + resource limits the use of the nonce to that version of the resource + and also defeats pipelining. Thus it may be useful to do so for + methods with side effects but have unacceptable performance for those + that do not. + + + +Franks, et al. Standards Track [Page 21] + +RFC 2617 HTTP Authentication June 1999 + + +4.4 Comparison of Digest with Basic Authentication + + Both Digest and Basic Authentication are very much on the weak end of + the security strength spectrum. But a comparison between the two + points out the utility, even necessity, of replacing Basic by Digest. + + The greatest threat to the type of transactions for which these + protocols are used is network snooping. This kind of transaction + might involve, for example, online access to a database whose use is + restricted to paying subscribers. With Basic authentication an + eavesdropper can obtain the password of the user. This not only + permits him to access anything in the database, but, often worse, + will permit access to anything else the user protects with the same + password. + + By contrast, with Digest Authentication the eavesdropper only gets + access to the transaction in question and not to the user's password. + The information gained by the eavesdropper would permit a replay + attack, but only with a request for the same document, and even that + may be limited by the server's choice of nonce. + +4.5 Replay Attacks + + A replay attack against Digest authentication would usually be + pointless for a simple GET request since an eavesdropper would + already have seen the only document he could obtain with a replay. + This is because the URI of the requested document is digested in the + client request and the server will only deliver that document. By + contrast under Basic Authentication once the eavesdropper has the + user's password, any document protected by that password is open to + him. + + Thus, for some purposes, it is necessary to protect against replay + attacks. A good Digest implementation can do this in various ways. + The server created "nonce" value is implementation dependent, but if + it contains a digest of the client IP, a time-stamp, the resource + ETag, and a private server key (as recommended above) then a replay + attack is not simple. An attacker must convince the server that the + request is coming from a false IP address and must cause the server + to deliver the document to an IP address different from the address + to which it believes it is sending the document. An attack can only + succeed in the period before the time-stamp expires. Digesting the + client IP and time-stamp in the nonce permits an implementation which + does not maintain state between transactions. + + For applications where no possibility of replay attack can be + tolerated the server can use one-time nonce values which will not be + honored for a second use. This requires the overhead of the server + + + +Franks, et al. Standards Track [Page 22] + +RFC 2617 HTTP Authentication June 1999 + + + remembering which nonce values have been used until the nonce time- + stamp (and hence the digest built with it) has expired, but it + effectively protects against replay attacks. + + An implementation must give special attention to the possibility of + replay attacks with POST and PUT requests. Unless the server employs + one-time or otherwise limited-use nonces and/or insists on the use of + the integrity protection of qop=auth-int, an attacker could replay + valid credentials from a successful request with counterfeit form + data or other message body. Even with the use of integrity protection + most metadata in header fields is not protected. Proper nonce + generation and checking provides some protection against replay of + previously used valid credentials, but see 4.8. + +4.6 Weakness Created by Multiple Authentication Schemes + + An HTTP/1.1 server may return multiple challenges with a 401 + (Authenticate) response, and each challenge may use a different + auth-scheme. A user agent MUST choose to use the strongest auth- + scheme it understands and request credentials from the user based + upon that challenge. + + Note that many browsers will only recognize Basic and will require + that it be the first auth-scheme presented. Servers should only + include Basic if it is minimally acceptable. + + When the server offers choices of authentication schemes using the + WWW-Authenticate header, the strength of the resulting authentication + is only as good as that of the of the weakest of the authentication + schemes. See section 4.8 below for discussion of particular attack + scenarios that exploit multiple authentication schemes. + +4.7 Online dictionary attacks + + If the attacker can eavesdrop, then it can test any overheard + nonce/response pairs against a list of common words. Such a list is + usually much smaller than the total number of possible passwords. The + cost of computing the response for each password on the list is paid + once for each challenge. + + The server can mitigate this attack by not allowing users to select + passwords that are in a dictionary. + + + + + + + + + +Franks, et al. Standards Track [Page 23] + +RFC 2617 HTTP Authentication June 1999 + + +4.8 Man in the Middle + + Both Basic and Digest authentication are vulnerable to "man in the + middle" (MITM) attacks, for example, from a hostile or compromised + proxy. Clearly, this would present all the problems of eavesdropping. + But it also offers some additional opportunities to the attacker. + + A possible man-in-the-middle attack would be to add a weak + authentication scheme to the set of choices, hoping that the client + will use one that exposes the user's credentials (e.g. password). For + this reason, the client should always use the strongest scheme that + it understands from the choices offered. + + An even better MITM attack would be to remove all offered choices, + replacing them with a challenge that requests only Basic + authentication, then uses the cleartext credentials from the Basic + authentication to authenticate to the origin server using the + stronger scheme it requested. A particularly insidious way to mount + such a MITM attack would be to offer a "free" proxy caching service + to gullible users. + + User agents should consider measures such as presenting a visual + indication at the time of the credentials request of what + authentication scheme is to be used, or remembering the strongest + authentication scheme ever requested by a server and produce a + warning message before using a weaker one. It might also be a good + idea for the user agent to be configured to demand Digest + authentication in general, or from specific sites. + + Or, a hostile proxy might spoof the client into making a request the + attacker wanted rather than one the client wanted. Of course, this is + still much harder than a comparable attack against Basic + Authentication. + +4.9 Chosen plaintext attacks + + With Digest authentication, a MITM or a malicious server can + arbitrarily choose the nonce that the client will use to compute the + response. This is called a "chosen plaintext" attack. The ability to + choose the nonce is known to make cryptanalysis much easier [8]. + + However, no way to analyze the MD5 one-way function used by Digest + using chosen plaintext is currently known. + + The countermeasure against this attack is for clients to be + configured to require the use of the optional "cnonce" directive; + this allows the client to vary the input to the hash in a way not + chosen by the attacker. + + + +Franks, et al. Standards Track [Page 24] + +RFC 2617 HTTP Authentication June 1999 + + +4.10 Precomputed dictionary attacks + + With Digest authentication, if the attacker can execute a chosen + plaintext attack, the attacker can precompute the response for many + common words to a nonce of its choice, and store a dictionary of + (response, password) pairs. Such precomputation can often be done in + parallel on many machines. It can then use the chosen plaintext + attack to acquire a response corresponding to that challenge, and + just look up the password in the dictionary. Even if most passwords + are not in the dictionary, some might be. Since the attacker gets to + pick the challenge, the cost of computing the response for each + password on the list can be amortized over finding many passwords. A + dictionary with 100 million password/response pairs would take about + 3.2 gigabytes of disk storage. + + The countermeasure against this attack is to for clients to be + configured to require the use of the optional "cnonce" directive. + +4.11 Batch brute force attacks + + With Digest authentication, a MITM can execute a chosen plaintext + attack, and can gather responses from many users to the same nonce. + It can then find all the passwords within any subset of password + space that would generate one of the nonce/response pairs in a single + pass over that space. It also reduces the time to find the first + password by a factor equal to the number of nonce/response pairs + gathered. This search of the password space can often be done in + parallel on many machines, and even a single machine can search large + subsets of the password space very quickly -- reports exist of + searching all passwords with six or fewer letters in a few hours. + + The countermeasure against this attack is to for clients to be + configured to require the use of the optional "cnonce" directive. + +4.12 Spoofing by Counterfeit Servers + + Basic Authentication is vulnerable to spoofing by counterfeit + servers. If a user can be led to believe that she is connecting to a + host containing information protected by a password she knows, when + in fact she is connecting to a hostile server, then the hostile + server can request a password, store it away for later use, and feign + an error. This type of attack is more difficult with Digest + Authentication -- but the client must know to demand that Digest + authentication be used, perhaps using some of the techniques + described above to counter "man-in-the-middle" attacks. Again, the + user can be helped in detecting this attack by a visual indication of + the authentication mechanism in use with appropriate guidance in + interpreting the implications of each scheme. + + + +Franks, et al. Standards Track [Page 25] + +RFC 2617 HTTP Authentication June 1999 + + +4.13 Storing passwords + + Digest authentication requires that the authenticating agent (usually + the server) store some data derived from the user's name and password + in a "password file" associated with a given realm. Normally this + might contain pairs consisting of username and H(A1), where H(A1) is + the digested value of the username, realm, and password as described + above. + + The security implications of this are that if this password file is + compromised, then an attacker gains immediate access to documents on + the server using this realm. Unlike, say a standard UNIX password + file, this information need not be decrypted in order to access + documents in the server realm associated with this file. On the other + hand, decryption, or more likely a brute force attack, would be + necessary to obtain the user's password. This is the reason that the + realm is part of the digested data stored in the password file. It + means that if one Digest authentication password file is compromised, + it does not automatically compromise others with the same username + and password (though it does expose them to brute force attack). + + There are two important security consequences of this. First the + password file must be protected as if it contained unencrypted + passwords, because for the purpose of accessing documents in its + realm, it effectively does. + + A second consequence of this is that the realm string should be + unique among all realms which any single user is likely to use. In + particular a realm string should include the name of the host doing + the authentication. The inability of the client to authenticate the + server is a weakness of Digest Authentication. + +4.14 Summary + + By modern cryptographic standards Digest Authentication is weak. But + for a large range of purposes it is valuable as a replacement for + Basic Authentication. It remedies some, but not all, weaknesses of + Basic Authentication. Its strength may vary depending on the + implementation. In particular the structure of the nonce (which is + dependent on the server implementation) may affect the ease of + mounting a replay attack. A range of server options is appropriate + since, for example, some implementations may be willing to accept the + server overhead of one-time nonces or digests to eliminate the + possibility of replay. Others may satisfied with a nonce like the one + recommended above restricted to a single IP address and a single ETag + or with a limited lifetime. + + + + + +Franks, et al. Standards Track [Page 26] + +RFC 2617 HTTP Authentication June 1999 + + + The bottom line is that *any* compliant implementation will be + relatively weak by cryptographic standards, but *any* compliant + implementation will be far superior to Basic Authentication. + +5 Sample implementation + + The following code implements the calculations of H(A1), H(A2), + request-digest and response-digest, and a test program which computes + the values used in the example of section 3.5. It uses the MD5 + implementation from RFC 1321. + + File "digcalc.h": + +#define HASHLEN 16 +typedef char HASH[HASHLEN]; +#define HASHHEXLEN 32 +typedef char HASHHEX[HASHHEXLEN+1]; +#define IN +#define OUT + +/* calculate H(A1) as per HTTP Digest spec */ +void DigestCalcHA1( + IN char * pszAlg, + IN char * pszUserName, + IN char * pszRealm, + IN char * pszPassword, + IN char * pszNonce, + IN char * pszCNonce, + OUT HASHHEX SessionKey + ); + +/* calculate request-digest/response-digest as per HTTP Digest spec */ +void DigestCalcResponse( + IN HASHHEX HA1, /* H(A1) */ + IN char * pszNonce, /* nonce from server */ + IN char * pszNonceCount, /* 8 hex digits */ + IN char * pszCNonce, /* client nonce */ + IN char * pszQop, /* qop-value: "", "auth", "auth-int" */ + IN char * pszMethod, /* method from the request */ + IN char * pszDigestUri, /* requested URL */ + IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ + OUT HASHHEX Response /* request-digest or response-digest */ + ); + +File "digcalc.c": + +#include +#include + + + +Franks, et al. Standards Track [Page 27] + +RFC 2617 HTTP Authentication June 1999 + + +#include +#include "digcalc.h" + +void CvtHex( + IN HASH Bin, + OUT HASHHEX Hex + ) +{ + unsigned short i; + unsigned char j; + + for (i = 0; i < HASHLEN; i++) { + j = (Bin[i] >> 4) & 0xf; + if (j <= 9) + Hex[i*2] = (j + '0'); + else + Hex[i*2] = (j + 'a' - 10); + j = Bin[i] & 0xf; + if (j <= 9) + Hex[i*2+1] = (j + '0'); + else + Hex[i*2+1] = (j + 'a' - 10); + }; + Hex[HASHHEXLEN] = '\0'; +}; + +/* calculate H(A1) as per spec */ +void DigestCalcHA1( + IN char * pszAlg, + IN char * pszUserName, + IN char * pszRealm, + IN char * pszPassword, + IN char * pszNonce, + IN char * pszCNonce, + OUT HASHHEX SessionKey + ) +{ + MD5_CTX Md5Ctx; + HASH HA1; + + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword)); + MD5Final(HA1, &Md5Ctx); + if (stricmp(pszAlg, "md5-sess") == 0) { + + + +Franks, et al. Standards Track [Page 28] + +RFC 2617 HTTP Authentication June 1999 + + + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, HA1, HASHLEN); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce)); + MD5Final(HA1, &Md5Ctx); + }; + CvtHex(HA1, SessionKey); +}; + +/* calculate request-digest/response-digest as per HTTP Digest spec */ +void DigestCalcResponse( + IN HASHHEX HA1, /* H(A1) */ + IN char * pszNonce, /* nonce from server */ + IN char * pszNonceCount, /* 8 hex digits */ + IN char * pszCNonce, /* client nonce */ + IN char * pszQop, /* qop-value: "", "auth", "auth-int" */ + IN char * pszMethod, /* method from the request */ + IN char * pszDigestUri, /* requested URL */ + IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ + OUT HASHHEX Response /* request-digest or response-digest */ + ) +{ + MD5_CTX Md5Ctx; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + + // calculate H(A2) + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri)); + if (stricmp(pszQop, "auth-int") == 0) { + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); + }; + MD5Final(HA2, &Md5Ctx); + CvtHex(HA2, HA2Hex); + + // calculate response + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, HA1, HASHHEXLEN); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce)); + MD5Update(&Md5Ctx, ":", 1); + if (*pszQop) { + + + +Franks, et al. Standards Track [Page 29] + +RFC 2617 HTTP Authentication June 1999 + + + MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce)); + MD5Update(&Md5Ctx, ":", 1); + MD5Update(&Md5Ctx, pszQop, strlen(pszQop)); + MD5Update(&Md5Ctx, ":", 1); + }; + MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); + MD5Final(RespHash, &Md5Ctx); + CvtHex(RespHash, Response); +}; + +File "digtest.c": + + +#include +#include "digcalc.h" + +void main(int argc, char ** argv) { + + char * pszNonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093"; + char * pszCNonce = "0a4f113b"; + char * pszUser = "Mufasa"; + char * pszRealm = "testrealm@host.com"; + char * pszPass = "Circle Of Life"; + char * pszAlg = "md5"; + char szNonceCount[9] = "00000001"; + char * pszMethod = "GET"; + char * pszQop = "auth"; + char * pszURI = "/dir/index.html"; + HASHHEX HA1; + HASHHEX HA2 = ""; + HASHHEX Response; + + DigestCalcHA1(pszAlg, pszUser, pszRealm, pszPass, pszNonce, +pszCNonce, HA1); + DigestCalcResponse(HA1, pszNonce, szNonceCount, pszCNonce, pszQop, + pszMethod, pszURI, HA2, Response); + printf("Response = %s\n", Response); +}; + + + + + + + + + + + +Franks, et al. Standards Track [Page 30] + +RFC 2617 HTTP Authentication June 1999 + + +6 Acknowledgments + + Eric W. Sink, of AbiSource, Inc., was one of the original authors + before the specification underwent substantial revision. + + In addition to the authors, valuable discussion instrumental in + creating this document has come from Peter J. Churchyard, Ned Freed, + and David M. Kristol. + + Jim Gettys and Larry Masinter edited this document for update. + +7 References + + [1] Berners-Lee, T., Fielding, R. and H. Frystyk, "Hypertext + Transfer Protocol -- HTTP/1.0", RFC 1945, May 1996. + + [2] Fielding, R., Gettys, J., Mogul, J., Frysyk, H., Masinter, L., + Leach, P. and T. Berners-Lee, "Hypertext Transfer Protocol -- + HTTP/1.1", RFC 2616, June 1999. + + [3] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April + 1992. + + [4] Freed, N. and N. Borenstein. "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message Bodies", + RFC 2045, November 1996. + + [5] Dierks, T. and C. Allen "The TLS Protocol, Version 1.0", RFC + 2246, January 1999. + + [6] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P., + Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP : + Digest Access Authentication", RFC 2069, January 1997. + + [7] Berners Lee, T, Fielding, R. and L. Masinter, "Uniform Resource + Identifiers (URI): Generic Syntax", RFC 2396, August 1998. + + [8] Kaliski, B.,Robshaw, M., "Message Authentication with MD5", + CryptoBytes, Sping 1995, RSA Inc, + (http://www.rsa.com/rsalabs/pubs/cryptobytes/spring95/md5.htm) + + [9] Klensin, J., Catoe, R. and P. Krumviede, "IMAP/POP AUTHorize + Extension for Simple Challenge/Response", RFC 2195, September + 1997. + + [10] Morgan, B., Alvestrand, H., Hodges, J., Wahl, M., + "Authentication Methods for LDAP", Work in Progress. + + + + +Franks, et al. Standards Track [Page 31] + +RFC 2617 HTTP Authentication June 1999 + + +8 Authors' Addresses + + John Franks + Professor of Mathematics + Department of Mathematics + Northwestern University + Evanston, IL 60208-2730, USA + + EMail: john@math.nwu.edu + + + Phillip M. Hallam-Baker + Principal Consultant + Verisign Inc. + 301 Edgewater Place + Suite 210 + Wakefield MA 01880, USA + + EMail: pbaker@verisign.com + + + Jeffery L. Hostetler + Software Craftsman + AbiSource, Inc. + 6 Dunlap Court + Savoy, IL 61874 + + EMail: jeff@AbiSource.com + + + Scott D. Lawrence + Agranat Systems, Inc. + 5 Clocktower Place, Suite 400 + Maynard, MA 01754, USA + + EMail: lawrence@agranat.com + + + Paul J. Leach + Microsoft Corporation + 1 Microsoft Way + Redmond, WA 98052, USA + + EMail: paulle@microsoft.com + + + + + + + +Franks, et al. Standards Track [Page 32] + +RFC 2617 HTTP Authentication June 1999 + + + Ari Luotonen + Member of Technical Staff + Netscape Communications Corporation + 501 East Middlefield Road + Mountain View, CA 94043, USA + + + Lawrence C. Stewart + Open Market, Inc. + 215 First Street + Cambridge, MA 02142, USA + + EMail: stewart@OpenMarket.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Franks, et al. Standards Track [Page 33] + +RFC 2617 HTTP Authentication June 1999 + + +9. Full Copyright Statement + + Copyright (C) The Internet Society (1999). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Franks, et al. Standards Track [Page 34] + diff --git a/dav/SabreDAV/docs/rfc3253.pdf b/dav/SabreDAV/docs/rfc3253.pdf new file mode 100644 index 000000000..4f4b3e6b7 --- /dev/null +++ b/dav/SabreDAV/docs/rfc3253.pdf @@ -0,0 +1,10329 @@ +%PDF-1.3 +%ª«¬­ +4 0 obj +<< /Type /Info +/Producer (FOP 0.20.5) >> +endobj +5 0 obj +<< /Length 1433 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm;968iG&AJ$Cn7^,n1r6YIF(2%A=skA\8If_M0F:=KY<=t50:I3nU@!XApPW]h\Y/(0JW.]hdOLYRGIXDMS=^S'II8L:F8)$>-e#;Oa:.bU3TF:o"bhc%d368"Dj#>Ns1b"C?h6IO9b+X22pRuL2Li`2a'f'YLiK"t.p0?khrWrhP@e2:<::FUsa&2-cL60[FAgHE`Z:I6^/1(+.drfbo,NZBJ>dPr7HDL![bJfsH:`d-]Z'/,?.5nmi-53JBZ)6J)4ZIk3=DAWn!?[[='#MNpME[psQ>n[n2a'M@^HU]L.\^_"=+Pc0cI`OX7YKM0k:U7d^inR19aDhos9mgGK6"N"jHh0n:f'K+6COn1FVsCC%4/X<#kfG%l_S&8p-1+LabYHkNL)@CW'W6[f=qLA7ZYVOcAJ>dB5mjS+a0MWad?IEi(N7qu=];1YFObYhr9'dnlNF-\W>4W"1S4T!Mr#9-B&)ja-ab5`Ffn?dqhGf>rSX5[JG1p<'2kJ0EIhAmi]^*TsB6!A)Q579HMdmqP<,_^Cm.3F7.EfWpjl#sPE"G0]:5*N;5W@C.,W0@M@'\REYZ:djf")iD8@N"!'!L1B<,r1UX/94FprF:u&/_-a,K#Gm!,0q5:e`MLi>/F@4lY>=q)KUD/+lR[6=2H`SGe0C:KIm#L`(8!cA+t[M-cJrY'Sc0YBis+YDQ&"MhQ.?[NihIIl1@%X"R-Z)i=T'hJ@D.8*2'Y0ddtEIh]lX-$7/S*o)I+]oHK[@uGKGEVVK)Kk>Zl'M]JN?-Rh<>gYO_A^(ZK+%k)Kg,R1%N]u!0g^lE8,W[9'`Gk#V>bd2b._fUGh+#<5:4sjLCcE^+K]3n&Xsb^*V),SgEOr$Sj>D/GFhoT*3k;iJfg>(@Z#k="T9=A'btGW("HS9D8@R_7:4u*[IfqT5[g(2hQT7Dp^m=8N14iD>Sn4psR"%TeT&AI&43TK1"t[ujSVA!u_c64"ri]1@nCe`+[Bs@(a0\_8a'NC%]N*$rY*mldEf\>3Y*apYb9QU+7pg*Ybq^+Yk%X\P%PXU2b([>$qf?h?S)WdA>5uNq~> +endstream +endobj +6 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 5 0 R +>> +endobj +7 0 obj +<< /Length 2417 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$JhfIL2&BE]"=7DU&*'X>.XhHYt3nLuZaA=M&S$ij1;%cY)F1+Zps8F:s?u7G[@9Qq/Tn2]=ljp_/?*Ur-T)JLH)IO>R_=[F0%IVO2!3(Hm&/"S?KLC?)31)<&+3pe+kIGsp(_VkYYKlaop?,;eXQ6Z:?OT+-VLR)R;/I!!$Y'lYeC_[`X7bpY^_%@@?r5Vf[Z\-FH*aT0I'ueN/73;Fm58Al#D,<7n1u^d&0n=a``mP4#J-!#KP]!=#N5t)E%%0rH(r-?pXuSFkc2;oq7Z_(gT)7u=Fs?\a3\)Km.Aa(6=TP=bZJ[;,C[jQd8aKjB#u#PqkJ1T:"&-UJcm?Rg,U6E:Ul>c&;DfZ:3A6^61qXXnAfcAA4@)KHUgL%-;p/kq7*'OjNiRd8OM:-DkgS:$cg7_l0>WZcoGnNlN.SSZs%NLpjk.Om-ug)]&ol4.+*(Cd=mHp"F&(eGC9t%r-^*T8rb]2D-(\]E,+iGlc'bS9NYoFSmt$XTHUncXP77C\g7l'^]$EZ[PA.b$APHb%KQ;Vcr7]>01Ap`QW$s:C+Kt0L*,>g(-ZL&&JmVHK,,X(_?WIfZ7$HtCjFV6Vuq6FLq_JSg4t1>l%:cg1)0H#ghiQ!6[uSHnK<\0JOTat0qYniC;kVXp/_q&pEIbup#T2D0oK5A?+qKF[8c"P(3>iHpRH"QVr#,8enOoGC9<$1[mf3ZM4IB7XcN-`C;$9Vec]%3UVDf,W#XIOa`rb(q:pC9K,oq_-IHXqso=9NC>J;^fB<C3$4aAlQr0%S;@.Re@Pne9gO0h_952(2s8>n[AU6Sj-(X-^PH>bAMpL\=HB2Mc>DD%cNs2hC2CXR;GA6d%COSojr!%[6)iF9^"`8rn[V*XGTb#pABB"TY+]N7**IgNT!"79C@:5g^M]kH[]92^F,$,gW7sF9[ob/+[40bKA?tq6is4mZSh9n//2&`ZrPG4;:(DSk1!e1nJa$d[=G7eI0ha%81DXiucTKRre+\0:^'#;889JEUU/P.Y6t[jhuE(A_VG=$(ljp*mo\0^mZ#F\\S"N^)(%#S%=;Jr0*TR'9s0P@s([?!VBGDFeItNoFY=GsJ(.dj&Ts%+-+g0nkiSq`:D)-?>BjT*`X+GJI<&PkHFQF1W$8Qopj"T[T9snnD2J$[;mr2$4<'>$meg+UD^5`uO/\^Msl/]"+?DHS`ASNlG/Lak.LPfh=mq95u;OQ!W*"I%$`][i7&`]")bE#.f3b%ip%Xg`6PDKGJp-7*JLJl-*3I0-]="`G^tFLaJJ?j2ofgH#RHr632m<:#j$(LM1kX_B=L.NJ%:oSX6?C9"TN!#;"/0;fO&/9])LpL6Xco,L1ibN'J;B]]7>hm>J_(:_IXQ"k6IcD[V4N1@8C&aQS'd1U]adO^D23Eje-QoRVToQ@aid$,m(I%RK8[655;BLoAEYY=Ish;SoDV8l[@)J-t8,34Slia^ke!r*4^V1fX"Bg,lGM>#M0I#So_I3U`b-=MF_DIkDOq?WrFpVBp9s3\40uK=]'HQ28"R_B*E[(.n@!:T$Wi&;tdPro#oKZQ*4UJGK4DF!6n!Zi:'CEAZJ~> +endstream +endobj +8 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 7 0 R +/Annots 9 0 R +>> +endobj +9 0 obj +[ +10 0 R +12 0 R +14 0 R +16 0 R +18 0 R +20 0 R +22 0 R +24 0 R +26 0 R +28 0 R +30 0 R +32 0 R +34 0 R +36 0 R +38 0 R +40 0 R +42 0 R +44 0 R +46 0 R +48 0 R +50 0 R +52 0 R +54 0 R +56 0 R +58 0 R +60 0 R +62 0 R +64 0 R +66 0 R +68 0 R +70 0 R +72 0 R +74 0 R +76 0 R +78 0 R +80 0 R +82 0 R +84 0 R +86 0 R +88 0 R +90 0 R +92 0 R +94 0 R +96 0 R +98 0 R +] +endobj +10 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 82.0 686.866 136.45 676.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 11 0 R +/H /I +>> +endobj +12 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 671.056 198.38 661.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 13 0 R +/H /I +>> +endobj +14 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 655.056 189.78 645.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 15 0 R +/H /I +>> +endobj +16 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 94.5 639.056 120.05 629.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 17 0 R +/H /I +>> +endobj +18 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 623.056 159.21 613.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +20 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 612.056 194.21 602.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +22 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 601.056 208.09 591.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 23 0 R +/H /I +>> +endobj +24 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 590.056 211.99 580.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +26 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 579.056 203.65 569.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +28 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 568.056 210.86 558.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 29 0 R +/H /I +>> +endobj +30 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 552.056 362.24 542.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 31 0 R +/H /I +>> +endobj +32 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 536.056 261.45 526.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +34 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 525.056 400.83 515.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +36 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 509.056 300.87 499.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +38 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 493.056 245.59 483.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +40 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 472.056 193.66 462.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 41 0 R +/H /I +>> +endobj +42 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 456.246 203.37 446.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 43 0 R +/H /I +>> +endobj +44 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 440.246 207.27 430.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +46 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 429.246 268.36 419.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +48 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 418.246 276.7 408.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 49 0 R +/H /I +>> +endobj +50 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 407.246 147.0 397.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 51 0 R +/H /I +>> +endobj +52 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 386.246 187.81 376.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 53 0 R +/H /I +>> +endobj +54 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 370.436 220.04 360.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 55 0 R +/H /I +>> +endobj +56 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 359.436 168.66 349.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +58 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 348.436 213.08 338.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 59 0 R +/H /I +>> +endobj +60 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 337.436 265.02 327.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 61 0 R +/H /I +>> +endobj +62 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 326.436 287.23 316.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +64 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 315.436 258.9 305.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +66 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 299.436 255.03 289.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +68 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 288.436 221.68 278.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 69 0 R +/H /I +>> +endobj +70 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 277.436 181.43 267.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 71 0 R +/H /I +>> +endobj +72 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 261.436 230.58 251.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 73 0 R +/H /I +>> +endobj +74 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 250.436 226.68 240.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 75 0 R +/H /I +>> +endobj +76 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 239.436 193.08 229.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 77 0 R +/H /I +>> +endobj +78 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 223.436 169.21 213.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 79 0 R +/H /I +>> +endobj +80 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 212.436 239.45 202.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 81 0 R +/H /I +>> +endobj +82 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 201.436 232.8 191.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 83 0 R +/H /I +>> +endobj +84 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 190.436 230.58 180.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 85 0 R +/H /I +>> +endobj +86 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 179.436 232.24 169.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 87 0 R +/H /I +>> +endobj +88 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 163.436 221.99 153.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 89 0 R +/H /I +>> +endobj +90 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 152.436 244.76 142.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 91 0 R +/H /I +>> +endobj +92 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 136.436 166.45 126.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 93 0 R +/H /I +>> +endobj +94 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 120.436 196.42 110.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 95 0 R +/H /I +>> +endobj +96 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 109.436 252.8 99.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 97 0 R +/H /I +>> +endobj +98 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 93.436 214.75 83.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 99 0 R +/H /I +>> +endobj +100 0 obj +<< /Length 2167 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauaBhfIL2&BE]"=84M@3P1SfrOTHX-.?+?^_"r>:"'=`gk6!MN,a12It+aYGQkCTU_GgF%JB3rj:Algh6lh*jQu7[ohZKYhW3EokhVjS02t0Wj'8/8#R?04P:,hj3=/*(hKWD?akpaM>JboF82AN7m_,.uL&81TO8*Fs8D\STVT$TGqt8-V(E`ZNI]VPjb[4rG.`q7R]+:iAJg1n!U_sV:IAf#u:_midUfuaf'NV-"A,qk\_Bbn,C7:(6CnAu%A\=u+$qHM^iS^RsEK`N(bBuKZRil/4m3'[9aW]35`!04P6mY0M$AG%PAAJV/9WU^m'2)*c>NA,WkD0f0KlMs-)KE"fip)K<%5&bYiYi=tQWJf`QHY-n0[YijiDes8-;K\RS?O"]+DTmm\sW-H>Coc8R*_q+*YKZ6hF(a<8&UE#&=Fr[lX0B6IJZ.GQtGp-Lbr(.M2E0J+dg\TA;:N"kOdVMoWKPh_un.=K8r2sY]/(r/(B_C[=]4&-hVtD+0E4+$jb5h\n-S?1Fjr^N6\M,6'7#SmGNo>EBnZ1c9Oft"82ANn#B5*)C#9C2N-r27Bs&[^$k\Z$u[Nme\E84?hAkQ?1oou\)Y1WRn#oH!:;a3Zq9Fi7u0&7MKhBg7B!gr02#-?0mVgS6:O,)uYrm_5M'2Q5;0!2-nsnqVT1;6Kb0huoDr+L)"3Q'qX)hk9\9V($8K&\UL;im_c;\=!3c#W<)P'[[U&[Dnk6&"bk:?$?D?`37=>P'+MgkV%G))g1KoGGSi16OQOY5H!BPn("WQp#b#*IF-Y5&3nE'GUIH:A/JGA?MA5h>'o&>3IYT^L&uZfRDbNg`+f8=9(^`sdYL6-Z[+8O/d586*t]aL8BhS2:eS@Y/E^gDPHL?VtsirqlLP:U?[$cB-!?F(W"s'e@1j&P&)Z&%-XZY+:Xp["dO,;;"cL6*hOp5g@-S&Yrj@ClG$g^m([0mXO,[=L7+J&IO40;Ajn=!_@Ku#cMGTrV8TDXnfgW"UT/kOS4OG',PGt;V=XO8XY3g(tmKP'N1Cc"`u\+l)s(]_\3O+=!q`]gUhZr-p^YA,fKc4H,eK<\IPi_XoK?8@_D95NFV4;>0:P=M1d%N.c>Aah&,%K^$cedEc`GF=d\kVAm2enj_+eg_BhQ4+@YD[hJV_ia*Y@?/)[7mQe#T2+a0'KY^ASNm-'+qq\f.aBpMD24loe=R7("=)I[<=CWP?2G726Q=D5C)'ne&kQ&15O)]/;:C(o_`W#bhkS%hj[cVG<%-c+g+s2%3%UWBF6DU+Wt8RFo`4S_KtJ?&$-+l#T>i,PX\V3@a/digiI42epg78btdno=f;)t3Afe0&X"a6`0h%0XEJ-7073@\&kklj`h@,iM3PW'b'\JA^T7AZ:R8uQ[JWIhE[Gl$!:qm$d+Z:.U!Cbb`fo+qUl&T!M@lReZf,/Lm:Xg;$np!0QZPclj,`AR]AS]=H$\T5Nq<(V&&&(h^S`T;neO'+rZi:&qp;6G~> +endstream +endobj +101 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 100 0 R +/Annots 102 0 R +>> +endobj +102 0 obj +[ +103 0 R +105 0 R +107 0 R +109 0 R +111 0 R +113 0 R +115 0 R +117 0 R +119 0 R +121 0 R +123 0 R +125 0 R +127 0 R +129 0 R +131 0 R +133 0 R +135 0 R +137 0 R +139 0 R +141 0 R +143 0 R +145 0 R +147 0 R +149 0 R +151 0 R +153 0 R +155 0 R +157 0 R +159 0 R +161 0 R +163 0 R +165 0 R +167 0 R +169 0 R +171 0 R +173 0 R +175 0 R +177 0 R +179 0 R +181 0 R +183 0 R +185 0 R +187 0 R +189 0 R +] +endobj +103 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 719.0 241.41 709.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 104 0 R +/H /I +>> +endobj +105 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 703.0 225.61 693.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 106 0 R +/H /I +>> +endobj +107 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 687.0 207.28 677.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +109 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 671.0 236.73 661.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 110 0 R +/H /I +>> +endobj +111 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 655.0 246.18 645.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 112 0 R +/H /I +>> +endobj +113 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 639.0 226.16 629.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 114 0 R +/H /I +>> +endobj +115 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 623.0 215.06 613.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 116 0 R +/H /I +>> +endobj +117 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 607.0 217.83 597.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 118 0 R +/H /I +>> +endobj +119 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 591.0 230.05 581.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 120 0 R +/H /I +>> +endobj +121 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 570.0 242.28 560.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 122 0 R +/H /I +>> +endobj +123 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 554.19 214.49 544.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 124 0 R +/H /I +>> +endobj +125 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 543.19 187.53 533.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 126 0 R +/H /I +>> +endobj +127 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 532.19 182.53 522.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 128 0 R +/H /I +>> +endobj +129 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 516.19 230.58 506.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +131 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 505.19 187.53 495.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 132 0 R +/H /I +>> +endobj +133 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 494.19 182.53 484.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 134 0 R +/H /I +>> +endobj +135 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 478.19 350.56 468.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +137 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 467.19 335.28 457.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 138 0 R +/H /I +>> +endobj +139 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 451.19 340.56 441.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +141 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 440.19 195.32 430.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 142 0 R +/H /I +>> +endobj +143 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 424.19 196.99 414.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 144 0 R +/H /I +>> +endobj +145 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 413.19 219.76 403.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 146 0 R +/H /I +>> +endobj +147 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 397.19 225.61 387.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 148 0 R +/H /I +>> +endobj +149 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 376.19 186.7 366.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 150 0 R +/H /I +>> +endobj +151 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 360.38 201.71 350.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 152 0 R +/H /I +>> +endobj +153 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 349.38 221.69 339.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 154 0 R +/H /I +>> +endobj +155 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 338.38 228.92 328.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 156 0 R +/H /I +>> +endobj +157 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 322.38 300.31 312.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 158 0 R +/H /I +>> +endobj +159 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 311.38 240.59 301.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 160 0 R +/H /I +>> +endobj +161 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 295.38 214.49 285.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 162 0 R +/H /I +>> +endobj +163 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 284.38 240.59 274.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 164 0 R +/H /I +>> +endobj +165 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 268.38 216.98 258.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 166 0 R +/H /I +>> +endobj +167 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 257.38 273.36 247.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 168 0 R +/H /I +>> +endobj +169 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 241.38 225.61 231.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 170 0 R +/H /I +>> +endobj +171 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 225.38 221.16 215.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 172 0 R +/H /I +>> +endobj +173 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 209.38 210.06 199.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 174 0 R +/H /I +>> +endobj +175 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 193.38 212.83 183.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 176 0 R +/H /I +>> +endobj +177 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 177.38 277.27 167.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 178 0 R +/H /I +>> +endobj +179 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 161.38 232.83 151.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 180 0 R +/H /I +>> +endobj +181 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 140.38 166.15 130.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 182 0 R +/H /I +>> +endobj +183 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 124.57 182.53 114.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 184 0 R +/H /I +>> +endobj +185 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 113.57 276.67 103.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 186 0 R +/H /I +>> +endobj +187 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 97.57 220.04 87.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 188 0 R +/H /I +>> +endobj +189 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 86.57 220.57 76.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 190 0 R +/H /I +>> +endobj +191 0 obj +<< /Length 2112 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauaA9iL(3&;KZO$6PsVj09k&ZM'T8Yf$$o'ZiUWc-4"f(eBc"at^6h^L'QPi`78Xg=h'>LS7DuNq1QqWn$Y&dF"k5)PAL;K$hJ5'KdG1+Hlb$^p1Pm,9A$MN97sc@ht5ki(#Kto16P;$>G.r$\J:2fP3eo;F>MRH/HBM*"KQX]p#+AWHt2sl`>>7::@F-\&;,V&n(nKrEaNlL`s0>_TN+$q/CVK#mU3YW/V@GG%J@rFT+&=.Qe_:$KBg\78Sf-&^2=C&o1+]/gZcO/;XA'fI;`OVE:2>Ms58Ec71[L_<@VC1TYI&Z[',1VW3r$'M^uJN0o&bBiKqnUXGRH"@V@A:dETr:g7[]Lle8Q1ZEX'A6]J.cmpK\RTYjT>gt@3JB!mXB>(A&W99UO,\5+)l'57OKm$K"H9$%V"Z/,/S3JkTV6F>QNFH>Q`:5^rV1Lm>s).HdREhMp,61k`c$?15%UgWFeMm-B5-n)B/Y7+D+mV3nKAS.0UE!QY"H>17r,n[&e\5sCX57OThr(RY-oiL>lNU0qW;X!,,DTAkA"Wp@r42KpV0#.I*(qgRb9bZ((+IQourF?K:QFcYt,uj=&XaWZgQZ<1R3-S768aUX/piB`_4#sZ>l"$XPEq)nt;3aiVYCXKk+VfDTD=7'dGX^K:8k/kMDKI%OiD^+bnJKN91L)r:YNt#dSKr-Qm3Y,_oN2p82Qsj(rR\Q@b(VOW;)fGD,'d4Jp\OUD+hm)\tcbc5,mq9.*k[9/l)C-oRlM_:2ce7*Z^$&a9%.F"sUR<20J(H:1ak)U+(2m\:LH[m:PSfMMOo)`_Abo]uY!o7RG@g=VgaQZT-_QeS_LC2CNe\,F/=`Qaeq1_PS2(dWS4iXMQ:JA1J1k^aWkW71b(0A!1jgbP90cjM*!$cd5h/IjAH[EDq+!ZESk0>5iXr`@]TN$D>\0W6'eAbGG=/Yk*uB^ESF'5]Z$h0DGqYA;%-+[7#G/ZRM<,U(U9n9VdMd#q\RrA&kg/QV$m>:j4R1$HO?&!Jt>C?Po?p]>"LgPcZ^(0SneO^K1"Eh4WuO,ZtWY[\mdl5Q@87ptf6W')Pu$X[Z0]-;9[Wu]C7Wa[fm7*PQ:FPedQiXtK9B`p6.pa,,q/?fAER]K,tsH;VffFEnt?iNDKR8(ndT2P[CR!E!0Gt!"`!aXnBJ2@.^o!2AN%fKX^YubB9hAcg.P>H2F5>eIi,piS&3P&SY+qoZDQmD52PBg'0@-n.a4o5IX3"e7_)`2hsr=^NVWh]\Ho^htfhgN4?-YpOn>*YsVK,Ol-eFCD&s:CD&tR2*"bHd$BrRHuEFEiSF!?+_%MlI#;a.L_@3t\.&e=k"mj6)"X;\1nZ=2Wmpl.2e$aTE17G+bs=g+0VBdg>?^.PX+jY+!%kZG+k.LmnMqH52lu[OLK4rNe4b"H/-rfLlSYH9NEa8D,i-SGF>8Ni?d:#-O"*X^p4CeT8]-p%_3GOFdcuC@Rb816'dC^'SAT;9W0@B.2fs[or1#t%TG&I]7SJs*X:u\.,:ZJk6J*IRhMQ-Cm]PoIfR +endstream +endobj +192 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 191 0 R +/Annots 193 0 R +>> +endobj +193 0 obj +[ +194 0 R +196 0 R +198 0 R +200 0 R +202 0 R +204 0 R +206 0 R +208 0 R +210 0 R +212 0 R +214 0 R +216 0 R +218 0 R +220 0 R +222 0 R +224 0 R +226 0 R +228 0 R +230 0 R +232 0 R +234 0 R +236 0 R +238 0 R +240 0 R +242 0 R +244 0 R +246 0 R +248 0 R +250 0 R +252 0 R +254 0 R +256 0 R +258 0 R +260 0 R +262 0 R +264 0 R +266 0 R +268 0 R +270 0 R +272 0 R +274 0 R +276 0 R +] +endobj +194 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 714.0 205.89 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 195 0 R +/H /I +>> +endobj +196 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 703.0 228.66 693.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 197 0 R +/H /I +>> +endobj +198 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 687.0 225.61 677.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 199 0 R +/H /I +>> +endobj +200 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 676.0 193.1 666.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 201 0 R +/H /I +>> +endobj +202 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 660.0 221.16 650.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 203 0 R +/H /I +>> +endobj +204 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 644.0 212.83 634.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 205 0 R +/H /I +>> +endobj +206 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 628.0 277.27 618.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 207 0 R +/H /I +>> +endobj +208 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 617.0 383.92 607.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 209 0 R +/H /I +>> +endobj +210 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 596.0 158.93 586.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 211 0 R +/H /I +>> +endobj +212 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 580.19 167.55 570.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 213 0 R +/H /I +>> +endobj +214 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 569.19 190.32 559.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 215 0 R +/H /I +>> +endobj +216 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 553.19 225.61 543.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 217 0 R +/H /I +>> +endobj +218 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 532.19 142.27 522.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 219 0 R +/H /I +>> +endobj +220 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 516.38 214.49 506.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 221 0 R +/H /I +>> +endobj +222 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 505.38 236.68 495.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 223 0 R +/H /I +>> +endobj +224 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 489.38 160.33 479.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 225 0 R +/H /I +>> +endobj +226 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 478.38 208.1 468.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +228 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 462.38 148.64 452.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 229 0 R +/H /I +>> +endobj +230 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 446.38 225.61 436.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 231 0 R +/H /I +>> +endobj +232 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 430.38 202.83 420.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 233 0 R +/H /I +>> +endobj +234 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 414.38 231.73 404.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 235 0 R +/H /I +>> +endobj +236 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 398.38 210.06 388.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 237 0 R +/H /I +>> +endobj +238 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 382.38 237.83 372.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 239 0 R +/H /I +>> +endobj +240 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 366.38 222.83 356.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 241 0 R +/H /I +>> +endobj +242 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 345.38 198.92 335.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 243 0 R +/H /I +>> +endobj +244 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 329.57 214.49 319.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 245 0 R +/H /I +>> +endobj +246 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 318.57 187.53 308.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 247 0 R +/H /I +>> +endobj +248 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 307.57 182.53 297.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 249 0 R +/H /I +>> +endobj +250 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 291.57 212.81 281.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 251 0 R +/H /I +>> +endobj +252 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 280.57 225.02 270.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 253 0 R +/H /I +>> +endobj +254 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 269.57 187.53 259.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 255 0 R +/H /I +>> +endobj +256 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 258.57 182.53 248.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 257 0 R +/H /I +>> +endobj +258 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 242.57 270.31 232.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 259 0 R +/H /I +>> +endobj +260 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 231.57 255.03 221.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 261 0 R +/H /I +>> +endobj +262 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 215.57 300.57 205.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 263 0 R +/H /I +>> +endobj +264 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 204.57 285.29 194.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 265 0 R +/H /I +>> +endobj +266 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 188.57 225.61 178.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 267 0 R +/H /I +>> +endobj +268 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 172.57 210.06 162.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 269 0 R +/H /I +>> +endobj +270 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 156.57 212.83 146.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 271 0 R +/H /I +>> +endobj +272 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 135.57 218.66 125.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 273 0 R +/H /I +>> +endobj +274 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 119.76 226.69 109.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 275 0 R +/H /I +>> +endobj +276 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 103.76 215.03 93.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 277 0 R +/H /I +>> +endobj +278 0 obj +<< /Length 2227 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$J?$"IS'Sc)R.t$$qCkO,#_qr!&n4^\5_SmqPfneklkRk25`Vj*cuT=i9Q-g'<7,((;#cV*`?K-XEW,aV)KT:C0/-HlQe09Spe6kouu\FT1Fj8IC$bV+VdHU7LBhRjZM%CDY-(uDAFAR$img>Ur?Crm=Zl]hl-)r+*^4L=&rrg$@)emc2s^i05].u@@hk!R4Sa6JJ[^':g4aB3)\ufl*-HgY!c)9.g5Lq!Od:D;DMM4Gj'XANO"fd)3T[F,TTTX2kJB1o0r](n2H%"dh.U?$X-tZ8J(f^hU>i9"'>S"TBK[s<\jGOs*Go:Oa`I-dU^6(Sc6R;,Q:@hI[KT[D/eLYS@'1@X>43,;L'YR;jXnLLW\6`b2HEg"Q^j,1qkP>'/NK#0R(H"3VpkWgDq$I^$o:hlt;0&3DU!Cl1GGWgodfV3#=8cWd$%4qlV!C_39kGKn/p^?@.b;p3q#=/XqlUMA"i;Xmbf;d\rZZU'h?+n3RlJc>W15*;2E8p3/=6+=hN-5<(2I1-plgI^h(0CQg'Z9]qf9a#g:mo5kcZI4[00+1DL62ZVs+gM?ZC0_Lrnu/5Tt"D9d71ff;Lk\t`1Ynfp5kr*B!d!AJOu^d+$aepM?C;bnusUSqI.WbCc5Phf2q+^30$%d8kH*m/d8<<1Ulmc3PR^"hshCB,9AFYSi)*%^2$\AgqNKNR$\G#2^*dGO9^lZNrR]?%'V-jk;('J?I(XVl)t6:$9>D;f4QW,&R\+6TV8'/8KcoP0,"978$]cG6n>2$>\a>+eWUdl2D+,0^n;DVfY4>5F5N^Sc[[T$/Nf.7),as)g'g3W$,/_FJZ@-nkmPigdhI.NR['Z+jAce%:/Yj;tP[@+BY4/g,uMIl:_gO22o&=i7FHW[V1]"_[I*GQ]87adr@:0aJp'qhk^:uYOsm$52nVdU5ka(o$$ZJBp=VTC"iYIs]="P9K&Y#]rS@.IF6AO\@P\1Bm%+k7A_e5:%&J%!g'T6"Z'L'':LZ/5njs[lLjae')bWU[[@dJ7pRRZn'$3a^XmX@+SY5%K0@q9-fakQ5FV/%Qh%-*sb>b4T>ZbjreSI=8_fojF*mp[2R26E2oFtV)]2t+K5Ss)M#"F(Ig$pebEp&W1eh$(i7ktoq+UZV&k"-]7r0TMFu]:j"t3E^mI*B(XYM:8%6/8=:_H2FBGY2oH&3GE\X+.*e]up^:kqqGbs2[.1RM"h+-K,qFo8&T;8.R:gpVO&kfDrOiM3u8`U!I:EQ$#M:-`FHZs0BK2G"u'lTs`rOcVoOdGN[NWchA.FS$B(kW$lb5it8]J>dB;LC4MJl_t4;,FphpnYpFZL=JNpQjAY>q1@jF-T^>Lq>e[n6bcb]o8b,?J+^r>BO[GVtE.)>H1T1nK0@3@I%AL/I0*I^kEss&KbF?N0Opq*@K6PCEV;=Q>M~> +endstream +endobj +279 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 278 0 R +/Annots 280 0 R +>> +endobj +280 0 obj +[ +281 0 R +283 0 R +285 0 R +287 0 R +289 0 R +291 0 R +293 0 R +295 0 R +297 0 R +299 0 R +301 0 R +303 0 R +305 0 R +307 0 R +309 0 R +311 0 R +313 0 R +315 0 R +317 0 R +319 0 R +321 0 R +323 0 R +325 0 R +327 0 R +329 0 R +331 0 R +333 0 R +335 0 R +337 0 R +339 0 R +341 0 R +343 0 R +345 0 R +347 0 R +349 0 R +351 0 R +353 0 R +355 0 R +357 0 R +359 0 R +361 0 R +363 0 R +365 0 R +367 0 R +369 0 R +] +endobj +281 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 709.0 150.58 699.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 282 0 R +/H /I +>> +endobj +283 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 693.19 280.86 683.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 284 0 R +/H /I +>> +endobj +285 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 682.19 175.87 672.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +287 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 671.19 196.42 661.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 288 0 R +/H /I +>> +endobj +289 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 655.19 168.11 645.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 290 0 R +/H /I +>> +endobj +291 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 644.19 190.88 634.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 292 0 R +/H /I +>> +endobj +293 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 628.19 214.19 618.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 294 0 R +/H /I +>> +endobj +295 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 617.19 270.57 607.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 296 0 R +/H /I +>> +endobj +297 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 601.19 230.61 591.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 298 0 R +/H /I +>> +endobj +299 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 585.19 226.16 575.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 300 0 R +/H /I +>> +endobj +301 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 569.19 232.83 559.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 302 0 R +/H /I +>> +endobj +303 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 548.19 158.38 538.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 304 0 R +/H /I +>> +endobj +305 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 532.38 278.93 522.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 306 0 R +/H /I +>> +endobj +307 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 521.38 302.23 511.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 308 0 R +/H /I +>> +endobj +309 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 505.38 254.48 495.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 310 0 R +/H /I +>> +endobj +311 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 494.38 197.54 484.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 312 0 R +/H /I +>> +endobj +313 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 478.38 176.99 468.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 314 0 R +/H /I +>> +endobj +315 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 467.38 258.35 457.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 316 0 R +/H /I +>> +endobj +317 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 456.38 243.91 446.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 318 0 R +/H /I +>> +endobj +319 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 440.38 225.04 430.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 320 0 R +/H /I +>> +endobj +321 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 429.38 315.57 419.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 322 0 R +/H /I +>> +endobj +323 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 413.38 232.81 403.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 324 0 R +/H /I +>> +endobj +325 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 402.38 318.9 392.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 326 0 R +/H /I +>> +endobj +327 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 386.38 231.99 376.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 328 0 R +/H /I +>> +endobj +329 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 375.38 254.76 365.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 330 0 R +/H /I +>> +endobj +331 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 359.38 224.19 349.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 332 0 R +/H /I +>> +endobj +333 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 348.38 280.57 338.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 334 0 R +/H /I +>> +endobj +335 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 332.38 230.61 322.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 336 0 R +/H /I +>> +endobj +337 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 316.38 224.5 306.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 338 0 R +/H /I +>> +endobj +339 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 300.38 220.06 290.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 340 0 R +/H /I +>> +endobj +341 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 284.38 247.83 274.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 342 0 R +/H /I +>> +endobj +343 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 268.38 237.83 258.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 344 0 R +/H /I +>> +endobj +345 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 252.38 232.83 242.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +347 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 236.38 228.39 226.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 348 0 R +/H /I +>> +endobj +349 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 215.38 156.7 205.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 350 0 R +/H /I +>> +endobj +351 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 199.57 175.33 189.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 352 0 R +/H /I +>> +endobj +353 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 188.57 262.25 178.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 354 0 R +/H /I +>> +endobj +355 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 177.57 268.91 167.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 356 0 R +/H /I +>> +endobj +357 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 166.57 194.77 156.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 358 0 R +/H /I +>> +endobj +359 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 155.57 273.89 145.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 360 0 R +/H /I +>> +endobj +361 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 139.57 219.49 129.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 362 0 R +/H /I +>> +endobj +363 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 128.57 180.88 118.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 364 0 R +/H /I +>> +endobj +365 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 112.57 280.86 102.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 366 0 R +/H /I +>> +endobj +367 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 101.57 180.31 91.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 368 0 R +/H /I +>> +endobj +369 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 90.57 180.88 80.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 370 0 R +/H /I +>> +endobj +371 0 obj +<< /Length 2195 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$IhfIL2&BE]"=7DU&*3(;fQ@3Zk+a71\O@^jk:>6#o.)]&g);Ff(rUhP]#-Wiul($*[[CbuOe$IYmXG7hWoVN.3:2b-,H]J$?4\k&qmVmi)Dn>4^4NA/HYBlgfe%a5OG;tC=3:M7-CsguT"T`)$d9o/!M`=GJjf;^[Yjt!@n'gVr^()C8>e8&*[E+5-jL[K11Ehu+MD_lAK2Ko0#N]b^Y%P:T`T:Yan2+/O\'`'[m+GYQB"pj*//(V&?J)AbX7\L:-N0LH?#XLk,,l+#MB7t""eJTR9EP+^4QS&'&)SrKWi)VJAPi\?uCi8DC@!S-PKKMrCAEU4ZQU_Mkn)C'#$!P?bRE4'#(V:>g\!5bX[+rG=E%/1do'E^dN65_?$k&2M]p1K6:G16MAf>P%&+*q:]VdSWiO7c)O,k&eJr`8$J!f05Ko#\=tI]hCFFC38ILuTBFXt[%Gsk99eD2V71;6UFXWNA)>A8G2I#%PiNY!'.BdZ0n^tJ]6,;Aq"X]?6%5JP4?]P[(&"kdlLcI\eUKV<_@U^&CortpchC+F>MqcY13XP^11'-A;5uJ06MY^EV5s9d&G&]$N/LNs$2Q[tQ@261Y+@=FT2fbdJ5L*d;HeZA-G@XsMjr`.P)5LnCJHr7[Ddh_E^WhGCV6`+"meiUeb*96C?!clSBU#(Z*1HUQ51,&!LZeTY!,X&s+4odQ*1!R,r#VG:cfs(UZ\WXU!K'7d0=>i".DeY/UlH^3MFhVeUZ"X-_Rgbd#NJu!TjQ;(@-^bQ&:NP/'-reeR7oVnZ/Vr/hX.[kHF;COeDHYNON0tF4_Ra$j$6#=7bso0d19UA,PO%J8oih"L=2d&S-_XK34TcoKVF>O;()8ZPL8KmPPaGQsYoHmpGm+jHGlK.%@XY)0G8W/YR,UHlRo-hJ6;!fq@-c`W(Y7Sd+MoJ&ut$ZHA+\^.bR1$%jW,$=G]H:BqJ*ZW!r5"3iVZE'dpd=k,#\5fC:E%B/H`f[6%N4DoNY;tm4dZ[P.N9g_X"8aEb*9?,u@;@/gm0^X>4LcI1b`:WMhm7)tc"M8(it3eBc\PPj>,rdb$!T\A\>W+dDamN[cY(L?&%6Zm7G-tYRC_m1,q6-ZF;.&pAMPX4pAV4/*#hLDlJU@iN2XnMde7(UQ'C(d%R2p?i]sMU%\h[1rsr/);q4F)"j@HeHU=bu!h7(I)fZcO1%j0@E:]#DqG2e0-NMDI\?&MY@(YPRLWsJ-1>sjfALTtX%>*F+TN2bd^)&!8X/o_W(*J>*ir3C@/,`jJM9Ou)YJMgl=fomsbUK:aNpubDE[b%ih\mGYck\7aB6:]-OdtH;+j75HRtKsiV:la)JTD@t1''N;GS;\pA +endstream +endobj +372 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 371 0 R +/Annots 373 0 R +>> +endobj +373 0 obj +[ +374 0 R +376 0 R +378 0 R +380 0 R +382 0 R +384 0 R +386 0 R +388 0 R +390 0 R +392 0 R +394 0 R +396 0 R +398 0 R +400 0 R +402 0 R +404 0 R +406 0 R +408 0 R +410 0 R +412 0 R +414 0 R +416 0 R +418 0 R +420 0 R +422 0 R +424 0 R +426 0 R +428 0 R +430 0 R +432 0 R +434 0 R +436 0 R +438 0 R +440 0 R +442 0 R +444 0 R +446 0 R +448 0 R +450 0 R +] +endobj +374 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 714.0 232.81 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 375 0 R +/H /I +>> +endobj +376 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 703.0 212.53 693.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 377 0 R +/H /I +>> +endobj +378 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 687.0 196.43 677.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 379 0 R +/H /I +>> +endobj +380 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 676.0 219.2 666.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 381 0 R +/H /I +>> +endobj +382 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 660.0 240.87 650.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 383 0 R +/H /I +>> +endobj +384 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 644.0 230.61 634.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 385 0 R +/H /I +>> +endobj +386 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 628.0 226.16 618.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 387 0 R +/H /I +>> +endobj +388 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 612.0 217.83 602.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 389 0 R +/H /I +>> +endobj +390 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 596.0 247.83 586.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 391 0 R +/H /I +>> +endobj +392 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 585.0 280.04 575.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 393 0 R +/H /I +>> +endobj +394 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 569.0 237.83 559.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 395 0 R +/H /I +>> +endobj +396 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 553.0 228.39 543.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 397 0 R +/H /I +>> +endobj +398 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 532.0 252.25 522.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 399 0 R +/H /I +>> +endobj +400 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 516.19 264.49 506.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 401 0 R +/H /I +>> +endobj +402 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 505.19 232.25 495.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 403 0 R +/H /I +>> +endobj +404 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 489.19 218.38 479.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 405 0 R +/H /I +>> +endobj +406 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 478.19 304.46 468.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 407 0 R +/H /I +>> +endobj +408 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 462.19 230.61 452.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 409 0 R +/H /I +>> +endobj +410 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 446.19 226.16 436.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 411 0 R +/H /I +>> +endobj +412 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 430.19 224.5 420.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 413 0 R +/H /I +>> +endobj +414 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 414.19 215.06 404.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 415 0 R +/H /I +>> +endobj +416 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 398.19 217.83 388.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 417 0 R +/H /I +>> +endobj +418 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 382.19 282.27 372.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 419 0 R +/H /I +>> +endobj +420 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 366.19 242.83 356.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 421 0 R +/H /I +>> +endobj +422 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 350.19 237.83 340.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 423 0 R +/H /I +>> +endobj +424 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 334.19 287.27 324.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 425 0 R +/H /I +>> +endobj +426 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 313.19 239.51 303.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 427 0 R +/H /I +>> +endobj +428 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 292.38 189.5 282.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 429 0 R +/H /I +>> +endobj +430 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 276.57 202.82 266.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 431 0 R +/H /I +>> +endobj +432 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 260.57 239.45 250.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 433 0 R +/H /I +>> +endobj +434 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 244.57 211.71 234.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 435 0 R +/H /I +>> +endobj +436 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 228.57 169.48 218.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 437 0 R +/H /I +>> +endobj +438 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 207.57 179.5 197.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 439 0 R +/H /I +>> +endobj +440 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 186.76 176.15 176.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 441 0 R +/H /I +>> +endobj +442 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 87.0 165.95 169.77 155.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 443 0 R +/H /I +>> +endobj +444 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 87.0 145.14 133.64 135.14 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 445 0 R +/H /I +>> +endobj +446 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 124.33 183.93 114.33 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 447 0 R +/H /I +>> +endobj +448 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 108.52 382.23 98.52 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 449 0 R +/H /I +>> +endobj +450 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 92.52 210.87 82.52 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 451 0 R +/H /I +>> +endobj +452 0 obj +<< /Length 1220 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$G?#S1G'Sc)R.rn--;9p>(CgW4="@re$e[QH$A?BR@V+HrJI/J>]YND4_8VFsCN&MD_iUZ2LiXYeFX%u&/!c>kM=Cd&A.GGHHWe(EWAQK',>au[dfA4.R8Sh5qd@cKXZm*FLR=+0I\We"m+i^oBS5nor42/\c_S%kNO-oBa5\'"esdEECN&kX4CXOP"nu."&sXn.M1+&15\\]XH_(X'JW3R-7dK3+\CGkM(8QT(^UKO23;(c@2MRM%6c.qiFYoKJG-5<).4a[Z3!>D>J5@0lc#?h'@Pcq/Ds'>/,PN>l?hqI1ibSh'*r>-]g!4k/5"=4U?I*ci1P2sV4=1aoPuf%'F8`2)CMJlkKA6Q-:n`e,"`XJ3*b]$(CnHD1W8Y5Va(Kob2?I]SNOjbc2D$!XkZl'06mG#V2a:n&*1DW]JsP9GV)M3i*D$A?*+p,ncG0c)@CX;q81_rq;"5<#g^ZLksg]`f/[eEPH*NL>ueEGO]n3RtU,,H&^(!FA]qi))l6"%<(lih>#%=oPMWf@>1ab/moO0(!UlC,@HWKU\F1Q#L8Nie->i%E.%GHrf_!9iC2tAe/>*62?V=H_%u6+A=>Cm!iToAd]=GW<`mFdn/NX8;.Y2Yi^?X/jWKqX20G%k(8!qd$DjYs)`'G8YijRnd0IpCVT*C'N1rVPX"#0G<[s[S<]KjjqKPHNcnmfhd674FAhhp?!i_;T0MZ=QWLY-*l=:)WkA/>0O&D+!>K&/njIO3F&R,1jqd:=mWfVDJKi"Ag930RM52VP-L3?iKB4YjE:m&,7$o`1.d>4H5(jko[[7`.qZ!s+aK+Fh2f=]/k5~> +endstream +endobj +453 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 452 0 R +/Annots 454 0 R +>> +endobj +454 0 obj +[ +455 0 R +457 0 R +459 0 R +461 0 R +463 0 R +465 0 R +467 0 R +469 0 R +471 0 R +473 0 R +475 0 R +477 0 R +479 0 R +481 0 R +483 0 R +485 0 R +487 0 R +489 0 R +491 0 R +] +endobj +455 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 714.0 215.33 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 456 0 R +/H /I +>> +endobj +457 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 698.0 184.75 688.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 458 0 R +/H /I +>> +endobj +459 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 682.0 214.2 672.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 460 0 R +/H /I +>> +endobj +461 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 96.72 666.0 128.38 656.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 462 0 R +/H /I +>> +endobj +463 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 650.0 263.35 640.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 464 0 R +/H /I +>> +endobj +465 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 634.0 189.75 624.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 466 0 R +/H /I +>> +endobj +467 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 618.0 350.82 608.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 468 0 R +/H /I +>> +endobj +469 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 602.0 256.67 592.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 470 0 R +/H /I +>> +endobj +471 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 586.0 235.59 576.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 472 0 R +/H /I +>> +endobj +473 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 570.0 198.62 560.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +475 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 554.0 173.66 544.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 476 0 R +/H /I +>> +endobj +477 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 538.0 348.91 528.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 478 0 R +/H /I +>> +endobj +479 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 522.0 302.8 512.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 480 0 R +/H /I +>> +endobj +481 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 506.0 280.03 496.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 482 0 R +/H /I +>> +endobj +483 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 490.0 178.09 480.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 484 0 R +/H /I +>> +endobj +485 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 474.0 335.85 464.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 486 0 R +/H /I +>> +endobj +487 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 453.0 155.61 443.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 488 0 R +/H /I +>> +endobj +489 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 432.19 275.87 422.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 490 0 R +/H /I +>> +endobj +491 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 411.38 96.45 401.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 492 0 R +/H /I +>> +endobj +493 0 obj +<< /Length 2691 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^9lo&Y'#"0DY]Q&/*!?9360'@cG1g)cjd#%?;cXAq?q=$1`*`fPdGA6F4l?RVJJj>?2H]?T7V'ro3+Mkp2'IIkkBFn0b7Q_8M-/Jk6lt>3]a,0MNncc/d&Zi4YZY'`1(q?bW_NP!tjU+fgrn5:5uO^ddi>n95fl2Ta/iaXm/`_^=u_BOI;`m^=QThJRS7qj"&@\QB3!DIVg2Nc(-*rr4o^Y@Z+ol;uQ,'jT]4=T6k<1Es7tTVfuckWoGBiRJ)^-)M1*\7R7O+J'Om/9&R:&(cD75Xt/1^d\K)3M;!]KJ*kiU:C:NTFfGG2gY4q-]AsCpOoMR5r2pWZqNoU#FfR%Z@/_Rt3FV*$L>f\b']H'5,$8D!jf:$p"*-`#fd&VU-P<33J/*YUIoF(#-7a@?/71BkroDC$bpbPkb"^^pl4cUABbU#Ft:/TPb@Yq+L(HF#t"]Qrh?M%$mpY&5aR1$4N@Se7MK>-n/@u%lJbAL,i9%/5?4SA?D)VWVu+O:4!l])WQfXc#9-o_9QG6J+=g'!C32$2<)h'J\0dqC2Z0p(TG+RV%=3?D99`Y(Oqa$o073)..6,tf)/E=n3)P86;g&a]ntkA'##:_gY>EfZjLu2i_kF`Ol-LFf\=eu*?Eco[Y'No,0aP*p6m_DUAQsE8:+'b6&-Mp`$dLueZ;o#bf6)WVEZgpTp/dheXS_DYY@Fi1qOraSHDiAmOfqYG$dA('ph0e#S-$r#55CC^_BRA::b*`dbjBaR2'?&3Kd';\5V&0A]HrtW%@q3M$fAeQn).MFCKq_)OlJnP;CLb=j+7l^jO+LeXqAciV>aj5$mXt2XWo/o7JT^9[mft6m.@201mYt%m6c;)u.HjqS?c#b=58Gfl[(=42gYcB)K*Yo!VpEGqja:nYLFLRAQ1`ClCi\h;!,k6H+qZ_q`cNl1Tb]HR-o'ZL=HW:(Lp6B"MURL,jR-_nLtep^\HC`WQZR#b#mk@7lcD4ob%j?,<4[^Df6qEr9&.;sk&5;3E8O,?E@!m#E;>"A0ZMpn9/%:oO*5S>K4ul?>^)r8mqFFVk@i5:eVuB8IGCoR;J;FtN2sE]7I3@mYrcHWSP_eBEi#gs7D?]ieh71C4\`T:fo2.&?TAMdr."qE(j!m2P3T%9(LioRM;D>g#T(rSQ-26D$QU[6WLP-i+3lClZd)IGd#KZ_D3F*W1].&d]Wu;eQ;KJL66d/!0-G8;qVE)0-&3^(BDDQV16bTBnG1[%8E9!^\66e/:b[c]qT@--S05-r%?"Thp\@\^4_!k:E2*_Y%@9:^.<@!4^GUN%"q7h*ng[KmT5G/:F\ID]"%_:0Y_ii=QhD&hU,^!hu**;qti<2AF\X7eV=[q(&'NsTlOK"5kQ.BN)?cI.Ceh["5%ZC3YVaY:hBEI?r+*M'2)ej9F-[kO,U_!ch?q$mN9Zu_`X9T])DY^r*\7Y#]VYH5)K'aNAuC9`b*;c0\UH$nn9:s+1`^qiFi7f:;I-TuV/5cQspkl@s\RGDZgpkC*K_mdGOdbfd +endstream +endobj +494 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 493 0 R +/Annots 495 0 R +>> +endobj +495 0 obj +[ +496 0 R +497 0 R +498 0 R +500 0 R +502 0 R +503 0 R +505 0 R +506 0 R +] +endobj +496 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 444.47 369.866 486.97 359.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 273 0 R +/H /I +>> +endobj +497 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 358.866 134.5 348.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 399 0 R +/H /I +>> +endobj +498 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 240.59 295.894 286.15 285.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 499 0 R +/H /I +>> +endobj +500 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 490.86 295.894 536.42 285.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 501 0 R +/H /I +>> +endobj +502 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 476.66 251.894 521.66 241.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +503 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 177.922 133.4 167.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 504 0 R +/H /I +>> +endobj +505 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 485.91 156.922 538.41 146.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 23 0 R +/H /I +>> +endobj +506 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 113.922 144.5 103.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +507 0 obj +<< /Length 2361 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0F968lH%)2U?B_Gu_fS\Nhop4_p8Z-8Yc8L`?F.aiIXsOdhV'J.jY:m9o&J.N*G14PVO\HJ(#N,\Lk1A/:Ze(o43jrP#en:#N3HHcBr,JjW'0hDGfQLb?"62"T=,G97H?[+g2&8^2m;>X1SGM89'$I2KpGbR4+IM@.InB*>cj.IBuRBjaX[RU,7I-!\mWHFl'qSX+pdH!9MCKY^'+#TQZoN1k_od8Kh9Te)tl@hC'@!B=!$6X>=;'_JmtfbXY2kB]RIqV:"V4]#nagcfC;g(>r(FA(rt+1N6NGb8:\*(%E\ThDt*7.n-&idcEIe(V1ga_'FdE2,qZ3mAX+FPJ.I/AAB8AUQ5=QSDD4"&8;OXr[2U81O7AB=RQqil!Fp?H8At*Vf65+l;8=q0',M*nIs30;O9H?HS/rZL51pqSf["Y3dRVm40chaIfgs7MRuC+D_g=?,(h35J5's0'B;Dh'L9/2N&I="8);Of)e1B@iqJ>\BV?CNSB%r@!dO.#P3^QWBhOONnea91:c_2nlf^W8ZTu^8G6)g&hb,je";7pUECJdYp\07X)A)K9rjHBrH%Pu0S39I4qFA#9oXXg3$hk#]QN2DBT.&@:[Z!UD<J.BVor'?j;mpibZWSYI!'@Y,K#s3RXlEY?t6]LU$TKD'9_F!eg^ULLeo$KCkj4?l]o^SH!E`>$1dDopdG1k2Tt3U!0f8Mf'rB"%Oq1b?.+K?#ibG88@:aIi!9r.CJ,p#oQ&`'bGd2E!+J`/BEqUNEPe-N]Un0!i'P/Cdmt`H>ZcV!/n@d.PC!b%9X)BkRd:#TXW!g\mB''A(s!Y3 +endstream +endobj +508 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 507 0 R +/Annots 509 0 R +>> +endobj +509 0 obj +[ +510 0 R +511 0 R +] +endobj +510 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 325.87 660.0 370.87 650.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +511 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 436.73 608.028 481.73 598.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +512 0 obj +<< /Length 2185 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=gMYb*&:O:S#^o=lQk;baQ^7m(96Rp\Mf.jX%s89,&fh@B-*JRU%BpqhUi%/EPjNm0,`Di^nRBWsp`38+C/YjI(=VJEL[bWZt.q8Qk/o'=tQb=c5LuH!YDqB3H]8o)]H]f!6rSnPJXU`0HdP]Zg,(I+,*h,;kI9T'u:mhC/24O(4_LX7P!Zn/SrD-JMi&AC:K)P#=G!obce)b+hSH6AI&5Z\g%H'F3N5o+HGnZd//n[iXtND5!Rp]nP6)*kSH/XJ)Q-mec(\Zd\GD0:!D#mu3I-fne?AoIR&Q[sDhH=;qgrgeU:Z]O$G(/I;JHFLgMpBeGN5j&2&rrsY)2GEPOd`h#\u8[77%"-;X@^,d%!W8Ob/iEJH2&qQ;o0+K[7+bSEdff\E_,_LD-Ise:\4DnQbR@.u\%/C*q+P9q="u^uhL5f;XPcDo"1Rp%O@#o_iJ4j%64Rhsi2jn,qgfA!_Zgc,Ib9T6pm_h6L>-.BN,h8;2,h^;HTRPOk`N?B8BEf"^\sgRf&bQtG_r^?3EIU$iW(22:^C9q9fIMOU>V5\I`6/c/0J22A(]mXta!_4&=D]mFlPqaZnM=$)+(2Nm24fje0qF'TRn9f1=T]Tlq/]AKAXrcJ/8ek>otn.KTEL.2Wer(%bh0G*7%In>R49^0R>S#dJ2,4JXr3=be,W<2P1EHpb=_j-$C2m5F]]A3nf9C9.NSDZeX%[YGQuZ^Rs^jJ\gJhpLoP-?#!5jZAPLBO3^p#J%R1DmB/"*?msgtB\7Oi)KD4m&2E&;V".J^SOTW5o-<=Ui:rf`V]JM%1JrUP[:V8$MY,R;B&8&-RFm!AN"Ce2n!g\FV)W3HG*mX2>V9[%IbKeYG8#C*NXiQl>QOi`XK5IF+73fXudC-nTKbYeD%)"p*/M)`^2ZZol.h?0%bJ/+RLi97mE,.?.C\C*?m%G.!V^[35ge6cfaoqo3j)W"6M[&2GaXOB:.HZ@raX6UO\j?P&D+Y*#*^0Hr*LC#2iOf':R$uQFi/1JOhhS%+qXiTFes'0EZOjrr#Yn_'Eb[!O\\)63YA7G\q=_Id7q^8WJa&uXX=TS4Cj:GB(r:0>F>\mF7)*eupJunI%sEhbgH]/Z2A)3X!7#^VNrU0Riaf_<4^N=.p-7N4\t&>ng35e8^W+-Uj:<._0QLaQ2O+5Y)e7PeU<>,`F\+\A$DXtAYE!<=M/Se#NBYP;@]p`?`0DdSD^.%5:_l8^Qg1W5*Sc"6PQSpYu?OJUGM:5&/+1D));1(#b@G&!6[kF#:BU;82i)'^kdC.Qbp!Z#R/h724='gogO'QD#@7hq1ob?0P=7e8.JhUPFXr]4o+r*_-%*!QWF(S9arrZ!_tG0S,~> +endstream +endobj +513 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 512 0 R +/Annots 514 0 R +>> +endobj +514 0 obj +[ +515 0 R +] +endobj +515 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 350.83 668.5 388.33 658.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 182 0 R +/H /I +>> +endobj +516 0 obj +<< /Length 2510 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;fgQ(#H&:NH>0`89Y@]cT@;RA]poetQJ'.<+A"kl/]&nMZ;-'b4U]741&-(@8Y[%(GDRK.mio.kC4baBWF2sZC&)h3U(@F!HgKfRmoTUEq%U7#riG(5,knk4]F0g?;3eS+J.<=Z)\*0ZW*ilr9;,jK(Xf4@j,m?+fWWujm@]OQ]bg-;X6bKu685p14,]<1/^/S9@M+CSGjI64D0jA!Rc^q@7Bu0)pqpk!)qYDe^s3Co$%(Pqi>H,LCXL0>b0E[OdX6m%/)$pDF/?fa02\Z6dB*t;>df9n('IPGgptuS0Y=VRN-mD!\,cU?NA@T?*UrjHNF^=F8k$Lt8A/TP%I'ljDU([4O5YWC1N#3rKS30D+!Ykgd?*/hn=P%E91DDUMM/'^65rC/#L'nj.&oeqmMAS.3^o`BI@#_:/AglZA"He>5A:_qRjUR>F;hW][Pt\+:FcCTB_'Nb5gnAtacX#*3*@'a%VEca_j(??jf+fO#VEF1i6$6ig,ePO`76J^0'l//Zk@uI22'+.&r$S?kE:QAIe).E:>6U%ik`elDa.u6\V9k0cQ9/qq.FN5N1+A01N_?L=FOpn"klub57e=LeL6mpCQnPu4(NA$?s10g*qN2Hdpo*H1K6h9Wms*:K[AEOH.%]X\e"//JI4jmID%8\*67@e!%,bYhm3\-B7!7i<1!Z-KspL[-FAIC&"iZ?*2"37'O2Re$69Y?Vi#L4PRO8)+4\Q=Y;aDu/diVgYlTIHW>/,Ip'8)F6k_CjI=aY_*OtTgNEY9oD6-d9qGEoX8+#&6REFNa-:D9=sipK][+rhX$PW9+#)5"\nc%TbLBQID9m7&[q"Na!8+_5&E@>]0>>>\]!If+OGs3$N)@_m/CL#j!6k!:?l?VTfjK'_C"pEC1C;7Zo5K;tWpXXJ+uGD_2AfFJ-kB4N?Lro^*]3$^N?%4hb?CZ'CY?cD:heT&)(1?R<%nQjT4O>Ot$$:Gfk'UmVs`0=!JqNZ&01!E8*$dBMG&,Eff@U1ZLe4j!"$H@9'7.G&d@F.2=9_Vf5RVgnmcG7I`Z,MPnE9si#N`g%+C]$)8#AqRVf6;n*`jCY_C3qL;Oi#"s7YQ^&r;F2[m!0iS@]\GjZRA:R;:8DWoEEdL`QG[C&U/^\U@e,U`[JEcZ+Z1K5I!,b1Sgr!U=&dd_9Hqa[Y/Q=o&MEm2(.YU@4AW@1Il4O..3IRh?i:GmnO@lJSn[;M'itEtLBKk@2Q.W[F\0k5-MTk[^erW'XXTB)jloUg$S',sU"US0j93^5'ZZLq"ZH9O'fN.p(-2*rk[8c$`KWu=O(Y'me6P"`CtLg6?WZY%S5J3]aiH`fFU+LX/DdBOd5$+r0Qnjp\SH4T(AE75*;ZHUrZ&`IEWtL.JmgWRnPf]m]A^T)Y'$eV0NP\rJ\Jb*C@]rpihqCToo97\"2-Z8'panXgHGpR(?4l\mIL+OpRg"]G$rFG]%&jiG(kh`?Gm_A^YiW'Tb,UnqK.8iAGCV':%[7c+."'5/_eKqT!_g1r-?+!i[8Jj[QQhQE"Vm4<8TTqM.Vben4sEB1jj(.nSE::iSr+nT?O2^#+LA%N_G3Q4oQK_Q4X8`H&QV6CCWkGNnZ14cHpJ+I\^`UX.)qlgZ@GFg\#l8Kj)pFnV#[U2#>bZEY3^Gk3_ni@uiQ?oO*o*43;)MC?g2lZD'+f=E9Y4In;!H1QLCfVOPTkg[93$T"45!U,J/9WjkkkqE>Hq?ZZ#"99,GEL[;b%@[QdmhB3]7U#*s`!InoLirD*eS1r@/\Zucgn>Ai2V+j4sZ_[aT;j_)PDbd+pOX6#6GfHD:h#naI[MkbcI(K&g+EHSSl*qlUH\>U4WCT`m`T_B;3^Nj%"OQ/F:_/88`U.?Js!-?^JMSHN.4NmJXT_7BTQ#nO334I3EIu&@L$Xk;\bQ~> +endstream +endobj +517 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 516 0 R +>> +endobj +518 0 obj +<< /Length 2128 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<9lo&I&A@C2E--tM/=NC0+aTSh&kcrf-?3cNc?/%c+;1t6+UNl+/c4EAUhF7>!bB9p_)!D@?l*b=^0T[AHn_$#ILKKI7fN?@QpeAm7;coHQ6/f]s!'b:IG_''[2AftGgfH4jRf"BNr2\u>Ahapj`hWbH!rWXE;C$Q-O,#BG<'2GB=a_6Z[MC&[Tl<4c^3I8f9.p@f8@8mALFcHf:(mRf,)38]$%E)CD8hslkd.l"aohM1is'd-Mp[-:Oqg^*dr`Nc'><3MZGU[sm%cT<0_=@Bn;%BW'tAG:YBj8@5J<`];lj*'^??TZbD[L[hl6INk`:JL!rU3ks(?i,cMg$-4BEaT9?upikcjN2Q@rhbhL[)MWTi7cd(b/7j#p4HAmmp@&6Z/k@>Hr]4O?=MFe+lqi$0'TtK&3Vf[qBFh05r;ZctJ%0S(JD/=.clCK%?h\h?;4ofbhF*k9;Us2/fLJ6;"IcJ27gH+@doL_29i>8HZ3k/r!`90Z*];?6i!E:e/W@,2H@k-;SjEhH;W-X8DU*?U2DFfpOP_9ngrg:'IEfbRs,t[8,ugen6!29GN"*sW.HKXW<4/gFu+P.?[9>k1-6BV3cYlNsbj]-T+.FF;=F==P>Q&d9S"Z-D,WW?-K%Gcq=T?`[)C.FP+b=>8G.:ti0J/1mgQSOH'bC$O6NMRP/>c8cc.YA&tc0X&hhngL?gACZ=]$kLj,L`)%,[K3AjtAZMO[i!g/J]dkn)0Ti&NXW+s.Dg*$]F)TOLWh\p$7m'l6Tb5rM`MbXcdL@UmA+k^),IFL.r>RP;jLHS$'uk`&_Onk'J33n/-:>*;UBi0?T+(DJZtYX6DqaI?s=O1'aq4XNa!6X)tda;%b9OS!E2_h52;,QY3\M:S[2ShXo/'3RI$eX$[YOPW#NF9O]?C\%tEQgs,ujosfT8Rr;UBdlHh1GhpdWl5dub1Y#Hl<;qVWm?%Kk`O[,l!B)cCU*Fq?eoGl,<5T=DJURf0,4b_9j"!I$8:=T"l'uGeg;fql\5MQif@=e2ITaB8W`/>f.!VplTH[iW-X3\m_ejcERLYl#G#3Hc,Y`qD2kOlFCA.?_?GCh\b8BeaX3VQP$Q@RuSafHC:1_ib?M@7?>:.=To*(u!7/98<9Y.ao6)..:iIL!+NO>cb%XBp4p4d"N#VCO>).\d0'YbW_S)J8jR3D(kR:jtrh9_N*.CO_@.>)/4V:D?:<(=i0!lJoI>17DMkWNl09[Xmd(hs:%0[sDaE5JLr3FAGf/dbONDp`U!A#7PdV`#@thOhu!+>;krcgYGA$AYLlej#GILP`NMN7G@F)^pD($XKO^l4*f"d7h,Fh(V6!J^2!C^a(*F*,T0.kC@@_omJDd>sIm)>P`RnmhmRR?mV=EU_??ZPi^DdZmY\GM;3V8\^H,uT5VCjZh`ND34ZuG~> +endstream +endobj +519 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 518 0 R +>> +endobj +520 0 obj +<< /Length 2536 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`Vhf"uT&:Vr4@HAr#C(@I&ESjYQP;I=4L,398'\o)J"2)Y_EHeJ>0^e:R'-bP:L*4-0_B0#m[q-(\I.9CosU2oAX9ABAKn7+0P'C9rGa0+grCeQ>uP;COjG;_'AY\HD-TN3*N/'HX=HR"bJ'j"F[2rr:O`moG"?BO1'7Ikf5GQPH;RcHYb?eAA(gE.);\QB!UdBTF-rI!KrFq7hL[eIkV88J!`lE's"b=#D[+AM194BHn-8ki&a[A#khGhq:?iYJk4"nGkFt,l\=TbrT0B,D\#5aM'X7&/MmkE("7=[/<_JS2T1/UPtCVej0[e"EI7.TQ\AZ[qr/8o1jeoa-#bAlh!Ak=>)#?^t/")A`!EKMOZCM@5s/T!:ZGdBh_pasVUF'+fWL-ZO4c0k7+qU6rk]-M_k^B?&)ga?[LH<_o86'#Y_HG,!PJF@HTH0D.]%7Phd6t.mhopAWEA+A-WG#EEd:).25&jo,KtJ+&l/#RZHZ-8+^m'`m\W05_i'r%'f7YO"BuTrgA[$>^palOp^AEn#YN;$?PVNR>CG\ac!;J5bBETNK>+e_,i,Rd1CH;ZQT'ik8&CFhfCRgb-MOLWF.d)OlWQIF9-i#s3+Y)ldruQ*5EPjj,d"]`N_a3k3Q\"K.!*D6/c[XH8Hi]=3mQhruManqkkqV%S\)ZJ@#b8Ogcgoq4#1`_aZF*bpN1PKJk_kM%nG3?d_hLJoI^,(/GX5jLQbl!)#fZQAQatM(?((.nkQnV3l0k+n+an)INb[@8#aVe49M.(5aiuf5V!/6O$/qHdU=Q.ec.[*.14a,E.BhtA56=`5Tn;"7I=.:O4P/ZsI!bn+$0pqS>$aP=4;lWAj?M1Q1:Yk=l.TD9/JXMRo4Q6%NMuf`#YVGcu*P!ssOc=\AAuWGOJ1#Rb3h:8L1AQ-n15rCH7CTCuhN9gYmHob;m];FC]#?tD24gp:3Zd^2K^n!>M1J/-N=Gu3(;5+gASuH#5BM=!F`fm?]tZ]f22^4OpLt=_[8*LBoO-;RFjEklgl5Y5)-Q>QFQtt`LDgd<^r*hpXi#:RA#R@W[icH%fprHcGa^6c[/+u67G1r0+L?KWl_BJb-1EY10D*;LY6f@_k(Sg"0`H&7rm)VUp5pDo.ilIXG!B[frQ>,l7cE/0@$]U13fu6["Hi)1hBs:=+)AEiD0]d^mT@XqIEo+KNEci.rdu1pE>U29$u;WErL#)p5+=k494_+:""q*lUCTg!%]s,<&+0lUfUN`HUAX#Q-j*'TDaA\tk*,kZ!mMHT'&7?"AQN[>b]+t)$_^mCb*W1^pd]>V*rd(eNT,A+IIlFgo5JmKH%(2;;(KujcMga\YtKZrp.GJJ@Bs,O#pq$7iV#Ar^s2X4?RoDk&'u;0N;_SqS]YfaId[A&B;hbIR&7#q'X@tG4W[Lajq8!E,M=qim5sJE0Idg`m6`)qg%aiYY+3no25aT-=jVH"hm]l%#ODimA:'Q@;DU?,\HIdb`G67p\$)k?58G`WEc=CHG*IF\HcqRXg1E5^sRf*Ok&8CV+]6n*;`o`GUb$2=lgn>VC_be>[Unla$%-t[0#mjVahS]*BDn.MS#Q*jHJ1\X"*'5";*i:ElfKVKql!TY3/Zu"KU&$FE-<"\@okVpWhp'k67]n\%/'#%\f,@A]I!]o(`*YOls3ef)7O-OU"t-$2L.UX`[`^[3oh8-0c-0S`=QAdHdMl8$3+II)>N"O9!Qu5rNl8Tp1Rs +endstream +endobj +521 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 520 0 R +/Annots 522 0 R +>> +endobj +522 0 obj +[ +523 0 R +] +endobj +523 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 125.6 111.531 163.1 101.531 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 150 0 R +/H /I +>> +endobj +524 0 obj +<< /Length 2793 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^>Ar99&q9SY+S$5A(ZohNeg5$##Q=1*-rK%V\P#gq%r`L2TW$]"R;Z_N^7!MqX0^!YN^aH5crQVl6[Ld;RKYZZr^7kthDoHaU_S$Hccud\nSH6OJSJWNCX7@H@:.(8J(N5VY-*7ENu^6mL0ZqbX-Umg=pIFSiE$;q*aA4dTc]Vc=](*QGiE1#+3+=r,H#Z?sL;16\1"U(OV[F\87E0o^p.?Dobnc=H_\H).j\T$5Ui4#'cZNQm8-N862#c?cC=V?dfS&+I*%*1+h;hA0#Qk.:(u1d)@/&hKUcVJp/!^ecnh?78d4,.Y#:^]*n+\>A.s)D)fsq'Ld]OLut2(dA3Zp>pV0!g]ka;1SHmA!-]5=Ir8+3'Lk!meC&8\m.;(*$@j)c`T)N^B5h4-%M8RbaS\Ioodn#Tge8]W[/SB;@W:hJmU:)d--cSuDg"66Y_%:1%q7"<-:,d)>8_8W[N2Li-=P2gU[E7OL1(p.oZY,n$aV@`sIQ0H=+o$D&mRkr:lKjPu#0j&hJbK+KLubckB$bi4^:]qC<^>$?9#mmf$HT.Zdftpf;1lggl;TQd,9S?f0Yq^&.S5EW,FFlLs8arVaB*lmEas4u8s3?Y'NR^dX8C9Ghg(da.McC$^%Kdd$T[R9o2.[cPt3'&q%.u8tb'X+`KO`+&2q77q#UXu;0LX_PFuA;>?$VuX%D.^4[a$*ag*BWbiX(HltW;'!CaAOBFkW;gT)M!J940ko8R/-`ON-`3K*rTk*6mtA=]FC&EGh\\.FFG2?S>aq!KgjFta]jmg7PGb[C*lUfoT)@c$&C;AO/Sg95KSdD12-$flqRg_q09ahqi#s*RiSVc!D%=\,:js='&J:89T.KD8/@,QM!'NG`#9,e,BBMM7#26H@N8O^Mi%iCP^gqPN"R5Fi7Z@ALU;V^am_n]W/_)mI%LshhJ@A't;[ec*gp6[OiBC+@td;;'E?!Gc<^G$@BN1e4G\dMZEh?>/kM=o_#LG9-t@)MX*>/TpI`qg-_k7HZ,kpHbc6JqU3;DkVQI,p;`BNF)5=L^>8-UM[Ho:;EuSI3;4[O3NR&4EUg,*dq6lGl(7oPkb9!0kl/::&NR,\HhFJ/bkh;f^`7d6O2<2fo;-)*!ZIE$=U]L$L%o)e9AOUC_n)mc<="us-j=L^X=6Or<>:tL?@#T4#qqF;1%%o93:itcQKKsS$X'.0+K2]&"sQ!)W]j%T[[[\1c9-Ui)s]TP27>UTNM8_K'shSh;DIuT5+b!3Uem:A3DHh2Lg[@L?0Zj7fZ9HlO3J]9+Rk!Qfh-"98B;f:h"OAH#+!_HE+30f^:k8fjOl]u-P:PE]#R0p->02@@h-`b#o\o5>;T(tX!>7!'NLBBTNdsj4>VquKY,Q9?WhX^S\0gPjfUtPJa883\[nLD/'`^1`\LFULT&M]qI9JJ$dRJdEMR_)1an.'l@@1.kg0nEB6hT3p)f5*7.]=;QVKk`00,eg6L+IJp*p@cY2=f8iE>_)D)GLFd)HSo&`>j'b(fZ^F1uUu+El$8G@:_]*(2U_WX,3=_7Gl8a5uXnje,,qM4'5%?6(oQmY]:JFJu^h@=98jrgb2SHJcO>XpFbAPL7::FNiPWK(]F"0/-/-#X,`%k:[e-nN&8'_,FVg%64W*WCTuk>7hccQM?9KY>8-2505;&rLSGd&X6_#o%`hlO<5$1+p'aL8;[HH>rNhZd7k$Rnq*_KIh4dqsih2p$2BDq'$F?NDS9^A.Q]8N/*m\-RZRcIM8Xg>Fb\H\o7#UP)oZ%N8ouoUbs#%=]F@QpmY(qM9%Qb1omErAd$8mf'nM#IpWGHaqMsCqcj]SC!aIH(rmB1MLJ!A)Uk#mj5[2*4=&DchS+oH_'.nfCFV%.5]oY\__;GJEIj1f8O$:=&dVrKSXHO-d8d*j`PD7Wfl[g.jG77s*13usHrnelK@[2 +endstream +endobj +525 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 524 0 R +/Annots 526 0 R +>> +endobj +526 0 obj +[ +527 0 R +] +endobj +527 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 241.96 110.495 294.46 100.495 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 71 0 R +/H /I +>> +endobj +528 0 obj +<< /Length 2458 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;fgN)%,&:N/3n1gNQ0ELYe5.ED*la1BS343>l+jL0X5o(f[.(iZ%`ue[X,%3chP&.V<3&;Eb,-e`bB+K//]pan??Fpu]O_mhT,MTdI@EXu:YS@^0k1^C:Hts@!+.lgPiZ;3W(q\>IcX&C>a]m3K'aq0^1B+-2H%FJd7/o5Bb:N"m+QTaF@>VSX?^Hl\7A31GIQou@HZhF6R8a:[1fH@&h',kN;rC$5cGWuWR2WVbQ?2Q2"V2!'-IU"3f&VlhKp%F#)[DsGAN/0E31!P("r_U/jrK./[XC0:(XUE*/=Gmo)\\)17scaO"C]dW:@HTr"4TN5/[FR:6]:lJ/CKn#,%OPmVk=;34T(_8`SgSO$iE=#/U8[E,T_3(_*2BE^+UT8^hU)*Y)$VD-f)::n3(\'pgbNJrHOKl)BXk@"4?[DScKr(Jec(73oAm*+=8q-Z1'R1ht1eoSt'HA!9LB`r">l>r!8,6MQ9%Tgc`9_Rr%LaD`ID@;r6Z4%/H,j'O^h,]@!cjt-NR&*BPq%ul[ssc3a%DhcD6n0/E-ePm3#?UEYS!tGP"86J/1kcU@(ek'Q.*D"=VBZgjnj77-Z?J14ke0pmq9u]\ujJ]&2Df,)\a0D;D%WX#DQ&,GdP\S&Q^XN.2rRFI!2!:fmrI$Q:>?/ZT<`_^9/a`OCts4&QC\\N6$g2)^sC%oM8PWUEG@<9b)@O!XjR"3R]7$Y>(fn/_C$u<%HtpG#to]oEEE@(*+PQXd,e@lbCu:JR7q*+UNm8RQc"]KR0!TW[#MZs)/=a[iqM>PBIMX(%3I=T$'X1RsJ?f@>.lPtbTW(eVYSG@>A@4N/OXX#!rTA'22PER)Jp-f`WuIPTp-oIBqp8>%BIY(QeD4CXKK,,Pm?>_`JYEH_m2cVC9q2!KJSF@b-)c7T27;=k^Y$:iLEV*kSMbrU"?lgZQJ[+4_eAbA'aPI*A#4dGDBr5_]&1ZHFOp/jD7%rMK\#7`8fAUsh!T"n%EG4LDjkr*ml^!&MDN(d1'S@ZqH`6h!A=Z1_WQ!NI@Ai`4h/f$UL$S?X5d"i,e+Zbfl.e*8KeK,aJ>:p"C/5u5gog4jLs6sJ4uCUc0g=^+MWi4Ba!D@`Jb`VT;Gt(Q#_G,r`p([6X`EGnYHXe11LUeYfnkNX-1Qf9aM5P`n=$Q?0YiAHM'XXR=dGc4lG+-j7;\b%Eo1dbeEQnr9HV/1(6/2nq.I>PMTE2PP)Fg+k'J53P=rbk+Tf2%Fqrp0@$?CQ8+tIMgH5O2!lhL'=q=hP=f4eR*#M2cOct$I_K,;%Wq0V5r"]TCJ[Su$&i8HY3a$ZmXoq8fHcp&,A^lWgFI^uGo*O8dL:IZ0D-dlneW:71bGWZ&mQDrO99eJ,Gf,^m9Kn1@J?`KS@e.<'hD3$:0fU3PV+acu*8H]7u;;[o[cLPYp-W(_V-WI`41bRCc^WID3U9js5?Enp+LAR?@aLigKS""K@_P/HTOm^1HqaN+(te(>LsWPj_X[ipDY]dc#aNJ/&RN8RH/(M&oJU*E:Br&5nCFZE@@NI.(,4\k91,3%$Y`Pt-^nQkCr!hlcl%cV:$+*S];RfKkYa<6004pg&Ql(#L0[)#p<(E8n(HSU(+TZGrA&9#C/Z4UI2K]6#?@[X#h7_*@T;]eA6UI9Z6+2h2#io#q+Mhcu?MYNg34emS:Ode<rp*\$M5]9elFGl+30rYR6DM@O)i\=\P..Adf3/ns9nWEJ6U6$;6Z5IUo;Z42(HH%2DLW3=>+$ZtK!$$.mXSUq?3ktG+p\$h5~> +endstream +endobj +529 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 528 0 R +/Annots 530 0 R +>> +endobj +530 0 obj +[ +531 0 R +532 0 R +533 0 R +] +endobj +531 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 533.59 711.5 538.59 701.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 122 0 R +/H /I +>> +endobj +532 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 123.94 700.5 143.94 690.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 253 0 R +/H /I +>> +endobj +533 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 245.31 564.5 290.31 554.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +534 0 obj +<< /Length 731 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%`9lldX&;KZQ'ft&)[42IoVRJC)%\;i@j"SRuc9]:PXXPA:+Z\plGk0o]%6T_j!\9V\mO(M85a/F$O'7b5..RDef#HB`TZ=^V3cUip+dk,e&ODZI[6j3\l@eM$SZ65&.>Qq;$&6jW3,"^$hVgdkQ.ME>o5]_,E+_[puCAbHHXo0QG%3r]rl?3\u+2mM*0BC?jea2Pq53j%Ie[YV#b6J8A$^hP$Ob,+KP7^N>&7uT84/)0XX-*WbsYXiS7n/FX8iOl,Q219o@I^FBOG&fq=28`e.?0JJ`5kOi!.R)6qrXO,Ub9F_B`j9q?_lAadu&Hs7_opj;S;a_*Il>!>MBN"R+b:6H[p.hDT7KHE@d$if,k0@u;FNrP`7F!lGm4[p&X'BcMiHBBR8(37W)^Jeo.BuHpI^2Mn>Yh<8e!c0Cp\fG?-@Q6mk1r&\lgNn5JrTqN?PL:R]3(!4D_IQ/P^^+@4@B9Ptq7EfQ>S[Kf"=.XoOnU(f_`3TpVt$YK\kAD2OHUTFrTS/YM`G,:RG;/-"/lCb$ZoQ7$qf+8ln8Ck\0q~> +endstream +endobj +535 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 534 0 R +/Annots 536 0 R +>> +endobj +536 0 obj +[ +537 0 R +538 0 R +539 0 R +] +endobj +537 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 369.98 671.0 414.98 661.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 95 0 R +/H /I +>> +endobj +538 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 459.44 660.0 504.44 650.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 99 0 R +/H /I +>> +endobj +539 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 209.19 649.0 246.69 639.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 150 0 R +/H /I +>> +endobj +540 0 obj +<< /Length 1826 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#]gN)%,&:O:SYfTPnWDS6MGfb%gUY3LN"%>NlVA-$#@TS%dcm";M#tUL1]o6<3&DUP2loD\.IYpn7J)sn%+PVd>-an/IJh:4)@EWl5Qu@`c]:Kb>XcG,?FD@@DgD8g&5-V?P7=KDdjE4U9ie9al?G'`eh3@9UqIE#Z$ffaRpRq7Rc(j^ZE4U+$a`]ua2R:;W.r?$tO\O]"TG3WSd3W[\W&f.?fE#Ys>W/X0jZH@N0=FhE!jAC%/[k+MD5TJi-4BRbl7U1Q7k[Pl&Rt9C,jTF9NSu4[_J:d:V<=F&Cf35+9=*j-e7PXF9agg!?^lkcjPtY#U&cehuR_e"14Q+]ne$$&DC=LS\[G5G>3>.Xt7*l=#W"-,$Hj.BKj4jXkI]JATQaZI`Zm@:d^NhG-V2KUHMZ'I$O>.QMo5s7Q>[S,%a^f0dbcI9*f?2muPG\!JZM>-*:@H5O1L(ncT@$B$R$@_HB!I1S=]jN;[ngu()7$eag/TW)<+6AF!$SG](Wo:k:SjQ[<90Qg5L[J%eCRZA^j8P`[5Mi8P3h[!H'CTP[e6?-S%jd=:kl;/9i&/F[>-8)^gVpuR*$`(HRm_1g)OiQ#'K[[lu,?3Ntapr,+)Nr+;aN*+_J,!XTG5cB.g$4i8O#FEc\9mcDqYXeVe,`*,M?;KLgs)'*5K$^B?HWnbRm/Qopk'Pd`P;.F+%?K[3$>F"_!E>ZI=MR`6A6?9o/+-r/?!l+&Smp*RipHLCUVCfE&jt@qLR@og^dW%0PZ&kA)imofRI)''2PnbY\#ZeuW0mkukt*.X*HLC=*Kso^;>_P?d+@OQ%Lc:+Ggr0=e>=n%pffa)SZ)I9LFrG!R_2[-9ZU#j,,37fYN+$bpI5JZq&93UG6ME4NejbMO$6N[R!c1sinh-s(hCN:rf*gLeP;gNgDmFIp2"1o2)Bif#q/11]7$*d@6$lUgUf(f3Q#5N<;!."#&IcR!OjmRMR?@[EQYW.Q8[r6/FZD\-!jh'aAQhdHtGi#q)HFG^r+n"-cQGtBob=?sdqrQLN4;?FIatkcoF2f]2P+$UWbrileqk6r!?IAF7:t'nY8f,@62CYWGM8]K2Bk>_fbc"ROdj6#IML^R-ETe,6D4(qO4Wnb+:7a;Xf$qM>pT%*jDVb8LJ@~> +endstream +endobj +541 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 540 0 R +/Annots 542 0 R +>> +endobj +542 0 obj +[ +543 0 R +] +endobj +543 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 381.65 680.866 434.15 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +544 0 obj +<< /Length 1681 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;eD/\/e&H88.Tm_iSRM@8K0?(.=F'YkOPB`MEU8[s?Om#oG/U;Z!B4*rQP\$=SM0.O[BiY?B,#Z"=kFT:10>ENCT"h!8[ZPEdV^JoqH\*hRr*V$#@a2B1KRIXKYWeS;B1^LWqLmEQT.]_Oan'O)=-ArKAMQt>GcR?Sq2k2ZQC#`H/*.Gf_7N.DXC(+p>e4&WjVD!a7i_3'O4O)MiDI,Z8Fa\-0_05DbtgP4h[Ihd(LnW/ZW3Rp`mT=P9=[.Km/K'0rOc.0WpqeNkg=/+:@kQRl!Y9\7[\*bB,C%0@OOOpBuGgOj9KmF?L\j&R@.XX3F@S5bCsuMjEp*=$PO8$J"mhcLq#aGfg/#R9q/?Do"7N!Zp2X[1=,*()"O_EAECt4U"tH6>E)T(C`644dpU4%+QI@?0krPfN@ALO"^.5B0>pk$V"@V'S?7tH(>(bIoD8.1n7UqiM$7/_sX<$i'NA]7]ZT1S,N/a?,fX'O"=h#=)uI%%'kVVWa;&DAL8b.q$aP8DS-]f1I6==JpPs6*OtsK,nfKemqpHhV46mNKJ#R`lbGp`P*)3E^#cEa&iUP01:h[AK_%r[k7fb.^T+<&J"!:.pTsDCLk,M)W9mQh8in0.Z0@M'p-FAPOKD5!4B;ppU+T!+!$\PMp%e1uOKM4=fd90qT7*?LQnAA6kXbcGP";m44I@iWe@W+`9Jm@a<'kQ6*2aZN\&sntpRA[A9`uMql]1.W$a)]+[<9GbK)p*?#2^l;2-r<(0d@'!f@'n*j#4a%D4$=_O4O(0@Ft:3_WFW)Pm;(ebg*rM??;070uls7]W>T)0iI'9g7,.Ju81CFWH!i1gfN"lJ/6`V]=Lo/^lgdk_"A5n`Fl]#Ja5%.qj$?@.V1-WD"(4TJl$8\._dIlB4QG"tT(`c,%:,LPU(j7EU1]V,b@NH5)mZ*o>.u"&:F\nuIB*U;4n6o<\0&NG2C[ZG&MF(pI^3#U>)d(=Sf_5i"@a7ajD_,?8@H-VO,V&4400B0-W@Glq\jC8(\ZX.+\[CQ=9J'"8V2HSriPJ3<*7YN@D^pQtLF:Ic-0u&D@$p1:o4[+e4:Ci;83M--Ha[F!0*e1^tXgo5BR:Ed`-jM/dk=3QtmcX=n!tqi\e)p_=#$G;f3P*EeU2h]@"mM#)^+@CR>J?+h/tcQ#8eHnhlT5u&9bs2Ueb48c#;BtXk%"?#Wg^CfS5Q@nMp3u+TOj`$/HCn'Vs,;u8X;@_i:YlL%%<\$)3%g,q<_FMJ_^Ijg3/<>?Bj1Ld,@ZU:*$>N@#N7]_ +endstream +endobj +545 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 544 0 R +>> +endobj +546 0 obj +<< /Length 1507 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatmeCdes'l=,nntSY-6"=JS'L3HJfpd!ncNIX`G!KHTVTB1LUb59^TgJ&DYl?46CclatHLE5!AL`"4qK7NTQqlhLDMo=JPYP6[\>7'6(coi<$@`sX=1:D?TC:JREucLc%'MWYP`5%T(-Zol1Vs(N%H:RKG6b1P*B(0mkPZ]_/t<7rh_i%6hLk2DHG.MF,`NIb_ZiZ@Z;h,d0Tf'>=LJ#uF/$5`Gb%rg3k["_=4uJltAE$LnBr4jA8_f@`>\*N-1Um5PZ"3'^LP^?;2OCh!>&Qgok$jnNSjD>4qdh9]uEG^DQ>I!0l-kK^^`EeD:oALJ\FOI.pt]T>8;$*Pg%7ho*P)2he]dI8j0VHe+DA>PQr]ai]^M:q3^YEN#M$90c(U1e*i/+6p#TW)La?#"Zt1Iu=rJU65'M:fLeC+NFkC<+TjjlsP,kPoW;JX*'VZ%,+g@6PJOc"9?)CIK+M)Ku`5-)+@:j>eK.44l`3Y6/tNK`09?m&j7rG5p-f2smDrH[-F:$,^1Jlr5dcY]M#U`/:^S[^U&"(edgUM#)_fjpd`1;aoZ%Y_[E/!QJTL;O?q4TE%%!GYPqfs%2L(LlE)Yn:-WNa!aAmSisJh.aa6>.>H0:c&9GP:QKkXY9Hk6sLb_6.eg.9\Un;ge-l2JAXCIi_Yil!:eofF11`+]9NO<,ros8a"2iM2+7BSL,D;\`j)&e4M%7?qtIduW1a]hN[3%JFLC+GlQb%Fll!_#s8fbfB:q$%AZ12"V':6)09mjVWmm!rI;fcsJoh=d=<9$$2@rM&'48ONtfmBBt@=s*5hKci538@*&na3U&Wi-ML["`pIloBa;:e+tosE2`94&%ntumpm$n3L&_RI+$;Cm[)kM,gTNeliAo\#0aSNiPDOHPnhIX>'io.kR/8gW3p/?-+,8V2`GC4VpeRCZq[UhEr9dUY29@/j1OOC5YKugB3>'Eh%?$`/U\.Chsodf`V%dFm.tHX\HYX?jD$u^m;Cp8@#"rF-!i.nbSEt*rV.uer#)3%7gCUdF=1l'OQAu9nS`$XjSbMB)Um_<57rc9MO;*mCp-Qm^,>n`He!/eEr5dOn+u1&CQEY!uN+nno<7a(^@!e42U4A+?g&!+6G133*SI2P,l*8,i\&m-KB~> +endstream +endobj +547 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 546 0 R +>> +endobj +548 0 obj +<< /Length 2342 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHMgN)%,&:O:SW,KsT7Is9re&b:H.ZIZi@`rII8KJueZHuF$6@r7nYOD$+E^9bklq'6e&SYXXHhMUuHp'\TH0^G@dkF;`jcMR75I*a*r)oeo%IX?S0W\Y=?]RAlT.OE/`jSr"4VfR%5,@gd:,=W=]/ae:ZA9iZ/K)geGGP355gt\^KIgS(:r`)`Ud$B=,]qeqhnE3t9^q\Z0+=h5W&PX"g[Uh\lUQL0QB828#uh*l8)S["=#Yu(LnS40+]F<=FeQIL/N*MrLpbU0#AgoEJuJHG\<()rlHsIs1]m360IB%6rWL+C;W340mkT^8)fnSeOG6IP[>WnY0O:TARqN%6!0ZAii3Aq-9s#MTerPW9C@SaTRCg"G<8k2!5K%t]/BBED:%o"fm(t^N5$KL[K:r1#,f^0YIDi`MN-K22uI+6HMT7=oVhHo9>WZLUt3?l1)(a1'Y[?)[K$0j!dTdFF:ENFJhtppl2-3PLV;?P3'2r0VDDlFW_&-1X$pV<,="d^<9cG>?.O%kAB#O%>\2]J#7:$`:Z=jc>LIT[D*ioPJD.M6ONthMlJOGC9m09L0Ga?bqfCK,2crRBRa0.Tr:ZN]qWQ7Z41SP`'"d,4sipe-J.[)_^m6sm+L[R'Em*fb&ke:frF0L!0_IVsV4cG&(pY+sIO%:[aR\*RC)Up$OaREuUb#jBepL*-8=89pd%)jRq9nX3968G.Ub'aP",XZj`j`Der.-M6D-2[!9[Bet`SNb($j&b>c+MTV^c?]9Q04[5F?jVR?"fj1ZP,[VXp4[T5_1Z\ukRMe7i3kb[Kh_Bj9WNeNJ6,/17=fBa"oLH2>f2.4O6X`gDR36Vi`[ob_gP&j5KXT/d*Gd)Ha4CRab$DO42J'_q/(2aR9a1UTN6J,@I/mFV$uFWjjrEfS9BSqhWs/5X1ib@/nNV@TFEG>o7RgiHr7+;l&u6C@)]]5bE7KH%2<=]3VtFcfba=`5YKr0:(J['*]p!hbhB4%\Cr%o)&RuB\;:UX?3XWJ<^-Qps@Hd2.AL_hm=>Qh`:1:fd>2b#Q"8dLFU@k:X<#7ZtM)lP:dtX$TXO5f[*WU01a_#cm*@l@aM5>bHF!N9\4'55K.&1Q3_GU0k60&DZs)\P"&!5GtPq,`>ZL!D3Mi"O4ON`?lRg!q;ZPNb5VPlr\_n~> +endstream +endobj +549 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 548 0 R +>> +endobj +550 0 obj +<< /Length 1658 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHLD3(/G&H9DYK);u,2&q>N+Zc1$f;;GA]A(kRqPL\C*,mg7J90@ZgA^q)K]"T;+ku]c/$6%nN+\`-k/I8+4T#)RD^!/EXQB<&Yo_2PjbQ-37BY8NS^9j"[EPb6kBDj=f6>CGCZ>3`lfTSiV]q)CN>51%aV?E@\O[,(bVqpB7[7is:.WLn$gY.,r**8BG1J./#.'u/oGX'4KJ!TJDkP1;Q9ZVtYHE]N@A'/:c[[`H1W_EN0=ZP+8Wo=9[(oh&:(NHeFblj4R[,t:hhD`]^tr?(%p]2=.n0LD30hg0`Al,kn&.JL4XRVo]W&(FfDtg7b7B\dEE4AR)oWV(b;XjpflH*f#_#'0jReRNO,&j5V!!d;IEYfYPJdHP)jqgV>?W^*Ma]>-!3dr5$"N9N3sOl;Hbf9@(*":b\mt#^0h#F3cmsOAorp=gMNbm>a'hnp?0Q(`3['T`17h8/3T_%Ye/;]Yi#!IoNiZ'%]?O60.O//&@SSZiAXI1hT/"SG'!'-EE^heiH""cs%+@eZbI9=gBM,U5;u\DDV78e/sOb+a[`OVBM'N)jM+ACV3&:sTUl&2U3d6A>8DdV(`8IG+C69-R'c[gY1O;#RB;j\H>;XP26[P@Uuni7#[#O'!/32Z.R[E$HGmtN.r!;n?K8Tu2^T1LEE&GHsas<]\b>k+$Uu]dVUqq79Rs)Lq=UHMp/[&Fp]!0TEMeL@IHZbocf5KR2;Z0IW1kFM:QNTY+1=-l&-D"/^Yu*>6PTRU$5.;3Bi\FfU@<&@q5kMG&$s%46=_j2K-BGnKW/uo4XYc*Z8f[TpTLcrKl8)+9RRp?5ehqE69(N)`R[7UAOcgl5^iYYV:J=Y`%Cp2U(!T^;Hb:Nl(K'rM["GBl#KW<)J=n")Kf=Zgak1pqtW^iRr`7#DeWa8TJ)4BWT3,KO,3\1nJe6o]d?g`Gst]Hd^8SNlCn]X20H5h2G%YMZ2X^XgNZV[^]LG05bM\!_5P_4fdDP]_V&p[bQ23CO9Kj::p`EeW\TiY-E@b2(h<+oJcH9IJuu6$@XGcF&#@Ne%3bpafSk&b^P<\3>?%4714Nbp;DG&JY[&$5kRPD39et'\)5?ZEc^FuYl!kMF1H8/EPR/&DY.d)KPH00.Hl)O4[XN&rbja.,6Z'*cU;a)cCcI^;IrBf1&)$Z3D7>HrB9m3s3T'L`4Kj7,YQkr3Bq:kisb/oRFihk(l7,^i4NiWBK[2r`r2"iiE5TJ_eXh0$`=Dqj=7E,5ptT&Y=J#I!Pu+Wm~> +endstream +endobj +551 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 550 0 R +>> +endobj +552 0 obj +<< /Length 1490 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0C>BALf(k(RKBR"PEeS;k4C>G=PPMg&!ZX^'(6]qZ_1,&l",WW;Go&KW3`lbMn\`;ZeNtCJ9HTUEWmb=[!N-\iXP_9B.hK8A]9/"d_HC@\t4Wu]^pPgM'G\QMMpHJ<-7_Ra0GQrYRDL*!i_5$c1*e]8%:XbCBDS0ZII6+r2&i.XsMcZLRBYerc@@lRPqXDZ)i^LV"8f9$.<<`^GW(LFo^Almr$Q7or/(f'AL9T\:DO\06):O$&0NL$(%(21gNY3)n,<%J)h[XeeCQPp?,#]N^@DBl!>NSL.XQEAb0=6kQ#S.d,eJt'`k(nR@*7!;!#LJaf04P3r:M@*prCAE6JZM`@@h;-nL]D-\-qF6Wil7>u+kZY%UP#WR,E^+kPa"65.:DKs`HZJNSe.0,3"OCkE,eS#g*mL!A*C!;>Ij"qfc=Se5,[U+n6%Ma_MKR-Rah2]NqQ=23pfhP+!$DC>ec"mON@?f4fPU%A'YWZoWVnt]lNL7(]E-Zp+kN/q=##K1[FTMi/?>=<$+-DMeU?!=aGgR\\r$2fgk#oKEJ:e<6TWh#pG-dnr"?ahg@N[a?QJ7m1hgH4^9CIgBuoGlHA['Z11)PO%(3V+$-_a>tVPpm+OIQ2jnUngDdVQWts^m-(mIQ@;-lplbPC(Lo4,i%)S>Tt^^__>n$CDYW9RL79ckGG#j(]\"B"dcf0V*MQYBJ@)?&.'?c"sFO(pDAj31DjdJg:XN'5QT`P3c=ujQmMa`?/u6B&I'm@5N$dEP9&adoL,(,ncN8fFj5-'N9KHfqr(3sR;pD*>pCDo#..%?j1-9*"oP5j/e,BeBY%k[/'R-&S1dXp't6\9/g4kj(\^G4io3Sh^3as%&`\mg;8[`C,nt8Rq^t\2oAB'qm_f8>E*RC>VjgOESll'9L2DpXlkmWb"BD'_(@cG@K<9uOq`J&DJmE&@QXfIi:JIn3.%R`V';_Jo56XM2NVDjM,bN@Pe%!p5lp:I:`?%bR"nkA&J,~> +endstream +endobj +553 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 552 0 R +>> +endobj +554 0 obj +<< /Length 2310 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU5gN)%,&:O:SYj+m0M.g3`;KNplP$2SJpla9'g6jo2'F@p4ntLYrATdk>?'(;T+@/Ro4l[Cl>V,T=]jLlT$'$@KdJ"ka]8-FDO2dc/BV+JSN](ptn9aBp\m]<;B[`_P7rK;c"2m9q#5eP5=^01*ES'o,Ip@hhNCI>\cl"j:jUfOKkseLWp@2?o1L[R)bURm:!t!6%9RF]p2E]eN[33r45!(nlM'r[56&0GX0%+^0LD`+a%F]EIHE;Sk>l1B%J$bHO^8K`2:=,->F&pjK&G[oWLM5ATkeVScRcsF&(_o5dRL[I[UR:"4l,^?"1W__g?/mVNi2cO&>l7.'RAB*_ljIi_%]BFB?QMcZ73h44[<*GBJ\8@k*iaH]&S+BXcELF;^4>QrT/\p0#B^lE`39UCNsPRe]So;-n*h"GI@Mp)A+XQ+k-F4sF0m\a(`D:CDS;g.^+QDE)M2<-N7\Y0q-Y4WZAf):0N$dU/am[2NOs>33_8>)\B='M5=;h/G[U_jcX@F8iZ<5N\9*:'A-*m`$CV%OWMioLaEIG[LJR1l>Cl:u$?=4<,.C(*D][W1uC^F.Vbbg&nj.kS:PXc?Y@MEq5-!EB(b5so)`N!k8P0aHJY_!mo]N]Ord[)8*OsU-i++R7P77]cmq(kEV@l2EQ0Ob?QSh=8ADU7=c4=2mj97[EE8l-pqPu..1Y(UY=!quVpl\gJOGLrH?cB3T1kK=#FmYb09f?SVRo8n*Zp$crWq07<$i5C@dLilsuQ$(SuE?1n;*/jq#:NdF[ZRY^0@HjqX^.oQ5(,8.(q=upFdhNqfK"iok3lrn6qE5%D`YPohNPVMW9eIcZPH-t8Zea`3(O+Ucjd3UBh&X#R%6L0Ko]uK2V60uM`E)6h+LlZG]2L.sCW+]PAT7TVE[:p=^6sBFr;L;KhQrdU*jqL1A6%.iH6Sru>uid'&Hcj_BU@gm`K:=`b>T"=M_NigF23E@d"B`ST_h"l>KSulF7HjP]<3<(&nM2_Wai%BV/F!C11f%g@+7p8CakX1_;@noD4r3cp9bub/]Zs5l.^,9;d;c#CJ\r"S]OKm7B5_IR]90Kh%QO5L$`"mCFhAe9O'QW!C[;E=\e&'DCC6oA/mu9EuPGare8:SSEcA]Ktg=*DOB8:b.kU^GN7dkX)I8o8Y`7&n5MkV,^*oq4F.T_NBJP.0MdiKkcc;_G+PR6d8-'Zkr_ard1TAEsu:jor*)!S@ePThT66REfWi'^K&o?*(6E[?tqH'Q8T?p!MmCrSCkTFhKA7EgCk<'qb$3[ZR#S?#rC.SX3LVQtnP(aPDNo8>##67'dLSt\+#DBBk(Q=,Bg%9ZC)[MY8nAG/k_UcW]l`X?"24tH41Y\).3>fD00-b,q+k62AqeY)f+N#3E1N.,T=81:2WSC<;G7;rE23$I.>MJO;<\du'4\TLfPQGt?Iq#hS)[XKk]<&d@Rtk^:loLF'h4ta>u6,>on^.lZJagE9)h-b_mWP6r`GPpH1U<;7hr3c$kIaLq\s`Y#LOmtD9K3Cag;JsdV[mnSpaI@/5@5e>q+Z(h&^b:p"5%JOeEsO@%LAEbOh>QoU"b),X!ul.rPiRqGaIId8dGJ3K[607.No]NHn/g*g$\28B^53!k&(sG>C&~> +endstream +endobj +555 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 554 0 R +/Annots 556 0 R +>> +endobj +556 0 obj +[ +557 0 R +] +endobj +557 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 350.0 175.657 387.5 165.657 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 150 0 R +/H /I +>> +endobj +558 0 obj +<< /Length 1718 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<968iG&AJ$CE--tM_Bn^r82jQr%m*M?1iBClLJ/2:#[*ZiUd@tfp$]MG-ioL(LXZ$k+i/SUqs=ISlbV9)i9^>D#RMkHH?\\QGA"16d$#^UiGfIiZQI4!a'PUTDeFQj]j@\4dZ/!s:FJ0UQA]]F@Nc;_7H1+!\1K,=5V&a'C=f#LbBIorGU15h_+;`_%ja?t-+"+APX('aR!N,2]m+mt*bT2Lq6R)kWN'*kH[Z[;Wm;`aPG5d9HeEcClLRHJJ*qaX#/;$SXk+t`^r4sVY)OWrWls84Wp75Jr,H%%A[U0q7kT/R,ugTIMeQ9mpD69d04h"Ypf'dj:Q+B]*u0u@.K?0MLD@a0m,I6rL.qYgDkX4s,)J)4LS>+0b*;lOPq-tEmkki[$,+PGX!)o@C,KL?+XY!k>1SOn%#S0Y,JGEU!-AHNC(&7?N8IN8i7!WpgMi(__mTC]\F_?d-r;S6!_+bMKlLq/O_p=;$G2VVsa=ES$.$duX:e<#,Vr'jY.?DkkagfO^+/fJC^N(m\<2gmO7)?X>c".6.PhH38u.l10a?i7u'U::.ufp!&@ZUuc@GAXJ>D(b:GcfGA&N?i2"rt@+J/^#JF3NF%gEs;&eG9;E>U+OR,Q+Z%`=qaAXFDk-ocCHrcP'C2DWmnD;VEH!K/s7K9X&&ohFngfTRi;@7^sA"45jh%s^IDcF-O'od>8iS;uh2!8*OHB1*V7?b#&o]?mfoX-3D+oNo%3$GS3S^/0NY>;F_9r5^XJTBIcW[bXW8H^t9`(Zb<:h@u1=6(0,j?QW.>nIQfD6NdpmiGc>=-AJ$V1N,0!YA,.\]4/I0g,d"BmI/31ZpurCG8gRqDJVr"D.LA&2j.EKY0536Vd1GtdCe*EF[3p`Fh2E0(>DW(BkqhgEGHRY=B`46QM#E%r.>mgT6gm8RNiS)DAG4ZNQ^.9<,YIZFn$;;Tm,$S&'HaYF1)K2,/uU+4eKB&]].Dl.ir/LInTtKZ&2/ce:5@_c?;`GUoac/'JURB3s7*bH6qTq"o5B(OA'/B9q*)QJ%i5Yn]iF\]`MK4$Xgej=VK^a/u%p)@F-0iR.DQB8?)Z8%b^:F`^QNkC:u?>n!>:L)3(*Ue%QFfEgErV67T^?L185?%s_A3jhkjgHM/nCRn*N!Fs&`H]b.)Aq`%>dc%sC9LNHFG@Zs7Eg,?_-(t1kdgog1^IS8*Ggm<&~> +endstream +endobj +559 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 558 0 R +>> +endobj +560 0 obj +<< /Length 2065 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!Sn>>s99'Ro4Hpp&%"XsMt7hH1+`G$88rFD%ND6*V0aW@qCYL3A(Fb'"C<`KKV-fjT)LZ%[=ro:>>t3g0'gm,5dLCEUg-N7m4+1@>[7@IZQm"IQ[oQ>[u)R&.g3G:@+H4=\TCj1@&CSbM^bK\LsF5((e;5g]SL8Ng'I%Dk#Dup.uuDh8tS"ZmC6V0_Ni-',n5@nq8Sid\gm7c/_I9$rR2Jj?Z'dIS/*@S=BAWl9Ai-".&g=_!DWKi'_)nj-,"Q.[j*k#9`gTWF="6EnhMkt3^.d<$^b*j2A-58`pjCIhSt)(jYF"P0W-oOjQ>/jNP`ou$Wi77AB[LANf:e_+KQ2C5mSBCWr82Z=YikiP"ZPXOqnQ^f47laP)&3EUg]Ss%gsZEWed@t0\tqg)HJE7mKYOebBCrE;K*/HKG6JDI3;)b??9DP^kGd/\P#StNF"l?^_MC@JXL<8G1:V6FT?7*@]oi:LZA.1>MiBc9HA\79ZB)h''IaAJOXTIaJ,8OnKfBR+YR!h@T(Y2f]dsJ1TntJM>r=kLM4e,JltTAQriCpB.>0S>@#Z+R0U:;J=+,#o0aE]S'CNoTisli=J9d)k\!`0;X9+tGMEKF:g)B86lkObhA)F+D$N.Z\'Eb\P3nBg#6'FA#Y)RK5i+43itk]15Z;VY_W7[*U&uSu)dQ7;A='tpq96EONVc2&7hY)O$*VNhO>[Lg.SR6aY/_sW_m]fl8b_A:,`-=GnZ)hd^TN]M7`p%I/k0rGZBCNaVjU?TDVkll0[!AiYjW_9l1WU/2Uu@_VP_W8]eUBHB>5)<(\3H2REUWM>hP4aU'6[@YEe$rXo*ED\?1>E]k*(CuH'HHFR%#@U3>cs=_Yfk5;E`j9k/!lS9b+Y)R]^rhq2^gSRo34jTX[u7IBn];c2n(])nX5N2A++Wae?3:qE,?*M)+!UP%-hArU(5%N?A@eF?mZ:0.Zr"\(0q?,nNKVbQS-Zp@I'K"Y5W=+ROoga[f_>'\7UcH^]nF)?c=8VVKb9M+5EmelS%*Thrs1%1elib+Z?(8SPCq83*K7MRnMONc$-_jfn(Y$g@5NnU>=5-$LND5at2^r,`K.RJ3-H=5XG>fQT,W&3rjl"enWUKa4aRT)GN6h$kY'`bPJa(Acdel3/H'h*D>'oi-MVJ<^@e\5_*kngjE]Q;W#C&E3_Z~> +endstream +endobj +561 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 560 0 R +/Annots 562 0 R +>> +endobj +562 0 obj +[ +563 0 R +564 0 R +] +endobj +563 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 227.81 126.584 277.81 116.584 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +564 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 130.6 99.584 180.6 89.584 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +565 0 obj +<< /Length 1952 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!SmgMWNP&:O:Sn7YILd+la4f-0>Y0LHXVVlEKtk-t$2]1Z?o\u%"Dc&o.k*[*u8In2lHi#fPAY;rbk:6]\;+UcGGd,O[c;%:,uEfBVXqLM',Z+E^d&7*q$^/YR3h>h"??!T?l6,'t&V)S3]-a\:UD%dO[2i0cK4B=k%_Km/l$S(N_^rm#K+dnjoV=VsUs`$bh7?Eq-K(u9\(^Mh*E"*$2[VU\S^!B[Np*,AK`>Na3BG.)>UiI?%OF)`Z`1W&9/8G3DL&,%^*!Q5lfg6Vjm%QXOK+38%t`HfqZt6DL"eEO^Rto-Td>i-cjf!hTG)-eM[Y&mnZFJ$[G[dmOod\Ig!7c5^+%)Mk'dg%c1J`(h*oce\1A(RcA\jkIFua^W+k!jZntP+"f`_X=q0^S'Lrre?a!Eh"N.90-a+fcSg&oDWL1PKJJgV5gI]V+c/cV`8]?\$c(e8Iu6&mHo\Y]p435V<@`3RSS[@,J7p)hft,SOW(]Q`<99a*,TqBX1,UH>6oD=QE''$M4lG;`8.#h<(FnS`57?_E``T1\R(gG@2^BV)+JKr',\$SJ1LI5f"(NN)27eC@LR#Lj_33&@n-'b@V+K(0VeVa"K1hNnQL/H?EnWXbVXs_5FTlepG*Z@0Wu6Z`e$@aa9[.G8bpW9/,.L?@;ln85lHBj=M72#FWa,qu^hCaAaLi6ENt/VFo"D:jGF,ULBIt+?AuKV$hE+=;FI-KG7Vp`9(X8S^E6[dK"4cr5\WuPIr%t%B#hkD]!U`*ek9G@Q,7JH+I_OC;,WMHBQOo^XAC:#gh),[TkdQ2YJ!%5Des:\`Z22[`hq$K@\?faoJs``cJ5(2]r/LZ0D9"$,j(lA9YIq%lcTs3tf[W/))7jZuVXH%UG@7pqT"=\o&=SN9$,-A(4Rfk_j<2o.1n3Nb1H9"b\o,S'1O'SfXPD*2OsD.ei3j0bIQ'Z\CUkA8nYY4QO0]CrZd*OZ<'B7YY+p9--/Ylp,65c':6aSRpKEc=p!MY'Q&\r~> +endstream +endobj +566 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 565 0 R +/Annots 567 0 R +>> +endobj +567 0 obj +[ +568 0 R +569 0 R +570 0 R +571 0 R +] +endobj +568 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 481.91 641.5 531.91 631.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +569 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 614.5 162.0 604.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +570 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 322.556 162.0 312.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +571 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 130.6 295.556 180.6 285.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +572 0 obj +<< /Length 1275 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<9lldX&A@7.E(jjFjhlVj/PXufI\iTi["kL`=idkFJ6YZhl@3/l_-4'R]/(L85Y0Vb\*r]VI[siYHC$ZYkkRt;^[RfWMnnl506#?O/.Ht[R80g&-oldX\Nj@UI=_Hoh8YYs@$RF[M&`BG2#5TLM>a??-3b*12Q8:5"f$EFGZD^]k$"R#m%O(tCJGW6Sf=OJH>\8TAT5aQT]Hq#'oKlaQaE\IkY`d3p`.=5;PM0stt?''ghjQ@B'OLmnLr%U*-a;;`c!ISZ;KDSP)1#o@?]]:_JG^2^,k,q%da#l[O*G5u%>KaL:iY&AFu'RU`7\+bd*Z($SAULEfWtVF&4qq_2GT=4(07@IA/cnCZ>D#V[U.DY**NLS4-*Fe4`c/]F08spiu*Cd3E<-_*tL7Q$7WLfF,tE9cBa.,NNFRMJD.rG5\m\eis;RsF+U1krL1A=cicL07t;ssmT\dp!qlGQr%(Zt5b]aC$sUObd+0i+^#4\D1GnBjPMhh5VJIP$jc!ZJ+g/s&%T1o:!?Ta*$ibiY"*)+JZA&QELGhW;p8'`h:E_O0#r\q2q/3"39S:+fJdR43:qPD',_d>'MBp;.V1bB*iPXi8?WtkA[i(>ECkI+WTdYY_(\;Po*\73QB6q`cNu_]To+4,[05oA&W*p_kG4R2oX-uV2CkV"A[ANP+t/nrblrI44WR#VM!.J-9tKt]88s2.bpn'PAhrI4rS0L]=&>i\m?AAK/`@6qi=(,H!-GNh>-d4:9TPl3)XZ\kf9A5AXRW8ICg#lTCo:pIoR(#Ym\_<#FVP6Rsfl:$GBObLc>Tg!JJ0/RN,7MRQ*jHIbg9bW^'%HAl76c0pnc#e5JOp8 +endstream +endobj +573 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 572 0 R +>> +endobj +574 0 obj +<< /Length 1672 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!Sm=`<%a&:XAW@,DqH@`Tj^7h?A2'cM3640Cmt0S2F6jN?2FLWVuEs*]Y8W?$jh8\TeR0OSjdhDr;1^>\oIs6.^aXluX^.K8[);f7V;7=u[fYmKdMR]G_.V>,ob+;KfMUnfJBK_/p:3ETlU)DH"/p@&sCK+\S,gY&Z@I=W[IAV3\n&).eCggsKE[-"Hgi_ljD@)XS?q]CX3)#M7uK@BWgd+7Cg$=b+^B##K=GH;$5b>AObAG[+QZ%t*=Hj:cL)%=G@A]u>0?AGh\S!XGo9rjk4_HLEq@[]s!Ub]d&c#=r,%,_b+[FZ)Ko.%u.(h096`DYJ]NWM/Ucd=T>qT'\Khf6'91a>KFl99-^'XfQC8@cqXSjGCG&%XnsV1.K=tD!/rJgf?JtK$B\go("?3&E6r*\LOX2C(^Qpl"n.\$1]hcG'Sq8Wl9ORCE>nqI4[V>#;g)n<3/@1T3Zn4Y@dl+'o#;1UGE#-+i2.E"YR=(FPC7hR#;+UnkS.c:N)rd&e-5JZ@G/@q0KXm%g^at_>jEel%4H$k=bs33mS[:RIC7>VsiL6DjSV>C;J+5IGqs**QVgU(eIef2rlrSi7pcFi`e,>7@.>be^[*hQg<.Kt^`W^C!`cEG0CAIqr0/Wq4tM`M@4MEmZ%PuGp`Mcg,Q:*FfKecU6)>.'.J3QVW8>1]8ja:'B*e9KGC>o2*63kOXK4j2n(@mJ$N_mjk`BfE`3$\&.Xii'd:(+6Ai,A`JA%2\lDAIJJb')mXmk7IJKCgD0(ntYFKo__kErt$6<2:kb*'k(45!25J3O.^J$bY,7O]]I;N-k4W?Ye2*:5CnjhO_?D&]T@FHQ/>/`a&))s6hp3d>+6l/Rb5LC_hlpj/91$+7BFG4<(b5L@ML?"LKB=LWKou%:0o64V)fN?(1&:$'#koGomsbJJFYR1=PQ3T._D>r)A%)8WeaS$bo$HmO7C]Uh9"Ul'Y6Y?JG\U!-h_u6m%.C&lDU/[\c*aJ]!]nY@6;&If[phAP3!!G#Ed`f;0IFPatB-lH*ti2)!3qR[_U_8VK1\ETnRE(3'7n*]&%m$e=h-(!9q;_7%EEM7$"hW]5^sQT>Q7Xa_aN`0F0ODV%\h$eSs?hc"9J$pUt,%p-h1i`6EcY+D_6Z.BmTLj'dUS)Nbn,1mR@NV=4V6FFqLM4G3lX4\Ft9?%5X!a.LKsNIj8Zk4j5+\l.+]Rb4.aPHPeLZ]P^B.qSDCXg,h9O8];T=CTG~> +endstream +endobj +575 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 574 0 R +>> +endobj +576 0 obj +<< /Length 1720 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;egN)%,&:N/3Y]&)d#nu5#Uo*/ia&VE%OlI^/g.n.CMM*O@.7,1,/*YY%U`a(pOjk\@`Z;f*0X01mHhIO)L#4tABku?dL?;9XLA'!3RN_[7jq0=<%>ljir0d9K/7U\IA0F'PJ2c!0$%XTP90&pCJ!!69H3:mDN=WP-]cQ76.GR@]/+Hs7OV'O6@iTSiW!&)N:O@T&M/>%iB`7elOc>MF(0>`h4q.,V00_Tj6<8Z6=,&8bl8K:u>iGc#&/B:4%M>/-Fk=I(u8R[OnPT,GApKZ[pIr^KP4<6-Al)S5B6XMYb,o]rB8n%W^.Ch2:"1djV@+fDaiC)5]1U^2hqDOVdk]l)MJSpHcHIkmY!9P`r"p6>3TRfLku.0e@[!t3MR_2bM"97l-T%#o*QB]"drSQsnA99Cj@'dGTnncfJOMe\)2*GlAcUk_.lWms_'$g-DV2u=p!6'qLtOq.#=L)7Z`mCgWjD@PaN?uFKpRPA`QNB*/;J#$rn"^G&<@CS;eA/l>QnO&XFmW+D1[;L*DtP4dRJAjlPsoc$,$*(\W#29J"FhYi(apqY[otj.\=YNkE,.9)e2c]N5^eGR*M)cG/f-k!/qi2CNk@4*Or"q4i%AG2t%mqL:qPI9K'Yg5n1CIEn5f$>(A[%dSS!Mk09%%5BcS+pAPnPd#o'A+B4H@s"Jl%'klc#BA[Yr]f_0b:$06o(LH-e;(^+uE,dCNC2-=Af4#Mjh:HR=9]TWfH2U4B^Noi#IL0WX"Iu?iceTsAr[er:mUpHS=GRUcY^^Z_DYWpC\AL0-TX61VCVOD).@\=B1aO)c?NI,Sj07:<3m+P`\+M1l-`,^4<^:ZF?$=1'QlP!GuE)&QWM>c-F(a$="l@FJkXM>c9A>N;9NXj0*P#?h;87ahg_r:@!sI/RjJ_?WsXJqlq"W!2Of8p!bR*..J"pqW'uiqnU2"*X()nGFo3crb:8Dj665-QQ@-=[D$nY+lYG%k-cOM`!Lq-#i;~> +endstream +endobj +577 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 576 0 R +>> +endobj +578 0 obj +<< /Length 2030 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!Sm>Ar7S'RnB3d,#D.;FOln?mq+Kmaa6eZc;B51jtH33YiotXs>sNIf59Pg,tTjbO&J@fLm@/j>lE#cBZWFg!Onbo]aH*,*.i4\D'1FH8Gp[gB^A]q1e'.i7WeH8RRe4Qll9HoR+7c2@Yu+n-&%mrH/-g&#.,h?G".\\\S22ht'.V+Y-dII%a1,dhe-4D"p+s[-NPL.h#lT:1LRM=q39!/*"P;3@eiJeE-odmcrouM.$U3ZgZO10g6M^ha*F?P9`^+-H'r8P\8>!9Q.(@?enEOY1?Ft.cus"Tjn71@ne>V6:]4u[!"+%Sn!%@]>V+f6k-+#XR(%Q$cbf'W_Y?u:(A-'D]GnKdAS1LYmpn_L]E_+$jV"n`S090]@>h7("p,d`8^KtN;Gb\X*eN0U(nF'>hI&:J?Z,00q1NJqAhb%(W9pq[):D\smbBfG3:KSDMS_R]]Cf=P"(nj58)+N8kqiPe/Hd^mB/.Zq$*>.JL:![1[]Fs3VBt\%Jr9\L:?KW3W=>TIeIiZbfP;1)U-?`)d:dJR2@qtNYjnn!2nBN7_dK!=Rs%6km%0D<#,%6TL'C:IR"#T%nfqHECj5KOSf15QI7aXCg#5mc7"RG\A:rW8^it498UNl"cAWS,pVIL?,TlPl#Nl"-.%J)laWf/5>>g['mH,6"(dg;DeA9J!i.\(BdtF"16S?!m8.00A[RKg*)&?JP6.OS?f)>E1;_YYi]0XReKlFjP'iJA'QKH[";Rp5jm:f;lhCl7fjC'^W!MO-"lUkC):nNlSN$)@^usp1rb6[5]prER6u<]SpkQ2VpWn.o`?g5P'h6q]EV3XSq&JQ:MX(/.GcVhCMDd-`(Uan$*(;0f4Cn+_]Z%:'?EjjNZ%B!8%"'RG\WI>\pQVgT3Jh;.86_S2LT0.E1b'9B]JnOB5]T(N\V4>i,E1J&H@r,^f/&t9g]6'I==b1@bS/e2+\Za^4$*C5H-9oo@fal:XF(fff@L@Ee(fto<*1V=m(r9lBRX3VH@1MRNj8,?YDoL&`qUu0$F2Ni6G6Vc9'9h2EpEaoB>)nnY(IhDSCYUf>"'24#%=P](0?>aP"gHb4AZJO*RraJYiqDG4=>a$a(\@$`$S;H0GVE>al2nOUB^i%/-_.WUWX9g200;.@1jE!O"t-_RaL;fpN53dG+3glBl]S]OSJ"%l?Oa53"32B)QWeI*'&%P%S((uF=](DlCn]N>7Y(Z0Yer>o=.d#PXV+g5ERuGTbU=>JcB8G!)m&+2CMMLPYYk237)8!.1]cMrr;bJNM7/)TT6Z`S6`'#HWkhsf],C3:W$$h6BmjBY*(%nD8uYI#199KT!c@ukE+)'9VD<@AdlTDf5tL8"G=cI.@g-+cH0`Eeck7mreu1p.l:8GT)&FOb1:fC+1)0)GDYc=U02PY@=8Y9i8i#n/K'Ws2,%tsL)t'odJ#4-V.d)NcU'*:4I"Q]0==9S%.EI=HD/d;JRT6B*\hU3@=\Ff#uiId5SRC\gFSZ0(WFmUPa.IG`Cc$1*\i?!o1j>Dq3fH,.mALG7i;?:NrdU?+Y#Zt4Yk-nfM,N>e?>+nL2`9Z+>c,T4=W&U3kX#=u%'3$QjMNh`BaC_e@^HXf`W+ +endstream +endobj +579 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 578 0 R +>> +endobj +580 0 obj +<< /Length 1796 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D>Ar7S'Roe[d,%Z.U.*lQ$jGA/=h>tog*kd3[L]`5a$O?/Q0>%>pH,sM&nT8BL&ccMPNVLbk=32Z(X#HE_&C`9J$[LdLBU^8pqmHY3dc$+&)!!&n!$U\T?ht7p$k/sdblVbeL:7kir"M)l;kIkm-U["Am=!(jRh2u(/2FAE7KGs6CP,l'@EQS7f(.qRMShiW'QlV4H3hho(%Z[T*LkjP/,CN'`[oYmC)2$[[3;0V)2+To@#k_bi-c:;XT;aUHrT@Va!c`45,0dVr@6n'8q,]"]M]hc"PCoq_.1F=dc=ne%'cCGh;\6IfHi&O5+Yh8eZ[B/q)f7b.u+dU3ncPK>LCo)A5gP5=(p$OqA.`XYu/p?B$iA9k^#@Su_2drODA2*>E[8ktZEir.lEhe\$aP/S?Fb*AIT8fPKK-N'ljeXH'VQ*%DeWt[U-Bs#*P.9"!ooHpfI?"DjtRgr6EY/+I_dM-QO0((s;nbq2g/f>;CYD%O>dqXQL8IK^umsBA,6;M\DP"1c!&(9:2M_)%h"UZ`tT6`qi#I`aTBLgb,F_#h=gNU-qDXLOfh7.45/]H@1RQd'I@IYLu9uhc\RRQbJV3Un#j_5EE(cb(/8k34rW31+n(HJTJ_O)Cn_oQ]$kUI#IK@K%>feSka0:+]D;NBu&2Cb$(Z%Q#Z1j#^%(]JUj8>+&'(Nh[uKI<`;PWp05hm@3Q>@DH(K$IGI=r.pVL7d&DojNQbIoa"idPp&m4$+nj,72$O23kOdD$+[eR=963.Ug!:1j0-^K$K)h;o;Ts-&32P.8fXQ-U+2Q$t$uSTM(K7)U6$"`"C:;eC+\ujibpjUQ=[Y4nk[m0a8.j]27O\#,U!i*;nore^`r2FlW3B?I72LHZerSbG3/C0QoJE1$"3C9aWobh'UCCqhfe5d%]1Zir^k1^UU=SgFE=NZaXh!4VG)XdG^I%k];R=9f\%R*QFCGK.e:JL;&KM2/l/r&ncoI6Kh0.P_:kXR^+t6@^a;4jqLEr(mVF%EBIqiN-_%)oiZFH0:9>Jr!?=C=';p_Y/4&e5C/YP5\G1,,LX*;Nn'VE>&SbFfQK#@%j#DF!fnq("!12F-Qs#@bQ79Wl^b[9&AW]>nU=5_S:A3b#o>&gS0G^2ib-M!PIYC'spel`r,$]U%D^affqC`^BHg.AmNro&'cHW?GfVg1ZIZ0hl;eBu7J[bfC1Nk3Ji=8ELH4ONnriV.hR7RSE(-p]4qUp'.uU_LV&>#OoCGO:RZor@UIH%!]\$#&._cq=s;d/I//:Ib!^Fi)kF[j?`Gd?%Z:V*,NE9_,AE!-f?HW<7BeTc`*rrZEW0/]?lsh>UWLA\/:LZ9uhps=3g4N0M1hs1WRg5n?lM2l]-t[dZ[J9jFK\NJ-0Z]n>/AArrY75[''~> +endstream +endobj +581 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 580 0 R +>> +endobj +582 0 obj +<< /Length 1087 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%"?#u3''Y_ns@(^5eA?uXp">?:mR+gi+XbQ+pirt-2?j%73NR`OY*ko4@9%l=o@G&Knf^h5g,Hml]ngq!^,LhTo$Yo&FbbFT:UPof%2M)H8-OBST0401bI(J0N8#o@kppm%CoG@toC?+('`@>Oq="a#WBR+t/-ef(:?Wg!'2iCIif?mt7Ai%Son5aUS?df7jn%"*5@Ai/5_<65@md\lm;cNY`KB?G6j]Qg2Rt)*;%G5&ZD82uV\W`1e\h&g(4Gp;-XQ?EKE9baS2Yl;">W2G4Z+I]_-+Dk\NIMP>p9W>?&KVG5CKY@KC&:+lm<+0]cpnCd3:T@(q"F"Dj*$%mB3cC8/2MP-+_Nk[$(U$,:Qur1RDX)7Hg4ispmc!m8ZH(k;=i[IVB-'!Od/$)7R.tdc/J6l*&B_LaD1(8Fe!!ZDY[/85f?XM:>/L)n\b;?#tJVEQ0E"Rjc)9W^AFP_mqc$SEMAVn!-#92r/s(Fj46$%%:e`Pm46[D0"EL;+,7c7ig3F$laEKYq](n.?+V.9#RF_%hj`9&g\CnltPaFkj;W1/M:6N[58g>qu38[nr7&5+95E^0rh24QG'6)?0R)L`0ZRL788bjgFP.TG!Q/PDPVcsrWg_E%nTm(bs>sR]p_,cdTd$6GKeYj9N?d^+C"\LX1YDlA?'076#=t8>J/(dN@5NG,bH"GSrN/M?s@XV(_e8p5fU!mUA?~> +endstream +endobj +583 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 582 0 R +>> +endobj +584 0 obj +<< /Length 1450 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;d>Ar7S'RnB3d,#COk!W,!@2P*&%b>)03lgV/l9M)6M2,.t)CN4tY>Q&!R-gI:ALBpi)R]``Q[k>7u7]pU/qmRZW.+9@^rK`bT&6ngX\@W!,SB4Cm46r(fIA;b5(:NtW)oH*$fF(qp2hk[4]g"+A\k"*mojq.oUrr>;/G+812I'Uo.,ngks!H4Ff3jqLTO\8P^)3=GH_98Pc$Bi,g'Yof81>.:pa.UPCmqC95?qh-n*(#^3`[-\.^2pKE,o7Na=N&##[4!qIbTU.,USQ$WWSk'5PlSuIXNP@l/qcpi`AQ-e8M`*qOQ',3b2mgD--&coBZ>ln/(7mAReN7bP8j*=c(R1=[c6D;_%3e5L0s%o/%l?6Y&]gR4He'%c$g1#0%!(lI+qPS5oZ'OWn?eUc[kCNEi&:&Fn`(l"W:YtTrpi,,^=U*)77t+TSF9f"'X]o2i>"_/tg'U0kA6INDr3r)Mk\`%ojcT4%cJKQi6RM#SN4Z>lsb,h1,3V5Hg>s7U5V3$oCKmF;^`=`7k;/r@-Y4FcL(lDbac.&Mu,MuD.agPQ$bid=4R/=HnPm/Qt6i_Ag6>uCl"eL>!9a?Tr)HE+"2_g5V34:"tIqa055]kNr<*=I1@Yi]l"IU6Lto0/(PqP7pJ97eeNkdp#Vt@"h>l?^DU9GoECe,B57iu]3ij'm\jaYftQU=R%0)/OJ<<\fkj]if%"?%'8"a6b;hN2>3#Fu!O+9c4Ydk)(.1Oo=omh;I'fIlO.$=?hoB`J+*9C&jqRI71]'j,mH!iL80-6@_uEK&JKHpYAPS8:B.FHl+UNa1f-*KrVrRb6#s^h7]J07M$1O+gU366HFeWQkUX)%DV$#a;I&EpgARDp)Oaq-FH^"$;+:]YFnH^>f/0_U$mE9>Vi;8>/i,<%;PK*pAT+\WY*_^C:+KC;cP:_M/a3Tq3XYU2E)u[b=$>43G0h%2)Ao/mQ@&ELNZqA_0O_rA/6cN3.+2DOp!m.bEb_mM!nWC]`,h3d!M&=$r$5S&mhslNkTqM,?j#a[Lih=NBh;Zlaa`r.'&"k44h>~> +endstream +endobj +585 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 584 0 R +/Annots 586 0 R +>> +endobj +586 0 obj +[ +587 0 R +] +endobj +587 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 636.866 137.0 626.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 99 0 R +/H /I +>> +endobj +588 0 obj +<< /Length 1840 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DD0)1+&H;*)_<"TO=L+*fXD6)03Ql[d.oKgQnHUSF[)h!S\1AE_;cPC$*oSBHGYpZ!p>dm0>c]n^`1/96X,=lsXF95"qg#'o(sXn6QXXs)7O^`k.=l&!'QVb2oU@T6]8N7"\.*a>U2a/n]I^@Ic!0:mpX59NEB4QQt9u\J(Ei2SfYnZU)b@X.X(*OAD^Ii;D=RTF/:Ll52T*?$K:4&qY08QBqpV=TS_AnK-?:qJ-b"VI/_m]EcO&5+C&nf!+;ucCg/7X&]qp]eN9=`eP2c(XhG+iqrHX3(-nh8K?#k!+`!G"JWgT>4iT>'>.$p,HGF@QaX;F89'\-!,3?8KmZ(6NR2DU**k$.H]J`J(-HW:=u'W&b7V%mXj?F?$'pRR=a`Zu(,n]/o2m?')bR+7REmS;flM_/]THk[qt2*l>lo?&Zfo&-!TZ=:SrQXpmIs&YX5)?hFS'ulTtnLKqV4$%hMlnYW;7!uIK27b_p]GiQ$KN011N6=1jV$Eg7Y@+[La1:nZJYqk6/E<-:'59?!eN+'r;BD43,Io%W"RV%d94P%6T^q&kI7b-J'>XPb#`t=6=N\bL3=PHU"'-Ri#Siib?#dBNlC%?W:957+UZ]>-lmEr.0ar?E6d,U7Tk`%5S]uF$+kU1;sT5&);P7A:LsV;$,K%j"F/.40G8(*o=/LamseM.(15UT2qEm'PnpRIgNUY[^Hu";"l@)1!?>$%N@h%eFoAu],TPf$4P,P(:`rpg8a%V\]B4]3+EcmoTD'ueUHO`8#s1JKSAPQ[F_bTunm.Ac\1HlGq/]KP.4@*\q\%Z\#R8MF\Z#)]oKo'O+URrM%3k=S3KYP^F)i@a@EK@BkRTReJ/PCEK^tqo/>2^0pU),A)]-9/mAZ&,AH7C"NVTIP%I+iZk0S=8,_]GW$3=Ah1R'h%B>YL+OLp!=J.QTQ[i5%jm';$71(^m3a#qaJI_$(D6=nM#b>HBcS2O#\\<.;BH([/@;U3lh/DA".2mT(RWGt^>+fdi$iof`.HAdB]@^3NU>$7mGn:_8";%J])'V'5AUPW&U(Bq_h^4hmmHC>9.Z"f"BITRT]*+*.hjnrL%8]PDS9I*?CnC]3?A5DTptUu=O%Y*VI^VC-Y-KA\]>SLI(^$YQ*o"_:6#+f\!FQ'#BEh&"o~> +endstream +endobj +589 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 588 0 R +>> +endobj +590 0 obj +<< /Length 2168 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#]D0)19&H;*)61`4`=GZ!;GVY\,&uA;A4)Ra?B&\1tM8Ne[J8iS)rd>T9FF.sLS?t*Ni#+e,Hb7Tfqf:l__9!l\q/Sg/48A^7AFA]:jq1sE1u"G9o@3AS_pf$j/Pl@*H/=>r+Pm\fi!JJ=[[[`_?-CjuGqCd1)MN#oa*'_k(s.*i7SpMem$4ad=KD['/Pe3bd"k/-\$F]VFH^CS7f.<(o]>n[@*u<'=m"nR=i'Z&IpgH]hWnL8Z^K4#Hfs]TQ);"P'c]\g:jP/+3Ks'`d\kduu_0DQJS99L#K5F_BnCVQ@16tY&]lFVQOk53.UfmRU"b04Dp88$I`>e\H2gN9cnF*GdiY;i6C%;_J+gHHk<`rTA=d$!cr3g%',qI?Z83G3>pak;TPB_d5TV^?B3c7=#1trcrVM(1q""[;MoXKNJ"@2HL_9a^Yfd`"k]Qn"Sps>,3CED*H2]SN](1k/4-F>a2!B'a8.iC#5Fh()n0\$ph>R*Ua>os=/regp/@".DH>'c9P1+g3)h=1p0Flth:*8LY/c7[Ks_D!UY-AoH^HQBbJHZ^Psgoq5^!?HWJrXP_6nX=l4pP"g;iZ;$^Dt?(Aau5Q&dWIkp1!%H.*XMQJ&8^Ngth+1)[)p%%2%cn/L>m@:Gp_hqdZP6A\cS')/([qFFetbi#f-lTlZU$pm"3Gb6:&:gpC<"L@V+p`-*+FWTBL$OT3WuC=spR"Fcl:/Zg^Y&\fF4Z('O?NM+4#cG[coiO9'>biD3Uk=i7DT!M?#;XKF@6OD`L*/+F_]'@X:a,\N/>5PR[08Tnc,2SKX]t!QFuGL\]STQL,_p@R_>,(+IWpN4&ROECe/[r^2,jfUY/WoIrV\g#iAVcM^ld%Sj;D7^_iS?R_q9b.mGqO=d!IRq.HEQZ-nO2tW-0KuFJ?T(d@tVnj.<8=>YB>!QmB)i*c#^m86n\;"Lr<-E1!N]3<(ri@kK-fA6pmNT[g2F0Ym>4L7sm3[bVBjegj>HTP*NDo?\N$uX[t22,HU3oIclj+5e86*I2iIQ.:,#agW;@#-br4J_q9<6/(b9F#uBhT4'!cLUck=GUis(=HHQV<>"Wcl0!ZW//5Zq\`97uQJVZM.(bM.`Gf,23q-(,JS%dT(ED6?Jaoh#Sf.KXA@]G5$+/c"5O+A[R7(k.b?n?G/VS&]R4`,GkKqd.2VPoRl-9GO`(OC!/[+d[t>YheGpnYq/gtlf7&Y/b$%LRN7~> +endstream +endobj +591 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 590 0 R +/Annots 592 0 R +>> +endobj +592 0 obj +[ +593 0 R +] +endobj +593 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 197.26 159.656 247.26 149.656 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 114 0 R +/H /I +>> +endobj +594 0 obj +<< /Length 1036 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;b9lo#B&A@ZcHjhg9.a)0Y2bM1;D1WXEdci#;.(4pp!K5*TP^d$Xj(JH4P"^2cE:GR/mb>3sdXDX/?d;s%+:)c!UkQ4SQD%a_1GlT1\HYMuVkXDOmI%n^DHkS,N9)-/h'bndKVroAIDfIhHN*E#ZGp]Mdl[rC[e??thoeEt9N3>.MBjqX1;$>E]!t,D6CsMBm-HU"]besr(ZMq?EiOBY-f;MEB-BuJT'IPlYOjfrE\:Dem5($dEh^;>QmkD,h[>b3qT\4r3Ko9@_BoT4P`Mij,nl\7WZ7LeVh]=Aa7[\::tC:H;Zrbj)Zk2?G"bmm(D.<')ctpj?o/,1WT5@[Mjr'Da%OW4[^Q9V&^r%AW&:>+@*,S6P"LNW;F\SQYi^P`RE-]=]0+ZL@2$TaSJQWAZ;l*LOIh3B_nd5#m>o8Rr]orJK4*bIJW.:#Id`&A'*-X'gD]n*@5mCUS2QV>Nc7"Wa))\$pZ>]\@V6'n9HF60)3#Jb66k7-=KC,k*c_^f;KM$*R]l\ErWgqhG?dOlo4_sJ>nabK]IbuKQHI\K^ZtW6X)*%m>g'-NjW/6hc>ieRf*i0%gH*\=T&oEU)j\f='sMLYb$O]LQQKtrmNiY!G"ZHCB<<[;[Mmt*Su&H`djYgbL+d%'6I[5<8Q*'IlZc/a]ZXfR+0Vk^rH:1'J$1bQgTkD-(YKLUHBh'$cU#SS*2I!&c3kk!`YTh09c)jkODG4*D#g\QH*4G0_NOZOH>!r_+6U";MQ6?E9"Y9gMUI`^VX]E&P40*o1_n+Xl[UCf+qi04bQjKf<:.a(XNA%dFang^WK]@*WlJgA9i +endstream +endobj +595 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 594 0 R +>> +endobj +596 0 obj +<< /Length 2412 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;f=`<%a&:XAW;"GA;&^08^W@/+`N`*BDO4m3X,0n)`>ZhI+lB8O6rq[+IECS`[Ua1@:!X.L?47g_ic)uPcSG`"kcsrYYpEm3[]D$9]H8b\2]n$:[#56,IDU=ju]h^9`la5:E6J2og\GV;K1Z;Dt4-hom=nhM;DqQ(l(MWJhVBV_&b;*j^o/P#2c"0elV9EGVG#\fJkeCDRVTi,.V.$3b8B^cG/WJ9<;'aVGk5#B0b:GMA0BYlaJg[;c@<5p2M_\5WYF7PY7)+X;EYPX,f:\Cd:_X(7cXrD.?4+AO<)]K^0\C51_mp$p7F]X5Un\Pk<[gk>\M_R[2kaY;GK*D[4dRs@8*ql;3e?%YPGL>A6*]77arcIi_`:L7KYb@7D>Pi"/h]pl/n>qH/@%[.1;V-bA22[h)Ce%cLm3.H/,9;Y?aP>PC@O,)/kNItq+_#4?DSi6Yf=$'rNIkJ44nQ/oGeRg9'SAKPF@@Ec\/!G6'Q:pk&cI`WL'nfbUU8+SG4YBNNZA%_aiFU!.PODhlK*.`'I@mEGuu4r`LnZ*AI;m,\hW@prAGBr!uW%8O7II%ZK8@N]!pqoD(aMaa@0b_AX.Z5e)?#f,\aOm0Y!hjbf\SA`#8H!R;(<=$Y]pLH7K?!CHJ3lpT;o6Pa&fh"YmO3KkAmdhSMOor@8E$DJQZ]!Cg#=kn9F)q0s%*uH)B1m&l+6,d"LK#<"q_@XdGkX+WhQ<:[o4ES]%no5hdJ9ZCQW8/G-(KHPaBfU&@dNmR\3&`iD,$-Y4@Aa#TP1R/Fo0MI;3Lh].h&L,+TDr!qbkX$:Bj@2#fuP3IM-?m-*D/U]oub(%g--P^ojP,tb*@ikKh2IBej43Dc'19=8M329%IXc"06mf_P+;e:?nM(F#10nbRO>CB\OHIZBF]H=.UHlW4@b18PBeS2"3.eb^4g6J7AtdMqHOUZ;^b>E[r5-FN'IA0Za7'ugJM#35k5C,9p/ipTL>(8Ym.iscTI4=i=e,30IZ+jn9jq"FG:rjnV48.A`4J]*0WB?177\I@Y.Ml\aW++;K0#)G&J)etSAI2jjKI<_sq^;XBK,t"#a/_BLCqkb9Kg4Sns7!_-#Ui1GmHu5O#qH-s]lQUn[Yq.2^j(He-;&N,L4:E:[Z;qo&&b>#5Tj6+jSb@-;(69Y7h3^@%!Uo=0XGRj0nA_7*+RbH@b?*liaX`2!RZ[!ROEhT\\/Pire@/.VZN7qr"in$KF&*++#Vr4iUQ/?)D@^GX*pG8#7]nUVCWh5'@Jk"j3PMMoARJG.]-skThnP[31&LM?o[eNMn%cFsFP4[+:?0U*.$CWj:_[CkZjtCf#A-6QA,haOBK%il-&`/ifH(GkrYA=?K&E.!]h0^*LEm$BqNg;[:cp^6%1Mm%%Dg6^GEZ(Kj!6%Hm`nKfpID48mT")]D,'1BI\#mTPg?neS%Huhgs\=0.ubM8"p9j_5WPb>Inet[>9]I'>,0LE$F*eL%]B\mq%g(C#*b:rrm3j'5^*P-*HL\/rA.t7[*e;NZcnkKC9k)?_73b4tW&<1Q.8-e[gmjV'_;kkT-r5!:Y=a_^ae@kBjip0s1(\W4V5VKUP?('Ik8ZMhL?c:B?9o41e_,!G]a;9P"OXMqGZ_!BO=#I/E'=H/3W@3i89Sqp5ruLc5I/<:B@D;PY=_?*UNYN4I;=I./.X\DKBVmH>7*Cs-I,`d8bb,XHBZWl!dWA=,=F"r?EI[iU![3g8)3E1[=,B%jJtjO4RYe%[t(m4Eg8?>D +endstream +endobj +597 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 596 0 R +/Annots 598 0 R +>> +endobj +598 0 obj +[ +599 0 R +600 0 R +601 0 R +] +endobj +599 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 454.4 647.866 491.9 637.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 219 0 R +/H /I +>> +endobj +600 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 276.13 517.866 318.63 507.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 282 0 R +/H /I +>> +endobj +601 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 403.61 517.866 446.11 507.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 304 0 R +/H /I +>> +endobj +602 0 obj +<< /Length 1803 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#\>B?8n'Roe[@/H24/2@io94fP-C"CsLRp-QSDPu_$#2*U?&Oj3)p=HLX(^VF'k@&"Ir.=FKIc&Q#&b#\Gp]>6X!/BX^7YWfC6-\=PW-O36Esr:=8+W)$\"hj,5+eojrlhYj,^N"FKS2cf;k9'1=ORj7N?$d2q3SQL&)p#QI]Ng=,bC#5+ii;\c2Via\hX7DTP&%D51a!,-8^8_48p=:GJ]FNXO`>dgViNgk0l%u"?X`YY2+t?c@k"=4C3O8EdRiViU6&[Z6Q,I&1?gGYq?e[//ZKYP$D0,5p?2tjWl*KSB_AhHd8c9++1>^G!oJ!EtW>Q*FCcXZe`Q:ga7IjjD5DR$'4#?e\aO5#V/=PJ\<#-?G'2T]o#h6UusR.3F"8i`9.ZE7+?cc/Rf7P][G@28TIg[m"YJq4BGa^H%XmaJm14FYUb1S[(XHEQ]PX0p2sUXheJp)c:Dfn`%A_.o[FuVgi[ojl=j@e[EQWEV,lX:sD%8pUOHT:^N%=2HS%2U`-PpLeRS!P+fp5VQY,:%;W"g5kDNagZ3F3dqG'Q'"f&2Jgs31E*DM(WreeV@NTc+gqrkQ=rl][)=K05/#nOLtCMe%=&1ZAdr:q7#f$QuSp&3B:aSmgYZQ>(c:>na%QVn[b%RPJ#'RAC>daq5N>;KrtO=RAmIRfqb`HZNIDV/)e1QiP/O`0VIWQ[nOj9sS&'FPg6jbEtPHiLF@#nf^S^'7j4:(fb>kQ1:#;n'a5MgDmj=9kj,nA'Jt=,R9&?^3l6J*Kc]pkWju)$d%#MaO*mFTn,`DrcY&5(6G#J0M+B^MQURuH*n_b5A^E/q(4dI!U.@23L$cQI5QaY%.sgEK^'o8u36E6aL"lDKo,i?-U=g:c]=nl"%NgIEE6i=J]>$Mi[DVR(IA*>##YVZpill-AmF7#/1$894V8t$Ka(^UGmi]2@DOmCO@N2.INF0gV2UE/3:E0$I(KdK&t5\EXAoeg`L-B1"qaf@~> +endstream +endobj +603 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 602 0 R +/Annots 604 0 R +>> +endobj +604 0 obj +[ +605 0 R +] +endobj +605 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 313.61 714.0 358.61 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 199 0 R +/H /I +>> +endobj +606 0 obj +<< /Length 1937 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#\D0+/c&H88.OmAq=VH,8&nk60N.5W%"[8RNm`/kCh#P$qsSIbQY7Bi0-0Im%VCT/LQi_HdErgdkg5q4^DrjE1;4`8NSjS&6d=3$P/B+,UGOF\L\&!Ic]3b;LAi915dn^ha_DqPOt5sQGem/eJfGR(a+>Z^8,H$BR)aU"P7h`HN67QY"f33bdfReBKWBNC)6pj.a,BOb-d+@H?=Z@C[4mSM*f=(-JsSVB8-:es`E$6H6/1n>JUShq0Nd^Q,0,1??-5aaM[Z!7o#kl2H'GXLRNiM,//D[8Mdj8R0c'7\&^Yo]$8bMpGVS0@A+U(q?@BKVOUk^-W?A65"g.6<'pjeqb?Ju7,LY=7Uu;s@gm*uRMk-8-?/djDo?>]iimFC>bUslV4[QiA,jn/%H8s+Do[g9:B0.oAfd:\QRD0u*/X_L2aDj6#>;BM!cgaKbT.`&:aimijhXdXL"ECM$?d>Bb[RQ^C,?%rhe2q.dBfAoFd!_,]F'a-=XMe-C:HasB7CpP`<4)%ig+?\[3Lck9ZGH>k$gtK&[9r/tD'#=#O1iAtWsTIXZTMYbF`#@d3":KQf)m?f&W@h6iD/ud^:Bk9ABW0G!H5T`fb)M6N_>aTe_]'!+r!:Eh;QBb_*!VfW[OH4.b<7SDP$`C]ME".LXVai1Rgd7"OkdS[IVM!Ye48s%%OD&*jtRQms_SC%NP-kjTGaJP!J>@,>N,2-pmlu]TAA=G9R(E,W,U$B;$L)_2nntafWYUN/-1."i0g(&*\f4>8#=-Ec/=RdE#+lD!([[W8#uI8X(6\j>+0mpt+Z.:MGOS@n_tFW:V`oGmLXPRE`KCUb^b<,(5:;fecWLGL!#:D]iC-!D(BRs6O@N`VXDgrV:-Z!NRm9!'&*n5k&;agH"g-F'-A[+5]8,VJK7]&Jj^:XL+X3$-h`kkf9n>@:8mng&s79qBPO1-6A5Eh9,+DiXm"e9,>sFpNO8->EQWoV_hbSjc=cG8F/t6X>U(WfF!(%?7[4D&e8)jlW4N7)pK0gCH$"mQjH^K<.GI/_TZIl+SlQ8OSt84LQe0AqdqcABs7d$cM1h&IpnG[@c,oPK*Y^33I2N7nRaR4B`c'Za965b#,Y5N'#"95@[:e4E/jr!FX>jna[.?\&2F;F(1DNS1O^S-caQ`_r!6ak(QZk=b`YVMd!#"O=`V$W`Z,W;c);UG!FETs'p0]s#hb\!6`rU2=ZkB!&JUcNQ)5,%0`V=K?O3qa-cNAFjKZ08,Y'0mijE]!.+'uXF&>m9;Qr-4`7Wh?DISu^4p]3",@dWn&W>trWDsMM@e2.WEbR^n/*5B;JC,XXFXW0.Tk,3BD28sr,8""uOJO(hBN`qJ)&Ff4eKMr6K$$+)4KCHLWYmgr6Ko"g5F`<*G`Uo1Fe_lo2;?$XMb/tP~> +endstream +endobj +607 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 606 0 R +>> +endobj +608 0 obj +<< /Length 2086 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm==`^''&:W67@/XV9Kb=(LmF]?]WAkCo7MZ^K"ct&!YMTNbe%hVd2p'7e0[nLkf3_=sIlO4F4&_4e+plPB,RI6k=chRV/D(<2R?C-P;fEr>gZbR'#C2ELDE&D"75'[khBlCn-1U[rFuC,M$dN*%;K"tSiafT]B@n[g.X@)QD_E3XHlCG'MX)PdOY'k"M@kh[1sI)^BC)&tF#J3pYW9XfJrG\uUA\0WD#D-s.F"#UM@\kX2b0Ti)X"q!T\7[%FFS"Ka&YiIC^ji!2b*oW(N@T#VTG!u3R[WMQ[=M4B!c1tBq,_(<8,VV97:3[@9Hfg$t=_i>q./i,5^We@EN3V1igDM?A:,t7(:_sc-kAY%cM0I!L?\n"?G+#FI:\+&#]+;XB$`UKO6kA'7n!"^D<)*6E]2KL*"HZm:AR_%0\7b>h9`\$!\I*D8D%ZkG`2ZBf?W1]$@&0aqfGleUFs_X;6'LG&!1&TM_00kN1Gu6SdkMM9.:!r$cSEiT:tV'`?NZMSeg#A@m.EBEh8p@@\r98.$F%uK>F\gi7u?Ol99kL31-KpKWR3A>gV:\9\)SD;d)0eO@*j-#)q;*M?>34F0QcVX"Y'M@CHr8T$]*YH):U]7eRR%c*.bRG_OpIZNYR`,/U@^4]#uQ9dbfq8NZrhqJl@GH"C0Iou7jhGQ,N+Fl$pJ(hoS$A6$O>#7fUk3>$,oN-t0bRX9?5#Ppk))oUXjXknP@%!?8LKh0f5#3]nT4dWpc-"?(L7bXb2*lhgQWcNRSn9tBXPKif`OQl-]dKf^6g9WS\/D8jMRu?E[?=W&;A@A+!jJ>=d6uM^^XbCpKXo&R`0)W"g]G"k]t1Mh9dm`d]r6.]9o&g&#ks09WCME,S%U*rmKi8TB$qH]f+.redO-,Pr!U?E;t0OnY#RSgMiMkkDgu#4oWctmbHlQQ%gl9hh%f(nL0`2QX8oHJ?**Or3qq[VqA&a?q'bBV84=eEggdlGWu,+;0iiI\U:@KtkUfEt+bA=J_Km0SBrbilu8![9t2%62t\tJ:'bXC20VC&*t3nbc!&/F>r]Ta#l@.=M2-JCrmllp\jn@!*WY&77:1%iu5]g=u\;/5k-D,+Kn9;>%/YE-?qg7g1k*R`8G38eEDK4G0l*\X^Y*"5<#C%3!r6oSQD(c\HiPs$WrDRdgI7E05bcr>hp1,"Sq+5+;$\K5mH]sf%Sh1VZ'lf$6"^+nM%qY-S0_KOYp&n$0ZW+"%g,(O48/IfDAG30bi(ZPAGDUF(tSEq#Vsd(^FpS,i~> +endstream +endobj +609 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 608 0 R +>> +endobj +610 0 obj +<< /Length 760 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm8bAu;j']&?q>*>;JQQNN#:1bCGIE]'p_*]$,/7^!0VoGU>'@M>XjTe&hZiS`l!6gbT(^ucKI*s`B)GWof:=LGsC5K"qAMeS_uNTF$6XG$*l#9!W-W,)$^<1FLZ5X"D@o\3p5'W@g4@_i>12ZhkJlqP>l*sjO[Jk!JI)*?r$#3S3b2BJK;]u?E#,1E6ufj(-L\7!o8ldI=4%[beJU09KQ92%r(n+SG.F==`Bf%-eNopmni6D(O:K2ZE;pa?h=9H$4fLj[oRk^_`8p&>_/#uC[dEplS)16<-$N9+YJM`e#H)0SB0*R]o'(gkJ\jP5J;A'`.b;AqNu%^obr:EUnp)nB+%oQ[7f*hc*ht9G;'ZiLWbhsbf.aHdlcLul\0ACM=&S[;MOgLQ4&tXCW`!PR=0-7WjFI:bG74BmWTkiCtAu-MAf4k9aH2@9n%$th7Btc=#l*J+o3a;;^pK^7tn!:?kEU$U5L7a0W'*FmEe%Wi#Ib2O,?O)G`Hc;Fj,VF-Ue3,E6]lQNBXEHoViZC%tVp'o^4j!Y-Cog9Kp#BtB#P6RB#JfM\N;~> +endstream +endobj +611 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 610 0 R +>> +endobj +612 0 obj +<< /Length 1983 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=>B?8n'Roe[@.TW,`.U3!]A&`3FgnuHFOHIsGGjj2!SK0Gi.;prs*g$==@XLK)4u6u7@E5i1s@aZjCq41riRAT^+DZ>R-'B[7&@C/=3<[4*X?e;X"4JVMB3j1r$#JY8D@>f-RI#q-i$U`'K?,e*gT`$Fl-`cpo_.<-8sTbfNh?hn3Kd7@>I6C7!?(tC2l*)JYnmY'"WNNM+I-VP)5fbLjg'^S**7sP0`J-)Y6hfs3:-r0@;2o&4rakI;"pd4u6/Zecpa&9QWgjBS.E.d2&"3%oRf[n4]e0la/'fMd_uf.)aY)cQeQW#:QFuS6Z^Wb('gb0+\ZfIPYu[$Ns`8u)DF6RiNoV/nJD5\>YA(8c5\H#LJjTHZ@R)n\e2i1/C-ZL)9oKsHAmI]9Uua5b4@F[iNQcPNbZ!>6n3R5.!gZ2W9N*:%NnUO;j)k!2Nf`_ZdcsoBqWXpJ]9S0\8"lAj%`A3cCjtN4u1IK*W-r*S$_k((Jm3lC5m<_M^(cQE!YB@7&/#_4L,3ZB3XWY=oS3I5D%IS25r3:*^a6s:VdtC3@XJrs\okFEqDU+JHm@*p9*tqBA/7!InLM`[_R*8gDeZ`#r0bqldJc]'F<)Y6%E^0@ZH/gi1@@(LV6l!.Nfj3jX4;8VB/7_BnXes`VB3VgE*ODt2\lgCB=]Ro@>%&G,gLt/JCXA;r(X"pBA%GG2e9g)f]r3L2^ZWs$B"s)OH`4DM*P1"me/c)\Jd4.djX/iG4UYBT,JH_VYWNlJFZC:\?iZVGu0q%0B6PT4"H6)M>9W.ApP<+>*MkE9R;cDfQAka63LuJSBhoItTO`?tc"VOjL5j]P?cW:BXbWVJ?m6#VIo2Q8E^,[9OGkKJ"j7Jg*PJpdGaCB58L)KdM^Lo(=nC+ht@6KBhD7GFea6c:q46~> +endstream +endobj +613 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 612 0 R +/Annots 614 0 R +>> +endobj +614 0 obj +[ +615 0 R +] +endobj +615 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 340.28 563.894 395.28 553.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +616 0 obj +<< /Length 1278 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0B>B?8n'Roe[@/Q87_u]s+`nS#Pk-CK4G*B&]mR*l_"D#E7>D8S,s1S_7OK``iQh>t9M=5iTU@0)l+dcL>P91GGq(B\:f^qtS:Z`?o#/rbML.g[u4S*rR4bGQN@Ihb*_i#r\*C)3$!o/)DkJ=mqqIo<*&8FeRI3ta+CDtCt-YQ`dt]#A.]&-b%?]*[/baMp]2)dgqJ^>%c&)bAClctADuf4eAm9JNiMC7>r8)5kI9@_*C;K"]InKZD\R@(XoGZ?P_+=#&LgZ"f1AY4bX=2!/nMp9.BG%*h.,W?7&!<$e+1<.a\oVLr2_^nr9'5[:q]Ld$]pces,+_#X!tmQ)s,q&A!N$76Xc)0&mbHGshGW(n*JS0?og+ce#5p]>%G!d],HHg`E&OV.Vm2Grb@3`cL2^(Zk_qRPG1[lc`Q="u3`n8Fn*?lWk]aV9NF+0W7aHk)UXp2_i0A1q#j'imTe!l"hY4Z)pOB_ZF&AT$+DNX:V_r\RGq8+kTPWTZ#a;eXpO$9Ab:u:cC2ruL6Ro@9j+(MV\OgWgP'pL"'qee]&mQfHK9&@oT]fB^i$K9['Bcs!Q:BUX[>]/ROts2f?Il52.=+3ON1Na!=tso%E>`R#PTL:^>=Vam8.%Y[!h_H52q"mOotP?~> +endstream +endobj +617 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 616 0 R +>> +endobj +618 0 obj +<< /Length 2207 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0EgN)%,&:O:SYj"g9YuojSGrhKn@X4,smA@=^1_]$Q-6\9\?;uN`lbDmTU4`[V:lHbB^Bc*ifKFRC$bogarH]M'[V&d`@X2K%%nF':_4PRt%Qk-$WG/tqZ,hnXY9WYkR.usNcX1%@H^_e!nMeX49'W:Ja2V1jnHXmB%GTTogkhM@EC&ZTekY!oCk&5q9?TXaGa3:EYJc'XM;L`!A9F.Q%q8hJ81dk6b%c1WUhiKIrj&1!8#M(3Uu$;G&7/D`ik_oMF0melniTdRY)WFLf5#iA>L<_T`W9jQnW:1MWf=`]WX,)i?/"\m@uB=0g7"cAin_i`J`XjQ&q8&\V'Mcu*J=X=pQ\l?Na\,sru.aeb1N0&r-Td"djk7ipsVdle"I2PPmD+sUp>CRm:=6c\4k*u-^^:$YN-/##iW3fr-PGttp"*BkeUbrnQ"g;t's*-">+I6TQ:hqM"'qfpFIB1\8,cOpp\+1pfeW6.!YhBl$Qaf+Y0)%G;P(+Xf1>40J6LjLg/Bk!Tj&r917^3l*2l1OtXg\9=k1\#"heJ'f1>XA5./O=$MTX-u6oqOZl(qd3.a4$#R'@'l1N$?$mEF%j(N:@T!La^q`iO9O$?uY:^PC9;@mU`qU"u1,gJ@F!q;qNh!HJ67W.f5+TAQ>$^YBk0Y0&O2_Dh&+aMr7Mrp]Vt[[?;iHD*?i[PG=!MTq2-CZqa"mma&1/G&XEBA:$pEC?I)bhgQ@hmmRhgMg!!;.'Tgh&G]A_Kn*>Kq)bJ>iG/B&mG5)Mdik3BZ$oXWZNt3m@qLn%p!F(oECQOTQ,7(ZV]\iLAK&J08/!,$Bn%DIGp^T`?"CkKiGE&QmbKoQ@>"8`auk2SJh@)lrgl'Ec[W%=UEF_4FqY=dBaIc^_9P\&/uj#NGBssIPprS%]j,foBsrZ$WNmt^/&8M6Z"4g1o*Y6#uJ&:AMI=t88f=-P=R6=&XNfQhdXZK_j;(G.WcE@poYT(CB!M0NqhtS3_g3^Y]MZW12GnbNPF_FjEFL)#p%lPLs5kj3MY))!Kpq#OJL?M:Migo-nP.#.$Lt56KR'>GY'Bn,$>+1Gred1m7gf(J/-eMSIA2#piHiCPgpb"qG<)f2%-(P"SQdV(!G:\kAKU%jnOBE=hDt$oi^03;,:cYp``0ZHK2j8GA@9o:S-!\,ut.GL3DY78L]`lNXeXD'=HP#?r"C9D>kgmOV9"=P3#Y"0-=n(`GX,5ZCT?a*'L[%Pa_49kt*DP#2VAu.kQV+$3VR=>C(j+F2uA/VkFuQ#6JX&0l)J0re-F4,%d^q8NUlb2]@sFQa>#JL;mAk\\mdXhV1n3*?@%+hN'l"N3;K3iqV=3gj4kK^clo,Boc%b3kJ0>9rha)aX(D;;Zlb;2M0GW[.!L(kitjbjZ@If8N]^l*K&"uk^&@]QR'hC)D9bZE(N"8pt#6Pl%/TpZ='Hi"Cs\1=gXsGl?!(V:cI*r89kB,AUR#;N@Umb:TG0coSjG4<%LuKqq$LSaj=pg=#"VGr^/W3RaYu\7HOCDXk;_r^'cIGZ$_(O3o/\q8]0B^hjR>(d]GP_%%l`HkfMr=I(8YTc:>.g*QrJ&ME.!eg[4[p>;.0i:uT#9P4?K!]o_-.Ln]mY=]?[G`kMhM&@`gkjN@f!JC*l"@q5,jCTGR)EV(@L]lse~> +endstream +endobj +619 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 618 0 R +/Annots 620 0 R +>> +endobj +620 0 obj +[ +621 0 R +] +endobj +621 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 196.4 604.866 241.4 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 229 0 R +/H /I +>> +endobj +622 0 obj +<< /Length 1887 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!Sl>?BQK&:XAWE;]q&F#m=*pFs(QF9pb95IGIsRRE?e!_2jTGKV7k>B`.VLS+Fl`B;b,R6C,72tDObNGjf(TN,>##pEEFKlPlU!HChGip$ECB)Q@nVO6SAmD0[G%-BU5^Pl4O'JKU+,#@cBu0ED4;>nD=\LV$'kffZq\bD^?K7igFIr_I[JGpa>=8KZ&F2QV\7`YAr(E-q<5uH.R&5'*@X:Z4JMpGV/[^_C(=C,e)F1879Z7=$%:;14P!E.P#)fYJaQ]<;Fn9%%NHkre*F8kM[&uh*iLcsGin(,t[4.bqo&H>Y*%?%A`]o!s_q7DTh9BVWf_O/[ngeF9;s`(t`HI9q4)J(I[Y5^Q/,!B;#5Rbc\&_(.gJ_j!.He&pT@;jb*t,ZHn>DKt#/W5_qeqfF)AW)*oRQHp^.a_ENI\CkW;=cn"6Co7JOg7*r^jjW/@B'<(^>VV2a(1GH>O*.%TT/g#F6<6"[KB!&_i6iO:IT*qGpHo%O*q@(3.('-`nj_7pH-VF]'pp`V0_kL]_5F&iLMo2r0OZ@]"c?91[d+X36EjJXEjT\?DkeQ,#p`)X;+V6Om7gW:K)1M5knn\'*hgoLe;84PaUbq>"dgMpdh$)gP.q2+^nKK]kIH\GT&=$1&eijHjse`'sKmdH:43(d^frm(LnM`,88l_S31N[Mih_,%+4&g7_`j;ZG"j%IK>92f7c"@U!W*S^bfsXIn\7ML0G(l=DB?hdTYBIW94V$>/B"#[$*H$!C,s\im:j1!cgkO;PT;rEZ>:"Li!G7S%aS"<:1%oFI*kqo@B4$^;l+:!LEKo"oqtjr^ic=bX,#adCM_`S(W]g&Fj[P7>T-9ZD_sKcK:074ej%\i+[Mt&hQAuDU?5W![9a%_#t1(#+_+4MD3B/rUBEG!Lh$90H!'(\DjaSS6jHJcSlaTeITJ<8bHHt;]/dS6RF)$mrSh8Sa!tF!FmGr[t6.No*s%IM97LR%2R\uf8Jm8.RmCMoOW/P_kt +endstream +endobj +623 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 622 0 R +>> +endobj +624 0 obj +<< /Length 1485 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb"/'gMYb*&:O:S#R(P\5g0tBHesoBl"8=$DPCaJB_oM8<%P+%\YPI-rdIACCf4?hM_%a/I7QV#_r8huR5qWLrQ2T7q*)XK"8oj)\.6qE#CFP?9Zr.['ZR;XEMhPf5#4phEuiIdVE0[O/o'.o0Q2:Z_Id@Go\*a.cn[3BZAW;%F`#d?pMUk>L38mWUWOWn@%h#R,A"s-K*^'+L%b2t"2/?hnY%kKp!WbgZ\22lh@fc$)2Hp)c2Rm9=W:u_8%+><;*-f>+2k1,<'KF(SC*FBd+NN^!kO%_a6R2iO]@R2W=ZW)sa*-1bd?p_-q1!^!g]UsY`rZET+N!XW.AU,9$1lnE0hT[)Z(NGa9K.P8mQ6\#_0l#_-kSmbd'3W-RL8:#+-'9M8GoRM)j[VoKq9Oaq%8!X1eceLqRlB\[!4'6H5b6Hs=QC?L6G3a9[uYN\n],'qEL%DGKgQaXmea=>7Tdsk;QcOWaW)C.a2E6c.>NAaMJd4f8m$)`0Z49rF8?%qGc'-(,T??6a6E=PK7'rP+hs75I(NL[Le$OYr&`3s8g3>OB/HCrAOc&`Aj723pV`cEkBShsB+(^2CoSP$;E;GOm(S@qdmm=uSE/.)4P$[V45WQV!K[8dmdOdG\-$a.uLjBW@pg7'eN2NTG:UBGB-_B1iHBP)1E+T?<-KUUH`tbNE"V[PQDeO_`WcL0EUGL&)ZsV_c+`f\*Lp4o3r_Ug2,XP!II9,?2]`#,^S#`Luqecb'-[nU'iY#UfAHm_6?XZKfI#RW/NpF#>UoaNVXZZb)C\a,:#k8AMnaaJWhPNRMSn'6f5PMR#^`Ut3A\>q3,C5SgA!%96^FM.`TplJsL%-F]JC2JsH#ni@\F6S)A:H<-?=)HjCHXh`aegBZK\OH(j!)>0b6GS69*Q_PC6B.13t0JjI=?51p]S"b#89V/-Ij3P&G"ZHh@HlXKs0R`Z,H]:?K5i).YCmCRM.0]JdKel-FY$s7[=l6RjE.dhd(SU;5#2:2kJWm6:F072OV6:Td"Fdga)VeW7InLL.(/!Ko-`D(5D6+0^<('dpnE1N9q7Gbgf.--T.R@E6X0><[LWN`#S8kYl]:(1.IqP=hVN+>:Sk5h6W1LKlp7aU%F^mP;4`q*BDj/ghP:=P;\c#F*S-4Mbdgg8B^9a@0"8D)$VfHT7VLp#2RiM2hH)QDVEuOs^g=X=m'b7=ZcKKl^SdG/OC@J?cjP6C*$XFEX'`~> +endstream +endobj +625 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 624 0 R +>> +endobj +626 0 obj +<< /Length 1868 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;egMZ%0&:Ml+\EHQe<2r@P)AE@fM&3iNfjNiV&T%/l[cggqh'u#grV*.+k"$[Ba78kT]SMe0S2iBHk"'d,3;N*X3RK]aRk3,qKdT4mReTBJ=FS$qIZQ9AB(PL$/[L7CsPA/7BnI3pE9j%*=l;3o3f5WjKK,rEor'iE:(?RC]gfFdiFLrR)3=LT8CeSFJ,cgrr)&I7+G\+].0Q=q8YEP"FDBt^&fDTBGKSLnU;;bQ>^$ch^s@mr!\%eMA%($f!:O9EC/*TN=ZN*^kRE]2r\]Z6o_F*%1Y3.(ItUXd.mp54)3BF?$`Y8WoZ4*l]16a';eg!&/G5a0Ct+Z5MQA)mmr7;afXbl,F!Uf\u`*jqB0KP;j'=3?tc,WELKbHcX5](WujJRrSig#_KSgPTFJ&9n)g9W\qM$]/RL&[c)+*a1GL;J%9\)_%cl;^hE$?iJ`N1<,g&YdQ,2LH>8_8URO^s@PjM/fi!5,pIZ7:;_*Pk5*p5SjBS4=Y@^pu%%m==>a_K9sQ)sMcO?FT8D+\-Pihep$O]=4Zk)=_h*&YsBplVlPBbmUN.>c584,>U;/1)GR!qnB,`.L.g#a)C%X@d'2?@b3!#/k#QgNKMr$#1-T!#X4Z-:N*-MmH:fNmX'^6Bo6dW8Il1[sf=C58@M7b,XY];G^TWHlQ4FI[^!-C$Zn9S(%:$noplW,8)ggRa;IsX1[bRY83;c?a0Z:;d(KMk4T(Wtd@=$8>sZ&$#hkh1i),9-btC/0mkB:a?-"7$An:B[3PG9q!2geA6;K-JBEe*[$s*8h3-Pmt=^4'\Ye^I;K8_*K[YB:['$S&Y7S;E-^*VOGCcgKPl6OpQ`-_FIXDmAKF8=7bbR9u1k9eon?*cYaeQ(:"`c;Spd![r*8^,L8?jaZ2&k0549Ad0eMZdAjD9(8-'<`QpGiZZ&H$6j-a"u+d4Xp,6u!&$bNaZ^V+$.1OD6J,f268Vl>aBVt+*>/7iBJW\Y2qa_bi8ksam#,\8aC02a!2'#c*Am60IPVp@6jmkLYa83GhK?,ei4Vde2.FG$@J[G@R)1dP!Ch2<;#T_]Zb_qK^gSY4o\p3Xo4X=eQ5SBWHP%$NU20SLcWZ@:M[*_"qp^I1qhq/e30^/HCjj\*a3X=B"+l_'3>Yocdc>WQBj*F(:C*,VCciHJLL5@L`,@&L,5Qf=l1RLPk0K?RD!$Y28_jZrT9&-RDO_>SKb&:N7,X%arI&Lrgd;_,d]=MSgXnDd)gu$Q;ugljYP'+U\hdH!5]F9Jj?g56_n0;TPIT;GfkT3<;PpMP_t:]5XO^QoJJ_N(ib/[?#u[Fs$)^N=WCqg#uGXB]=_kINQ='SPSc_/%nF#QN:U7\91maFTBW?!,`fXG9c]I'8-c$>CCk3JKB@CqaRf0\Qt%5JT4">l6NKd1+i@i6EOcjaV5k$e^j2'TmdW`D6J(K2(\&i_/[\Hu\^99gtFY~> +endstream +endobj +627 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 626 0 R +>> +endobj +628 0 obj +<< /Length 613 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat$u_/?#%&A@6WHmDU0YmCt^laDe[Pq#eGW-`3)0JEs7@'dkPR_Q%#8F0'-e.n=IW8DlB?ju9TkIYg'*A/8&E$W>>+AQpFNZ>gbfChm/UJPTV"qeg_RNh3?0;7MI]Bf/X?^IbcdhDKY\TXFX+^mo]KY2XYMooq15sV?$d/ZZ^hWqc(SnoDlJa3iqQVSC=,/IICDl!o\AEh1W,V.`qSsUiZ?CFW\*gli:==iWK#[TtH\(U*]SQAJp~> +endstream +endobj +629 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 628 0 R +>> +endobj +630 0 obj +<< /Length 1895 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;eD/\/e&H88.Tm^^,=qgRDD8UlTFg,]Jg8/]XU$2e_U*:eeW)6Hur;3%$=ZX]i-$MV*+\3_Sk4.lMkMDS5en6?DdhL_B3M!&.+#Z8G_Ks-;/T_+H.VB/:(js6I_:OADcWGhYRu.iokYTH!\@`?/gKU\BkMOG9Tk?'Up7,QiOZ4.@6\uiCO*@cB7ZdqGQm?B)P=KU@#cCJtf?A'IdCL.7F9)'\b1%KL\##eF0;EK4!W&n=9%X8"ZRgjhO7uiYTM]p@_p`2VcZpi`J>pHFk>*:K_FK0bFJIpA18RTTl>d%0]$;h/:TPf]_du_G*/0MgU8h[i)Fu5@X((U[#\RJ?7pM](31>_dJIn#8g];Doo`f*C_%Ba(A:Kfc21SiQ*j?g[D8opJ5Z^iK1"3[E";umCV,<5,Lu(6,7Cu()o]M/@F`q>6[5=fD=N_Bt/!\`f)sP%Ol[-ajb'kD<`\egBGn>75iO$+N6!`?![I/mc,2frc\g)efk_$4VA?,b$KhXqZWN#m)5TLW^\7"0g0YLUBDO0Zl:i'YaMIV!u`or6]JI@6[Y->YI1(cq5CFoHKLaYkF(+nAE-q[BIKPT_F*/I>X&\@#k!3mKgd6^@?(jgS!1:IjnTK,m>1&X@oW(aE+Me<]$0WQ?>'4q4r=q=\$c!CiCJI6Q(bWSq)Y?ES,./a`EMEtYU^t8;8#i\g+bi?/k6o%m;PBg+-1K`nkJ$,$B%&@LpX>'U+ToO=PbtP[+E*s3.r/hTmLaQi9gbjdrooFP4/Of7H-NSV\"Uu7qrChSG_+Bi,]K(Qba8!&B4*dXr=T>"P.Z$O%YZg"7_S\J-2[>Xd_;TY+!o:MqJ?k^UlT$oJ]Qgs7)7M:HQmk;Fab#_>#Yr!qW-UWUk`!h(+#Kll=dRZLT%"dDAie1)iYr!qW-VG\h*RbM34\c9Z_\(fYX&9Pi$q#pSVYuY&ril!`c9h`[?0L!kG%,Io.WMf'u'l'QZ(1m3!)3&cQC)Q_!l,l/\PYOr3<9ik07.E%mKL6U,r/M0W6%VO8QMN6`5;^9^-2#O5A\Z+`f#&kE>JPS5%(j>JZ#]LJJ3 +endstream +endobj +631 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 630 0 R +/Annots 632 0 R +>> +endobj +632 0 obj +[ +633 0 R +634 0 R +635 0 R +636 0 R +] +endobj +633 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.6 462.003 253.1 452.003 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 126 0 R +/H /I +>> +endobj +634 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.6 415.112 253.1 405.112 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 128 0 R +/H /I +>> +endobj +635 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.6 206.498 253.1 196.498 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 126 0 R +/H /I +>> +endobj +636 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.6 159.607 253.1 149.607 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 128 0 R +/H /I +>> +endobj +637 0 obj +<< /Length 1961 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#]>BA7Q'RnB3n@l^I<\3"2im&#icj^A!>7*sTh#SQ:>A62ZG-DuZpXek2Ce'X5j[*BgV_=>[S]gcl>bjQU%YcX8KcT5Ve+peaD?ng7i"'%c$Zjgk#ZGNMZp$U`N65lIAQbJZSisMG=ju!,QYcD>q_7.OK?utO]CFGLZ":P#YBpt?c]W]ZZ`4cSZQcprUU62uo/Kr%/9rF68K0=$-2&i)jb4PUN%J1=UTJa6aZJc2(H:4LqTJYCHo\q#('MoPNhiPCH$8lTdoL3Z'(M&*2NhI1f^UiAf'H(n+?h:c>TP[U;p<4)lU3%2ma?D)7Zh64ZDaQrO\6lY>g[;Oh:m'\Iq<2V(:i9(+.2a?&#)2^/k,B+r+m1AGFUpENLknR257j$Fna)Snp5'1_=^Wo*CG_c\oFgMsMuJm$LS9%$Is3P50sp,2Cmq?*^u]PE^Dp*R=W_BXDpJUO$I.Go)`(cUrAtWgMm>Ph1(^fl5GRBp,^uj5Vq(n:\&I-!0(KinuMpXRarrlLf`/Kq>#`_IAc9+*h'FU<<9KpNPo5nu;M7:*jTh([EifA]`&3d/\Gk;15NM0(/0b\4)!4DJd*j@e4$kie)K-Q#<6C4jkK,,lO8G+P$;/A)j&31]B(d702JD?")?gCUV>qajXBNkFRn@2J]T`2i'TPSPc;U$B1K+\V'8+B^gprmYS)gClh@%;*=0j3g<(CY?:oeh;k\G:cm_kb%9JLU@*"l1B2W#Rmk2;(<SE0UN)\N`ib>#rmSM8*hSaj50Q(N+\?h%F)e7D-&d&/7;5iiqbEqA%$!V2boq!o=o+2,AElLrGlOJe-(W`&*X5tNGuHWU(W=]>DADJ;!bq8@JaDZ]1go4hb&ATr]Ue1QTA8gZ=Y.$^0[73Rpu@RPc:V06VQT^69qRlr]/sN!6D;>\rbAIFnl.+TpImBo!UXr]WE,S)*2!9$k%:Xf'8og7>Jk;Q9aBMlo!6(nc5Mbt6h&?Y\gLMU;l*pA8Ei2"7\EP>n,&*_&:"P6&CS:cI#F;k9X'#Tj>]XbXY/JR1k\on*lmn-9,mQkb*/44+HtfFQ7[S,TWTLKcF[VeTuV6$W!fKjdO#R4W9`!eE7cC]GZPA0Tj!""`8(%h.aM0_Wd)V=c[#j^G-YF'[9g!NX@YUQnpY\EfmOfem`1?jC2#:qVS]um,MWHDj9tu`/>*mD;CS:35n=o8qd&G5q9pGh>2+8tG;%6q/C,U^%H*eea9-3"]l*mPN#dD#^ur%eM/pZ/-_!Cbj`3'DIu2/E6PV8m!nVEsSRHKm#S,SKQEfOL_HX__72`;hfV_Qb^rAqTA40uQR56FiR/^(b#;-J4LjS^O3kCUG-'4u^+.ksXq%kGOY9j~> +endstream +endobj +638 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 637 0 R +/Annots 639 0 R +>> +endobj +639 0 obj +[ +640 0 R +641 0 R +642 0 R +643 0 R +] +endobj +640 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 366.93 495.48 411.93 485.48 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +641 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 376.37 479.48 421.37 469.48 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +642 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 350.26 463.48 395.26 453.48 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +643 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 359.7 447.48 404.7 437.48 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +644 0 obj +<< /Length 2175 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0ED0)19&H8h>6,2)/"`_0^:F@rYUGQ\%a,+fi9UlV@3*T&f."JD/^V1'q\ciQM29gBc"396SZd6AASmM8Wl"a?DL8HL#_aIQI-UcA@O!nm+jq:s@Ai%D5.A'BubrSj(>M%iOai0bF`Lh(P@DK+JJLabPdnm^7T@!6lNYiZa;XmH"j#5/r7`hDBaB5$OZ@Lc'Sgn(]rsunY.n$Qq2Yaot+(3QRa75].#sEAJqmKpY+U/":LmJ94^j[!*I-!C^_[ZU$6mZY"?Ck8b7k"MK?)BVc(SjX=&u5h.&D3)lOH\6Bb&mkbJ7[!2,%O%)!Zbr9F4?);Q"+\ljfq?nFQ8*9MHQ"Jrj$?H&U8>c;aHGWoD3m$6JWsl96LcRpj\$N4i*L&CE\46tKVNm_e#^J"9D@(VObUrD,K+&U"S!gL+nCNs/\J[,p#Yh=m7Pt>Q7RQ"'8(dA?&BF6ZPC!#TZ%kscCTYd03VC5`"&keg0]O>GDKQt^6if_GGpdet>rlhdk`-l!fo`j2G`ZPA=;#ptX5Fg@Wdh[hs=!h-gnj6V9[A&V6C:Yk^:X90D>Lq320@b7SB*_m=Y^j`+rGY7&%C6-CZ0dd1+rEj`tjFdP>s]hL>A[,m?(R?5fKX^/3Fic1XgHD]u*tMO?0sG#Oo@$M]]eh"om7f;8Lc$Z\je"/J\8Go6Mo][uYAMk[_^]R4=4L[\M?@b?b]jE-)K#!RR\$[g%dJ0cAXh/TF'N%K)tfTMZg#HZH0MU/)R%!$.tENgLQ`('D2NeGFe6rWPcL:Tfjg;B]WR6`YHCWRT$9qXrD/sYTUA(E0j;$&5r3;R9C>ie,B$9b"2R_*&E1(?K[\$m?HTHW]GQOYG)oSWOl9ZTGe,%Q6kjbh_>qMrh@Lu>n7=6KR1cY6^+)7$Gk*_LWLqLlHi`mDrCpP!`tb8@J=itrX&&@31O+g_UgW;Hup'mHWe]S.#thL2nufMu6R7pL5FmT1TX7Q(ZS_Fm>31:C5[Y(nMuP+iK_RKX]:N0=.>[[Y@34/rlR@SjHA_H)gHm6k/aY7j^`9[hX"YA<3@GJQKC:06`uD4mVpWI&3Baue]JYkR%U'?F%hEbf&9?@8-T^RLPj;6?;aPA_+?o.345aD9&Lmh7[$_G\G$/D!lQLiH]t9h8\K-"G),np^/XkDNc:A26,03V/q+dt)LJ(sil,eJq6F1:o>Zb:\L/R9FOTB?IZL[FmDr5UDh1F8IjrH7ag7(l#b.'6@rq+2Q43KK\)EN2TBJ/]LL2fq$hmnup1n`s8!*Ni8'^N*iZt&&D2!e/YS3f(5&)(O#qNj3%)gEF]j2:0j&`]`sS+&lUh(HCq;bfO-oqdo[k24!Z3dh=;>^s!^,iMW2JID_CQ$:1J.].s')]d`b?tC6(%en)4X]?jJia5Mq>lO^)8HpXf@(\(a8C/nUE;YY!jUECZ$.f2:Jm"^aTUQ]QCbSBPn3o>&^KtF7Gl~> +endstream +endobj +645 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 644 0 R +/Annots 646 0 R +>> +endobj +646 0 obj +[ +647 0 R +648 0 R +649 0 R +650 0 R +651 0 R +652 0 R +] +endobj +647 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 249.74 442.368 294.74 432.368 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +648 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 251.41 426.368 296.41 416.368 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +649 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 258.62 410.368 303.62 400.368 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +650 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 268.06 394.368 313.06 384.368 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +651 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 221.96 308.368 266.96 298.368 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +652 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 326.95 292.368 371.95 282.368 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +653 0 obj +<< /Length 1327 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm;>B?8n'Roe[@/?,3'[09Vr0CsDSa:/NfuD93VsT*FEm+YZ`p`tI!$0run,Vp4"^F(+,E>uh,6u?%Wgi?aSJGgUN/]Vct>e="G01^Hl>2CIVQu3*)Cem:8L1A-pI+cS&#"TSk&\GHoG%eFH0jbl,cRrq;@cjNF8rA">DO>u\+$8N"9^5F:`ckql#O!BoI@@;]E\+EEFj8mX9:GHeQ'L/EqaoC.cJIUMXF&V+q3:EiDF:^-<&-``duBkY,j[G)\)`CBh+#DHHHOVOg+>>>F>(s6QC1!5A![kNW5AD1C3-)h+54@%=%X46ku=rT=<"\>l*D\sitO7'FhK5+K*\Pa6:8Y1on8'Z66Bt17U(iP/T?5V96-/lR0%kPhDo_*2o!**lb;,`B5>XiQBXlIk"-k='"M`?2<>7d"EkILr@JuLRbt;FJA?&kkI(l658g\hKU;QHnotEe).=2H9bZM$!A&.-^/fo>^$8&R,%Tk:c0@?a6UFAs];Mm=9hai;DqbaU2`Jssiq9)kRW"ipdQ+Yp$tCCT>s,N_?(DCuJQBViWcN$^C-&`=J,5B]-R@P92F9#\1b4r=NAE_p6sWhE+&CTUV(ZlJBG\A6cN99Ec]0\U&PFV4cR3:*qi&1NPd,p^Xp_=Uq=M'L&skaSQH5p%B^59Nu@q6-S+Ng%K-mDII-OG<4NQ5BECU`nD?SDh*,A$>`4cCg[8"\cbsR[O%?9I#S6$/Ef6oCJkE;>[7"@Y>WN:BQ4P13B(X#Sbr'LZ9.3Z`.L"h[>>7kNfCB#Sf?51:W7Rl>O$KY?2qeXbn\]L*7&]b+78ELaol^nCYNa!A+8eSSe~> +endstream +endobj +654 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 653 0 R +>> +endobj +655 0 obj +<< /Length 2496 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^D/Yn7&H88.Jc$sZ@Lb(FVVfh/^$DZ2WuAItWTf0q/GU86/_F^3E<3F/]9@I@6dC]O9jM6YOa:l"^3N7Z`E]G@)=]'QRF&_H[p+gT`QW]k[Cm+pY93Q&2AU-^)V^\Sf.`2.Yjq01>\[LEAArV5U];KH&#d7>s,2E92O]7Bp;^EF8d#]ZX@h]/Cs/?lSr@b@>?McPCF\GH:!oV$d<:=aj361Yp'8)1.!9T)npk)5kA.)XE;@6/Cnm`jReGqJHn(@U.3@oo1:eguE=Ln!rPI)#XQXUa/R<"L_OY>F4TG]A'6n1l;d(8BmfT=/#^8_1P^iGaGIucCc+*c-',odp3Yj+ei2.G$92kSE=-qYBF:d;VQEKKU;qt&:K=%Z7o:O:_&?]e9pc_qHi;*`)F118Smh$`>4NXM#PF(fmOg-#=3n:sos%ef@Sp]Y&#;R0uIo5a][^STLQ$=,B>BS$C0[b[B$Cqjg3G`M1'R/^FGh74bI=8%/c+RP>g4?M<<>6`1K*DV,Q=Ts%!09gni[e+XiQr$gsPHkfFFK?RU';D+WKu.?WOre+qOV&bT`.,#+7OkdD^tRD)qp#TMhi44PYUt9jQ3@\0+#H.U--V4C-5(58Q-6PJ&71%-W=,*DulVefR>'XSb+A>YF(-l"eZ;;A4=EId+^!W!Np1RP`h3Y)+oSqj>[ZMDYGmVC(\4bc!O%S!&W5'Gl'W_5^mc64)rhOA2UpDtibT:ESK1<*qMhf_lPh>?qsAZG&iNV9?!I;X=4;'`5c7Q9S2Hb]qL_2p6qP(a3FdX@?!pB1Y&G3BkL?Ps*0h*-:iIn'"7%d_#MF'pfJOC*FOiWYJbqMZU4iX=>R97>r)T;N6?LK(fn;%^li>4fL'@M'\.0,;'aM9\J#jZ#pA4%G7EFB8Wf`<7=,M8WnS>e"4f-Pdr>VQ,VUcIA8]^6"Bk2a$Oim"8^+\a)Uc%raAE(!&Xs`,!a>=H7H+7dd"/&nIO>*fIIAQ)1S#U2KOOH,r>h1GRUI'"^d??JB9"nahe^@(*-2mMl=]+[^K371#VN-*J+$rrHY2\DI~> +endstream +endobj +656 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 655 0 R +/Annots 657 0 R +>> +endobj +657 0 obj +[ +658 0 R +659 0 R +] +endobj +658 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 470.56 288.422 513.06 278.422 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 399 0 R +/H /I +>> +endobj +659 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 199.76 115.422 242.26 105.422 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 304 0 R +/H /I +>> +endobj +660 0 obj +<< /Length 1003 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU2_/?#%&A@6WXIKp&ojY]JNbPpbXL-EU)mtI0#DY?j8oS@*B(tcG["*p>'&$><`po#F]Ql49hU9QDHQ1?RkANp#U4Xab$ETJ^TqUWP2k"IR\^=%r-H/,83u'VuLhcCa:`d\W5bCfPo(AL<;c-1/8@^_2<3)c@!6!%JUQoZK"&<[?7,POQ_4k^5@pG#(q[U)Z(-jfFpQne6I)LWUXiDYeT]_'Bt^iMjn-FV$#.s?gV)a!^N'SO31q,]b2\HBFhC4!\pbUY93-RI`7D"cTogptbta\4EWD>%6:9ucm#[L3(&#_+!V%t%+5XV2d'aX3.Yu\SnPKMnSlr/LMs:CEG4N4dfZJD;Oa2UfDp^j2Y/i`jiC2Af]uV+eqihoJ"PBA6<]?bqS;g+\eFP-RFBGrHqn`gncZfI.L!?]c33t12Tr7+JN:G@JCd=u>.?AW5,ZZ7*6s:qU^)RF7\7fS/cKf+>q#AN3PDQKcg!3@YX%.f%[tS=,nI.LlQdsE(jtWR#SOf!"m6k,mPIIUr*/kpue2J\b";n&;GtPD6V2Em?%@VSH\4Jhf5/l/PsU)pCk]6Cl35_=9,fCakrR-q`LV0TVGccEF=7[_Z&Y8RDAZd=m3(Xh/'g,Ua9"k;6h;R>%YaATrZK'T<1g_7=W,8O9==(Yi]>T.?50*qRFpaJf7-D!*,?08m[e@20CLAT`7$K:B_IW]pono^a0bqh2'XCA^pV>\D?hElbCep,'IJ:mLKJ'a?0Z=Kj>2=mQ7RKac3+lSM^jmF?Ru3bT`,qE0:lo(fB^OCl./#s~> +endstream +endobj +661 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 660 0 R +/Annots 662 0 R +>> +endobj +662 0 obj +[ +663 0 R +664 0 R +665 0 R +] +endobj +663 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 475.35 679.5 517.85 669.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 304 0 R +/H /I +>> +endobj +664 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 311.38 620.5 353.88 610.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 304 0 R +/H /I +>> +endobj +665 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 276.42 528.5 318.92 518.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 350 0 R +/H /I +>> +endobj +666 0 obj +<< /Length 2495 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`VgN)%,&:Ml+W.'TZYuP*u;7duId/8d)A2c^qgH0BUTpHPm=KdEicTQd;2^F6Kng.p[-/lkA_>IA:1DmXIrL2L6VY,8q8MG7pl[;n*>ckfk^K&+H]"2Hu=fBMATP(!TY=nN@$6:R!Z2C&c-a+#$I]G)>\1A8T(KX,j)$tKU=P[*7WW*?gEKR":GZsTHFd\nd,\Z'\>74Sc($\gPd)B?l;,[9L+B%.7OTM[ha)FY/fo9,^OiU0EdM,n1Gp+XT@8N-BZ;0e?kNIi56@LQH5--;K?Aum7*aZ5FPWYhY$QX",oDN!L+j091#=?K*i6D!)EM3&+u_IT'&KL0s='@kQs\Kjq8TnS_dcN=trTKB''9GM1p3oICVC*eCoDB0#b@K)ooP"W,SVNaX4o0\!'Y'D-N8j\RK7(=uBEFlZuq=C(`"h;K86q/@>[#p+*7V?p!@YQH.Wp$seE`XZ8eZNA0KOKej;QfSeGEcSI+GIodWA.hTtL#_MnW`"0d1qJ69b*%S5rp+g:.WcdHqg.a_3)j!T!:/TuXRHC&M$g7c''qRX\^AiZRkR%&[itk@#\i7t9aV\%%)rPsg6D-%MN3J&4Q']V`c>qK:9Q[>%N?kl;_Wh3#>6DL#7>eehaV(^,5k+24WFk;,!Ls-i`Es;lUi5H#E^?]Hbcla_[t[C5m9iJFH]TN2D&V$ZpfVLG.g'B.<&(^=epo^G?m\C%nmH.TTn"YHUr7a&d`+=%n5*D;DHdpuG[ocD`7BldOd(W$.I&Am*0R%P!UeJ!SXlLd:gaK4SGnB)UoSQPLlU&kYupb2!@Sa!b[81JfN-)?9-ap!3.Rj/6a3m4HkuYQb%Z3YA#&/YI)9!RI#1ApT@tL.C`-VnLGc/IZR[>d35aSA,TO>%-"qP/)9.V%B*aC()ofBX]4oJ<`,XU\?m.OGr:`;Hhdsu\Dicl#%rXo^W;4FX6PH=7\(hIGPf"Y=a&Q[;T9bSW`@ZZ8Eq[Pco?5f_h'u0i`&,p0PEl[+XpJh+X8HA"ON(CKj>+g\!gqX,2DI_T/*j*mo`+rB"G"58:#-g\lsb%,Jqs+24/VRU+-DcXENPUJB6n;02'gE$b>NgT.(kpUSA#1cBMDj%/d;\/ls,ip7ZPB#h4e]?DQ!9J"QK$&2I0GJa.RC4@-8OmuTV`4;^'2P5i$QilZI),]EB738#pp;]XrQF[%]].0Nd^eb%_TNSuriT\6N/0DTEBJZJ+Y:^`Y*hlK[g;=:.m:Ep`g-!a6?tr4Xg1jcsR`:DdW21+$`L9;>1o$%6nU!]K3m]6b)b1q,'V/J!FoTRI4.Y^LC;o!eNc9]/GJrA^Z.h)o*s6oL=KuQB16)lLBgl?5QL/ejgsfORBnD6KCg3O?d,KYT*d$%s)@%0YfPe2bJBgKIodKQK-6Wmr/,K^SW028J?BiA=%heuW`%k^;N-6%U$SQYH(sFXIMe%I'e52:Keg]4*e[Y(uf_&ol__8o;%U8@hLtI.Hn@Hht$=jP#6_hDSkZ229Tq`9[7i$]4,,b2N%$D8V--fWD=h3jF4$]lQYf8Em*Z1le%1hS?-5dWKLKpBEbf.3'F0mSn;Rc:,MB`3oT">*(>r'gb0AtGOY],#l46k$1kDZ`=f24u=oqsC4boWE!FX7I>T^;^#,F2L:T=^*6A;VA5V!jM^@TouEBetL?:#Mo\\p4X69IZ*^'@gBVWs(k;d2IPDnHi3\]C-3a6H")6(Y;!nQu/3W:^5W\$gGMo1Qa9sG+J*cofHg"]$(_5mXLW_Sdq[IB)ZlInk;4)H[u]/Y3,rEd%=&K#lg8*sdU]2tnEh!rQ9+j8-13Jtt,X7m*C'Gc$,DXf^?:T?gE,A]PW^.du)(mO0'/6TF,\'#Kc&>YiZlX/5j7*ICVZ:23Ec^0qlH+?19UrqY'6A2\r20=%n5/H~> +endstream +endobj +667 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 666 0 R +>> +endobj +668 0 obj +<< /Length 1942 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0E>Ar7S'RnB3d,#Bd<^k,nXC)mlAa[ngl&+'Z2Dfhi,W[8%M_7RYbJ,-AM0(/0P0s_i"=DB7Gg:O$b\ru7B.lRVH*6=H[O7YfGCgf1r?*KXeR%U]/rH4EhYD,:]mfRnqTN+;@!7NO(5XocAZR#+A&!3SVmR$CfS1pM/.'0d@TEM3f56eYNLG16N7n(p@h"NAP(jLK^*=HH;uq!8s`a!6E$@#EfQJ9>OI6:+D7^Q0*>#\YA5q0VG9gh_K'`VnLpeeC*f93_D`ciA)S`hpVYl52dMLcVeYkf-8G-YGSVc/^&[Z@h!rNBD:E&W%2&^;60#BmtZc-Tn_pqG$K-X.D[OX$R<;6i!!]23E-4fI/CSp=Zg,!Z\*W(O4@g(Z9iSc-8[\0%C>X.YlmOXWS7Hm[e%.3hg:)>72&mqJ[GlI)#b6/.X/d&NMU1icXR4YS&Cm77\#qA')EqFb%5RkS+Be,4`_e'Xg&Kd6Zp`(uS1bEdmXo-4[M[%""d)!$c(u)-V&AR0epH'^6A;,oI5t>jioh<@e8#t'/D&7GHXEM@`UI$_RWR'%aW$R+odK@!$hH)BE4Nf"A4b2a!lj:s'mg>pq@9tRX/Bd;>&$XK/.4$9n'?^@-rE5.I#XH"\6V?upZ*eTiBT$'ZZOQN)\uWsmNQPKj[rdO`IZ]gUS3t1ch.A"5Q[p*rRaf-;D/sGo9a6"+Q_l(tQP=h$Fa\Sc#!/.s4](r(Z$Z>oI;Vf(S8<\h3O:]X^N@09b0Aoolg*.A>6H+7L6F?Ha81h$o9O:@H;.uG$YV^Hd@1r=rPB)m$95uYDUN8Y'PE(Go=H(i>s&SeqlgU9]ekR$Xkr3KANl:8B%7"@MZH9>LpqW0.V'tnftQ>UDX0URH;]l^R]?DITBe!Pn@l[&[!t^A*^ZXR53$1?CZnGKCchOuE[M!Go(8el]!1sEGjnlA"93E[&?-(kk.)Z%8\`LXBp%OhAp2-DQ4=,Z*PiU5XK/%ul+S_4ld+iMGdLra\Ms@3$N(BsUPUM_"Nsq3:a[GjR+!TQd9P>e6[)Zj\VnXC-E\k2[4qf)Z=R$cU/nYQS1(a#=rB_%pT/A_.Y;[6hSDqJMB?cKY5G))TB(h!p+?@X#-gNTKrfQ?/dRf1Sg?H0.2;5NY(_`S%\CcG0S +endstream +endobj +669 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 668 0 R +>> +endobj +670 0 obj +<< /Length 2261 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;f>BA7Q'RnB3aRlIU@)qio>poel8p?UgTddT0^k6*s8I!3F[aVV`IcX5(,=cdEO=n.!"-IYHFO#*uEo)b$K+"`@1f`k&mWr&ddR/GBr^D6gFSaA34OnWOUNum_Ys0Q6G7l=o14.SRF=Z6jS<,R6-9b;[UYkOmkA3cCir&K4PEM@t,9`J=#V_bPF?-t&Od&#Hm%YYX+n;mA*'KXY_C4'CX4s*U>J:%:(/oTV2h1.Srb/6JHZ#rH&7A8qA!4X60pGugobQaZa1HYGr7;"]JdLP36uf*^0T9'Yq"j>F%",c4MO!iUZ%V@npc6h)pc=SRRZJUaOoe,53-YQaQt8%_a+2>W:=IlUPT]Ce>3.:>Rn.Q9-Zb,PF-954E])%;SAGF4ZY74IE*qYh&UYgEh8UTUVTUNU=O+3e2-u%]cm']_*!N\(2q&7lSm;iPrqp^CTSt<6R!3D[=G/pAL`7EOPp`@H\3feJ+5>&Tep)/me6-:`]iQV#kUNTs^Q-d%.=>$3[5PHo^`g2Gni64)TlHg^P9ctuV!a8PQtgDiY>R`p2*JssUEj^YZ13g6RmOh\GE-ljBplNjrGO\bXp3-13?;R#kWdrqhj_hR5A:&ZOrd$D;c',eqLn$f-]ENT]RV4]_Of$ha7?T0kEBOst"]jQ_[.Jh:[rls?pe7">RO4I:.LBoRJF'YZ!@KN:M`AgLE0/SrQ45=qjr+DKQatp7,")j*n3g=3XT@*Fa].D[5NNa.,sZQ$4!L&6ej=L\%0o1spaeIaAJQeM5RN:fD?^+20\Y8b1pIU^LiG[lN<;kaQrf%)o*+Nc^kmPJ]jluF),&iI2]:^_^:A-s"-TmFVKR/kg=:9D59&r5S?K1u>->^E1k\`\PlL8MZ-n(HfW?\X^t!!WnmaioNcWM/DpG=mVW2"$SR-JBmeJ#PdV`"tj==oZJuq@pDYV.3-a;n`'9@Eb'/&J-9^DMNTgZ@@#_%,1];9p13QYL2b`,enh+R"Mc[FNV_D/]j1W.rsGuLP`ACY95a2g!ZOS3Vg&fLgRbE,?HN07jee5XqZP03Vs-IO*_sqDar8p\kbSnWE)c>f;,e)c)Q87c!`ZEtlj'S5YZQq'REqI1N^hV&PZrt8g6*8C:e[qLFW%+1$/;pmXje('#P_VbPDO7fm])RSm63$.M3:"S#iLl.LMZH6%tA)Mr$bBS&p\c0bjH2>8B@2;c7p6-hiVT89`a\fqF@n3H(M%<&UNu)T$I%/,Lag\lISPm0o:9]6D=c"CAlDX;rJ=qj$[\@?C]Vk"`)6B@?d@R^HBX`oVfk4BkGZ-Tl09Q(1gP(TV]O-rL\gtBe\6iD4s_&=gg:-%!Xkq*Y=G16&NSLn\q.F'anDpWjRknS5_%*WNYQc%Ka"k_Pc-T)q7._k+Rs73-`&3(5GtE4%1dMFOdLgXU`O^e**7<(0@jJSgcT9cBc_pUYnT@nX?.13^i2BL5P\b4NCitq[E@#U7@KmWF0ZDgQMK8(>gQuN'i$Mo;[]El/IY%&OX#9\jEu?l)I$H3:=7i<,?.jaSrbH^qjlQpt`6#8r7k/U2Ys'7\.1FA:4'8s\"gRN'qFP-ifc6$E?V=WHR0-8HUj;5oN(Q;N0r!/NF'Yl?kaZ$")fh-?-sf,1rB(3PJn_`&>err>a=$P!~> +endstream +endobj +671 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 670 0 R +>> +endobj +672 0 obj +<< /Length 1738 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DgN)%,&:N/3Y^V`s&Oc*mdUPA&`,a;WDGu<^3tp_,U*,A(MGQR\Z=j%(?!^3V'L9cXqHsg?/Jbs2cgG9Z)1ots,X>q=fVQKYEmHa96otr7Cejb[k9+UJ?:Eu0q*FY`c29Rf_ffVlEc]0p:@cRm2]KSR2^1mSMSJ/e&bH"CJ3*C+X/5!5kgSE(h3&aE)0$]J0hL%=DlDh\3Q0TRr3G,CKdr`itPjSfe-Ppq3,,>u\Ek45;B%d#k4PjtbBM3"0i#XV%45Qjpok"t0iAfrXGTH:04o-ZljNfTc!NK=A#&8_VUu*jNKKR#KN>t_6M3bnK(*&M_+GZ&^anFi;JHG4L70e&HP(HBM\3Z9=rK&p0M2N\(^^P![\h/@:H[(dcQNQ"KQZ=?\uPHjsB2.\>8((SW=s\8X2etU'50[Kp"%JGNh-oKdC<'k4F+m#''Rna9J/,Z#OaMhhpu/3cDGp8]DIa<0iS3Q=QN)Z+(6P_)=EOWMo%d3*!6p23Rr:mLoHojH@FsH!UkD-$-&)S+V@@PRH'GTE6_i&WYP>_KSbiDePT5RXtZ7H(0D]nAi>cKF2$FK]*eJfQp8n,&3l1VtUeZ2VN*5Rdk29]e)baQW1QR#]A]qS(A\JF..hGRYeP_Hjc9,EngRNaMb<$i\k9h,mCk2qIq>@:"M3a'_!*EOq'h@`cOY59ZA:bg/EujP/1-4L#^ka;8j#"T4YG4pL0Ka`+VG]%LtUbFJI/@=3]`H(p_\L-1Ra,_#c&4`+^n3o>D.q#fuYJ`F::FCqJAG%=,=6q?3.=P9,V@+s/AZ5j$Njru'HrJ,\NPnIF9K:o)Iqf2k%%C&IU&k!HY*\2*gNVodKMHLe]*1q2m]eadfa`cZU?gIM^[q0uVN[Uqt105o!m2Dr2qgcZZ^Nc3TN<<+ZhQXZD$67BD/@MALSl=J?\nZ&bj[ih*!"*^OCK2eHl$&`LZE.=l%9o#524NJeqrMOpoQFVNpP4]/N[gD*&T'>dk#DCLSO_=*Jnf5LeZb"\*Vj=.,E-0f#(;QtQes@pNi7bO"Ce]1%aD8h\okeEEL'o"<7jZ\<(d>34RQAsQ@KjrQMi6ai@ssrj*s#<>BSu!a\T9O'ZDo6DLH.qa\kme+9NR[#4UIf*)'^)L2kR;:OojH6*OKIVc8LW'm6e@;m:e=duOc>p[Y'3.^Q#MLl0dt>^ +endstream +endobj +673 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 672 0 R +>> +endobj +674 0 obj +<< /Length 1766 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0C>>s99'RnB3nDjmfLht2!03Oss6n.P$2Oq`V[3OqTn`41d21Z/p&U3T#%@oIfKu=4G#$ZA`bNZ](e'2D"XD7R1A+_ZXDo#\2oLdJgotj2&to!o)N8B3NaO8n_!`oHP]_oTX?(]_%Xma2W\1GVnRe^;i"g$<#.D=WC+MG6)'))9!Z^RGnni.Br&]W0I"##bPgYmp)\60E33C;aBW<=@ei!UNRca*(uCYti@8nC>@L6lQObI:i6rCoZ3EcOeU&0B>eZ?.P8HqP0c\L6Hr$^N.G>pViFIr46!e2YOf7d)k(mG$X0;7HB69gJ8>)S]SD"I`5W^ZXHRc!#;-sQ$6!],,^';"2PeQOKYFOP642"Dk&-fkaZZ,f1dGM*e7B*;)3qoDMaMr&.UtWpC?8#fs]-sR\Hd?kFL*SK:m`YNK5)WPd6=)0\(=aa([;:ad?]r'ciFk*[3mcjE[Kmd&44o,dn":2R1"EC>pDW<`/;nV,Y;QU+2!7Q9)mk]XlK1*k"FZLM[8;nu2u..tdt&2Y;\Gd.b$T<2GoD,HO/<>XV8'p",2%>TXRH+<9+^St,M]FQ2ePM$%^14$KD20HAfTO5/Zt6JCqpG>Gi`Qph0!;H2$"'.'.K"7OS<=olJY[:,nZ0G#0LEuEVqC$NN5U.M\Cuqlsou:?@]U8:(rB"(6KkL(Y%OKXlh'K,r=VB!!Ye<`>a#&>sktYHHk`U8l/cAN#0h\6h!NpFi).-NH9kt^D>/6>^FK\U41L%("0Wf.XU`iNNiiBfTMW7aXmrhO0\,gUlqZ%X)UGJ]B=(ZZ3Daq\TSA/[,;+=10n;TW$TAEfm+WlO_t3_Ls_U'KU;I)U:Qrafh![c/\UTdZjW_Z+brYbq%85bkQPfl)h5,TgU8>[RcTU0-"Ve^=.p0j#2^%hcG)5ac97)rbDg-<\]g8Cg?5JP.FWjF^B#W4qBg:V\Kc?h54`G@L:dKi`2Op@mY9*-p#tE6c)909\^HNHNn3BTql:*9_Bq)]#5=MR(RGbnX<6\ +endstream +endobj +675 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 674 0 R +>> +endobj +676 0 obj +<< /Length 433 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GarVJ_/@+D%))BcEbqR092aJ?DN)D?dNgY1FoW#K25R$a:mt#Cn!?@21,S*%43u7Afe83R]\nSY9MfbK#V$gOLjg&W9\28_Qu\G@B]DkZ]Mt_@d`Op@$i:]`.3TALXklf8ZpVs)Adn-e^MK/.FAX%9BV=+kYLE0?#$HI`GHLH1i+JcJ=.ise:!ARW#UV8h/LXOmS,=odm?I,?!K)MdPtdbh+Zi!XMbX^>*i+kM?+P\iF>C26h`k2!anF9re7\Gkc'KbpKI%$ZAZP=bITCt([Uh7H6*t-%#("Do?A]Hu0KmWM*+*"A>Y*?Y$.W\6X,1Go.@j8o;4RSbVQ>'d00d7[M#.m1C4E5fM'Q^5*~> +endstream +endobj +677 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 676 0 R +>> +endobj +678 0 obj +<< /Length 2248 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;f>Ar7S'RnB3nG8@LZ(d.&fu5Wf9jODZRs'YC@As&gbI#M_&d=.blM^QV^@BQ2[-V!+q1j*.ZKS'fp>]=?no*4I_mS[k].;?qFW`K%:[IT=j=UDM*]efLjM%)P<],&!n3N,A9rQoPn,-U\5K]b-LY69uHkB`P]qD6M@9.KTN51?,-/U8WT$F\%[OiBC-.qOQU#=f'FNGWB%'2piQ:ViASeR()#9rP:d@Hq>eAqd":?/3o]d2rZH,S\EpH3+XGcm'8P3L:+'/9g4>p%I]fgk@")a0.HK@Lb6=X'.k)Ppk"XZu@03\OhK)UW8m"11'&Xb*<-%^nV*$+CiA8'`b>_K%Uj]a9*!p^q#0W$T;FV4jnGOQg'Q_.U#&g?':sbKtbQ!d.OfalN%Vj8L1\u!hQOLM<4?[]:T(W';;.SYmE\g3B\j1JBV-eL#f;&JP1c>(drYmePq&Cmju/();%DK3^ppKlMfg.7C_tt2Kt/neWhV)cr#G3cc=SV91e26m`pk3"@TehnTWGG.Xif`]!qRSrXAPX957l;.A^#*)B`&!/T@WiIn`DuI@0#G[A@:;=3(=M(@k!P"jG-("D8!;l!&Q@mRddO:_YpkK[h30]JnV0o8r`>)UG$j=G\0'q!#L7X],_=q5#cJZ"!G+GN/G19UL?_m_$N[S@,s-!W5T]A6s8,>qYS>!^OVhHnk:3/LB8%/Mb:EDe*CGh]Hfgdb"dErG==&R4)]Xb`uIX$?(m,!-$*Sl/?!:N^+%==m=J-eJl]oSqNY@\1RWi'?_B,D.YYLe#I@4=D''UFQUlMe*fB)#%t'B3/[RW.XF;4%'.9<=!q3,aeDAQItN>PD]Gmq,g8H[J'YF[>\inp0UZHIJpJB*HTrjiUZ7DN/fL/LQV'URT-Q6K/kSV![+'ANf@lTnu]di&5O0q_,pPWNCt5:>I&HWlWa&>7khc,YRW0UCM&d*.f"C\:>i92;+R/AI4M6:^?HCTS+<$D=,f>e*gG[Uj,7#*Q]["6t`M$7(r2En%agpq7?i2U%J=$KI!":5'D6!#kf`tt'XH$]]V!fdd@`']01(\$E3@Cj,Jk67e.d!23M>b>C(%El2M137.*o@ZsD>tp+l?T>ZsI,(]:-odK!%Jk^+cO%MGDU@iXlmD[<2a`-8Ui!i^:&$:)`_!IAmq-te&4Gg`BQNT)b7i[BUB_HW:cMA$G#i'$$l9&(K]@O!'+/-[,pkO<'ni0ui>koo)$-Z[r#YKc'a,*[:&M$&fLe54,"C'Feans%;?16m_oaU]\(]q=d.bmA?qFkc\QZb^YFkg<*J_]"L0Cr)cq3?]+$=0%gGeqD8cGMbOEF=j7'KX0QN-aA-HZ?gOdearaTg\J=^^&)EZfhZR#n`$A+`I0,tH#6,glS,tdn'N]>$5N*[daV,tTRE=H`>YHM;gs&D27@*>:Urk-KUSV3)$ol]h:3XHKn-o'oYTWdbEPUK1@tHG,A8s[\'&Q(ltm&$&Z/0/.;qt^&&/rcVZDHamU1gg5t:SF]B>1p,RaIXdI7euBi[J,?r*Ebl!Dk&(?8?+mbh7?"[2^h*Hm_md&HUc^X)RE8MCHi*hPPM.8SOKA(]`M;p$B0Rp#9$\IZ<8SIN1BHg6-BH$2ek(8GUm&_tT-;_pR/PQp".j~> +endstream +endobj +679 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 678 0 R +>> +endobj +680 0 obj +<< /Length 1476 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;d9lm(!&A@sBOIMMm-GY!O;Q3XK?>Ys+[W`tVh8E4\Z.nIs%lg`3(D#&-'hj%uKm30f\K!k:=r:ch\2q-OG>qiS(bc]e^0=Qj.=RoI\nSu[)\hQEV(-MD<55Jc"ICFemjY!.d\GKPA4*];*`[5BY:A.o5-a5EopI6cNdrNF(Y:H6'I)?>So(($VIiai5[jGCGXe`EOe`pLZl!:De)+.bu;q**%I51"OX)BX@h7@E;+=uaIi$[>[MlX^@hXB%2d3_5VfO9>]#"+]fjVpd@3s#%;,`c3Kh$c$[tlS]pn&&ku-&7qTWj)"q4dqg<*C6e89lqo@ZVLLa6c__eC34MB8?j]'=MkermOQDLRjj[q!@99]R3K!!]1uh-B@]j#NE7ik,mol&+,$XT4r-F>a+)!u+ESQj7(JqKta-=Dac\L;5cO"5=0C+g.rJ__FsBs`MJA[lW66aYq%'9C1Jf%LVDP%bl<6BOG64.0\bX_Tka[RnccKV?N+!HR>fEn29?hBQTbH&2:?d);a`!A;/s'$Xb?kt5.hotE%7S;B(J0d]Q"":HMo-qqEsfB^d<4Npa:1uLYJ^uGR\HRju`'Y0V0a;GiH>pmIG>THF4Q6>ok$9VT-*E/ul^plSUJ5I3N'dqTXLcW%ES-JK^=RS2*Pq*B)%'/5\O,MQ?]QbnbiVV-IFlYiF3<'Msg)VC)Pq-Jk-ZVlqKL\di+E%Kg2^bWaK&t(kO@t?L%8H1QjhSkFaE5tSIj$e28qm>^F2O$UYp=.+UKsFN4?ac>1B\ob$bf6k8!jG91rhR\Nd;GU_$#&_OEnU18LG$"^/T,W[9KN\H[0Tq]t(kHNitY&"g_"_6bt>jb<.u*cr\C3o9&LnkEjlB/+l)LC%Z>iEG[09*56?^J(]aLeH(,9bU.)P2IHA8u%4@W>5I/=mIK'Y;X_<])7g665ip()gTB&i~> +endstream +endobj +681 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 680 0 R +>> +endobj +682 0 obj +<< /Length 2012 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHM?#Q3''Re<2@,DSG:d,;TBN6M+3`9%9;^H=n6W2C5/c_QHJsNb;2REmGcDi5D^TLk@#!UCBU/$.6h--`2*iW4bj2q8U%K@fPs/p(%C])?,"c^nu0JHpYVYj7;IuA,&Sl;t8/?3c?-!u2;s18,KnP%DjRA!Zj7PlM'BAn6/'%+EnR]L1k^[Nbq9X:l2hX3@u3I!;FE=_FTG8S[d%qdODPqh+aP;X>1$kI$N1/$TATdm)W2r^J-'p"2=Okt:M[Kp(0R_W9I\(t2d:.8b`W[OPiS3-2:QHsWm1N&6f6^KSY1fSotso\`)]V8/T.or80+m2UW+`n.:RLY0j2IR@jG-'_5B>k5lH^qGZkYOA5C@mK.MR8q\(rXG%Gs=O4Jk)mgqU+qtXZP_^;+IG.?('KD*VCBYA(IQ`N^8m!(:MQDL!=M=StW7Q7*sdC?F!d05uTl`,Fea5>`4HsEde>+0#(mHXHcJ)ZkaCO-"^i.kH$AF*6DAZN3'\,qla.oeB/SS`LH*ADHPW..kPZN.,HrQ/Br-C0$\dZ5'DI<$j]#B*$=iCYu$m2Hr[u'`:$LX)\=[\[G]uE1Ed1QD8a(2%@bq`4a2kL8!;NCF3LLeo[!EQd3l]U"4B!R\'bO6l'FEhl&nbQh?FpN]E9U\gCZ""8G:9mB'5u9F1nV;49[@I`NE';mR:scGhZ6\Z'WY6o^-1Vd?m'g+:LMEl/!57FlTD0Q"`:l[:%ha+h`SDR#O4*DU,N*0!HOq">BsCDV0f$=#D?bn"h#[&%Uk4G.jp(g>:5^&b)/)N-%u`LumXUYS:XlnBnT?39YXJHYp`G9bS3Q,Z3A+&B;AN'dB8ehCm?A^4RA\D$?Kbta5:HfY!2J2)*NghhIW7=Xg&:"mabGd`L9V(oAK';qT/B+JkQ>YD)#fXZc'fL:#,GN3a?hA^&SlQk=N21RT>=mgh.LJa=<`PK._]aFm#`Km_ssf]Y:lchWM+rgKdsBphJ5%PoTFUY]Vafkloj[$/ZCSGWUFn"\#~> +endstream +endobj +683 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 682 0 R +>> +endobj +684 0 obj +<< /Length 2135 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0EgN&cS&:N/3:k#]C@DlE$)6`("XN6mt3cSUZfnUg`#`UAb#Sb(?^#b=47Xu>O6'p:nY#98-3INtPh(;tB42";A^_X57iM9s$G9(L%n:qI3#s<;]JK38YcA\sIINlI!N_^(If_L83YJ,tVU:qLjW=4DMek`//R`3C4Z8L+JUb[4WM)gk8fWIdAHunsIfaoU9hLE6[.Y>;<7I:'$3/HkcCmqF%h&-.!jrHt]Q$6Dk7eMu(<-eH)op=-BPNg+r./b]Q's[9s-[%5(;lM^B].7r8E]0(Pg?/3c^8X%imLgC`=eIC-)XeWmc(3okV>ioC9f433P-gpjQaR/Q=+Z4&/BQ.:co6p)?*6!=F]O5U^_?P8?Da.9U"dGeZY5rKF.$Q7LW4h5IIM%cgta>a1Vs;]FET_=b0E`=rP?G*um>eU"g0`0HDL,[^c.m`je<=':26.eKA'nr=Po:*Roc\%M1K43qUa$PAdm)"T\?2`FWI`#1o=DeFtfc9ER89't6M)7\m:S!DP\J_hi(0el*=&.=J3q1q5322F8Xq@DH>[M2$)drAC\eJMRe;;_HT2O;K/WrVLT&oQi7g69:@!=d`<%XPB^@mT34[N+G&:cJEd&U-%(nBO:NNM.AF_J@4jLo/l::j'92_1k4M&ic-9op&".``/L3t;B]54KT5Jfq&RlSQigiW5sUt9t9UjI@i,X8:NbK,ab6$-0Y7NECOir[$@TJ>bcb[;\(?3N&#N[(L^ng7CWcP3J2mKI\k5'"%:a"_Y>LBeG5tNg.75P]r8$V=AYI":'+E$Yj%Vo]j#t:S?H.V8K!i<3Fic'Cp;GM=n89k28I>e4c%m=:%F8+'a-JM^uP,E.[A-4obhhTIZE=la1/PX,.M.\')o`AH^O;@-[Nrp]4KM_?CJl_Yg"nJ]O#92[r?23"11505nmj\(C'oJb<)Z//%aLuqatFpQSdFM"ptJB8?9lR4m._[o0)]/j1pkt-L_[!h"2:>5orJg'/7(.M3tAp8`1(JCF[^.a&;Kpn#)nd`%=05IqllIuBa3l<;kl6LQfB9)[WUAVca'Z'Y*LBQ2Q5Z#a0`H0.DSO#Lf.4CZW]0r3".1L@o+,u#f6sr]&l2KmWsF:J=r[qG1lgX?W:J&X8FQ1h7cJb7Z)uIB3Wc_MaXQ+F.$-4n8bQ:UYefL_Nn-ghoj/j5V5AP.7&$\D^MTsIVP-#kSBJQQuA5iEsB#_%?:eL.Y'Q4@Ks4G@_<'%cK.GjjP\jp$ge75d%NM86i0a]1tUN^!q<%.^7K>h-r?rkh*E;cOQ[eM"+942#`mdEeO$j3XDcFt`cTfujNp%pN8q3E2u;OM,7NgPo19_LfDio((m*cC"qX?!j7cm>.jG^<,['`T;DDk6Cg?h0+3N81R?XQEYF6"SbIPfhf//M+1RR=;A?;^c9K`>F^;_ +endstream +endobj +685 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 684 0 R +>> +endobj +686 0 obj +<< /Length 1881 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!Sl=``=U&:W67@.e%f+[gh`%l\JTLQ]#S[uR5ZmDO:C8BB4I+r-410p<^:"](3_,ro[#D4R('.:TK!pKM%qCc[qW#G25*Ec(U#Y,\C$"4ohr50G]$tO.6i?HV?[-:ckE:cEaq0OgDFOMpsQ(p!Bc8P3;(5su%T55!!4ILAAOTpR7X9-"UdN!:t!$O+80r"c$6OQUZ;uo=[]Q!hL[cnbI2/i+AZYb>Zg9sE`L7;4?K$\&>S=3!Bp#%nZC.7IPd35(+ABCF\PeHGRVXh6mD:?r_[=_fFC3`*$+@2&?g@`s)i_rPo[2Yg?t@N.c3G0C\./rpd4.Ru_OH)MM6^$fu)c#;)e_UN=1b>t"6eJRt)2R*"8:0_^4R[,cdW&['Pq\$R*T?ZLcd'\P1.bu)fB8idHIkc`#/hc%+TH8$A4'^jL>K'"!2<-2RXlGDaNu@Wn+"\VBr)W:IlZtDgY[J:R?iO8g<7+b:%04J__/6A#(Yk0Gu*S!2c;Aq,="3ZP/Nm2lQ-YQ.U#7c&-l4oj>C/tm4V/mfEgq*;Kka2;4%m[+51kk1TV-ILlfL13G1$cL._P\Z@?+&"AY7hH*S-@=`7_,SCQfNbL(qeec>gWpK1a8.ISM5LEGJ1>\Il8NS2K)/t2CZ]&`afTYA>^^7Z!EO=3Nie=+B=dj>!\Jp7HBHu(SgE3$ASS[.p]5!4K>Pb-F`u#Z]_]\Xlf,4f.NH-\_8RN4#sj=]QlFJooVf\GIJV5\t&-T2Lim;DF"uF":(l5;/osC_q8K2l1-_K"u4)+"&Z=DhHgM$0?pAM^]5mN\430t]oZD3)$d:fN#)k%[T/;5#6rK*HD.A7d`(kd)Y&a+6U?^jXtrSf4NI/Wk7pA6`QRF%Gt^P!Gf.Im,icqmq`L*48"&-6;Q[9XI*;CKRt2eN%lU,OW$\[VJjmA'(^=[Xm,U;7cBrd;%liUNV +endstream +endobj +687 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 686 0 R +>> +endobj +688 0 obj +<< /Length 1781 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHL>BA7Q'RoMSnDoGO@)\9^ZEmd?6PMiomD<$S*hl'GRC50Rqs=,E'pO_H%9S2:qQ?=jqi9tci`78#d29MHBNmAs\W::_\e4<#4alR/fRRgg?--Rc,W#pVbI=2a^TFHu(E\<_JPAN9K\p5`EII5]nT0dd]VOf/DGu_@epl>HBg3S`doF3_7*D>b46b=g#EB09]S+T66=,!7RB/KH.pS3"#AuGoY*sc^#XDj'VoH+'se^;-SM()T2"aBXBW^<0J^o(Ae7B^dMohh)V?Jr>Tbi9=-Jmi7a*6Q"B[/dJkS0*,F1#SM.2E$TQ2f`XPQCn.c_6"Yrd4g5Vj8-1X;R$^LlrPir"J/:K2NH'p"O[L^@ETYPC95T:Cd(F$FlfH.&8bX<)Wl)@0k&3S/",W(!>ho$_fE5j(b%PgLb5lo2I$gM/q\DbFTG@bWJf]^N\&oI3(-YGp#lD^8'QW1n8)sDjU&9b=0X4LT6uhN3^?d$E$R6GuTaNe9"Ap;Q$@jGFGCI?dG'W:6Ce7PLP7iL?ga8q@$a$6A.J%?PKg[UrQIT-@osn_;IIZ+7'/,"+V=9M3@Db?M'F9A)C7r0S^u_$9LVH,V1JIbIr<>W@j5n$%`jO:R&-c)?ie^%n^@$]6%r]!2iS]LZd!$(2kfX(]&N)l!LKF$?eKg'Yt%NWiGM9Ba@sC/]2OEIf%YriY3l+R^T`eD.a78R?:fop"FGWq<4a*L"#GHE.)^&@S%/7n'4k]'_^BCB>0_D+lje4RA=t=0IRM",JVS-H7g#H3hcU2(1D8Hs@5m1PaSu4_rhfAm)g^9>^h/TD(^44e8^(Opl_PaOG*"DIX.g:`;O?R'j5W3M)3DWbG.N)Y]bml5*>L]AQ0]!'pW.I-0i:qR/IP,1O6K+-5N9%lVYYj]O39OJQpa#lDpE.I!9%O~> +endstream +endobj +689 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 688 0 R +>> +endobj +690 0 obj +<< /Length 1848 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;f966Rn&AJ$CE3uYI@>;70hScSNXkg!V/8#K5%O3^6W9']VA?Yu\"pCA9QpD#"NjjBiY8R3,Far5,I[aVj]Q5o?)Q#Ds?C9Hq"k$*Opfp7JY%]bmX4j=O6H@CWkpUP7qr>^E2fUr+lj$>HI&J+!s:<)HYW'D>$IiH8i)%dQc=RB2%quKJh$I'rH#cF]":;bAaurhH"NtUu."/c"p'#$.:Je+P3!O-I\I$0PH5Qb/q8*I?-HY[FEP:8Tl##%>a6lB1:.\$"8`kN!T)^7KL\6jI%KN.7bX"3+fUDWTB8AB_$tN&APXG,mGOB)%Ph(0q""5mG0A5#q2:$m`^[+^!e5`=EdA/MfHuN;G/$OYC1rcgSno8CkNpb`<..@kP_)L"-tVr_6M/e5GH+]JOR2r3+jTedh"NE_im.",>\G(oP1M$9NT]7OHkBN,FF8D7kZ8f'*fXo9^33E.qXN-qoC)r4)QtX'XZAFM5DThPCc"=^t.up:M*`K"H+IA:M9RPp$*g2a-Q\lfO!oLk7h*pkJe^0l\,B)7JW<"[]5[RC6RC&7W#M_!b#uM'o0)Cks8TgZBbfeE?a51]W*d.tISku^pWS_`H,BeNE/;_Zm&ILO*X4^HMb&N)?%jKJqq2/9o7]IJYso1TT%+ZRfoP$oB5bm`),\AV+@>E0.\qlVgK@"B;GkpI_5"h<;rRCsQKEhD[6(+O2McF^p>7m(%jXC%D31Gli/2]dp3KpsDWQ41k7oce0>0~> +endstream +endobj +691 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 690 0 R +>> +endobj +692 0 obj +<< /Length 1608 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#\_/c;1&A@6W3$qW-K)gJE/MiSamFH/T.X#D7P;t$nNiC[h"[#rfrCStM%C?*sM(=ia%&2;@WF'kM^J)p:hb\sXQ>K<-B=@>P7LWN!&VE&oGSo(a706u)1k)#4F&lN_VF0J1oA;[Rj"sUDk&F_'fg\,\_96<5eW4WM5;"$\c51K;bpZjKGZ<0dV)-A_eQ:=Un]R.Am14QgD<;-=424mC-7;o$kje29IEjciXH?2UVnb5Bft.;2*Tls`k=ZaA]Y,]1)cGi#3rSeFB)N>"<@RpiYk<@Ab;b;^]c2@LsqauHpH%]\pV7"(8Y0Ro2V5Qj5apAPe&9o.@p^a&6YiL>9-0aR_a"@Mu9/lQS40m:tUEo-I%e3.cZ]X2Ur.>+]^7@r#FV.]Nr1m=)KWd79tfL11>VKEZ+//R2#JZ#lHe5lYnRp\e:o*34M2IFRATg*`-^GU))i@C_FW""nlm"RB[2c6D+l??gbF=tQ-:=nGl\U=e"""#Om]Z)V\S[34+A'i;kLIfT_W?fjK24YH@tB1V8NJsr:6%jAd`F1qK5>HIe;AR'%m.V[-sT.$e=+52/La^DK3'mj*oPYtKf#P@kOX'9%c)TJV@=BNrSF\r>Y"1+.e=@Ngh[*I.\)D8G;h6Ti.d(5a2d_hif:g'i8K=3\Q7LTMs+/D5Y5^"s,+eqP:]g[d8&;.26Y&PQL$2VCRnC`0*R$6.cpIZ$?XFUs\HG6SBq6hdD'30C"d\Xt>`SH,4-PUlcZ5$6Lb%.L,5HN-?nUt>(WQ0@?_o.%PI(8iOY8eX~> +endstream +endobj +693 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 692 0 R +/Annots 694 0 R +>> +endobj +694 0 obj +[ +695 0 R +696 0 R +697 0 R +698 0 R +] +endobj +695 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 415.56 436.528 470.56 426.528 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +696 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 463.88 420.528 518.88 410.528 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +697 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 444.99 241.528 499.99 231.528 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +698 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 372.51 225.528 427.51 215.528 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +699 0 obj +<< /Length 2520 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHN968iG&AJ$CE.!P@'gqK8hU:!>@jX!GWk*ts#DdsW9_^)&gDdVCRr-j(eqJG5Rd.Nq%m`KVE=2aXe5B.;XXsIE:Os"r!V=W:h'"BmG.e5^3n`ij*nd"$#L+@^p]^hB9[M/#Iq:f'P$=Y68GeMana@qGLe&_2W,2ZA[H9.82-+Z2;'MR)KaOtg74j\BM9dh&u9R_np@(ns&[DRN6,`pFOnDo&>f!:L\Bq+9Uglhd%)K?+4P`j\TAVi?`7(k$OBhOSo#2dgo8IKj8[-0D-S`?!jRYff4fCc:XQ"D]dFG%tTCLded]3:HOPMdNd+9?l!K8`cDA:Q3R,@j6Vk;ck;+)&(6XLZQ'O1"a2)k&PsJ@+$FGZIpX'n#q1'f^r8fUL'?lo*Gcf,e\nSYn'V=Os.20V[WT,>)V/E#i%u!')Y@)a0FoAmGOTTKd,/D^f&;FtQbU7=AB+o8gE2(5:m4,=^$hcPW3VRl0s_2/UJSJs>#_?kC(uCQii`'Za@*C;G[%*=CO.g-[CUU;.,\S$gDl3=JcNN\KN!Q@HhNlE7PFA-`@gDe-b+"c:/=jL>O93m=;c$Ja(6^S.4[S8&f/*#U&^GYUm\Vu0Pa>7^Y;(7TBJ$XIJAE#l1B-&r/&hnABSl-PYIWJ6iEjEe51FtRi(^.qo0X^tN4!,s+eRVPOYHq0Ze\'\u@S5!?#"*"ZN3?pI@*Tq#i%c6a;_kTtuqQ]-U\p:dk`G\I?298#E(bDF6G0?m5)Iqa8(9Je9Kn7^4p[d3mO:+e%)$F:nSUt_6C$pAQObj<"3`VqcGR&%&8Bk/#gKIH!ZTWtYoW#rEVZfQ9^YUs5i1d.NS$&njMJTj%I[_'W#;6[#lnsoBA6/U5#HY?%TmelDUHZ0Bb""YVTQ;G:r^278MWH=JUD8S82][/?3J62`ls0qlAj=]fJm5*(WnRl:W70I4+ZDRsIpB)=g5O+".)90!:o::!K+jUH-6t_4ZhoZnG$tn_^EKX)DAdn^;mJE'[\uZ4PRj=lb`&(d1I)W.i.#06]g$+.nV,Vmg021'+$\GhFm;nH%4f$'N49K)qb-7_o)E(\@6B##p>oF!

Q]2'51KW)j8EW\(J0+'RjcSuchN++*QK:E88BEm`Thhl$Eni!l3:5u:N)'j-j]j,\EnGmo#:#.cr!Le9L[/7n-bYlH6Wg.n+"o>&^DZto6=Cfr4S1Xa82kj54Q3GmJf_?K+>eL/HEg!`Ht[MJs-bq$/BA:8DafNq@7P9aqIs*$"H-L2'%)t8MQF2mfsW,)U=o"tM?e+c4YG>2!Dr5NVQit81*X^qQG9Ep!f$_0D+qZH?V\jCVV*$>==rsXu-[/%R83W:iU;]=.Tb2mo(T!^7tPLpp-jG('c^4BXp@`MYYRgc!'+bh[Pip0&/pcS^VVklt0rglF'46m+\mmZ%dd2233=qA9hNRH0--N3`%m^pq&;>[o/]`;=h/+.n5=`Z2$;ie+Xek.qqGcm;hOgS7)X"E;SB3^O3Qi&OeQDEEjoXNRo.A%$";g-:p]l@S^H4ON/MQWn2/G;jOm[-'I+s,P'5)3K%BSX.gE/m6!K_u+X$s`A!*M:@Yo4gqT:dl45:o1gi(pc^Koa_@:gjM+V?-u]:dKTjgSD%@DFB5Lr1RM0M5mA;KnUuu,YZ(n"W`)2U"iPN7I$u)CJ4=olmKSFPpLpnV`:eg?qJB\IudOf^KT-no`~> +endstream +endobj +700 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 699 0 R +>> +endobj +701 0 obj +<< /Length 1647 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#\gMZ%0&:Ml+\5,hV%9Jc^?&G@ARWX-d2!m3eh4iQg"!c2O)d,!G.W57"<+8KfMj3QN"cBrP\o;9,ba^Ja?Y1[(YK-inaICE9-f/BRJg[/h_irU%f4$C&31e>pPHUmj-*PT@k<gg-=@Z*H^*i")nO#.[`>.6DY@[=RBQ]S%qhN974aF?-nE<_I%Y-4X6nU_%qkm)IP<%R%C_)f71@]h7&Q1p)rA`VA-_:Z"lrSR,*AS*=E,cR=si4DjQDSh;=Ys)<>YF)*+o?u29gT?Q4!7:,b*#6tMZ?QVYJIar"&QJII&R2'^gRYfS+eqJO!f%M>AO@U:b&>u^#2bspgr*7#mQ`Lh-bWm`>F*2/Umj4W/hp#u%1+@QSC5l(OU*[@XRAiqa[nHGK8f[!:.H4D-&pu."l<_U!T)0n;*D5*pqK,_#/UprfH3'=^"(RWh1e1MR9\r@>Q>R>Z"chf?AphH*%$@A7SU0?N$b`EGZ!*lf0mZ"A3H!8m^rqkUsq\^]"%MClHS3)B)o89Cpf#5.fl`0SBls/ZU`WUT!W'X<0G+K+Q#h^P16sgZkrt8!O1_h0a^W0b%`AJI@kG2]W]mC@kXH#JiH'\l`DeA7tYm#cN!olIL&Jr64uADjT9HVA\Ka-3(5)>D9BZ`GEM&nl->%KqIRFp5<`TU'_?:\Hp.CQ$/$V`ohba;`+YpoOAhAXH(?Z+7Ee1tLlESN2q'uT^V?2N)_)8Kq"bDU`[]i+;A@&`#9UgCl1ok_X0IZ5nDtsu/?%u"!(eqYMTM-g1dS:u/33&.a/%guoq!dH677o]SWm,,JJ6W)b1AFeMp&h9g3m@NE9S>3O07)napLmj#AZT$,Kl;r'CLh^_S&\-UU;.ePD'/[5O#tg9rnT?0Wuus-.&Nu>k8^_C<+nR+:.r"G'o85AISEKBRHS\]Mj)'c2(b*iZK4JdpC+u3(igded3PQc"u`m\QrluLCik!WP2*I%^iKg>hgjOMe,@X0J*r'`,Qap^."jt^r^4ocO5^2sb3?\b$%)\W"M^1k1E8O5j +endstream +endobj +702 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 701 0 R +>> +endobj +703 0 obj +<< /Length 1972 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0Dhf%7-&:Vr4iF+C]&@;4%9+H[XY3TP"Rl!d&G"'jb=e]CMZKP1ah!D3dAP;/_,3>WQO;EfuO*BJ%^\c)2iR+]MG[Oas)MF"nCN+c6%kR6`&&sVHo.;';rZGGT^)g'F#"&Vs1QOQVlUO(IkGN1jNWtnqZq=`+Nn%[(1bV!LoTeJ:.\u37`]0$#bCAN)d:@:k`CRoK^']>P(h*egI0hP)Z-GS`&V5-./[?pm.[T,!Zas7HZjEob/VX5QD3Pl!/p8M`*AXA2+(tOJ$7i5YshAH]f'V+&@m5D#kn?C]&h'SNYXDp!FL8G2q,Xscb#AZh?9Y'Fe%'ulhu69Rop#R-2T\puk-fUBSfbbLrVUP"]jqtiL%15h>ATnS:H!lXf#nV;K45t%o"YY^K)9XEh'*;E6s+nKnWa+?]Ja]3*:/1G(=iYF(>fg`g@HM5tXq=X)k?Off$HdbB?%NP^K[#6'2-%s??O$[)l/f8*nOsApoN.bkihN=`CM&NXuGA^,OBQ3-EC6Bc^U*(s+W0Er)4\Wg#U"gHI6L4^H'j8f9)PDFN4O95\(j8h8@@sCuh$;*=N-,\ZdMpH_,d-Vq_U0,?/XH6>MDf;a)@&l*`PlsG/ug7dX4d^a0t"`il5[WYc1PIHB$j)uW[Uq:4TMZj-UNX]+#?'#RjF&\I9@tCe0[U9YBl3RJZ8*+.Kn'a]UTroJ//8/NUj)?X1(DPoSVI3@$W8NYEesEocU,IhlWeXV(@,H48s#6/mqV/>,FcD&!r]DB!kWCD(,od=a`sHRX_4XV!&_$A7/Mqd1uc*hFe,dj,Sj*L#^0W+#BV\UC]ZIklV&dpTtM'ocq_aCQHt!21$KKfXbqK,IOQ1./iAc[CBkK'O&t<9a30rI)(9?*='Eo%QW>^_ShC8p$0F55WC_G@q2Wh5DR8n=00_P;3+n)A=jS]pbCh.FlTf3q]a=AedR%pGcn2W>00[GfEOW!?IJOeU%T"/,Z/liP6eSF[Rp:DfI2Ft'5hJ,N>eUtb6VPjp"PWQ4+jUs9g676j(\_@ipMBtr'!@@9I#5f5'4^jZnt)$^kfQORA-Y"22*6KV_dPVb'qlO@/POVaW1;h2fN,YA>,G[tY2+M2X]MNaE=].6I%ps@`[6>Y60D%kN_r31Lq*6uY[/(=.Yj@mbmh5d:hroHZ>6%2X4Fg.]d*b.We%WAO8+DmP!:=/]\TAbNdL[74GGB2LKY5RLiM`\.Rgh%IW`RM')XePDW@l,7Yp[d]p;n:D#YUdG(a2/=,a5b#+Vqh`9U:b,'$BjMN#(Sbt1RSUm.Qb*)3X?7r2@S]\@p+i;0ulW5Xqg>'E9@R)3)o!:=s7dA$b;=OO#KLl,nWq'4(38;W90$YItWD'P-7q!I"M%1jaI$d>OBN`4Sfk[0if.~> +endstream +endobj +704 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 703 0 R +>> +endobj +705 0 obj +<< /Length 1772 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DD0)1+&H;*)_=^_^;p,dr?8=I-EHBD_G=e5M1j>Lf+(UH8Eu8.-43bN)J\dM/0QoOKb[cC>g6_!U>qN\s#,=iC5R9V"Qjo_Y4?s)bJ&lS'Z01m!fT'eOF5tFMZ2b*me/2X#`G[,fmps.'WRrnE[5!ohS%lu[_:@RGnG1T/C'9"m1Y=s3D5K&H=!h/WR'+1^EIN@$q=n[p&CdU)_;O&J!oHJg'tuQ+56mca*d(`G5i8fN-_?'Y&$T[\eK*uR[2`W*s?d4<)Q_I\?/[(D<"g]qjd14H*3fu*5'X46rkE`T!cTj%'X(\^W83+1HrIKfe\[(7Bi,#kQAk,8:S/HE;M=d=[uIE2G?K#Tc!9mr!H>,ZJ3oJBVi!C<=JUe2(=0AkZn)LNG[A&J%jmF;k@CIhWeRa$IueUkX/)$I[WBslTgX^aP_NDDc\d,,V6E1Ol)PSRk@@#fFs:hcN/gWenYNEJUgPoKdj09(gQMi?b!'%:DQM*8JpppATfC:A`Ts!hjmAtBr5`.>pZ>YYdZi#\[kcrd,H;b#*?NqBj1kUP\XqSMD?JQT]$8e-:FC-JB2>Ggr>:g/J??kJ/b4kJ/s'[jTuD^;_iQW3>Gcc:r`Oo0]1)R+5[0fjOEWP.de_@9>E/klYH'UI>3&G]XSFU#Cnob6:g,m6Bm-D+%qoa\,B!b[>ai'L'Z:hG4nTM4a`:15ee1!0;CtVAqVTVM-pSQ@47PUU_qH6/6kQ"?]rVLnI9M&4'gE(9,nJ]!TCO;FGM/EpR8g:[TOmI56Rs\@l<:sLuXZ#lWa6Q>4;iKL9'`!=r4JD,DC8Xt)m&7D]SFSY#(sl_7MKXWg\>*PeH!8LBXens&TF1MTchct=c@\LJlm^?\1U-M9F^[H]Y?E'A.tJ86Mj%[ibAX=j]mUKL(K_!KVW*Z*<`.OEF@As9k/eZ0eN9\&l(Hbe1QLRT*=@73mR#p-[kR6?];1j@@sn1L>^J=PgD"Rj%0sAbGF91;QW/7l*m5i[J]k(YD%+_N\rK+^rfZbQ4qVAqS**Ijatp&AdP?A`eP>Nc^hk/YJ(BGB2Qc#Fjql>9^G+i*D9&]e1jJnX`8ojHF>7ajhNV!QB]l7QVeT1M`Cm;iI/(4d]T=jM-8%M%+qp8"iVUX:UZQTaI-3=r45E\HR/bOtqR/f5'$5m/h@mn,^kC(i_.l-N5_+tqJ#j8Zbn[\:)hFfXe8Dn,$\XQO>YN/5L_P&bJqX/h161T[^agZ*E^u]9\m42[[J2491B%e_d"U3~> +endstream +endobj +706 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 705 0 R +>> +endobj +707 0 obj +<< /Length 1840 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;d=``=U&:XAW:uAA*@L/R"Vir"KOf(]s9ME(YM=U_2OMrqC-j46cSc8D9LPUWe:pQ;W(qgIp\Au'#Fr1$SGjsiP(a.9]#N>$b*'>46J]WCTJ<7`WqPJ%1BFld#*tKa^\&6&'1TScU:H0rJYujf?,9rgRP!qdOTel44;R.EJ_8Mi+UmR&T0#[@519dJj(\qbVrjlO5(V#4e2E&:=g`5@Q8=kuR22Z(9'#($lLll/i"@FccG7Dl9V#C)Eo_nDuH6*.ZFpYoFSEFGVrS?s[/,memgB"s\au'X/nTN6TLETid#Z*HXQ:?!iUphkXaI:b?[,_Tl6AW8b^-eXeC$&kE/7_CgPgk$X[?+_R;qW6(l'$;CG7(imL"'C0(<5&6USj`0UTW.EHIYWu;jne#Pq,*ktc;+V*YCXR_3[c8]N,'',3JFA5il<$'og?.=af.'Y$pb/YkpJ+4=E^mni%E@6CR7s=#o+^QX1DqjK,PZ%J#1a&uRaPO(>i.nm:o@XC9Z)W6?cHNSntFB;n#L9CuE7-PGlYQSWdnbP+&@"o[dK]=<+q[i>nrrn.*ogi=B@_:AZ,DO&D4+4X4F-da/q8$ji@Tbkcdikt.^Whs#1nQ-m(d%b!7A,F-r^"SOiPmRMJ/BaP98Ie=oVb[D,X>[cc9]m-%P*'C^W&(E]qQ&"?G;"u11pMNr?;\,(,H6oN22/jHDs)ppQuH$MoCL>bZhf6OGerQ:Ta;%,5m8&rFj3)L:oFo]3K>COjEJS_UA`Fe,mb[lm3_W_&i(XdcZHfs2o\poRGHj^tXqr@6"qdPnkidF_J!Xg&93;Y:baB6/N_j7cqYqu][`[lhhN]8h%aZ7^Xdc/efrXgB#G1SZ/u#qaaaJtOu-0iD1.'L>n1cJmfs""G_$b]pe#CMBlqL6g)jTs'5OD+ufpqE_dh90Dc4.je%2at2:dOBu(2hV6$AlnZ6(aPdBPmGWY9=sHec=Yr#GcgB]XAPQR[Z6IURcrsaSCj:b(YK$15.([rP>!LM2c>Qu=6+-QE2FPQlrj.n[^L/?.b=Da#SF%*#Q9(C\r:B^%rtp+M_YM%;i,blfq8f#Oou/ur0I^6(F5CSni3U<]_Yle!?)=S[Ko,*i.ju@[gp3;nLHcjKpO-fVBT]gGel\A9<-.DAADj";^Du~> +endstream +endobj +708 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 707 0 R +>> +endobj +709 0 obj +<< /Length 2056 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHMgJZcs&:O:S\GSapX)W'&DY8j`CAi/!e,"8Odb!EDoEioRdErr!3;mjPs*F<3f<&\6Kb-n5+2&'?&&!4:IJW%b@EWC/bc9iBcMT@%fsFqWk]K(!F[ag+_tu.fV.;,I)O$I=SGc]s$qX4Bl*!b\Bj6H5p[/MG_/k]##_^;b(!rCMA=QM\mq,/F=^qfX/LDq_)Y//7?2o*buMmKC\VE8s,f78Ppp6hgCs7^5-Mj]A,Zus'DpR]#&7*EUs<)6a`@\Y7hQGVK%Q.]dRsptJ7Q-8TYedlHHUqHBXHNHc+2(Ul>5A2nlkaSCDsaqP91*O)m64f-tp0\^Ufe[3Tfh=1hf:/7r^T5R6:_=atY\G*>-WLc\SqX:jgDgJQO\7KudXa,=.<+l=F0)3u[X?Yrtm_'NWG/HM#[&."n1>%l!@GNaseMb+K$mFIsl)psr>+fLmfsLjWW6BeN\^qH85e]NBiBd"CMeiihfl4Zm7%lgRdSue!EE'W!4!%`N4EV*nOjNf4Z#'r"+jFQ;#Um_<_Nj5AT,>7Iq):H3M+5(P\L!Z.5HN%r=UTrd\(B_IkTNYraT2*;bXtm#)oC>8e;J8Tt%.`B\CZ?rmZ^9VKF[WqRtpR%PB[Lb3d8eX/h6A&_Yj1<]:bnSlS&4@L,,K!o)!Y*@&f:=5'a_i;%Y'%9j_&q<6TbEO1PL9!?@ndsAHTM:aqXP[8gI_$D*Y`naE]E,MK(6U7Y'\u\.]bTFIi@]KsNhLcFJQ]d!Ti2Hi]gUJE0-q]oDSm^!]1(ZoY%8pDRTBB,q*C;T_^2=l73[Fb-tgLqL>\5jMD&:!PTRep8h61?X*$\_kZQ:Wg'3RLQb](<%)@Vo,ND)4j1W%t?Idp!5dc?F9,aOugT"P>=/$k4G'H'=_PE>A1tls=h5>#HL;c!k6U/IqOp5%#WjG!elf8r.*g&D:W&0'=D?dK+Xi87)qT;$N3@dM0K5k:+bR"^si'tPP&k?GVYVZLMU8!3Ka)1ADsN]aG5&sdW]Jek%Ph3e]DR.2,%3:H.aaOgg5JXI%K%OB_Qi2iq./R;Ar$\`lK/Z8`b3aC*7eB^$gT]>.Kn]\%V33-r,M!(X8EM7!n`i8$W[CLi9T<7%u![s,0'8Oq]"TRaJu\;'07K?/2?#Z7F/0O.fY7)7u`*%^$ZF[8J)2OX+[cB+9TtF2Nsn1E8].DQR`of-T[6`'R^^n2dXaN2cXC(QF1q,%B:9,5Mfe#iD^IKn2;5Q7[KWJ1OejOW8S`Z4PR4k>BL*k4F@_PK$l5XoLq2bMTWf*K$O;.D@nmfL"`5Bd2^Z/5dDuC/k,#"SL]c.*StkX-sjtmWn>3FT#h$2(c7qj"T7Si]#>2f3R3MJlos#W89[F-bTe4C)]VD!U_H1@XE?A'Xu7"8.;+,W9b&uZemZ;%CI'A_f,IVTt:H]5TS7b(9N4:%#)=\U7C+L[O=/@>kjQGXR.7W]?>='415C7`XSfJcd/U#L2Mi('[\e*o*dSe*doK-XJK@4b#oohE=5%Ene[c\@1#090Q]8_+'h^'ZO]Q1?"TH6-YHn,NjDR%0hMW3&K6K+&CW?S'l:P)JHdNp,'1boqEKWAE/1R,_NV0)'F3&@TCpeHhl@]PIfX:!_t3~> +endstream +endobj +710 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 709 0 R +>> +endobj +711 0 obj +<< /Length 715 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=(>Aoub'RnB3?uLn/\2M8:U1mMl&Zt@s74[[\[=*#0,ZY4^fVH^*T=GXG\[9i(MiZ-YcM*ma-bjj[UB\O?)h-;S`i;N;X?U1(VS7,UK8=%4)rnZIj-^Tq+pZ-7UkM0CnsBZ>L[E?,hk!sPc@HTiU#9_+>=5NDE?%IK,b`#kkKCKZ$hU[s)bR9e5Oq3L6&*]PXksQbB_YrHM6L9t8]9r#4-2<[_>dMpba4;3/HjU//nRjWUb4%@2?o77[_S9k9Com)(pRRlC(ZI#V^t&h@)Ui(51]:sAdTRkO\H+5c-O>\&ug>SjA=6(1a`ZDC4?dDZfZCV5EXH\W"O#*Ti%.dEo-KUeC/^`MD(5XYZS)]#YVm>V(j\HL`t9Z-gU3"Z]J3eL>=6(2Ee5A7u-20DEKC>F[bSuD[c#5gds;ok2bmIC*@1$ruW`LC1@h7OA;ro;RJXYni6C.li<,aeldpZFY@SsIGkrQ0e1O?cH&;?j/pb#bd%f,nM:qC(&up`A#"%;jF4pQ$6;/]b/7`6`:J>*!< +endstream +endobj +712 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 711 0 R +>> +endobj +713 0 obj +<< /Length 2497 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^gN)%,&:N/3E:T5e/=HPE?96QZ,\c&@1[VQ/l9<.3J0`3F,EW=)q3(9gGBo=]+@(g]SP5T0:a-23Sis]4!:%gFEdHkP`3gV$[[K2,jb-_Gr$.M'iB,p'D8JDN[^A:_LJ]_+.).7;@N@,[[W>R6Fk/[)*TcVLPDF.H%p,#"@_RuLQ`82lICbLfBSs2c#)VJ_@B_[WC[^tBo3i^oAZ@>B-S'0r0s7=3?:S.iUpmqkW[/s).T!k6!4>>u?+FdCaV32I&]L1NaMCl.4Nc8"f*5"epYe!gAiF)rJlYKA"Y4E>%NLbu(%O0(7D'?!;Hl5?09$6^X?dtm>7p&G!]bDN$[f5u9SI;Rlo3"e3'm>`OU'WO)6KWbLDMVfnMa1GRhfoV67`++jB!$_\@/-#_uL1C%4kt[@.*/m3@qK--scuAR2IZC+USScp+XqI1QWT7N>)^*RGac)TDtQ_CK`N/R`X@K4S4tekA-`.3s&M[V+)0n#/,[PW8M-4V-'h,fo.?n!>!U\Ct]a#V3$]>%g,e^NV.F:U;c@/`MW&5rQ1*,T0Ot"reHn@FsA:uaJ`3I&#Zi:T>5C7d6i,[<$IfM7]>6=UV=\<12>fTo"_]"6l'.0XW1GFcq2k$23@8T\S3;!%H2.%Y3IiK=.ju2+,j4kVra)D*7-OFX],.,(7nHc.k?^=E\49?*Y\2l3Oq/W+=eZSG(Ub5BgC!3MB&NIhMj#i&'>CK7#g`B9+H)8D.SO&/GI,kDg,Rh_<'f8+9gg@31aA*[+G$d7;K\\8sLltb2Zp1cg0p*%TL,Xgtbek=-AMkL=>)n\^Yf<%(YgG\uh]BsEt^cZ2$'*,$Q>5"%+XeC\0J[Ap>WK'Z[uS>eZ\l`:e?+c0E,j_1\M5XLQ%IO7lt)Rb_*OEXT)8\__!)Idkg59=7*P"(gm^]"+>L'+>%llo1/e/Va(pRZ>J7BKQ+3Wl#se/F\&`IfA6*Z[*Y#o5Q2&oDQu_Y)0dU$e:'V69uA'c4]7RR62RD[OYZ2PB"Rc$SlIlW4*J_I@$n;U=ScWq#\@6?F8jR7,lORri>dmq.b:'?:SFpp2UAYC<&G^+$32^N!\9PeI.HI]:%s6-i]$>s#]20Pk\-n3'D2R<@j$*9UQCt0Ksp4,3,"l#/aQZ=-XJ`9#l!Zc9QYgP2-Wff\43j0)2IO8&[*5W%O\]#;+bXXjNYic1X(<']KC%VI))epSe:17YS,$fJ>2%))X!0;.SptVl7b;ArPKLJ#P:!cK`hn!,GChpTdnk@+me9;2Th$V$;`T;6T@Wp6"8D5\@<@gO(snS_0IQq3au%YO8-H6.*'M/o.8dVN+Jl/8dc$WpE2#tPrkd9G$t<9ni6QU#GC:XWXn/kQsO-dG6Ui$-X?5]9FJ]YQ>m63p+>iE),`(AZc\0&uYpJ$do*8'XE0(\81BK]0/o'e)=8L('h/=%gFZZqF +endstream +endobj +714 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 713 0 R +>> +endobj +715 0 obj +<< /Length 2288 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHM=`<%a&:W67@+,q(Yo)=f:_Z`B3_4$jN^>f5i]O<@Q*^fc.QjfBk`>11UpRJfP+lKr%M3>o'$1/+2:hq'F42B40^d^]%VpWi6@\TB-a^@A*aKs"E%L&P!sh'jE!MBfaE/,9S;lcDAh;VtB0]hVo.qu,,gW&j%-^A92PJ(@ZLQ^1dW/s_`OYH,c`/kno;b+gm'?&ajrgQW.A/(\(H#sCg8fB8gjM%(*q,KFN<*^#s#Gp]/g#8c6^kBl.'p$a"4c\Ju:*_tBJRj5/n,+alF_'q@8rHuOT9&cck`)2i=@>H;th3^SD'$]']GUhJnR,d*dT(GU]A%p@uQGKZLG$Ik&K8dN.K[m+EBKjqF\-l:fH\_>RT&B5t[ILTOi/nId5&;j4Og,Qp.BHRqu_!YgAb3sGq-ag2d]F#D!G7)65V'e4'69^)ms'3R:n;q]A\R*FZ=2A`,[+BQgHE>3F*'ZA,S(gg>U#>:O?/ct4-3"f>Ek)$2T7#G:]^;)P"$k_uP*,"k\HD(TQ,!)]]&S1V!VI<&%-.@")8p,3L"@GHTU@MuLP84s9a.>.:79uGABahH]&@<0sJL2&/0?h?J*9%C@>`f)W5gFMVnS,3CDZ[7)cCnB:(cS='Z[H`N%)\9;i]&cH!jp2B"BG[iV@^,^PXJ@11`rV4S./t_$b\Aa:u]0n"VCG0-;B`O*12L0Bam8"rCi.&Dt!L=jrmk79nImYWhdql=9qrFD*:Fq\6Dp2H/^pmE!jfCL1''DN&rjAO(+UtJ>7qmJAKYdq7\\lQ!Ol@%FBdS>TEmK8oaD-ZUUB:QZ80GV>aJb'VX:=*"BCZ7YQ?2GR?ch,FAO?@VI@,LB\nY?]\YJ=G/6C;ut1@[+c6bG>UoFA"ehdZa&&N"EQP8?(ddCD1R]S+;J,-**M#a+bOM0folf(;j0A3P;J2]-]t]hX8W@H*goM[=bFM[(m0cMOSfQai'=uL0ETK>V2[IM>srd0@,77h5b;*;?qjCI&"aaM.*-bkb5FrNI^dI$h2-fB>C\Hu<22Hd!0@!GS-<5Xl&<;5[N!S%T$+V`%-^`N1Vk!7L``,%J7s4s_sG&O8mipn&M`AW'X\)J>mV]&oRSnOp`jkkAWuUqP3+HL_HZ-hSUVunKE2otERSi8`7KL0&$LM7)N3gq@D2n$MgKFF\bARc4q)B+kJJ6g1q\FcfX\PNmurJ9fNQ2S(G1ssC%<%:L]DMj--S,j3eVR4adV"9;;l5?NZ);1LC+=#P2Ko-Ju4dPZ+gRJ\Gj=5C4M"43H[:H#ddG<^?tIWbXlA_&WB2%*bLomVFeGQ_(,2N*"`>U726giUg6I*WDIB#7;.5k@'[P;h2OZB9jt1V+nh(,QFB"O>>bj:5u5r^`k&:qY#/AR\mD5&Qt>aY^2j.^!642R"EOr')sr&Qh4u$iC;16nQ92C-#MX'bFbH*SmMAC=\IIl9PcYr5'9"2"EieI2I5G22aYhMGYq;kieJIeaB[8aVqaj#eGmCgBPGG6uS4!0YR34;ed7(;*E8^(<`61bG)u4N`=9NACIWo1\c$YXm8G!'7.dUl-/aeE)1JHhSDd:!0CF<+O+Dhr&g]eSBA>6T!YePEahR.Q+HpajH#jl#mHMXj;l2;fis7UF.LX6Nc.VkNW9Y]3RL3p_LJ%\"[)#/`\?$0/um:t6n-1e+$E+F>Th!QsNhZP@H]B);N^$RN@O-Ks?K"b_Ik@)M/E>16l+"2)c8->i@<<_Kb!uU(0_R4NS<6JmBGHl67?[]B5GZ):tJZaD0*fHN[jYu1R?le*N&#UGp?s:c$0;fK_\VsJ\?*KGqWZ&S8](WQtCTKXV#itq:F7.=%U]s$c`ER!eWi_&=T-7J$0V,brP([S#.FSW6ZD0f<+3-%:IX:0-grL83aB6a-#9*`F[nm%`i,ATK\S:]dh'Zs7>i_)+S=4MUVDeSY2K~> +endstream +endobj +716 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 715 0 R +>> +endobj +717 0 obj +<< /Length 1575 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;d>Ap9+'RnB3T\C9p$C$t;)HE&#Y?0G$'1W@uW`nl/"NE/G[fsG!r;-s@R"IP0-gPYS7C'c1kOs>]3Q(dSSc/?'t5-qm-dmWlXUs.:9@Y+@e-:+"]WWapKZe2@O>u%[f/X]tO?oac-*u46Gmb(R$^@g9-FXsX2s2DFYp$/Zs6J16t17"8&@`>6J\]G_6VHbosOjFqMd3K8>D=$%DRHD:q\e>$!lEBqh&a>1"@8aBnToiH6=o?'U\QoOtW!rJJ\#XY:E(D)THE77OLNm,?$YEO!*FI"*r!IHb#I=iFK!>6qZ5K"u',@2Ir"rTu[d;6\*.-$q&i7M43&o5G9F:CGk-9F)!"j2UQ?qmZ^CuN\?#-XOmcW]=@Cf0!=Z]_B"PK;MKX?8A(gV2!d&ZhQ@&n=g7f[PX_q"Z8=4i<%K2T:lT^9`G!d0c@IB0&%[(:kdW,bmhWml:-.8rSKj@QWVN6M63/?p;lX2"B)98g`Va7!s@_1O0R\Q>mduA@E^ZK"sk$pfgj[;%D>9g*:,I;c]1JXe6H>t[6)cZ!/=G6ElZYb-@FS0@2Uno!-6+D5=rAF3HjJ31t25<2#,g5G+n2;]&#(C^LMcq#'r;D^u;bl&O3S0GTI)fS)nKoPG)nM8W$f.00MM@dG?e0$a-SjLZ@FB*f]Hd=^WdOE\4=`+fTnu8E\_nQMfZG:G=ZPC0KGas""HsrQfEF2O-F[OH^f>chadk/(grlkI5Y_$E:AN[WC'S1ec2j<]U3ma%S"<2>7*67H^[L`$+`ViGQ@E@"WdXHY:!,Q1LRcCt,hh@U@m1X?aOC=#opO%J64k^9e@E6ll\(d&\J/G!\s7S&R%fGL,[s!p$c8^^a%We#a\nh82)(j$+[io8C9HWp*/K#"Tr3[U_0'Gq-]5J',+#&`NgmiQ0^-/7">g7D\F+r@-1[u[*Ap)=OYg1!NX!'4.#:C5Q;tIarQ5':7'AVjMc5sE,1C+SE23W*;k3u1alT1=8L3:a/ei'[6`8VV!!mel#2+pq[fS6Nu)"1(c">gWS3*oAGnfeIr>9Za)gsAPV2;=NKofjOf8ESd^h^]$L(B~> +endstream +endobj +718 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 717 0 R +>> +endobj +719 0 obj +<< /Length 1842 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#]>>sQ?(k(RKp^q,hXsMui'4nN;[U!8`d"c0SPZSLC3d,7"V"E8?FtWW%9=!TpbRQF&I$+&loA,Gf?`#28I0pC9/_cXVS_1sR#r6>jLQ%n?k=:QoLS6G.)IH:&CP?DUl.ItAEdA><8X8?S8_H8n2>8.=[s+IJYKY2UX6K$ETDZMG?';9]k2i$$c'\LE?.@^`\mI.+((?:YU>:F,-#O&Nca3$;o)(24f9m++TR8Dsr;D#(V]RQ5cK?Iro#b5"EUra:c51U%S]59)R4D1^XElujkGs#[ikMMtjVQZqq/ridZ(QTMBj$ku[t'L7jM)$$a_f_iC12+1Nobs1-)87dA5l!hVDJMKeCX;\TauIMpl-s$G>it`6kVc\3_O/n/?VPilRFi;2Xi(?GOtgb-:q,r)TUlOlKOUfN?i67g*-Z]]WN@u!j7:*@J,?s1JJs?H!).$IpNhtj"I/HZYQ_cXW/+-*>d#l(f6R;/3]8q";4"2S9-`,a,Ir\?3mi/2AN3O8I%!FC/.<_8^5hs3QWaS#gQW%jbsrtXE/>Haa>6:(P"?C*@>dW/7B]`4bLg_dR^Y/=]@o;:^rmFS"D^h]FPpg1,ES:49J#,=RgNO@+:>XPut^t3!p7:$I.5RnVU%To^:3_a%H<"#\s93StnJ!IAn-d^WYX%J_YpfB2]+>7-pN)E(R@kErrK[hOe'!fXped/CS'j@gpWR=EN$!;$/N@8t&cjGka&B0c<'OUht\btLa*oJ-AL0`-70_XC!Njp^MK)L@,3Kdoh0M.IXWS+;,q\iLq'I4&a[_7%o7%H9Waco0iU)Rf'\IRWGlQRVHDqRJBY/E>!5gX54NcopT\#Q5_j#RLTY:Y9ER0$@S/6m"4DVdpb^f8dmYrCF-OCapI*jQ7."4HT.eVJ$_I$SW@qf^ZTGV%VC'.U>j9C$R(f@jEnFhb"B8HQbK&[u#QSU=LegSF@ipedlg/-BHK*Ld-O.e[(pAC#M"JC5==dA[#GFP%q[5nV/6'."[ZqRZU?/U#;WgM(:eCH?N

;ugK[i:'7R&dAAbah$k+YRXn(06QbV$S>4,N^hnGL1Cema8KLbS[j]TUtXOtTa6!W-9coIJ,cg_?[)+C\lRS'uD[#$21ta"eGrjmjF_Loa@KJB&e4F;dcrpfjkT'ZZd5'fp?>4tbCd(O46TG7c>AlG8i,7`(q"R^%`Y8Wq$WW3QSbC)c'6a-i^G$-k_Y\R!9QB'pmUn/MNUaXr/$_3@O7op;A0KUK=-jr8)a;F&*9BA=Yesg/U>39%!IM[q)U8fX[!A/;a't?gSEsXge,=-,EN*ap^0%jFUUt?[Y,0a4plh)_p+WPKO\S^Q[V!hhU1-VW+Q5$FC[?.rM!AZ43J:]Hb8VGTWJlKTb^fl$57hq2SQ5g&GM.7mH'?:Cp>S#Y&C=6!gLe]Z;I$Ig64cN,=hEG=sS]D-u7$'?tNP2]B'qR$X1X,cu=EB_^mKlrIQ$8CusGpOX1/ajQ1=9Q=RLR:s`7onF;jcIiYVP)aDkmG%B5ke6-d4o.0dGU")ETmoC6S?C44C>2\/[iTb=tO=k"BBgA5\\R#Pc$SO9*=\38lMgnuZ=S6`DQ=d44G[b2E*pLR`f/J[#7Z&p>l0&=tXp7kM);-J]#3qF+n"V+8/CP=\S"\eC4Ye%B$bpc/ninRu=TY^Kabka8)r!F"jQfUBe`5'jr_sN,'C.u]G.h5K-XP>)EoMP_2u97X`-fcgqY=Q5GYp^*,Frpm5ds-BEV'e<(3V@qXkbE`[^>Zoh9%P6hGf?]PbmOWbGRUQ%i?!VM$RRKZ4DC9$@GFHuEG^`/Gq!qWlYGq>06#dI"NR&`MrVG0p7<%WAC2#3eM/F^2hH+`0Y;kYK4*3r!5Z>7RH((TEK%S]lq`5%eQ,^*!0^W-3$nAVb(=W.S`"e!2kAkOSHo0C"j/SHdj]a!-02kmd?+k7l\\O64[k27WZr]Aj'le4M5O8QX4&CWDF`&]YsD+Tp6&F']rX5(8sc9jcd#9!_DX_bQb4S-8gJa\fLK3JTa.S#"8C#9+abBJ:-5+TmJ7@J&o9Y53UF4/1S+rs#$OE_]r:i.Oq^`l98$G;?9aJD__quo3Q>bE!fJ;BY*AE:U2("*iHaCt$@ji9p*;.b#G5H/2Dfg%$/71pfuqmOK!U2TlMH^I@g+b)$/=pb\\3@">MUI2X.Xedc*]^TE6V3PF,qdOmY1EZpL%\'T""cG((?p'u+8p:G0W`+^f0;CX%Fu2/1Vfr5F6EXG'N[`I`O8.&28&bDB`$3An,gKKYe7]`U1MGDJ:l.A]"3;K^O?..X8.k`W7pam%1FVK,"/Rb>&(Q,n36g-a)W<0D)lZAde%.m[HhE(5d(m-_B73+$p@7![q%">]eGo/Y5.4,Go0[R1sB-?Rqh63=I_+"lARr6d&Bt6cKqkSmYH+'cCuU2m>JVP@nsa4I(\aZk[BL1>F=t)J^as_-h/'?HpBc25G9M,*.?S3,ZVm?>k7m[AOVA0.+CNXJ8LiAS!BJr6*h#?Ce'BNcXZeOmWK+]jF7;g\"fp3].Jkqi`EeK!U?/5ch(F^(AO"(7K"CgYP0.#'K<bMa(b4q(K_LmRI~> +endstream +endobj +568 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 567 0 R +/Annots 569 0 R +>> +endobj +569 0 obj +[ +570 0 R +] +endobj +570 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.29 87.738 250.29 77.738 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 213 0 R +/H /I +>> +endobj +571 0 obj +<< /Length 2330 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0EgMYb*&:O:S#^L+c`3SftCf)"I;^kB?n_kRA!t`8!@.Y1C`0eVTHp+js*e\pn+O>(4YKd^2Ef$1t012T7&91;`#TR&QYK/BOf5fGF^)bbtr\.U&E?8mmh;oUL.7o/`tAVi:@+YB^;h`\,lUl2FBXs1lA]okb$8gBsYH9[C=ZDTD6!n]?]I*4l=gbB=#N3[K=&K?,s&n*jN"2\lgFCHZC(n!SW*7)G[sp0QkQTd!:ss#f`I@_;t.aAOZ3kWZptuko,+g@;BZaN^%]gfGt4E%$-Cd8Z^93/2'V%4^MspKAI/%iOQ+/DpLK)DOBHdmYOTq7Jh&4.*L`&A:f<+Cme`q.^DWL6pGZR3?.IZA*Vn\Qa%:j^85up>W_.6e'oCRZc"c1&f(C-@V'V(Zg^B_EI2M'ikLgpXBob;g.S`'c1.LqKT2d?E5#O(7ef*kcYte[F[Wl4.,,P#FA#=-BO@jO75%d?9Q>qk-jNr^'CG75Ud;(/r@MK6/-3=8QP`RBT&^5/hZ5#[gDq].=k?=a$F/AHZ&79I@GVos8)FccoPL+#N>B`CJb*$5;!a@$kZh]&Lp?QOT4h3@G3nn]E\se[7gm6%g+M8!mr83Q?E[p9!X%/TlN(B.N6h;mp>olXRSo)3g7OV_hhe4_NNmn.4UVb"`p9\0b?\bKaK#?;6_B5/AS(r2u5j?@B^k&-V\S+_C\5.bdN64!P5(38B&Sn=Y@]>:In7'-W.[U'IUq:ET5!:*76]^9iAbb1DgC;%$_RUV+V-+<%6"U7PLm;#W%cGi8>q,=uCe0QiRmE^&TNPGX@\medeKB:3rOHi">Y\Y-u]RP\DOs*&W2[KWKKe^>jpFOL8ldZ9NR<_73HlS./)2i%XOoe.s]uoi`]2#Uqs(6&%1-6Q>%#_nh:^.ThNR.O69W;l^(,5puV+<'KP&aOj!Q7Cl*h(VAM[MF5DFDc(5WieZ(f>W!MT0Obg9!jp9]65O-tjnJr]rHTCIFQ">`aX[3O./0F)^ZO3"4#kN3fJd+Y4??3L\,moKZ6b`o5KeStgn<]m%L2Y6F0^0l6%uKbVq$4=_-F6m#@c4e?:'eb'GB1CRVs*4%+.H6@c:iS@1!1(5hj&7\-s:oV0;(a(*OfaoBG/hS5!jGD"efH-r[9eO:(GpjQ.>^IVYRVZI(0L0_L8r>#.=C:8@>CLH=Tdj9U53t]N"$-1]8OQRbf$peENi@GSbV3l'+nN?sQ0g7,Oqp[C4Z?'5\jZc/g7.0,^Ci=(U!b-)RJ7hWW*t\etW&E%sos5""Bt6\A>aF*C9%Jjp1Y8?6#LAM2q5`JOr-KMZMp%Wb4/o$Td2b"acD)hNeG-%PeT/E"2AB?MK3nr"pF"oTdLDZ[7B$=P*p0+;u_l4,H&UZH#;1_Un+V/3=R%o6l!3DW\DZ=b'5aL1r+g2u*ko_ldA1HbFRkC[aN]?9 +endstream +endobj +572 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 571 0 R +/Annots 573 0 R +>> +endobj +573 0 obj +[ +574 0 R +] +endobj +574 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 194.48 366.728 239.48 356.728 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +575 0 obj +<< /Length 2892 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>D/\/g')p`p@I0==5cs>-efGT^DI9PPoF6Xu]8NI,BUOm*OXVk23Q^90r/KbRbtt4"`?#@#&hT6`B4d`\36KR*BcnU3*]n9Wh^G]sGg;nU27(nLlN*@O2G98O%1>s(Jr\L&iW&pfCa&;Y+>X"8lI4H#kn>5$@fhZQe1-5qGQ>WL"n#_1H&KiLAge?ZDL/=bRiuh^MR)'+6.N^o%QPrIP?+OmX$ui+gH#*bP4qH*J4S'Q70/Q=Oi2<(d)1)H!2:hH?T?2F#W+?lE::enM'a;so29Oa6DQ%I\m#Q!9rC4M=UMD8YE+Z.+PdF]TW+4f_mSWs!.lPX_e?RU<9:U/0j2VR:"YQRB$B.H_4j\hE(QSA:Ze0&'N[s8D"D4YWWlM[[c&3mKn?kNi:`4NS$#'JXB\5cEa8p#tfk)+-`O"ho>n(gKGS0;#V/F,>IS`\WQ)95&gVX,":#;qbm4VbJiaZR)Nf"r)Slo_i*]!;]2&1>/3/-p6`[:g[4s8aG5Cjk!>Z($A0hk0NfZ?i+h$)[fruE3XHO4HR%@"(5mZ"XKB3K683AM[USe2SGhh8P/QQ_3-/q(U@LFXcn<.Bi.uV&b@eSK'Fs;st!nJ&eOl=e>5r_0]i&A01qL\""[R%&<1R0`mJ2@7=%4U+4A2AD)5H$p,OMNBP&9E$!*4!b98)7Oma^%S"P0BjLh?JmjAH;r$*?4]B'nr(2SgiYA-315G@L(`?[b-7fJ)ppVU7/.>J8iVH:nu16OfeE&9c3UU%Tl1768831@YdCMISC;L0GVo1dJ\\YXeL695U4F=-Y`4.>N9\pYY,CnoPmQSo09,3W(HeTN2Y8T/-B&!Z0]")>542:^rnU:K4^E.ZH+#JElhV$8D,#?`\m[A7r:0MP'=f!FBM63X7>QTW1>6?R4->I^%cmcuD^uY:P(ihJ/t%gh6P?03HOh<2F+Xjnrek2Mu5a^:j4Z"=1@TC=OgZ9=q-!*=+[m/T-Q7'&=R.1=6K&0KLml,2TrDE$WHW+/uh!NPt>CF5<_N(Q!uSul(,O8_=4ue)_o#[_UP:^9#Hj0LGAkB$]?20Bo2Hm,R`J?S6K%S/$MfM$;XCW?Z*paLfa6^#Kb1Bb?GQK:hF54)jQ*a%%+p,Ub`.>lA'\Q7ebCaB(-67U4Op5ag#4NDI^*5EO\u#7X5WW)J,V.W5Zmc3[(lR"-9Z]LY6KI,M6epX@"Vfa@&@GH#h9s^3P;)d6=90GfgJIWNfPY0?I92b\p2-FOtj6s`$8POeM$m/>M*O.]cR+G:mT4hGGkO@tN# +endstream +endobj +576 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 575 0 R +/Annots 577 0 R +>> +endobj +577 0 obj +[ +578 0 R +579 0 R +580 0 R +582 0 R +583 0 R +584 0 R +585 0 R +] +endobj +578 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 369.99 575.922 421.65 565.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +579 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 457.22 446.922 508.88 436.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +580 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 435.922 181.99 425.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 581 0 R +/H /I +>> +endobj +582 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 188.64 359.922 231.14 349.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 377 0 R +/H /I +>> +endobj +583 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 447.2 285.95 492.76 275.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +584 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 153.11 263.95 198.67 253.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +585 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 493.88 116.09 539.44 106.09 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +586 0 obj +<< /Length 2787 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%%=`<%c&q9SY&-=DQ!&;`fi]IW'3P(PX2TeU7K)Mf8tmD"en."V$QXQC/d2',n$WcEaC*Y`7$_cC?b]lpr.;#Q`i.<-f"@**^R4)=QEHP:f3bG#gb4)j3rA6eFjeV=^YBm%rcts=,9TT@-[*s-(bO_6,"S8<\PR[Y55WsbS(4k`[M]iJ>kKU+o9D%XO2/"E598=.mW@T=AJ@W4U!Hk&?9bPK?%7-b*]]YOLA``\aM=:l5rco[Rj48f^3j%b^0Q&5-kN>Te&'T@UZo,rEe&GM&c$s&Di\csm=!%@G)%u&VkPL.+Na(rJ#ok!$'[S.f'q9,05J(D_#E0VA67jEkLr\iP93Xb38[$l5ZrgXHrefr<2W1(/1f6QU3,9%"]!JTTZ*9s(5.)DHf:4()*1&?0CtGa6Aj'84CH"q6;(.@Ya*0pYR$>JS4gJs4U14+J-\*`3Gt.%8I/*^LE!IRmK(WCN%&NRT3F?"Z`q&(G*=j)7@R8Xn8(ONn$eijq'6@":CBl:Zt026A:Rr[I=G%)g:F.uqk1GC`*i1`DPMoQ\,$E*N%lPG?Z,?rg+@$crkX0;.;"53YMOA5T#-%k#Fo4BY>WSQ*>(+("$>]H,joTU4XRT9GRoAFCpTqf1m^nT-VbW-A`Z7[j+)sJpXHR-hDJKbPZAr`\."Ju&B#mg4W6_q3.qE9`g>i6gh+Jbf2Nh`Cmer!d9iEIb&'SS=+:^8BrF'@up(jAbs%u-Yi&O[OQfl(*2ae!a:.3-]NsL8o4S3V&j?U)Z6SFM-WoJUBgI`)[>Q6P&100W<4Mh)+'PIKGN>;IHBc5!K8%MXU*dSDnaL>0ue[8@.f&SX5"k$fOZfWN1Xp@`7(&1U>9)r*<#eY@h2M"d3abI1C?M!$u%+'[AL5*)f0C+qT@ME*YCqu,knK0?<=sg;^=16UN1nd*t;9pA&]I%\$RIQ#2:i!A,A(>XR.NcOd6q.;Gl\EkO,=X^=p'b1Jpjod,hEp*XKV.Jk5PY*2DuW"d-N2G:P%YJ[hE*'l"rk7m_-.6%jK_7^*Li$GM!PR"8->u)o/VVl`HKcg*28bK[(;NleLWdUbq#.m\N1<'H9,P'GCc&&fB=lA2C<<0(T9II6+H?)4eiKXcgY=N&r%uWHjMt]^4:/4D;J?(c?8Y[AEgMCIj2q'serb2_,f20ohi+D,C?p)JMAOB?VhRi@KhXMR$kGN0['\q8rm)/qDAi@ft^:JO\ms$50(Z&--!#S3b.to8F9HAnhkZ.-uo6Qh6Y,=7iVmU0$?Z_Ns7Y1Odf'23\!.j:^0<4:ErI:6R'ha9/I4$Y_R=C+W2o[J'@dCRs)KU/D`4if+HA?hK4bfJML#km&8L\g6)3lMpT#fpqo5$nj6EqWH^':iR:h;6^0(D`ENHKcJ,ubC=O*_il5RHg;i26aroO":6d*oP2Z5)Y^(T;+iAkl,J-1*B[gJa@0"YM21QtOSr=-5/;[5FUK)`V"nXk'O9+ZpWfC'Tg/kaR@@snk'sO?'AfH3(k2(,&oa8c!Zg'0&&-?E'5U_2=4Y"iChs$u^b&^J4mn;.9!Zhq'49&s"]7dO_?CX/_81^OId8%-(0-YAcg=s3[BQM/!9K]12%>[7rFjQ>NIs"h#pJ\r22_ej[kt'Bf_5RTUF3n_fTRP]Yk>.-UmD[Hs*?FQesl0+aqs:9K2an96p("?br82~> +endstream +endobj +587 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 586 0 R +/Annots 588 0 R +>> +endobj +588 0 obj +[ +589 0 R +590 0 R +591 0 R +592 0 R +] +endobj +589 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 689.5 152.56 679.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +590 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 375.24 456.529 420.8 446.529 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +591 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 310.3 275.585 355.86 265.585 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +592 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 493.86 93.613 539.42 83.613 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +593 0 obj +<< /Length 2752 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>hfG8H&:Vr4iQ5"[P>K%QR(cktj4[hfA.g7K2bbB)ODR\mE=sPAIt%j7@2C30S[n!C@tV/bG5'a0=8as?qJ\G8H*61fk0"unZo[bI0)Y<1OUp?'T'pp_BV(3t^O(:q]H<^@e,%aApH2r2.H9b7rbj*S=j$?'G@/?@n,?F^+'A9BdX52okT't&'IA(/6@Y58LAjei_=L'=?cX3OMDe.=>\-%#]>qLKH)00Ho]%`c;5=`\''jH3PEQQDQV,XA;-NgR3FerZ1KhPgt:+))[%Q-L.%U+['(4m5]1#oqh+jJOnr\9^=MO[Et2e/5E16LUFi\en\glfH`:I$OBAP%NGfDthB_T)jU(MO),r+V'cO,*4']#$=X,ZIViNq3S=khgc-rl8Me*AaD!PP,EWGg%YSF/a\bC[<+LhgtnNj&F#Q=X]]s$4^8gD>$W5iVEOV15S%0U?(PBG"Ni>_>%A1W`"U.P(_R^[UM.W;"Zp[VQkn9+dTIH_i6\(h_h4.&r9.N0\:>):a8SVff?A!Vc/XXt&OlC?$2s#fCisPXXZtH-W:>B!-N(%4i.is76iE,<@?P(q[8mNE;KAquZ8gY;3DaYQ_Y@C/fbe0BX_?:Zl8_THs+\\f)6t(:C,APd2DjNAlQs*aPl1U^hFA#.0\o;/'D7/\'>qZOS,IJGt#8rTCNZ6EAHjA%Zp8?pmmE8c[4Uu+"0W*1jA-04]J=bKQ(nlpKc[p$jBOHBRPTUBl*D1A:"XH+Jp@UA[V=fO5LQ_Tm\,Z)RUt8Bf\$Zu&_Uaf--e2"8#]]&N&#Pg1eg!uP+O.Jn6[^s;QMN!Y)CAk)4UFn0^5gOgGB*>fsVo4@-t_nF:eYC3\jO6@/n?BcN=hPV')Bbmb:Ke-^NU8X'ue$20\F"mG!^]?nraHQlM?eSS(5Yp*U(A.ED0*SYpSU+cQX0o'rVC5.ro41T&@-o,Q6tl9bCC-@eseVk\>qQ?_'Yd?77E8%Z;`(X)Q>[g`5MV_Q\#/5"JsJlHt9aMG/C>HpXGq!PXNtA@kq+XMo;Vbt#P!c,;:S(MX_$0T9eCg6SOr/ur'VRs`oDafn"Xt)@81';,se7$@Hn[P;,$,Y55QQ[ufkI?Y;Z#H2pIq?V@$Yf.JEI]!I*m4)0QbhUN,VA`!LWl5Pb9mhXnJ*X-]Vd`r9(sB3^r-h\-Cif$o1]Bn`.jI^l$1"2q2u\`XNG\9a'l>IJ?0jBfbQ?<4't7KS5*L,BG`/>(aT&nGi1uQ6Frn$pThal:s\mkF="0$aF!0(Ggha=r\Wm_+>)b_ju!]sQ)@mI@B`Hk-O;h^1Y1lsH!2$^B:cj2CT#"2PHiZ#gX-^Kc.AdH'Vuhk=Sk#U=CPqn4<.M#0F]@WLuljrP9gBg(=r>,K&3n=aBuQ&SfQ\66o*GGBeu27Yc0:%f#!5J?$XDpp;UcYd^OU.)7n24gK%1nrhS6K.$e/$Q*c6m3rM#upA=R6p/Z[Zpr%JP]03%K^"a82[D?j.V*u47lEBeA]o9ER6;cQ=7UV,H3YgWZJ=@YC3XEVE521-]5>k#0e1^m`e>aeLe9IeeAiBgu4`*<.FMIb0`tul\;8WHtYol?2]&F2.oom)sd_LS9u*g6MK774b?Ed#C5jGH--YMbu +endstream +endobj +594 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 593 0 R +/Annots 595 0 R +>> +endobj +595 0 obj +[ +596 0 R +597 0 R +598 0 R +] +endobj +596 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 333.07 504.028 378.63 494.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +597 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 216.15 460.028 258.65 450.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 360 0 R +/H /I +>> +endobj +598 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 377.15 408.056 422.71 398.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +599 0 obj +<< /Length 2960 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHN966l<(>eehE+P5.2\_I=3'Y$r-p$E`RWT;gIkXXh0HX;KRtnKZlo!p80k4!aeHK$Y\/3BAK[G_$Ec.M,(A8]l0H6m^!^J`93!qo3$)PI:?:4\dS*o^c[U?=fO"'3Dg;O+5JH#h;sE;$6dLg*UQKj@*RpP_PB#Y^"Wt=kS*Uu:LR$R%3LqgL00`cn,,qZlg]$b4'H?s)[6e/1XSlTdm=pc4ah">2Y.^e^m1pbt`!I5W/'\ChcVtIO],n31$MY>UqjQ[rabI,)h6_f3qsQ=69@-0qKUmC$&:8'hR3UCTM9Om(MV#0Jp?T>iC3#1P-K>Ej5O-pt]T^7NaR=(sUC17p+L`!h[R$8YSZb(Oh9/l[4Wf.Ko(D%N79kc%Z[&UZ%Ge^VOEbY/aBUG5Bed@B!/J)VD@:o9'T5a/re*%XF\QPX8pLJb-KnQcFQFRHN%A2!`R]&JL9hX'!iY7$W=1DHgs%#m6ORQZA&/F:l@pijMA8'==@`V2p:RPDa?jlYO3&DiFDG$n.=a"+9LD&JMq-Omn6*HqO$.9R6hSk2/V7tG-9SP'BJ;RuO_ksQC*8S.mq^>HWPqE<\B[\nYbuSaLQ7Y2FYhu;2DeX"RR8jD6*t[kVRfnanpqEOG9`%6,cQni`&2FbjB-.K9Yb(irFjr5C+;r&W1lQhRQ#W/H0)c@HHlrVNm!Z<0C3@35Zu\IjCQCK1U\T%onZ)0dVGPf!U;LVm2tKnmdNc>Qc_:2gCGN\kV/>Joc$ua7h*tA6e"*ZPre*5kp9e.M;I9>7trk',.?SKa^(L(A8ddd9c*XX-EYc-V_#71-dN63I%sW2mo&/l.l#66:16sbf.$`O@Jq3HgXC3V']kR0Vu!p(^oAQa4b[+#6-M'[__[O^"H>YS#DX_!_^)QYI89u/SZn67"@U$(InF.R(j6i)*DZa?GR`+CEAb-S!To4jI)1V)ZGf^pADWpE;'q0%8Zdj,2HIouskPK@8r$g@2)Y;3c8,Rj(pbbnCo2H@QhSScGQ"P]\>T'-R#g^'=e1fJ']=_`mfiqCrf-ZeV%m2A]6!HX>P=a$3(Q-=PoK67B?1_"+L8XZ=nrHQlPhb>9(m(d<9@5>`o>poMK+t(;*]PKWOXr;\O[,-H`+F?K8o8$9U!PQ(^;pAZ!p'Q,o`)=>o."A;"A3hDi::-^DY=-X8@,#1VQAspZ_KBFX,,HV-dcCnsUf=G_\_+gN(8I[liJpU+)S(Z\8NG;2bg?*HBZ)YV83&;/F0#`HHh##JD/+ioja"FIj7FK%H*mkjA8ku[,`&+Sh3Mb`ie8+qHPgmb<;S;R2[_RN^c2Geq'L/CkLC@#SA+0`8p(knZPc@!FX([FE]8J-"l&(kSUt!pXF<=ZV;[NB`qfVkWAd87oGKs*O_'h$e`[3dD&.K2"Me]#WcU![OVMTNV*T=H/11r/f8]oH47DJW(/feXS)Ad/(k48J(a:YH1&-=]M"--DfP6;Z6=hbMek0[Ar&8*)Q6>@Z:Dn>Rn+K1Rn6<#=.rjRuQFnTe29n13&&t>c&SK(a^mT&T8Y-*h/5a*^2HACP,bt>6*.-O]o!AI?uFndQrA1+RQT/[(J][X%tpI4jgE28pAA`&KSpCdqcQA%;^p]Df+2*3KR>U!EqTsorOH=s3Su@%:MQ"7!,X\1[P!Mjt>gL]CkL#U[UL9joJ.VF^oY'l9'_^XD^CDL9-`T](FDiq9iT73BfsI9EFs*j/aT^fZC$U"nHqO#sogX1(RZnrl`FE(l,o3ae@@PS*Zk[l8W+IAV[f7(JDA/=H[N+\aRQYhu"%H0L\Il31"MRSHZBWmg&sZVDL5hVi:e\^G@]gYK]KZ:HD7O)5hX5I#"I<8rr<-8W0t<=h)kNf3:KI]G@hopC,/5\Y;`a>AOsRcnM.1>1?2jm('mtI>ZR&rs;PMA9'[h0184?C2dmU\s[\[),/*BO?EMedIj0p]@^7CNhUgjN*3D#C)ZmUF61`#8MH04ZtSkYU?^%--!$>IoB*J%N1Yn)m_dfF;JXRHY1TBB`aXkI/G)aU4IO(TDO67"J(II"o3(\7[la;:qaD +endstream +endobj +600 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 599 0 R +/Annots 601 0 R +>> +endobj +601 0 obj +[ +602 0 R +603 0 R +604 0 R +605 0 R +] +endobj +602 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 423.91 627.894 478.91 617.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 312 0 R +/H /I +>> +endobj +603 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 290.56 364.894 336.12 354.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +604 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.56 364.894 401.12 354.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 541 0 R +/H /I +>> +endobj +605 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 379.19 94.894 424.75 84.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +606 0 obj +<< /Length 2171 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D=``%_&:XAW;#,fAKh%TZ*>2;%OG.X!CWp[5!=uIm[^E0Rk_Pf.p"R&54gLR\MCkieB%6SFFXo-.,PAoS-mb<;?]HsS255s4s/9B_\?k.R@2[3M5/4UUs>9R%\__PC16WRH[&;:e^1Co,oi^atDr<4:aa'13pt>'FFDV;o)G7]`LV`>clCnJHE@OZ6Jp687Q`lDTeP'bNMdZ].,(Y!OXamP+h,lm39dj@1[D=gI+/qiA/PnE1qY=qb%nar\m5Yh\]&W^#XI$%p+U[7aYP1b5MQlK9'g9"k]>e.A"Ru'?GK%glA!9,[Lt>iJPEef:e7)pg/u$&gI'6]t+";r`mSSc8':n?N&&260mJn'6PodN&WtOB&%oT_5)79:[4l@_-Z84^^k,Bfs4/$>B5Kk!rO>%ZPY!cXUF&s!r*8@g`XggQ=?9S>fo>Fo&=f1A"FAF,,WA9mPp2CDj1(A0Af$7nQ`("YV>e3G^G@U/20d0!rA0gKG0Vg3&E&W'rO1S7@Eg68']8=cb/dN6EcpNLX<%2K]uAHLjc?[#\*sU71HlTqt2Cq&,2l;8<^oCU.\N(^@.-4PJ"&[I4t%*qj>=o>"<+6^W0&='-;Ih%JR0"3o&gb*9L?n']p$38*Le?@i<<0oj:r:P(dnE92_j;K#//]KE3]Sg/JR[iF?&fjHon=2`";6FlCPF]q68p/HVl(erYM,"4P$RR_T3O>dQElDgPH]:CcWUb6s9%areCUM+VrR>)s!A""`HY22;uj4ZO\1RIPsBkW'U.Yb>B^V'*2&u-H?XRpq4k:j[@7.64HfF_^'7gtA$/5\#?HnTODhDRC1'Vm#7Ue;Zd:36=(b=bZbtX5(A?n<)os[S-0$L/K*oln'!damJ6iN[_d41?M7kSGo1*W979V$&Q'ic6oe(Mj=5nkmg+[AQ3D>BI?&\+$4"oIK#"<^[GQFpk,N7WP0?2t_mXslB\SUI>0jM=F@QL&;X'a'CH9V0hdYM[8C;VajSphV=eMK%K9,qPr*tFef'Ui/\P`.,NdI3iso9L?j#,kro(Ha3iHBFJM8.W#+l.Gc54RAlG1dH5ds5X&r)fe7D`pWBIt`c`bmqK2\ej<;D@eV3O(G.\)^^nVqnL0r[M`!ngN,,PiiF[:BbTf[T35DSI].p'%\r=!9^GVC*Ok#S+m*O4/M&PMS8nGH?s"CmDCp.o0$dr_=Ps"?i<=!uK4h&TQo]A$D:!%+Y>'3uE+ZFalMh7jRkjXXV-,Z-20@ifI6cP^[Hc]9n;AFSA\+r-D[QN&YZ4UpSBal/5rWT[1C1Z%PBtT-L)<+KFkT@8"4ffT(^R.B4_f:7/r?h#10l8QRT*JV[t?_IK:"9smsr\EmLEWhrXd<)PdWjt<#eU=:PUVj'8NW4Q-bij*]S': +endstream +endobj +607 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 606 0 R +/Annots 608 0 R +>> +endobj +608 0 obj +[ +609 0 R +610 0 R +] +endobj +609 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 266.71 688.109 309.21 678.109 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 360 0 R +/H /I +>> +endobj +610 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 598.218 147.0 588.218 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 316 0 R +/H /I +>> +endobj +611 0 obj +<< /Length 1885 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DgMYb*&:Ml+#^L+kX=9:Cdd+DVRqCDm03PLQ!Yg'EA_InL9.DAJakuP`2c@)K3"*)Y"-P^8)%Pg3Y'M<#0;P<\dBmW.o#P:80:2=Oaj[=bj=r-'+EsfbHq>29jn1>LZc-,%0sgL4MZCO2AFkV2)Z]b*^o$WFjo"+$XG9-Z%,W67n"`7I_^:6?eH%=;pV8=gh2f%3.[Y)Tq[Thk+oV>`L9g;ar/);'5u4(,nFg?HX20FENH]`HHppXV-'RbVZ[DR2ilB=`AY'dZZ/>)Nt)Vtpkr]U!X6)!>>%#EU?\8*k)+cDq`3nQLtum7WZA=t@b?-B@*BST[DZ'/f\\oR-L/Wg"\(XFJrI]O1o(RI7hVM0;BJ:#8r%T0S]e/B^$BQb#n#T?e>@>unT&dK38N@il&4G1>#0:>d6-d'bEeWQlA5MYc7das&otM?h9-M4q;=3F!>j"Kg1_aN*S?Ien58N$rP5UZL%g`A"&Hg;76ZU.'qV\08:QK#P](A:7Q$Z7VgdC8]Bfee]Y_*kJIIY@mSK=\cg,:F'+u-D1kpRe*;;PrO+jl:i":TCBg>5]b\M?cucl2SIcor'o_^_O+)m\15m/Kc`g[\0kIkDHAoi1XVC";\W4;Mp;L#Y3F#4bL%!P0GTF3-HYGnSsEkGK4/bt477Ao2g"!#%g[@!J[[*Jsrd@@u1K!Z2aRYSC]gH3J,R!#[_AlbE@Y.[gGQOY= +endstream +endobj +612 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 611 0 R +>> +endobj +613 0 obj +<< /Length 2190 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D=`<%S&:Vs/&G"6`La>p-Z:TqNBt!F&aJRer./G(0ZqXMcg3QYBbH\YS^#i>dJ4W;n*?S$YnXRhWZn>;DWCQ(ms"D8Q02;dkrd)IDn?EQD$A8Yol-t9OV_.a6K`kJ[q_3ki`1:'AidU`LeaOW'@3b:N\&WnMbehm)BIr/8?;HBB*UI(Z07X&LR*0B/TB?H3uD@]4A/>Y6Ye`BmfVD!g;$fl$"rfdN%;@JJLtY>>L/$'h2_)D$CY(7UBt;4;QT(/>3?2T(-C,,T<>BSI_6@VR%C.`ff(r.)jte)'oP%E&fPhG$+q>l]@4Bmk]+$hl;3IB8dbaWm#LV$=@E==[7U\"=1WRlJu.\6s*f3R1f*kb>2u).NYZ]IL`iG2`bgr>,;qPm.6*O]qno%6i.ap60K55jB5$ud`Hm1^:\*_b#%^KWH'Ik)r$sJNHW$OOXs;=RY@"+&1rLRim..B*P"Qgos9,NDT1[ka"RCbSFq>gTF0Oa4l"c((&E'&D1+a%CKJBF#H5W];CqWg&Rlnabf!-Rq9D&a"aaJ",\[riat)j@hN/MQ[\p$\nD%L@`\#KR4I11Q`^[PfG"7#e<tH(K]]h23PNVKlbEsbG?[9R7WRH;c0g2,DCWkLplLg%QE]!/Wgbmc3lP23#8&nc-pLV@b'#%HnN_Sfi1@44f6'ff6.cqBm$99C+),$V;`hLN$f/AR9cu*QK@\G;kpW>2!JYk@2i#S/s7)E4dh`4S$l$1KGfIbq*]2NY$k^0b^Bahs-D@3$l`S4EA<'!7YU#.A(nrC!r]]c%VLp&E-YARHQ>7e2Sppb=+9Y>:OA-!N6TR=]!kY\.8patSp@j?9S@"V)NG)h,#!SnGXmd%JZ0f`+T03[dQZs5[-08cp'jjXd@<1ZZGbK$N!t"Fc7L@2h^cs]g"1;WSPLCb8>p>Wo'l!%WUP:?cfUmeEoL#rOI0q]V=YpWSliY1DWqNL3U.E\\Aah`3j_Lr*_u)gD2gF(EnB4[N5tlb@m4<1f+`ml_,g=)BI`oKAf9Z!oaR`h/efNCRd5.8oDFuA]lK&cBXm(2S<41?R?h/G'!BH6W6.,pYe^!>OU^MIFt./f1!-I6g=NCH09A!l?Ae$]f\.T?2`T!mn!m]5NkJ+%E#?.]X`>7b.JCN!*7%ICeG;HUOVJ94P&p'@D2988l8YuR5J@t[2K>ei/r7iFfENHG-;/1&P,Rrq!A?+"Y]+LNgbo@&N)4>i`X"I;LQ*9Q:RTa4kH-`Y9Jc##9R]8;u~> +endstream +endobj +614 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 613 0 R +>> +endobj +615 0 obj +<< /Length 2082 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHLD0)I1&H88._;rdF3f/e@KTPB^RANRDV9N;7A(o5`o`N%9bo?otE8os14&IPoaf@HKC9&l>)M4l"-3NK7N1N:V@RD6cWIq&RT%MoL[bjc)P%N5GVo)]go;Kaii'*9(SSs.'(@rd:`3goJ8AG455eO8;9S[`+rNCs_gk+c#"\P[d]ao1Mhn8C+tc?N-H0KkC2(TdmG'J'E,CC:VOhgk6OpljXH2!Ffo9]nr%aM2\d=l.21R-iVY6dKrqBOQa$ES*ds8:$o'%X<+\[qc^Fn,IkR2WUDZispiLL*3SZHPq1Am<:c&]Ei)3)NE>W(i*Gk\c?%7MKk`sal^A&MPUm%?I,FFfeBH">38thoANC`VOD(.QV5>MC7I`M'amjnc#%%/W,17k=XtjU1Qn_s+gLXp9-7q;@^5%7EEP/>op?_A-Be3RJb7tUi9aET*GHWZPU^rVarUCC/9.@=f'_ub.NMrJ[]rMCD3R.FFIE80>o_8S8'7M5L!3[G(D.2;&IstBg2M2gf?\/5Sd;GdNfHT$5o/(SkN68->6='5!4EUMe2a6ScWhm<=W$S(27/,ZLkItPm"tW@=JDLhu'HQ:-"3\.L!7RJ%ER+ZTNd,^E[%HWeY;o^:StkqMHXDgFhrgJSFM]EDtHq!09>2C?a!)/I7aCFp?GPFf;"+%[$8tl8;O2/[>-TdcGJ$3UEEXUEn@98oE[&jVn+DN,Kn[`c-B(keI:hW@U8liGsAX7&& +endstream +endobj +616 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 615 0 R +/Annots 617 0 R +>> +endobj +617 0 obj +[ +618 0 R +] +endobj +618 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 325.54 126.58 368.04 116.58 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 334 0 R +/H /I +>> +endobj +619 0 obj +<< /Length 2743 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0F>BAQ-&q8H9i98/*N(&SI;5Zu5NU>Gf\Fk]DF3[J7#De$Y2S9b-/LeHK$]6,E,\XeQe)WqC(Q7+g\%CNAq.VjX?9=T1dq@BT4dp)SOeL%XYG(b=]^lP12`trFBOB):qGT[N=0C"BX>kTB4qlT^p/OPg7Pa6gk"!EYb_YoNfi9GTg_]X#nnT/5U"$L'qD(%-sI4!I^F1OMoJ'ITfm`Ij;n!;epL`n2*;lTgcc7-0pn^T?9gG_(7]2/CR1fh0Spi`PDU(!.,2EJ8kcr$(11.#iLm?gZGZoW/bTLWR.%IJLdHZq?sFJO_3]'%N\GRC)+I3`Mu(c'DkPm,4ug@c"Z1`sf5RD1GHL#Y?)j%ck*#0Ai`8WKREV@uKt64("c'Pb2(7//]YT,C_)#ZDX1FPJK%pq&q+Q/Ru&7qdm0(]snQHL5F$TiYLfjJl:lC0A.8g.,M$'^4PXSQVeo=&UI5cS'uo)J:=U7<_!J@M`J)c_;,$&*[5,:eO"6fGh8:HN)VY^K0RN&j/sT7=Ph^A[.+ip7`4Cml=DgXGl8c#S(o"ZU^F?7UXXBKNW-S#d0;n[,Mf>1Mq%96^;[r?]_a(+C*Qd^=2CWgA)m(>R"EdL**%GofIAeJkF`,lUmB-%M4.."=<2q0Fgb"N/2#h?78f1n>(F)5(.]1pC:2PO3$"*8SK4o!XqS)gC^N"&5RBQJ'lM1:,_kkI3AKt/S4.q>UP?=]cQ/8T7`>o2d`PJ2ZZX$>c?.^g.[HA.Us26SVdR5cWRP%9YFDGT\'iV:8],5,"K:OZZr?T:\u.hSaCtD,RM_NqM"SlU6>taQ1I2RB'&d?@dTd4Up-dmQdlbj6!3)oX8Ib2i,Ao>_=Jqjh.N[fpEbN+Vr>"iW]J_qs-)&J_6)Asq@G]#fs6X9,Ts,&r_p%UU9k,-E1"eHB?U%nIYY<+6X]aH9lWCFM5%W+ZAGR[%..b`t']'6;HCnr<*sLW$Js)rlHJ!^CN,4V7`KpXe^b4chO!6--1\M.WlBVZM=#f8)BnZY/UCCJs56VuG[_$g#%LbT1t'B$O7#roW4c:"K5cOd49Q:QN@V?[$S,kJY'S>c^AXRilk;6`.aDOi6/RtoOKI.TARKdrX34ZD[TXOKk#[17oipiAr,L'WbZ`SPsb2#F)fB\kmJP/a>XF$"2-s&^(Fh_,i&lkKBN,&Y*'Y-bQookIIu>[R7(=p!j> +endstream +endobj +620 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 619 0 R +/Annots 621 0 R +>> +endobj +621 0 obj +[ +622 0 R +623 0 R +624 0 R +625 0 R +] +endobj +622 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 320.85 324.069 366.41 314.069 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +623 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 470.85 132.097 493.35 122.097 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 318 0 R +/H /I +>> +endobj +624 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 512.79 132.097 535.29 122.097 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 324 0 R +/H /I +>> +endobj +625 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 143.1 89.097 195.6 79.097 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 118 0 R +/H /I +>> +endobj +626 0 obj +<< /Length 2247 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DD/\/e&H;*)+ll98+I5F/.nrpK8[Gd>QRm,8TM7Yt&oA#?6rfuAUq_@+aYo3gjESY$!en`9ah-`-H]HV1dD3862.GjJ2OY%JD)rf.$3,es=to-/^.s&mP\FK3Nb1k*W!t>f2L_&K1#oMYj1clpb6fk(/ro#RGV8&"k@d]>$'V.]QPDm*3u3[+kAhcrM&c$,U%V2"I2LHtS%hO-\rZLjPi)5'i0,LhJVTZ+aIP>l#?^D:2J.rFWSVk`4i5lU0KC^iI[88jUE9q01+o*66=f.&Al2(/O6TH4]A9u@p-gW3Q*gX2>@\,@!K.(1[DM3-abg2Sp0sfV+j(];[78m,Cuo;o<\l<>Vlf:=%\C`M<>]UhQr:NSg@6c&)dec[A\:^h6WsLNbROT?h/6-`%fI08UakS7m4AKN)gKp:BWrG#H"_XF/?>rf+\W]i!`(3=0-*c=<@S=UrOXXMqt$deio`Imt;&"5N]-\E+ou6>+nn0gH)Pc6u)Ur.R]0g4JFp;`M1>"=BT/nF]KH[3dLadDjo1>B=@fJ4Li=:ME2[R\/`/k4m@Q@T8GblZ0UU`o>bet.m?*1*##-^cL(20Q$];hh8(a!%Y*6V2oM'a[3k(4=!LXM'OI]a].G+D1kfLh+4I:j6MA'?-sn.NnMbr<6.o3SepXTKFO?,%]q'\s$UkZZu75">$i?Hji2kRIr6mK>3-PjJS4Aq@rn+(-o$TJZ<.A#k2N4UnMGjtbeW5J<+1LBP3Sg[`_q\+eFJleNf1$@L#`&YjQrm['+H';@HEN4d%'0]56[a0^dUj+eYlo@l7WEXs3iVIi+[\W1bB&Do7MC(a]9W-a(6a-NQ#LB:AP#A[;R*5@o"))&$Bj!H+r$h_m$1d:euA.k?GB8L"P7cP.lD:#VWW=s>gZWmDS2nMac=sn:0?>UCL9\(+!UHa\Q*E]Z!V%]U/Sec;blIeM$?')O]E3\pEF\7ti#M.o884$^%U^&#%kJ^Af9P7gZ]uX>jS3>o&=fWliY34#`g*D@*fF1r(qs6I^9I.^Vl3,q6'V1JqKqa%E;X2_ac*4e8;>?(lf[p+"`pcb"giCh)g-Jub@5Bj6jFtLogi`aUG@QF3?:IR$DIE1]sNU>rc]W!4hk'?eP*Xh(it]\(';81NeT?l77d?V,8d6EVUja4,[f,KT#fsZ7+2*\gsqYh9h59s/~> +endstream +endobj +627 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 626 0 R +/Annots 628 0 R +>> +endobj +628 0 obj +[ +629 0 R +630 0 R +] +endobj +629 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 330.04 714.0 375.6 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +630 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 645.109 147.0 635.109 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 316 0 R +/H /I +>> +endobj +631 0 obj +<< /Length 2646 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm==``U]&q9SY@/4MAAb/)k;m(0-GVe#H-F)Z+LfbPIUA%"7\Usq?r2LBB7EbF*5g>_;;L40j#N]1cVPcUfu;U:#%IO-'gmm_oueb_QaYFBjUb0%6R8%"QOeNol*cEN`Zdo9C5/Jc)=Q>W7-(V\#1hRU'V%dllVd:=6SdXpTfc/TUh=MKVr1I'6=TKg^]TNW`1"aX^m('bYAGV^=W=Rf:dlV2ftBp[<>-m%/0UK?A^7cWE]jf9qll_E1OJ&ciBp/\-(_/P[&fS70Br6lTc'u;79M'0`sd+*2[IN?>1,h%MTG65DIE9&`hH9?]Ff_n_O7dq[LjDB@5LSpS#f@RJ;lX/Ls5hWaQ@8d>EAWg+*,`Pp0,`+9j3OO4uUaWpN.Cn=\A:3*1-

!L_D#:Z[k'V?#X=+_=Ohs+R!-R0G].-X/8CM(rZd/_@S0>P:`*$I*L=3f2G:[j_MF\lYtb>>"=bO)khG*ij0:\]1b&jVp+$>P)]:Tt)pX:"#f/CdURL.\g`C^;S]oO4Xd=ggSKdaf(8Rejp38TK'D[?'8Wek3mM'nEp!\&5P8W)LW77Pus(/rj]X_-@$I'cUOD^O%1oC`.K?i#l@!iQ<4H40N#%=)35SSF;T^_U7C\j-2kk0l!DNZT\e,WV/B?)YrDeSdD"+YKa-/V;oT3t+`Id>@65_6^#WJOWUa0OpZ_,MNtjL*4:g\pRAV^@+tk`T-n%']^?8aV9RV\#IAfENWPX(&\q[#e_:eq,oo(YK5WS6<'`A7&V,EG8D',a\ZMN`c3tG+8PIUHA&WPTqbN=."`rG"q:QN9GW`TX(-=-JKj@BF9Lr?\6L,mVB!m)A$#SB%3?5$#2aao]s)!'cdOhjD9Y-9S`4]A4Kk2c1Bm,n+Th@UW]$]a_JAr=4#l3Ym0Sr?/EQIX6404aPoRQ`5od4l^DS#5sh;83h/.*V3-#ttDeK/di#'Ep"&\\d-7b4^gB/jE.55[BR`P*kKdcS;[IsGK4$kK?+[!32j@Q;K$/HAu"rpUa;;_Kj=Wt2F^noEI@9OeK[[i:LcsE2Kg4Yfp(&=;!Y-gP+`rSJ-eE"gOh2Uf+ra3S&Yr1tQT'hNr:M2'emeb;)_NjG>+GoXel_fFNoFQ"Jg^8p1]V12O`!="4HfEeOV`U2-4RlXgjH'4oM'Lf[2F\4lq/+1(/N]?/84eu?U9-goO=G%7_s8L.m#njm1O+d0eIC+Fe/Gb/$L3`o-f(Gu@h9krM?8#b=f.Th/#4`N[_399(?8&eR-BW?[V90J^Zif_%atdhH]ldr*Pnb!HV$TY!q.&qi0k.i"2%$fBp-hJGmf`nch_Xk8!?f%ZSp!,^$6X#78WGV+0$s68IJng+>#SBi#pf@<(s-"@i3K7YDCgsHk"_"l&Du?[0F'kp7M)\0Wc+Z(Q\;=?tE&S>chfg<9bN\_CS43+'hrWP2IXZq%a$Ks'sIuY;iD5nGZ8!C%tIqaE%O)o5Tq,J&R1JF42X,CYH27Q(6t2rJ#AJrC5hE4PEk%8mEGrlkK/inA<'`HDL^:?(_4qP@4`9N+8a2E3HqAjD?nGOZ+M\*?`N67rZE-]qdWp`oJLASqm3/D+PLe_K`ZRlm6_+PmboOOXNHa++8_7XuCsFn;JXF6Sc78j?S6pjYh\9J#qpT!Z!^IE?cue&Ys*\eg'S5M`(3e(D)qu^!nI_P~> +endstream +endobj +632 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 631 0 R +/Annots 633 0 R +>> +endobj +633 0 obj +[ +634 0 R +] +endobj +634 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 330.04 300.848 375.6 290.848 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +635 0 obj +<< /Length 2365 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=95iQE&AI=/#e=aZ,dQ9f*X]8KZ>$dbZAH2?BR6D]?gmZ4#EhFnbj1U?5r'fhh]#Co%/YdcblNN^M6qQjk9.'A48h?a(i^goPm;52J3>q(tP-r=r)TekQRN`!bigsjp^QDT1URSW>M5sFrC0KZd%uEE3r-SOck$uihDSo*$,m^Ob0u*N9M]-V'$7oP\*$H9<-6o5.ie=MaG0cjBLTIk2Fk2U9*MX�<6jhB`+/3r%\l?rAJMSTk2jL)eu2pc:>=HZ15ULND,73jKGhC7D-eT"KL$9dsj7W.*g19VTO'rV:BO.)ks51-0lh+?H64%)iE,u8`l9Wg_"[bD)?D&."JEs'l!]QUd:FA3!>psrKbjV,a;)-(_)77X8'#VZ!!G%`pFga\pgk0^I%6r6,]8J(NRB,OG)P:$Li+]]q*3_NVD9V1AtUZDVe<9Uh2`RB.H2ESOpK]bsop)sqB,JAMPf4K_3noB'IlbkPgcNa@arEk&G6t*D9rXZ*_nGEQ`cCNSr*CEY5G4PCLqe%c!Y1*PRPf*^4E#1WB1.;BUu'L0,;kn_>gQ*%N-#qq]d--a84FrSjO0o,Ji=6P;=(2U5XO\cR"5*d1CA6V\0s5\Vfl,UC%$!()Z$@j4ZNOjYUu=ln?$IeRh/g"^dA28mWt`-4gc\0O9o\pd+aF4e")Vudte3e@i:#pjND)-/;Ma(6Q7VQu(45_KK_k;=ie,I:#0Sh"-59KIG&dA1^AlE&DFTo!S*C.I!H29R`h,a>5P)B,Xt]n0aZ6I9N_h:P`(ZfEs8;,Jt!l<,t4XAq/J/i6&28bXcK)%kQad'SI1pEn]NI;:?7$`<>)--4[Hg>BJ>$AuRg%mZ@*bT-t6(if1XfYQ"ag8Y-$ncs\7,q%m?-p@Q!p//BJ0&TCo=M]0/BP9PY=0VXs@,Khr,=#U>U0ZA.6f6esL#`?G#,kclJt4/e9i,ZMTi0(n.k7!&V5lBBH/r]JUKB,b>]"m-:CX*jYp358\!4%/O^^gX%bIlD$lDR*XbZsQX"tsn\^gN=N5E*J0O<`.AT7V-3Y,5+D&mb$su[:Y-,P@7arK7),3Y]K2e2O!M"jcpJD`4qh/_#FBOq`$tpN`s"e\\[GY`:3fsLhW4Ru?@#3HnbKlO(9=^-O3"pTB0-Yd;^6[YdYq5?H\SFQ.3P0n;GSPLheIe6E%pLH5,YLcLY>Ttb)s#rFjbYlaOXSIh3Yhn:\@qYG&2,RSK2(OiV6#ROl-N9N$gh%-A(grZ_OW=/b>s!D1tE%*9f!U5l%2#KlRH<$)Y8kq/Lo3jF^pJDm#8E-DGf18%Gb6=F;91*3/5ZF=>[!mANQf-'daWcD%sccD>:4<0E$glB.mD5o61o<6E[FmVmJcq^,%`27^E4\\7k7ojZ5PeR2%I=%s.'N_hTV$[H.O/.Y,A'plli%Q.t1,hLnmg0S/j@=NV:Ci[jc:$TaAh$@BF"?@*5qDX, +endstream +endobj +636 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 635 0 R +/Annots 637 0 R +>> +endobj +637 0 obj +[ +638 0 R +639 0 R +] +endobj +638 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 379.94 457.557 425.5 447.557 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +639 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 184.21 266.613 229.77 256.613 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +640 0 obj +<< /Length 2593 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=D/\/e&H88.@H>U8+I;[Q.r/(*:""2U]<697$u8)S&fhY5M^qYMfrs:1D`gXVisnEf^nSn3hK-`tF/NAG7uL#YAsnB6>A]DEg!,X:1Z?ZKKf4knAme`WCmEmTSb8nEd7sS.@P1(t.1pm,f]RG@6Z2:to`"\]aZ7)\]m(7Va\+8e]ip'uq0a/)rSa:NV&d6j[?',6;--L2e(ql]`hT3PNBi*Abm^^$mIk!1IKYl*;Pk&[Op:eM[MXkjjCXh$iI*MF#:1.:kE'6$Gj&Z.Y.-PMlS;@?,5VJ!7S\R@F.NApU[#14D*8e$A^>eD;f/`W!^`FXtr])OSYi0U3-FV`?nsOs1i4.+:5JR6pHnq#spTXgui1l)%XMhmOBq#.Cj9,1Kc+e57X7GRt!sK!WJZM2QWtO.pkY3$-QGkC!*50&CZ=p:-`3O\W+RdZ$,KUdI-2aeHD5'd^LF)13\Z"ID*K*c9e!#mpI3RLIgXsqb2rV+LndmuO<@2`--X!Oi__c^&#!-m7&Ci(aiXK6sSK_TmW+q[l"*\*,T+.d7f<2g(;Q88M+,8fK=Z:DeOhJ.qs!Y1WkL)Ipmc<.eJSKIrdc@IeVUtH-!rG\f=7%V`P7U%nm"ND.p78Hg("C]AKcpc4;o+UC;NH$JZGn)gl+`CK_7"j5ZNBB4NJZQXg2KU55Q`jFY$BaCfqJ%nn\O%.hg"/AG7QKhmNPYYWJKpKj\K7.:n3efndS..L#AM=31;HKT`a?YQ'Tlb:&[uhOH^0uWoc`@:T1`**T3$5#T?Fm4ZPmt>H#RnR>qUsQb/e?R7/lkL"O%^d?/[4K:s7Q)!)2P,NF+#hI&PIA;gS#53Z\$KR8&$/EZ>(2n1<(Cm+h@b9HX"kAfp;6cl!cA,.-M@gUqh8\BUXhBB+5NW.+Wj&:G_4SZe@hQ,:t5ZKn"R%"8I;5Nhhoc/rTF:)O9HZ8`NWROMFe+>FgDO8`T!C:1D7!"(Xi9h4&$HRb`AY%u\GiqM-U"oC^07_l0KL\Vt%;B+mIT+?/0FBpT[V7>^,7b-"FmaANa8"*a#`KEZi!jGt>Qlml4>OULL8lJgf+S,+n\/5E+Y&M#q#?kW;D+dg"`#ksOE8S*DJjS(hEb_L>\h'k48OE-cfEa>!^Y"oS\,b?J41a)?)E+-hn%EfSYPq5,pUb&QGRkr&t=cgCFZj,D[e:os6OR%68;4]$i'');$q6i")q/DB6JJWDR5EHTZdl"/:"?gB$9d-7RVs4Ho(pW7.]u^%^dg9$sWK2<_J(*j*C[O96qsP+)2WFphql*!k:fQjr!Ep59-lCt?q#P@eV.2QhrkdqYrUMXUY_Y1uNh+;GfYSNW%rf"Za,54uTd-$9"H&@!SnRUK[@]70#S(7'd'/ugXM)a.5Eh\!)A8%d!ae15Z\.tHBJNA2aI;`)=lW*ohc6K3Gm[P<,e>lN*F=guq$JHD"]8'%F$Yh;Jm,&WC=)mq])mb+(l,dRMKcVZZmTSrV%)ZtS$>^=JM/uspAsOLNEk\%ee4RU^ijN=S3?b5Y+ZtO7_l&YnnXL>]c^15NA]@M\#e/bo;X?UgI8Ap$OgAD++EoPI(LLiN4-D$-u\ESG2P +endstream +endobj +641 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 640 0 R +>> +endobj +642 0 obj +<< /Length 2780 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHND0)1+&H:NniTZ];1CXR+KDCm-0@RQ!9."Y]F49c6%)UR0,>JNVq=c7qNHRV):9JZ%R>eC:g3r\^fbM7S?Mnh,Rs;*r:);LjGHuPR9jIAS9k%IX(V,[g7?b]&o(D\;S&p@KC[QRuF2-'"`fnsciPb\n3!TT(?jfWSPo@>AFTm/.?).SUl;Mt94h-IE+==dnG8"Y=j@ZUnA[2#&C!sR6-t*r>!W_jSF7rfmRb]cZB@QWV(>JO!@hh$+I"W6N-?([2s062`^9d2AAH@F$bh7>r=0mcnTIaP!7ku?JX(6_(,*2qht2A5Oh7>s$_?>K(,Ld^8fC@ZDra(aqT#Hm68-l3@GLc8LS\62DQq?!"D5AGYO`oK+dG/#Z,;/dRK6?.'ko$5ZuMM.gf=YCo2K'oYHmkHI]Pbg:YrRJg]-X7tL%O8b`sS7E"r4TZ-F./B.^1W);PkX?_KD5T(c\sR@`0qJjO%d"Mh`AFg+V(*`:KK^2*J)^m8jd,-O>?Jl.Ch4?S+!RpIcW0KM'Nia6BUA"3l8!kYp+'UU],fZ%ftjPq"_)%7`bGE26C/1h24KpEa+//V@MQ>KK@'WFW6qWC9b^K2iO%8FtLAGjoEIK!V5"8%i/m\q)C5#oE'ddHQCgcl)-:)$2gpRc6a^\W?LpCMjp?Oqb8RC\MH=1h^DeQ)cnLQU/fG2Y?rlIPphCgatkdnF;N(rCrZQFh!^pYu2GGE@K[I:l.k:H\ie_%EP#%F0Hf9M'>\5jt@2K=n-ub^#6LB@qF3VJn&N?:k[]lL]iU7Rh4l5\'D=4>uV..4o7r!k5I95[:"SlsJnjGGeWB91[OGd,hm_^Fg/5;6EW$V*$A4ft_L(*#J/PDua+T=(t8]WOF+qU"k,%e3gXAl@5`*81?@#hK)7[_FsBVTf3'W?fQ/r'^2bIXD"R9OiWCT8rsJnHp\t6hLuY++#Vcfd0r"Z4cNB/2M9!`(0>QlCaW[G2+`_M#^I,g>kNlqS3@Tu93KjgF5COg^"0&3.=p7qXoej8L2IBi!T1s%p`:#)gntE#W*-@iN`BVTXu@YFLa^&g#tKaVo87'Djp*1D\@h.V):_%3CpY"j&__A>XqukWM-*J54f4*G\cLCClg=A1+*unTmU88>SOPC_`4)N8l]tSJ+qN14(-)^7c<$W%)sT8>e=:dST^2bH:hE$WH'n'>:!VO^="^YGr:"Yl/Lr[S`ToEP.qURHW\?Alli#MliaQar5[4RU#dK'd=]Te@!MqPJJYiH[K%g(eKSsHV!gY@9+MF0Bq/s@o61$Cm!OEosQl4V]k7/NVC#P<:_K`$pp8TE+^7WRN?Iik6.NhFVr._?nOI:hA)2.sCX6?g2CEm]6cVs4QWku+%o>NrAfD>J?K@94EX;0NK,:/#cQA"C"3$n72S0[!5Je7Lqj^.7u930;Q0&2^^G#(2AQXKEVUc4$9d&SE=\'2h1Y3&hjCKi0+tSdMbZ68B!,,`8,bCU4'ham0:HA?nUAN*pF0NXA]fM78!piVf5&)&YB`dpJ@Qft:n:YAW.XZI"0^c<]IU^L8T!(H@V4q]5=q\N=d3'eYhUm#8NIF_e&CnRcPs8RH2@Q/'TDe'*d!eWFckEdS2qmitbPeu/!AW3TLgBV`87.g>W=1WKDus#WVa%0`m1pc-kD`UNehbr*e]"C'CB5IEDa0U@JPkm75Hs.gaiK=1H\90kBA^sPZg:KR&28H[(9+Gm4rt-g?=2q%K%Ok!M7gp%qG)F.B"-[Aj5ugqC)B7NlUI`lHj0J22SQ5mMNZQ2][0TSRE''94/ol`#WT\.#a0SmhrR@:He6jg!oMb$`c!4$&'*)LDi5G3!gg@an,~> +endstream +endobj +643 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 642 0 R +/Annots 644 0 R +>> +endobj +644 0 obj +[ +645 0 R +] +endobj +645 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 330.04 356.137 375.6 346.137 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +646 0 obj +<< /Length 2941 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^?#udN&q-BZiQ>(sQ!j(tB&9i8:!IS#c>4eu]6.np%l252C,"Y!R#M(D$F%chO>**9&%D*B0M&=@maJ(SnWkoUQ['0\@<@n2rq>7KlA(!b^akof.$%)X$P[+9-%Eh+3q5:<#"!bJiS@)4Fj-X-a(9`"2,NC\(:4ForffH.]$=RX1(_.;4-\q=qV1#p/'ObPGFmnS#KVf[Po2i_cs_b7/;_T?3=sUrf5WGhg4BV/ak)tKDF1m,A&3+WO49D`=6hK-e_\/n#7L?%7.$^q.$@hH7B!:%8cUS_D3m7OUg6l7"ENmUD]/,.s&V"G](tpl.u]#@8Al'V8O],n]C=$sGlnSF30n!2hqqs'[5Y?QFg%,YP1Db'B3\YGMo@CZX9]IfSg=eohXTD?II-#EQ5%ukdX]qif&1+kfFYr+,cam;E](Q\e29E.(0Rf4N="%$/7,b#:h$T"`\?+Kco8N$A*DJSA5^Tp=Zm1"fT!IemW79-DY+euiK`ojC/m=lSCJ6R#s(>O@"sDRELZqL-mB$G-02"A!1HMD$H&I,E^%jIY;ls)UdVnO,^BOc84MCQoG7(.13T,Dg.r"(F%s*TgTLIsLsJ(A7qe,t.?R9jC@)?uF4X#.ZH2GLPBFM%pJ0WD7.e,1<=E&WL_3eSB5%a*4u\m_[_"!]@[^)Zr&P#ZgpLe;d/6a\"3LjDNb,flCe(`]YR"U*NN[=LqpY0eP3j8Wji$ppQ\$/G\Ip-U!rVHW%IsMkUZ2pqA%7S"falA];(=OWNb$glEVQA1+..-4(WIahD@,4R*#.8_NAF8;*`ETcZ@uO$hIY_d*1:G#&0$ZADQM=p7&e7i0TGCgc-EC(4kR8>D6'(l-D4saR&f0Lr"c?aWfH&6h9n!j"VI1hl7U1m]X#"uUQL-aa[N@'*NTo&jE.ijH-.gnFS(4e'b<]mO\VN%F+/MEI*m*!XqrWYU[2Q6)U"U'S04K"4ghh;mWbXLRL\#hSN4%-X*3'SGbkDB/n%$(KNu$:^&j>i+CE;3C`s,i'C[P?K4J)!:(/6WS&'51;J5?Q!+:K8g1%$Q%Q8(JX/b(>=e@m]E3OGF',u&6)q:K7nMMX#UGeVH/n35QI*\.riQffh;I[b_M6q-:l*k-(1Ic6kYicS2A`?_cj!:H-L(5kT0@Z%o'mdX7dZ&s]*tmudf8-ccEL;(b"TI(Q8><9)9cT&T36]19VnW:Fj,?dA(hB,9m'QV1Ssk!9fVGe7""Jt:VYqUrjmqs[$CaY1i\:]njG3pjJF*EQqou4l(Q]eT_GYmmX=rc')V:aJhr"PbuJ/;L2H9b])Lqc9:7a%23`DR;lD\IV^$DVC?0/'1s1!F[ITo\Y7]7Vm\Dco\MGEb!Bk&^Ok0%.8O6jQ)giF+^c96^k%mE19]Sqs;B`5hku$5f5l9L7PpF"\M0pVcDJNG?UAfjVd3Rtu\H+,iejDKf9=krI)/%UB!ci5(r7rr*$&l]&.PUX[+J6`#]rLI\js"Q_InLq&PF_>h!lK8kK,(0D1+0F\%VqQST`Put%SZJ;"Uc4*VE\nRo_S'=,%/b;0iPJbso]9K[6f`JuB]VB%U,qmQ)a/H22ZURc;,ub+X*giW2J-UJ>;Xg::2T>rf?08\io'2M!6aDgRYNYA;NEnJWtKOM*0N]dInKmZh3tmlSMsA5Q1t>H_13Qjr,)(B1;adXqH/sb:S!\Ok1;170pDcs2Lf>^_CKq@@ML:N?"ANSMk^YpBr*?61n)hGl+RiMt4k2:i-Ufj!/VGcQdg`l`]iD/a6Z.a=WCjO!`bT4gFs^r>u;APh7ZX[n8,/ouMdL5C([DfIb:.e9)O18;8I1e>eT @q'!WthB38^rr)Ps +endstream +endobj +647 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 646 0 R +/Annots 648 0 R +>> +endobj +648 0 obj +[ +649 0 R +650 0 R +] +endobj +649 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 156.99 419.109 201.99 409.109 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +650 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 432.21 112.218 477.77 102.218 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +651 0 obj +<< /Length 2505 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHM95iQE&AJ$C(jfru1pSCmfgCA"A.rU67G@nr;)&YS<(SN60)OCiHIs.YU`WlMOc-#s6d@XJHo%Q_bXRdhGAN\%3M%NAF8S#'`hrO;`h=uuq5/(4rcj`g^E7l.i;@hI@_;8leP:Gt7S_`*#I6@KhSpco%n3V&Q!lMc&Z$nq%bJX:[J"'s(H77P,0B0RBZsjX(JTmF=R%P(iYAc9(A\&?$c5QZICf0`3E.dM>jGuni!dBXDmg!jr'[#IL,7K-tO'($<;'u)^["1?a.0uumn#T%db"F$!^9^:=NYgoI:hD*\]$p>f:LFEg36)H(!.+c$Mc,/u&o=l9+41:XQ\#:RRmIX0f+;%AWL'Er+B#QJQk`:SY;QF>CpRsNp>ZDtO2bDs/4!XaOd%t<-+&]GL:1Ks^oSfKn+MHDNlrNbZ7+u$dmaDEU.PFt*('c0GL%qF04l$cc0UJ4c4lS4+Xp@'Ooh_a[0@$%]TTB15(I:bjiO>`[]Ho2XR,bB4`b?tT$nSdNPKLe"kRQAg^?/D^u?5klaV$@qmn"hcj^u4p;>-K6#fS/FL5.n8L5_gge$#`ABeB%3LQdI=.^,!4`o?_igpp>'#;.2E't(;=#`]A=&i)i?bT-+VXBDAG<"uH`8%!!XA12@e$']6"5im7$l)R^^?'-<2?O,Tf'aBKVU8KI?B<6:aCr%fS+gm[;\gl6A3sm/n!=D"YiJRs[3GHrHj*ie[8/4;j5F5K8+,s$j,S>_PH9Qi/OJF3C8E)W\:RElZ?sfA?pgj64R7Z$pMicP;fgO$j_]COH>70L?MupU;HBQ-FUo4UClMZ0!5T4S;f1aNp4^p$DdU@($,iq>p>Xi/d&F(P+-mSV3'?XWV!Da:!nE;nEk^_:h7f0Q9EuMg]Z-TL[m81:r/ITs)6F8BhZM4T[rXWc_1luoc;f%QZ$?U[17jk.\'BHdT*=k1AWGR?'UHNfAe0/`k+SCS.N*o9D3Tesm-mI9&P>FHscm[GcEP3D7a-7!hE6Uroj*VppXGD+Tm7iN**n+Xh-:$@RVRa#[65#h6jK?Id8aKc&4)aPG!JWa3c?3AT65p*(fePK[-df+=BV;8Rq%h@SP-OXMIZ>gKj;?6pj+_8>(Uuqrd/OUWhc&u/jc9?;$MLgK)WotC`*(SL*N,t48)<8;JgUb#Z3!_0//8,%+,,H:E]NQj$;pb7nX-P9rF"RD]\2\+.%IjZEUU6^a?:/=$-(Y-l"Ii'eQIdG'DuU2,U7mB]jOgYtdHkQ3[IJbTfO_'K+mSH^^P!?kKALDG-jk5X\agVbSY^@T34%C%k[f&(fu`jh+"8c+^0D4XoV/c;/[Bcg#6a^Y-S9SDm4aOm4pZTM'<".Ao?P5_>%@K9te%)`>^8%)MokMCR6m*nV#89acr6?;'*d.p?j7&]?]!Pa&jrQm-*0/rC2nOH.U%BU:?-]^jVd]!`"EUf_gOaK)&.9Y0sI?DU^$YR@#pl5Bs#().UQTI3XI`Ge^ph*i.n-d4nY2h@,?ARKa#-LkkCpd,&n"2d@CgWNB"cR8D'mHO!(8qhd9Jej.&]G.rVUl4V%0WV"em&-@TM)\_]0&6o,M?DCY(<*sh2Rk>:ChF;_Lh\LrCfleMo6/mp/"hL'9:K7(qb[O3i2G)]dE'ptR5g_bFgSOMMtYXX>^tJb07(\)qo+KeBVp`MgjRAD&lAJ7haQ7bB2!rN?!N[p4C"@/k`TkBs.V'&GE^-h\MUn>7RXT)57,/?/0ndnnef+YM_YCjko2?DW\+`M$.j5mk:QgQDF#m/Xm&!I!IgV'lW0tL'8\?rW8XlkcU!pW^GmgLBo0GkOWT+!ACSg/qrj:S+qtG<8qtXjb`GG0^A/_.O=oS29rUb(Dq"7V40mkJ_Vqs\OSP2Dg%E8g;kG(73+(j>>0hjCT1u*Cf1*#P8n!5r7H./E(j-ZC^iCI#i)pS>9[``k>d#r17="^3T/`LRHN)DPI'];lhGXEA3BU_nWJi.gG[LTIXEr\s+]PY!]W2@Qu,Fc*QJVOg^E/hY9"`PLI(&4oNF>&WjFO-MpCk90s-GC97@G(PVFa&,^B>QF^++;&uo@(J.00r]iq-<$Eg=9J^7%q%q#tHSN,>c?>7TEW-N9+1dfKg?45E"VX@PQ5m)+2Y?f(*Y7CI`ff($u7YT0T)qpt>8u?[,O;i;~> +endstream +endobj +652 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 651 0 R +>> +endobj +653 0 obj +<< /Length 2247 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU4968iG&AII3BQU40@t=d?F;&5K3c:+c)cr0Nl,5U/,\1o3LBGE6(CNYkH'Ai2:XT>,fcr`b=>@D3AL5>q%Ml%/@6%s-+gD7oWLcBr+Af$:X_\%WbQEAS0-AFNeT6W.0q<@`IN7'TD?65u#R(?cJ;]r;3*bR57Xc7u+E:E,P0aB5<)Ao^0a@_j#Ra]Z&IP73jOQf?bmRMahO>]]&*8J$&hIc5tm$Z9=-R4uM_nq&5?^e"9-JLu%'+0[:,P9.4mo1QW;0B.Kj3gnW)YHb>P"dLHhaYX,oJ4<^]T+S&uXp`A)CsIJDi+jS5DDCuB;Mf0cC.#;6?b_XA]3M?R`)>Er@6-*2)D3b'0k]OYd@[2CMe4[pP6e5-/1$X28IOZT)^I#EP(>493r>);@=jrC6n@l2Us-JliU3Kbc@dB#MD$D4H(Pes51bI_N.)+EZ'ELZR*c?SP1`Sik#;r>C_q&3lFbTPoQ"C\"XWp85)k@'sFc:!N^6/?(TJCYR3,QZl3,&u,fr5k]\>]YFihes92Yb<+:k;\(X$L`lV6N]p73JLq%'!9D;P!9`jL8>Q`3DtH[K?q4b@9bgU5WLI)+=g1k!X/#5*tQ=1Eg:BuMD[>AHS8$e'?3lKjGb*;2,(X?CoFf"hdOCkI[F6g?/]%\Wua3\h3Pln/PcNTF@/m3%Gm1P;Rg1##$7!UcjS9oOD8WQGuX/E:`82sM5jO;)s)O!A?7\\85@RGF"UL1tnrlX8?MgI<1o&\Aha=UL0ENPgd9:"@W,2s46jNA"%BPe$fI/Sl%/ek5mS9d$n5+IFCQM>/sm=\^20<]#/)aNb%$c:*F/1sfC)UPo6`_hni"g999Dn_`9FqdfS<(%m9/YT,VR-mL\jal`4=\j_CMR2F2\1X1*![KU$k"'ThJ^j&8VO>o;&fc`Q,%(M/%M`Zo!`DFIS$GNYZ8]#k@AKF?CG&O;]$eWl0%rpd:nfGZ4bA2"!)CNsJm%\;HoG2HWoqE/p\9hC]!Adon@@Tp*J\0qX*,)]Hgq[KT?Ui8FLnZ9(j7hem!Jd3eZnLS(W0^dJpVQsXP@AYOnYdW5jQ]s+DN9GjY?tXO<)cqliZZ2S+`q'<3_p9-%AAr\aZsc/f7$"la"4rWP0cEEA0gMXC)?mqmC>3T/-=,5i%OVDnl:RPgA82b&;<3e(8.>9!2fo(fn1o31lT>%H3_WOLh-_`$l[gd)(:TG$QW$l1Us/h\GGjZS^;Q!?q8$VG,9p^%+gZHCKI"diG!i+i8CN=/84X$JGue;.(tDJ98)7#;1oHd"LjJF>u14.m'\,$gD?d_d*(U_:(BHZ:DnJo/M^/AZSHb<^[pM"Q8^$A_4+X"[%n.%N.X#7h?PT@q*L+A+rI$irsK%tJ]h\8U2X%a*d"QSX[+(PLA14mmSaAc)QF2Op5\(=De_*I'S3W[d!J33LD_"<+9JB]AXb*tHd5<3U=1Y:Om"Zu^7Ho1Nc+NNmCBh(]YHrRk;1;m]TWbo#(@!F8K61*M_&A_]pW%<:;q3=&N$dbi,K1EJp[Lk8kLU +endstream +endobj +654 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 653 0 R +>> +endobj +655 0 obj +<< /Length 3075 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>>Ap%Q&q801)!0l3P)[!\0^P9i^"-e\N?`e5ePWsi%5^GE!'R4\49#!/J5'*22`&`#7>_N&\,*,iX%W'QmCS8qJ*#=K^"R)em+(NpZa/[WB,.uJ?"f"Z'<\e*lJ"moe'ljXlZZi7Y&6K(7m66rB[d^AeDFrf+1GU,4Yc?Zj$nf\_jEb]%ug84O>g0p4D`9.01D7!Rd++Bh;0P2[QZ+_1@TA/Baubco>doJ-DVmC.k+f<-cd]V!Y.WlHV-].5=8*P?L@C"*J:'jV6r7VoH?:o55Ko^:tNYHnA'k+%M)]s'J`XH(+1YF.4uLkS%!M^Ia+$4`@,t",Z"Q8:u;f-5eefK/>CRGY]!Se&ZR6R/O"r1Y\464&N4cM;;n\_t2s)2%o!)h9D3(35Jt^>ST"WaaaC1rGW*Wj%_Y+`0hPAW`au\LU^-h>:njC4`1_R1Ef=ba!c.O'<^C6qhY854m]'Co)Ej:*p,uD!1%P=_AWc2T1*CG%I4.Dfg)AJk^:,5=`,&c)gl1W3#[a6!4PW)MkOL_aokhhPIX/mVL.>.K[+98Rml:`@:V8E:XI@$ANrVNi!&@S@HT53*,d(hb@t]t+?Od,+a\>'1:D9&!"HiebOKos'W3rtWcu]_AY-#L_$=^A]?I=JO=9bUp25Xt`V$;debaE5\/Ds,`Em;M$BdrE5E`YeEP!mXgfX4:`M-12foctbp]3Li+q0&R\P%(jg@m&#!>%?ISq^'UM/WL!5-6OSN:i^V_[Tdi7B*:L&;!@CD1S$F9mHImTS%5KM?///%1?&sT(-@>/.H$W:/g=bi[`!ZMLU0UWd9LD8@k:pD`+!ADJLYeIHpRgdK^qa?UAE'LN?5it[?Us#$oK2#mf2!_ClHdWPa\[2]rW(1Ao(!hC;h_FYmpORD'\RZ7B0>>T1eqYDnL:Q`%i?[ht8Fc!IA@Zd)Rta/g=t#N1>KJLL^k$G[N%mXm.Ll>H>)9b7&e:F.?J6?dkg'3To*+/3ic+cKUoAPB1&iL4(c?oJWE)D)T9AQ_q!;AU/c"pJ(VpA7CrMYiZgcZ`K_OUbNc@!^q;X=J=(-11oMW[(tM5@-:nb4rgE"=j8la'/'b.1*fSjdXX#)Bo"eHL(:!%+G\Cl']0KUBUU`4^d5WR.d8/m!@sJCN1o!R\bg'_rbupG$WQ%7TGDK!GX9Fjr]\VZRg^5N$+&f`9bV*U.&!+J"s&GdW68k*PLHL(^0i`7luCY'8N@Ie&3#=-jpfolOB%,4AM!\<8tNUDb:NTY8kOfdP>Aufh[\3Znps-ZeaFBU"B-ED,9q8Ubh2Q@+PH!]V[D#GSbRUELC]lbD7a9TCpf#38:o,kik`'Wm1.`quo!G2#hLHSd<6.Yr5TE1"[$`3ng9PS+%\C.b+_qsOrXES."q;)RO$gL$9IA1&l#:WJCjJ'L(:B0f]khK:#lN!AL5EUdc4eXcGT^L:,:LO5-q&KF#bQ?G^uP/!IaE+?O?gt(Vt']4G%!2If^8*1@.:\97SG+\8P%5\h$aQ&]\$Q1q^(oM>0Ah@E%0U'CRns32:sl2d4A/TeS%?UYT:XW5.5La':I[T^+FD_hXd-$.&5I0?8,.F,B,U7eDUqj0f$9r3H%^+RJ1&3r\dB[bd=%"qWgDM]:6&RQA+6iNBV)!gH9lrpJK+=j";Y%b!&O)h,T@:oHC]EP.\1>CpV>PHIa^.?[7of]6]>VZ#Y'Ha*Z?GAHTKTu]0VFS2*jA^h[A$-7--,`pFt[7u&[V<9R2?IOes.)T0:N6WD4rt`8s+VK>7e_"5tIC$K'2mY[*-r$i9P'3Jb^c!Pn=bH##R!.-a2+TrK`8Rc?mhL=-,rF)ogDttO&]_#n3D,e64SaSD8$UD8Ol*O.A9R.8TRnMT4lW!Z89!]XHFP>U:!PT_lg!mg'B%KF%DJ0T34Ts=)%WV]Y.^FBe)0;@6"EF3b83]n2cmes,%i-mSC$q6.sh0.GBgHN\i7C5'sd;R^umeh[cKTebKX'lf9?^ +endstream +endobj +656 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 655 0 R +/Annots 657 0 R +>> +endobj +657 0 obj +[ +658 0 R +659 0 R +] +endobj +658 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 330.04 618.0 375.6 608.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +659 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 237.53 265.218 282.53 255.218 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +660 0 obj +<< /Length 2478 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`UcZ>R8'YaIJ?ufs:;e*UN+G\7<8_e22D7Y_]4Z#=C+C\%F\OaZ`AcMH=`jVI(ZCUFMm,S495Ml;0hY4`3^RYg'e%fuEgTbAD^p+pbY2tI(f8;`]/Ta>6%BWcAI6YD0rC"u5TbfP<:G6G(EQR[qjJN?k,qU!Ho$cFlgTNP;o)7&)9YS_*SBL5u`2WZd^bS\6YMld_Pg>p7b*W"SO*/ain;\`"ZNJ3Y'eR4Mo'tc-[qklbqI.W4W,PGiaUX?a!].K5rLc;s))R5JQu/B59gd*HgU#cu)Qk*O7(,>M>>J`j[`;j2^tGC`Cai;8/[nNc>#g6.:[^#1EOFP+#+g^X+5:*=\"!D[Bobg#:$X80CZAcTESr8)7Z803-/sTD^ZiaIj2N\k_6t_tPLmrL@$>.j.%&"?3\>?:dAe>NK>ELu52k+as(MOV*(f?2Y+D;S+2?[WH95lh]t1qgSq078&\\5t?K!2;SATHm84iUJ#mfed-0T8Ce!?Wa\c9s#BX%"nL^hrX`6$MVIYo.F!8):$7IhGF$R5D_6jr#,%`5JE1g_S594F!';p5^t:CoP[3/TW9"!]p&M/"Yb>B0$2Qbk]>$L:fW[$L&FM)%Q_GqV@*8C0Js.j_V7XLLMkO=uq[+W3k%1Ws?)&i:uH#WpS3rkSi'=@1H?f=?'h+3@BOj2.O2=^`Q])V1+GI%3K0]$E@?bbBH'a-i1i6&2old$WXeDN+m,sEH0[=pAT-P/rnJpBUMr:e"l1B8;Sj9l6RZ3SMUNp3m-3_m)=pi-9G3D-UX.+UbO\]0,,cO)2>!&t56#KeflW,gq/F8A]=JMSVNS$S?<,0goV_om1.OrV\a'jMg)G!m,';06ArtF(!u=pf6#'Ed<,2]2&lDM';>_"bUS7o<*(s,>dB7G?[]53=L(6&FM-uT*C]'f]3-1_JG>PV]%e>Upon=H-g`n`C[[ji/T3>H7PPgIj!I)XU-.&'B@I%9.CP#j`0L6k$@YgPl-a$EVE`gZR_9lLWkmuPO,48hJ5IUGdmZEOD?SU!KT%3Qf#\%/*Y*-QNYL3%G)JiXhQJu4<-cD*DV7tSo8[6''NEB.P!7=>AO?T6$D39Diodn\hm/T0?WnDR:kp%g9XIcnF_2q[(XFP@CA.."DS-SM5!_;4:[g9qD4&LW2kh-f'd/*m\VBAHF=D#d_8:)&hco3/B30Y2Z>L-WCl.bG@Lu=h#KsOsn\QA>a8at.rAt"Y$ffk]i3MZTVMY2Ar0Mn-a`&_8ek8]6,5e:.08PU.>(JAg+,)(7N1#T!]Xn4lIZ[LFC`?mWMuRtq6o:qn!tE:dQ>%&&fg$CH')6e(-(Gsmj2FNqrnV;XVH.I$l3DD8k$fRPRBXn]I(\L/t-SG$6tDZ^D@7U'%lZ_\'Lp5FD]-?K2C:tk04_?Zc6hIdIh>FF5Y]Un%,4cY^:[T&8dAnl:6$YKpFCeag$S1_'kFG@;A(%F7=@]R4P4L#!7"TN6JjX-[*BEOWF;WHAqZc9kTE@?=P&@bJ=`\d5/k25W?Zd0lJFute(QLuTu,hssJ?M#l5EajeYo^gmq*Gp99^QROYQKe"e541GLfTT74&`14l1qBQg(=T6pGi>n`GS_fX#)RoIi>A](jj*&_nSEI&Q"PKV6dj[t?%6d~> +endstream +endobj +661 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 660 0 R +>> +endobj +662 0 obj +<< /Length 2606 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=-gN)%.&q0LUYk$e`aoVn+ktO"r36ikWp1,LI1:rgq"%@eW.jUhI`No4'[s8W*&g8cpWh`1cdHEbRI2YB-/C&,LB7HONm/d4co??rbTm8I;Nb1@:4O;6%Gc"LsD0biqfpfh5!:p:k".m:\fsc%]]B]^?oK*`0(`_r_3jo,s:9j/_*Xnt/f&$WL2;n'Ml0PAR()hmFFrRdH&X-sf>l46crF5o:is__I+C$Nl^:EIpB5q>:p']4"]&N9Zac"?*(t!'`XV`,[GrpjOnpb7+`"Q.1tVO-.V*@6r/2Zgj!jk;9eG1@:q\/YD(T*[sdP35GU?!;o(f4lSmNV?JJmnPSdoMI@27g_u]rk>dN*[E#?4naNaN%HH?_,,N88/EQP^PrRV,mnNk5d_Yh$7ojNMZt@DR]BNb<6g.(5%gfQArpj&f(0=4o``EJA^7CX-jt[^sC`48!aE*o?6,P[&>imZ*pAFg,rXO#gpe)dUZYNO@c*-mhFJE@AW&?\qGUW;jS44ZJ[.hk>k0C';7OVQ0X=/:6mKMdY3p6c9fa!)eNl(C2=_S="i(CHI#6d?tp>D9o1+b_mf3kiAlS$k)K3@AgND1P'@NcL!ceG'\2)`JU?Y$f%/SnX>Ll$ir8mV($ZX?rn8WGhll&!D[&%89UJ__u.'gF(p`/-3f&A30F:;cd\W'ZluI?#?d3"Jk/lcd59oFi8*5i1+%a>D](s$.ZTK-%[ZU\#^kM-uG-bUU4T7E(olCUGOjH8c%l8c+hWY\Wbb13k4Flm_p$u/TA9-*38m)$G,M`fpH*IMTU%?Jl[oq5lec3&2pKp*;n/`)8m:0dL5X#ChX/B5=iY;R('%FhXBi[nlI19UE>?m08KLhtC11g9U\O$R?_P2%CsleEN"pead8YCDo0A]&,\(E#"2l.#_2Ys2t4Gl?^B,W+]?T@LfuKQ3n#apmnMHM@Ou[TWOi020QY4;;3HFFb_GH("iW`0tt[srik&:6XnR$e">(+<9erm6e[DAW%p!+j]4*`\-8LQq=NWdOpI@$&;"36,C#8F%n1V]$8Y48hTFM\a796O_#c^k,YOoqHKi`WUcdBUJ0se@?%L=b_'tACPe6#SP[:babAk3;W2Z`o,O?o47Ra^k'6\sI5"^0r;]EZ^]82)*.H'1ji954kgRtd0;20uam#n9Ll++LX\)6.dN8en2[-hAhp:#sdrrM5FC&J~> +endstream +endobj +663 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 662 0 R +/Annots 664 0 R +>> +endobj +664 0 obj +[ +665 0 R +] +endobj +665 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 343.08 212.937 388.64 202.937 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +666 0 obj +<< /Length 2872 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>>>sQ?'Ro4HAGWSc"0V%dg,HSo/_qso+`,m^I8Fa6W-.H+[\;G_?holK**I=+9S\r_aO9muF8I6GqrPr/fDa"!/UOp2T@gL5'5(p85A(bDik*S2X<=#b\$OsfT3Tu4RpA"6hon:_o_>X)6an!\7:$Rlb:e\L\15q1EqIMSN>tq;.L.UPX22#B)s1F3IebTHP.smq-H-AS@.bNnG*H(d)hV#46ok,:dm=OX7^k4X/G[P2_of==+1(')#C7@O'`SL!q15._KHLE`3m41Ql[j%H`JY0tU1o\RF9qH>*^3kUbS-"`^\1L;OVo:L(3f43_M.ul.1FS'0TbsP&!@JN5=*"7C7\8G^+mr%IFq8n.(sCco"CgG%O@#9?oUo"n%`Mt*jc1[(%;Y6`,o`%L+lBt7offKNjAA=l]:8K*WVJo'eG6@&WG=E7N:fk9\Ymn#c[_EPDL?It^q:C&i.fWQ.]l&f[\Zc4-l1/jWm<>JS(7_/@U2rB3tBTE>Xb7F9(=K8ZD@C%H3)\cP(`63=dNqqrbSU-C!Dq"_!+mm6EsCT'$_JCk.93RIDGX>#fJV31lR-0aV]BM$Z:Q?-!8j!?I/cn.;6k@9]]R0K&p?,O$,_p4$'7!@*pK8uJh$Q\Z990kJ&Q'mr,tn5^m%oIFXsnSCdWQ2ti2o:/oHFuc*\d*%ZO1gSME]8$5SEb*[O`XCBd?i[TW#^u9!tkKe-8e\B=Iuis1&0N8"hMkd:#/adjM2KpdQF6"kQuW.b)YtKO_fPC%ipKTaB,XaV\Q!<0U;,l*R2Cb%nphb!2YdLN9fJ(Deqq#IcJ(#:9K=3hil8)(t[!$cR2[N*5i0*&\BaCqV+Dd65"-f4?%)V5Kcbl_+u7rfu6WHrW)$aKa,DEmldQ807IZMs:D&5KIeB9^2\L2J;l[2`t9r^;B`2>_%Y&2Uue*O#;nh,m*HYdTf(`CZ3WK@'_"9[Vl3k*iNUL2$7$9!)_6f]q.2pttChY?77A*1h92qOWW-0A8g=3^X]AL,19JIMP`#fN$Q(eLOM_P_Lq4BXK)>pI@%#e.LZ4k1.A1GE7Y4W#:g-F$jOZC>EP19Mca[NE;Q[n4c:iMaM[C#Y7j,?5J`6`q9R.j=\dOjPX4O$RV<=Fu1!YQ@.3u:cC)L6WghTU(6M>Q;trmI-fR7WcfAqMC(okG7W/0Z@C?_qeiTE^B$l^p6b"=5S^FM[.AhJkgA.-@k3@?E:e%\fH7d;a6Q+mcF7>nK=!i!093aOELTtgE,bNu8]J4Zfi362eEI:;b3`qPd"-;NK*UXN/S.V)4Q3=Jo7L.Ukre@OYp;"PZA^+>**BCO;4NZ\]1H.?fY$I1XJednEuBpV;OJYa8-&Tr+TsC.$SVmOO`q,X;"^L!NVV/,GG']..$>S8b("Kg?:&A+%gClN*0%I+1hf;'-&qj#q>TgRp!)k=>.e\XYY)836tqGah]nhu+Wq1*!K)nQ?eW3TI/p\rKTuJKKI!Yspqm?DeAtk;$#'`UN%,'jO]QGsPR@Q7Q>;-(Fpi,40DZf+N-el^-,h\k>T6r3gjpAWP`l"-(CQZrO#G!?AVm@8E4:He1?!&'mg@J7%2%.8W1/\jWB!pTVM_CfSl&r4QM)n]'$qaepOf]1L!YfGr3?98gNHt_CPK)UOWjR/cM\$7HP#kncFI'Lu85j0ee!8"QjFs_b:i?-I<8d9cPE=?hk6Xnej`#>5oqtTT,8tJ7a),D5;o2J[_Np_!H0k`P(j**5s%M>]=hN[/epdB]qWU5GVAul~> +endstream +endobj +667 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 666 0 R +>> +endobj +668 0 obj +<< /Length 2807 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^97,F5(#A1Wi7#\H^2SnE)lfaaZ)h,c>ItO(0&eEI?q;IG0FT"seO=q6/qW5F3f7CW#lggD\m'$TBY)+bVb2bCG,::CMVF@Lo]dYeoIXicMi+_,Ml#X/["LRbA-`I8j'--C"OKsVem16gRTheo35:k3mk@SW]<=%*WB!^F^)I,nU58:C;7E/;rp$_apW&uf_'f^%qSSC$gh#4nIr@U-W]]?knAQjiFfZ/qE?X3K$tV\>ZM]g\S*s'CMN=u_^f]R>MB[cpSa6>+)JD_9d[:$oWBq__'I,(=(O*)M?AW/Ea_L,+La4fLo5G(jm>O&ncAPKMbn_o/1C=<"L)eu]8Q"i-sH;\_[66./h5n(RK;;RAk0XNY'Z?Pl3K@kkW@a#\'JtbV?=NLBm77'CR.DUI9bMhlrI3da$f1pC%0V*QKZ7J&$h?eV#Nn,HW&q^5nuYer)+^K7L%WlkjWZNsGek%_Gb6Um=2%=^$\J]3>\=\G&_[$+S#j=@'r3@f?>LJ3]Y)A5/"O0TAD'"08gNZAXU;Pi(@87r]t=Xm68HD.U;c#HV?4u[5q.aG:*3T$e:V4JUP(g*"=/1b;&SHb,C7\"7"QU*/l9Et9mUcbVMM&/o89Od]Q8@JmmfZ.a+`F+4X[9t%l=G$7skU9ihh*%hHJrNVNaKXb29kE*,JHCbD+2PAoQ3g_]WoQrj;bLR.2qM?F$s#Up^+GsVg/*^<:KE@SIbAl(6lVN;N%XE`V@EXQQ?:o=)PF9rr"Et_2EVZF(W5e:()(EJh&MeIA5Oo[E6D#q-oaE]Sm#N-TK8*6&@E;QhU^W)LY^$Y?C8Ad@qefa?la!C$'GhpBA2k%,3T%-T*(^QPJOpRs/\VRNW7tgH$gs]&i6dao/mFD@#[<1kI?QJNB$pVYi*B0X@u^poQ)28/m.[FTpL=V[Fq!-"s%HBDcgK0eCH/25WJp3i]gk`cruJS1iKHh9F8R(+9D9_FnSAAs?]MASoc[53`WXs\+l>M"ab`h7lP!*mTVi2b=(Ps%l6Xb1m&7+c4qYLb(J@lBa:#XJuHK-2LDNk$r;2d,rB(%3EE@(6J\i+%-jr?N!D#Mc&JAGGm`2X6'@00h_\1!%2/u]^7EgY"He2W9,J#q=raOQEOk=80/>^!"`9n;c"a,j^^OlFFP%PuIPb*'U_%"u'#5q_G)T1Gr(*+ZdOL>5BTJ*7aC$MDMMF=M!o238-)74bKB!'qB;!N5bg\'\2J%/g$^OUu5$t7@oBHtm'.+-giW%H:n`M*^cL)eUJs_Qk%50ANd@-JY+kH2M"]iHYmucs>*fZW\88UHubFm1*.d%IfA=h`gXd8M&[]GB'lr,J>sI.q%NWEsB+t)K\r'hXMV5PRNfehhu`,]"_1\jJ%2WM@hipY/:DcOT?G=7aU;+;]=]Sjq?4@Aej@u=?_]aD&>34H]Pg^OXK$oe7L_@&SJuoTYSr@6_.oHV^$eFL-+'#S3gQ@X/>4qbS^"T?(mODuBc_W*]KDA3q7a(&3TRlEmgams+i.fENLgn(9B&*p<30b)7Ac&J+bemhUhBF%YX=!F#;R&(.k?r-,e%0DgC]Pm`-qVEX>1`:V%%7@Z,>iT#.8.aI%Z)9g'$%o#9V=]?4;e8eh[`tWQ&]WL@bg#?_hODo3qXZP#0CRY0tIhHXN]O9jOGu_"ZhRQgBUD(ZgBR%k/Fh'e!^;<,-EM0hn"m'k1_d=JbVm-Dr^5:HY`tRbG`lfo(g!r+[D-+K;OTe[]((@b;GL:rg.7p#bImoJZ7rW'&$UGgWA5&R$g!rLsHMc7gERKf&JL!*H:VQ_oolepUD#h]4I]\?!Vo8"2Hh@JoXZm6-YQ4U(cGAF[7)9;/%HuVr1qP,">UsqnT+\%hnC:6SM2S\T7or9p*RNq%2+.iATm[l.#AAQa(i'-"hIH69Fc#Lm-/Hq%7!80pZEqq12En>:HT#k)iq,+@UN-1":+Z*gXe>TO^%oPL#6nu$D5H(G^?HTEj\l[je2?N6utIfl>RL"L^chu)/$('Su:#.$`>1fFK1gi_=V^O-I@8=p(>RLd1,2#%`ohh#D~> +endstream +endobj +669 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 668 0 R +>> +endobj +670 0 obj +<< /Length 1978 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D968iG&AJ$CBQUOm,*3rBJncf1ihUjERP`F!LH#O-Jj_L2Toboh4nQ4-6')-rPDYSr4l[DpYqLLW>qG^Fr8cu;jP't^Xl05gP;#8q.)6OB,M*=^HfFfa,6"NKURRH3:7aEB2Xirj[G_MgQFhNnc/Bf&X]ZW.BJf-tHEIKf%UuQ"2k5,GoP?ASD(0RQ?5<:CWm\kN"aJuY,<#'-/=niH_OiA^j@sGPZ=Ac[J&C'pg$$UmrcZ><:Vh_/_8u5AZ]q-JE:A1lauHF6e:GrM!BQ,UuD50#j/Y:;V"c!m".Z?\u]TE=[QtdXZ0G.-^?$?sUt2ZD%Jg0#ea]@ngQ\bSP!-dS@2#:BqI#!^mupcS)2cJo;(3ci0Gp+">"><8bX8Hd(KsIQdhpXMJLl<"8U43Y"T0J^1L3MB/^3fF=g3o>aBMT4OS2#ODoj%Q>6k*dGo\ef?EXQCB:7efDA*7(8K+='e?6>4?e:p0;>WLN^/YlYrDiE5'CN^t=jHFVSitW9llPqf!FO/!'#N.)9"SDPcI%??t;+Tp;>b+IBSdCkUqB>!f?34B;1k9U\d)r6AC\M7H[+0e:FYQ"aE,Ye%tm$=E)e-oXjX'gn2M8u-sZm2L?[fn,L%.UoggM;"Ie[[TAPn<#U`2g;:h[?O&D8?[6ifm6`kVRZ_G,HV8K'7$k>5nlil-W6^XY+"9m3$l"#].I.d!GCaV.f5:2Nb>"\J^Lq4EAK[odsH'f0&+U:'bk%Q%:QuMP+dUh;02K@[.&c/ls$#-0@s(ucfCMgfIr8-J0HZ'M:6*tJoY26pIIN9d-DegAH]NU0p#1.1u!l;R"2C.M&hk@2(!ldb;tJk48PIBtb0?6B%S]0>Pi&/+'3+kOge_q8P3;OQ)5L?_n%d@+@"i;sr_l0V07J0;Ti0">GtFj9lc!ThtLUFn\G@t`3j5"O.q4eAZE0?3lbI7T>&.7Ua`WVX'2gNoV%Rj*>8BcYm:&L2"T:,?ZI,6S5i=L*qc$'t$>G;*%/h"sta3l00?l%bt/=$$4WocR9m_bclDAZB6mBH+??k?;hqqA*jq]RSNPQC,fD`6JJaVbRM:-q=h54d&W5=^bV&n!ZF(Wp/b!_hZ[iUARQPeseCioiB'`cBYWF[Fbe&"+ggCcF9BC[jCbTU=ct\+ltm#Xj.t)^d$grDMW7e_.B??IVrBqB5=gAa\1-0Bfg.blEHD:g/[BB]m7%OSeMj1a72:!=,%Vg;O6if3Ba.eOR4YIlN=DY22bn(J:@&^?X;d.fPeiA(0=p*iCFeCuP.HiG$-C"?7*RA9).mRl[(P&18Xb,=!G9,kbTo5jD%"\(D>be#%be5_ok29.rDK6GpHFBCGugXTb@m">J,PqMc[bubR68sr?@M_F?'Ys\l=n$SnX[q][Kkba[4b*FIcJW'5C%jiQVAfVOBQ[j97N+VG8=Hai>P?\!\fBKV6U`1_AL^W7E"IJth^P;=)0iCs&V1MtBa5P[%gs^;Mc93LR4W]%t3`Brnjf_X7lt3aA1j\jn<2Wg'KX4Q^4O&m:%*QkD1/6Us5OGF:'5$&#o.Q))5$EX)6M(*JJWE[f*!m7,)+/RX4X`UNYDPq''6a@fYN\gP4IE#d^up1bVh%?Q4@he18.PH1gDLjBmOW-8,<=3t__SWI_^H5?63=gHckp[AHnj^2&F_=7tTe)[1Q96Zq3Y\/t-;g>VZ&f@N4f_Eht4^75T^DjJQW(.NO*FaF8Xm<4ZLaASQ>N*SFpD4;C^,0%Rp$rGL8E&lq7P\%CcL#TdH%`o3C7COSdiNngRp%7`Vet\AO;>K.<>?g>&=>sT%e&0B$PRF'K2:CkM=&LqurI)@"rDo!e(r,88!"gH*^&];"Y'/p,aqIRi")W-m7#Q"2WZdB%;$mQ023bo;e4mT?kQi-eG7E`244P%X8<+Rf]>#]%hJ)p%#U(Nt1EX'*8VF.tp2UBiRJ-2b7]X-:EFMF:]`W))&uK7G!5ukaHAcDW`L->O-4s.gt?=5pqL%[8&*]eQ)dm7d_$nCJ'<3#*A8#9[2_+2T!:JdKr)#>.'78\scq\N4I7X51GRc9[k<&AeaZm4h1(tt*QNd]p]&JL%ea99*WfkXUK2Q2I7o`B5b+_UAY0g[it>:0m/q@]9U'Bp/pUE82&VU[8;J%Ujgt-uQht+Kg$\?M/W1P[a#pDVG:]N`_o.1Y=psB4$b#*s`.NOr*72N3H8HTHJ:LIF2_-]eJTfe//A]Bgk*;P%i&`F-n]%/=*0e=JRd]UI:nt_6YLZ_OZVP%Y+?h@i,\0m`s)adj0stq;rMH[-*gC!c7o/W4b`kr&=Fg6``-DHZ2T#@uR`,Y#K4j5$i"qmsX#RRQ.KjrdHsEC"aD7+!!ZPFNUJm0+TM1R:eN8cta\fWM\)#WZgCmh,0gI&QuYS5[ulR#,nh0mu5AC2]M[?+gOZ"9QN,2mYoI_Epk6.JPS't'Bgnpm68:5]RHt+9Q:>VQoM5N`W,Q@IKLmk>mY5Ynug+lKcI_-f_[$UFitJ@_bsL#S3`16`B`F%:^EK['FK1&ZU4[/Yq/ujN&YcIcr,;k^i$]chm6JWn7?:-rW+"mX]b~> +endstream +endobj +673 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 672 0 R +>> +endobj +674 0 obj +<< /Length 2397 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=gN)%<%"7kOB`)=d-DF%g=&5$ZS]$M/EDf;@+6#DaaHa`!,+2`bMYm&M0@BnG=7W_hb[a+f(CM2_#JfnNFu6aM8![kVUY4YJ*8M]>,8:&ZUX4a>^?HT&5FS]D?Vf-5qL1=@gMX_"F?EU=P>(p-Q\e[;&X3-\H=Z=(T7!d_$?jbGe!Jh``nBS1B_I`Ais":?RFr3ii'QK+W?VXDFNg_1\3%CH`B8ueT&#s:f7M0icQn[E'AGKLRTC\"DjFs;S51L0-X.<72kk[W=n?p_*[[%?JsV3qHH@C4iF?'D#&O"hXj-eXUV'3/opFj;HC1IM[Hi)8%'RmZNJdls,!aaboE6DVDXnG=RAD[ZU04HU#eB21j+A$+i0:EiL,L]h^Df`Hd!cBindt^am%f&#(oR'hW=Z9112oAJ-0V/5:JMLY52r^0^#3U+.8%KnO.dFmQX,5V8WHkCh1m4)b0cgJ\CGfDgSjPlM47$,Tr6!`(\QJCK1<5XH-Sm'oYR\\h7"'giI:EL&Ij10!.--:8Wi8PSYu1M(H]OQe6JY&Mo\6=gltO!,;SM6Pf2XlNrPs3L(N^rBYdtOOY:fek_k^;.$,`UE%lT,-<^i./;V/PJNs6N&SYuYT5\1BC3=S@2M"V^(3-(ZgXbsceHE[`jpbIo@4>`*m+d]+h^r4=R[e)a,&at"b"YS2C8!Bn%AD9T7;C6J`r40nt1g^n/3$=>!Z9H[?!`Cpco-@&Q7r$G%P9?s8OL,mJ5.JlHeN2H,E1GhHfFM,U0DOZ7?"`![oL<,#fiM8C:'IE[2p`"SP\llSiT!!&ZV3-u;EG9:mkqfD8V$?KV(auo8rP?5%p]NSMPfpN*`4Ad+g1^=='(.3aufA3Ae[I*Pq-BMa7V'#`sKC8:GLa*eqaHGZ"nj2_GrN=:%qXa'HkJY!d;^:$:9)j=mA32\A^g;m5NS>rjI7g)D[!FqbGIX;-Y9SS9a%l-0TQAb63)n!>TZ.L;#aiL(I]K!q\2YM6\660F>D6!FsRaE>Po>4F7Pg_.,(c]3GoR7?CBQ4L+:k1=NAJR25T#r"m8KfU`]Mh'qFa)'/?!\Sk!_m^\KcA8K5W+7m7ua1H@DYEisT9/[A03(m>tia7hua3U!gk'm=*Z!eKi]9Tgg=+B[KrDNnigs4c.P193uOh[>DtgjCEYHDr[K$*=3!/Dm&Q7bE*mK3>7'"/-'?hGtN]>l5V5RhK1MF#Q@uWhe^W8o%d9ScuYFKON=VB/((p"3!s"JU[[.!q&e~> +endstream +endobj +675 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 674 0 R +/Annots 676 0 R +>> +endobj +676 0 obj +[ +677 0 R +] +endobj +677 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 330.04 406.028 375.6 396.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +678 0 obj +<< /Length 748 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU19lJc?%))O>#X,$","mgTNuSN!A(fSRcum?O.!gtl\aeZMWFWEmIf3ftMj;d4!Xo)#SW"=OKQFujQu5nl\uUIT7R<0hKU,@n&`H$PYYU*[;[sT'Eg8$p(Sl.%Y9?4[$Dnt]d1_+`_>,8[qbi<+Ml'S"eTt$S0Vi[==2,M/S^9"&=]Tkee0]E@m0mS]J*N2KRdE`IbX*q_(jLoS2J_J)OPHT)D9G&f*6X6eZ\YT'Q]nMS(i9bALjt@(%"k/JWI[1.T`a7Q5rI`Q1/3rlP"MeQ?+lm@qY`)C#4>!(Z3"jT!h68?k+flE?3]@`"ff2seXGq0(lc_J-Jic[41_jYQ>mH-Y^1e>tZ&K6,KmV5f!NVNSeBCUrY@<#gj@i[d)?\2r+nl:u5a^a//_mbQlCW#[-C[N:IMN5,QiIdl^hr;/Ab-Z.qa\>r"/MT.^0lUZ$*D/a'nk02>W-of/2BUm^Ah\n"";m)(Y@bp2*O@8%CFaqRUSUT'0`6NTE4qN2DWVM4[~> +endstream +endobj +679 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 678 0 R +>> +endobj +680 0 obj +<< /Length 2460 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU5=``=U&:XAWd&7J_3u5`';L6Qs3@<%I[O5+ELH#eU$(2W/Ue65^h!'kQGR:tb+K=Vh7=[d6pY5#gjmUBV(N6(*j'24f\N[8GpF"8)(*@0%\RI4t(RF*8PN?EIjj^Q;ZcOc&D=PO.]B/TQXPBIQ;3K;3OrN_]P`+.pi[%gZS0i:JU?6/eZMa]g#j6bM%l.C#)*%BM:Us"r&[JK_P&$Xe\Z!RRXB)-+0bSq&3,`@Ip<#Fki,Ab3@jlX`/@i)\)>H/6j/c#6=eh`Jp-@RdV`d?`Q,o;j`'l;SXU/W/%V3cagaRiLOPf)-ZYJqG2XX>6m4,[n-/nLa9^Zc_N3d@jTi)UQGmr4Oeji$`_'R]UKjY_FD+Fc+#(aRR#^iL"j:*A+J1UU).%Ja6G^c6g"u_Vn-Ih(0FeN%m'"SiD-4gpY,VKV)>cnN*]7-R;;ouP$f+*p$HGDcFO&i8nAgT.?ce!@3oRVShU8o0/&UhAX4Q'`/:T>ceBFPcNq*PXR&m;3'?*98r=]%i\#E\X2mS%GWr#d>^Ge`JP]H_eMAWA%,c8pQ5(((]Ze?Nk;_C`L!I]&F3KY%jm%N%tGZ/jFq-I"pQ?T9RqLL/F&Qb]RRa[F1ZZ-=!gT<^OV.#b5;!KM6X@6Hh;NS+b'F^?m;o6f*(NgV5PZi"77EI't""8FpECr0#PW[JgiVSYR?s=DZL^T=4@4e[k1hLMm'2ZHaS7=J<+0Ol0Nr%ebm1TDLf;/dB%?Ze=-cT(a&BD>bs.rG1)Wa+d.Z`co<9sBmH$Og\Wu;`dR7m$k9Id"-;bB]Ud't&_[k%%9=Y[CbC^T_6:c!>9sEksKaGX$u/TnUNi89cs>r)M2Q3\M`k76VY@Wpt_RT(8S*R_G.U7WnUrqU:5OEd`'>>_aRk2pSEdq"?+NH'1]B0EZi(*_BC2]obQV[1mF8n3CBZN)^):+P2bt$hggjZ@iPd/&JIe^KCY`l`9F`%3Y75!i!AS6\'rp^br_Z!<'H]mNDokZ9:l6$:.o[p[U2ifiDKBMf:F#mjC2J4kC8T(`)q*W"DadrjmGYG&:P6GhN6!s6^<8YYO)C^qA$kIsj;>r#$t:ns&"m+9BpILlG&8mV"Y>r\>27FVW6$/C"kKu$m[#X07o%7k-8lDEY%64F!`Y>/el\8mecSMMg^/^WF=.SW%AuXV6`C@)$kIF3jUR_lKSJle(nR`4[!]a7dHgVDP6MeU$nX;c]a_:Oee-AB(`gpcsWBJlr\OP&2c(pWmmV4aPo6*jjAr-Tp.Le:P4i=(aN"kWK+&ZZgd^IirYG%A<^%Al)uO.[>/g90G\"NkeY=&\g/3Iis1\jRfY(1d)Pj:i#JGs!G_ooe\1PWNi7>$>N9l.Us0J=!mn``!fdt60&@`F5P$33`C.8>c?RcW@Q[.?,r[Mh$te3uc>/".j+U0HduY2:^/J?kIdIP:nZP.@qSkk^A^EnCZ7aCs`t'fpIW8.X&s27,,VA.\;1:G*!&"ENqMJ9mQ?"oVTU)/R,u&S;Fl(6^_CPBi+I#H/YYI'plcn3k^4,KmRPh*ePW!lIEhDP;h-Sk6@0cJ5l+TKdBT663@+gRh/Lah-$$D&<>CL5$lP=O-^5F>s1T:1'MVPb:<1e9d0S5%n;X>q(>jLBhZE:CA!NoG`e[Rhm%M9#$7s=>8,24M)>n6Z?Fb*%3+oBE5tbg!8&Hmr8J+h;PW4S!A!;5)bV#>/PG%A[Y?P4^RV_fZf7J7q=3)$rkD0oiG;h?12?4_J]hV3<-/!$r\]InC8P&%&R3b\3sg0/_3T?Y(O\F9K4!0"f2dO"bfoDdKfjt93]W2-F"O(:A^dKnGESf1<(C/j1(^o\?aJ"9hJq9hB?(L7tt@G+Z`cVEN0;?eUQNpeF27:mX8N#]&".kf/9N`&Fq>$!h)C]c0`\6lu;MT1s=De%G"bT4mdnl"\;UG*C,<04s@s-M"d2Jr2c?1?@_,3hQd!t9\,1at4.E]r#X!#@/0k)3j7I1>)k]!`XDDK&%.jRfWK?e;"Sl)jB,NkfPH&-q$kHtq@+.495,Thb3_0/mQ$&Vrd1IMq9FDjDdN!ce"Z-i~> +endstream +endobj +681 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 680 0 R +/Annots 682 0 R +>> +endobj +682 0 obj +[ +683 0 R +684 0 R +] +endobj +683 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 321.13 394.714 366.69 384.714 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +684 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 325.0 329.714 367.5 319.714 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 367 0 R +/H /I +>> +endobj +685 0 obj +<< /Length 2720 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0F>E>8p&q801Yib&k8/^!/bpCe`aDI0C2EOdUfqY=d5.#=aD?X^^)oe:qIgWER$G*KdpT,QM!X%7Y?X:CLMnE3+puuZVF6Chr[bAH_cZUtsfc:"$Ze0nWiB25;E43o$hl]36E\BBJAp3JllfYIp4Y-C[>nr&m-2odj0Ac5nh"R.^c8Jr#f,8r'09X#5)SMV5]W.LSP,2lB+h&4kgb^Q1dI\sUYER<[aOCq8Yg\(/TrNj8NAm4Db(1YsDc^oeh(\j7Q!U?ilHB;DP$DB&m(Q*""QVN%n%\(FeYcLC+c&YZp^V)QM'eMMCL(;9eI3(frR?A66TH.?Z/l\?ZgbN>.qobSORj&]saA>M,2u-XO)VAg0N#jXJfW\ajHELW.,[5U[:C@djaOT>UroTrS;BqQ=:bjX'*DS,o59\bDB-Z1AJBRbbE)^u]]aiR%u^\CsQ?RB%_\No;f,3,b59@[D.H>KQM]o9+f\LY!u4"6h*C+`gN]S"(MF<+Ni5`0hP(H`;aB9FSH_QfoK?rIpt'6>El&d`thke4qEq[QgW;M#TdFsDtaZ]*nhpBQP<@lmC"$tE8&4=_tIs7ZH`IuSE=WZPe:+`;hB9!P.)7kHNMh?:S'RaIe('3PU"(P0-Q_gki+Fbg(-Qr./_.dRTZ,7L*J;=9k3g.6,A2$#j+&]17W/7BVX>Zi:_YbF^CeI?05KMg/u/:W18ZeE.]VL02#CTZ<1-W_5/q5NjajRC,n=n5fiE%f2uZpe^MZI"9YW9H4Xc+2(,]aX-AZWg!fe1-=L:;O:$s;l3S)l)n.NPcmX9^s-%)b\a>h*5O$?E@R!a,CZf"kEZNd-ID4mK!jo"h%[%TRXN@K1M6,58a?@(XCgiM:K%U.VtXGk=uRWX]HYCHrsi<5ONCP;**;Mt0,1EHLk7TAVN\7g)ADf7]HSmq9,3HtGr^*c"oQ6'9R=P=&3+?dg6=aL*;5I&<$9apopc'E`aLOig*QWVFn.g2$oBi9lb[U3!h7pG3!LuV"jUt&ps9745<>bR$r50H`P;;"?D.V,d-Xoui^)'lq,[%Q\YSa]n+`f_5MCD-&?odGAL9@1bh$$qW_DUu<8.e3uJr&^90W[VhPdh3FZKE*T]WnhDb3FV2)HrK3S930++UV!gAT)3TuZ5\$Xcfs!mNJg<40oOAm[^i=degt1?73A8FVpNO,F340BeDa_:IDUN=5^'B/fs-lG+&[&)Y;`u[8lJpnji7UVc1Q\_-=V!9(kLj:h5CC!@qTCr?831QibicK`hr$6QnU/op%0JYRgS4iZa@Mi#PhP`9+C\R9oos1k)P'6!j0k8CkHgVp>ImS:$p)YD10UrEu.*Qj'^qhM@Uen0kPT%FM@Qd<.b)`M`)F4`:Yq\Sh9gZ">MN*\V9',pWuA$".MdfPr<7ht>o[A3/'\KWW=h$0V+,5>o9&(E28!T#uh'LnNot=hmfEUE+_"m>I`M`d-I+YSf/RcGm,tZ>*J]LFL[0[B*c4Fad(Lm8)bDX3^&#J-e54UE+BM>aU\`erO#Cf>IIFZf57AqtkT^8!_17lUtMn1 +endstream +endobj +686 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 685 0 R +/Annots 687 0 R +>> +endobj +687 0 obj +[ +688 0 R +689 0 R +690 0 R +691 0 R +] +endobj +688 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 327.8 353.168 373.36 343.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +689 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 102.83 225.196 148.39 215.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +690 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 186.16 109.805 211.16 99.805 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 219 0 R +/H /I +>> +endobj +691 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 230.6 109.805 255.6 99.805 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 221 0 R +/H /I +>> +endobj +692 0 obj +<< /Length 2521 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0F9lo&I&A@7.BQU6&M&DHh8MU12C"$VP:7B;C8pdd`J4N3t,*=?>@XFt:^.V'4hNECuVs3t#P2HCdqf_]ck[3M6T"YV&T'Nn]q=9Y0>!;FG0)^';0;=qu)`qIbRVt\tB@U0L*hE0o1\e&5J(M!-*.?uoe%\s>PD.XOdFml$6CK4,r9eU(,3e,/AKlZF9U^rV9NgZnN$2:14t##Roqt7k.gY`$KKeOW`!_'sELYLrXrHGSE-o@E=r9,Y`5*eI>>k4h?sp!,EbA9em[ei\99[$6cu[:aKk*##:oB`9!(f.56:0)K&r!&hF(aY6but`>"bR.oP/V'].$CSG)mmRn9R[bHRY/K%E"d:01!us!kZJ:mhE_9/udC9^4].7F6ZJ1@H)55@D7C?2OT1C^8Zm8,j93^-[Pgt'@(>ulg:UP\c5mr\]$2DuuSAmW-ZERE,YgqWlQr].\pNoB7@>[3ochqn*Go(,JTc4=5Fh#7OKbf+q:C^EadtQMtaS6)X7NnS-]*;mbn6p-Scbt%%c94CZX?=rj9.BOf/d[:P'U53'3ejfFbkUS@_"XD0oBb#>Qd>WW$U5K.b@/*@`,VR2HJdf!U;&>R"f8p1V@DV(NJ4HO,Q^R$4ZNu/5&k9fNCZh)M)4C$D"JQ(q0APEKAXFjD)N,'S?ToLb\#qtO5Tt0[AE_L:,O*tr+oLjebR::V7+eGr>-I=q0f:GP0BGQe[JK"l`p1kI&gX%NnY_.#(m'44ooT-6DE[FN$X+T(:s!cAfsErIiC%3M`hC-#W`pgt'XaqPK*$L/J9G6lOii:KKshJX9as8%ZHh1B-GT`%Z)UX65%u/R;6q=`bc;'35(F5VOsLe#1-a?dH(F%X0?=Ti:hWLRYE:]/n1rTu?/&B?C#.NS"2*$5RFH(@>-P4KYgJ!J'LWl#%PoV`:Chmt&m4"+[N0)c8tJqbbK;V'Fo[c8Gl^^$K!#5SnQDJ')@i,kPtRAcQ]]=R/@LiJ,/osj0s%q8RcK;;1FeAF[8A354l/#-ElGXYCMp&o\5Y`E4rV-k?dYOj9n9\J(nJY4&3-h3mA$XPk6='9'2F6Wh>@Y_oM-/hm(#ugN*Y\R"b4.&oK-A>VGc$IEAf?GI=d?aj%pSn"jdMeB=]+>_bS_h/$6^*0!Z&gNCWU*9bT":^j"2;5F2d<"N%4#k\5.C8ElUB[="bH%I&)?jc@L0BnS0`@`gEM4ZUa0(Qr!!(r"Cu*i)^ljn[*Pap?1p'.X(L:H^.?SVHSm)ra_hIl"#R>T6N_=;3HFl2:_,deLeIlR,<=0_p)sfTX,r00)R0eLAtAN3JW'?h:`'$c$i0s\aVG-a>!\h/1)q&oBO06'CoOMSigVT3EsaMpk62]mY>g*fK@]kgO=/8tcoT]HP0)3C&n=_S:qh.cYSD@ud?QtrFAL0;p9pJiG=2CXA(hb2o-YG2O]fELoY3OG4$IYana4uZX+#d#Y-\5`[##PO1fuQVMV]KYk(4qR,qKQG=_rW:o/2a7Ei5p"9l>]*b74p%@o#nl;IrR\@uJB6DE?r4rT,#eq2kf?po&ou^Fi/=apDR?&]5D/J_/K2CE'M0WF]h>A(Hc$fOiM&g)7lX32&/Oei6;YWrqh3^qGFaEFnJens&T?!nCesi47cq\k[3'o9XF~> +endstream +endobj +693 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 692 0 R +/Annots 694 0 R +>> +endobj +694 0 obj +[ +695 0 R +696 0 R +697 0 R +698 0 R +699 0 R +] +endobj +695 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 463.89 705.5 501.39 695.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +696 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.0 529.229 254.0 519.229 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +697 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 470.069 278.0 460.069 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 83 0 R +/H /I +>> +endobj +698 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 208.08 274.349 253.64 264.349 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +699 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 405.55 216.458 463.05 206.458 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 221 0 R +/H /I +>> +endobj +700 0 obj +<< /Length 2420 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0E>Ar7S'RnB3&G!+@W)Snj#K))KZ=_bC/9SFWV@U%%4%(AL';[\apS0"g4HrFVkqM0eOq5n#4*Ti3ioJWYj<3@OS]0m4GLe._n`:j51+^0dIc@'>=]E\uZ^\;%L??-$=Nbsf_JsSF+$7(WXJEh[$hZrqV!Wi55(%*`BsV6"^/X"R"3iou'47_OGs]2$oUh/$cPk5O+8`k_H3+=.a%)#ImM>VqH+/I/"=hRjP5>,gC((:,4ej*M="$>?maBIrl.lV/pMIYP]8N+`kqj'QVBX?\mV9-JWfc_2_'LL6_[")3('s!bYCICg+9e/7l*BOKY9#R_#`L[X]-aH=^eK,3Nk_&`@O$($"*43N_=PkC,uC;r:nnJ8D_9!6k^\^rX^SVlZ:cOOW9-O8pC;SM6Z9aKV[>5\"kR7!\sapAT<:@Y,L<g2;"EG4MAhmr)_FI!?JU`u$!7[u#b?@sH=oC#2=%`"A4N0)I+.&lJ(i`(;M4Of/:JR-_(3i[hJ;H%-Z(EFQ[XOUeerRIehnB$4ID$dX%^-fM_!$t\AXJ-WOJ\c_4'"a^FgkkWqHR1WE8n:5WuMlt&2/&BeX]8RNtU*qP)mO#6?9_/9"ZED7ZqT+1Qi%#6.aV9K?N#e!#/,Sg-.Leik/1X*OB1hS2RM.86@3]+-RPk;5O>6N]#:f9MiFpI_OC_#.+k"`#a^6-`f3/MO]\6Uq=l%N'U.)gh9KWNHBh(^6cbsq.3J8cAj6n$1;JsRNJ0?\iT&0MDi#qZ>R#UK"Q:*ajho!$-p&1@q$NXE$qks=#OSE.P@aGp$eJI+-OQm=%_1f#SO-J1u#eut,8M;Sd]bDj"b,+[R%5KS/SVB2KP;`G[2"ia5u%#7VaM@=Kbk$3f1r]usEJp$i=Lc.UEK;kJ7S/'_"LMJu"_RhTT),#n=i8`IhaIH'E-U@E8XJ!i\B6?1=9LqPe$o)F#^WVDYa4P/l/+"L.%obWH3Fu]1nFVQpSODjku1KWn\'1h\1-X`YB%!p6E#=A4DmZ;X(Tr.p,XhC[4ZC;lPEZlqYf?n]*L*p*```?AdTT-q3l,(g?c2R<8P1)oiX4eVsjbiJm,,9+U%F=o`k<&Vl?4"-;"%6HQ[h*N%ESD$6N,&,c\19J81!Vd`4j^,`[RHlQ`>hUbU`PssM8o6'''RZF2e9B3:=b9T\mCAq)+KD6JKo4gIT2*$Tg7FYFQ0p_aD/I`B]=?tZUDVUaFX"On]'&$CFf,rnQk9Pq%08goM.+@'=48rfC*^?>lGqN87[E9b-s#Gru("#!@-?&%=^9<^:kFQ0h>b5r2EnN\:"Nfg.u_2b3/KPq0hQX33D"/W8BV#!(;%boV83+%Z^I(<\FXY@^J8lsZc,^1ug=$8q3/eK]]6gP'*&ncZJJ!lj^L'UC\g$X%bI=:Wd\N.l1.c4'S"70T>-`'JH^5QFq)8S?1Xb2$&qgL:T@$#mbWbAI4]1rW(]!@(s`CRG@1s:XRQ.?+Q$;gF4%2hVG']UG[WP2]$238VC0:_"[RgR&_!?*+\,l~> +endstream +endobj +701 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 700 0 R +/Annots 702 0 R +>> +endobj +702 0 obj +[ +703 0 R +] +endobj +703 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 453.34 603.109 498.9 593.109 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +704 0 obj +<< /Length 2521 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=gMYb*&:Ml+#_$Ihe?hd&K-EQ>QYhY(gMPIVMl%#l7)ds(P,h73r;BMPCFE,e@k@1[.78,f3&BaAkR[]0EG&uf8%RhQB=deUL,E97@Eg%VSYJ7P4.]4u"IUVe$Wo2sW4(_FY(UQ9ZurK-)Z2T=;I8ohSO&"No/"4F%c@7M[T(+"XtL^2*]efe'XFn(^fQa^nWGn.N>m1aC0*:?Vn'N'i"g*S[%K3n4MnBI+nV!2TQ`V18rn1)@9*GoWT".UmMb]A9H^\ZZib;.Mj:EnQg@FV!ZTnA"6%p`p@3&L9A2KOX`Ht6W$H)OO)+/?:*(jD3-+5g+Zp)3f<#r``oCh_plXmQIduf2cDnh3uXjkC;$$f:MNlk2hrQ,b8":]3dd+Pq:*HahsUL\-q0u,R-4Aj)$<.7@^&P)r*5LBQ>,u?M:+@Y\)S0q9XlKm$-RC'ISFM,_FX/SA9j:,-.Is!.9i>q@SFh6X_HI@[St6Y2s]2'8tT3.QN`KS]7"upAr)6b/q#?TOg-=Z,fE8"qD;MslY$<-OpZh1`uBi3l-b'c@OcXJ4A(4d-?>`>GXYK7a/\J3*`Q]Om6uVR08aiA$=8o?IN6gJ]1O5Y:%@[(O5n+r+,((89g+2%>,"p^lo^C\bP&aKCY2E;9L6GrAGUG1Ssh8tY%7TZl5`AjIJWF5eTf7\=+[pSC2dBRiTp4[r]*s>:"qqAm5?G#?%)'a=c^rknk:Uuqnqi'Mq=g+#g[QR]XBtVm8:N^Kr=Fo&D_qH\@%9j[sP.FqC'bfpeLBoG\rlP3I:"]`g\!90"Odkp[;GareCYHG_jSg?D>fWB"QZq1bSk*DmrrhRW(MX\lQ!^"F4msr^PZT:MR(*LsnY3MT8kCO,h*@coOsb;cjd(X6+LC\,\$(_C9Ag=MJ+QOG+rb.O/&K`@V/N]QaO1t:p8"PfhQS#OUn&;I/:dE.]adlO<^j:(>V$)'-,kE:8!ZgMXWZG=R+SWF*]SZu/&,NG]fBG$1:Y.aOqk#S<9kCk%$m4eC/7hB",AWB-fYpj"90p4EU8O7XT;9k!T_Irj@MVaCA:l_:Q400(&ausG@eMsEl/8UhC3F<_$^?g*TbUEV1odLWKoRr%[%+W$)4R95U*F]*/f&3MdRir+1YPF#L/DO)V:^k)7C1.qYVa^5[6[hE&r#IHH7p5aal!*71P-p'jZ#u)50s7Vj\SF-/'?r8j84S&-jH7C<)c%[r63`>6)WB6-:!H'G(YaW;$hun4B'6ClB@[3LM4Mj,dn5.Zb`$h2S:HO=#PZl&Kg/mN2XCJud:C]DimF`^=Ee\9g2?)MRi'RZ/J]F92uRe;U4GLc5$FR?*Q_To1'DhrI)$AF[1'e^)iFHJ>go]l('b1gEGNY&4"qP3lr3S)?W>Weq5IueC;&)Bl;mtigWn`7E0!TGq`iebpLrJ#POQg//`V%@BQ71]ob-OE)dm0C"uB@`7U*05j>*$aUERDPR/r48g;\W0%ZnP'EI53^@%p!'2eA/*;G[@K5L\-CIeE$@TsWYa7bVHdCPB_fDQW:_pp\]lk"dl:Ga_@H1UT"o>^I'2fD=`\WI/pW]EEKP(hWu\LI-?d5%kh`\nSKk'CL3:PC4P1^eldr/B3\(>Hd4)l[`tkQ +endstream +endobj +705 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 704 0 R +/Annots 706 0 R +>> +endobj +706 0 obj +[ +707 0 R +] +endobj +707 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 300.86 550.389 345.86 540.389 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 49 0 R +/H /I +>> +endobj +708 0 obj +<< /Length 2310 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm==`<%a&:W67+Oh4>LhJBZ1Hb`Y8#"M<;k$?TfI=$KQPL0j<-[&7d3%6h7<)t^?-*S6!!,[YcLK.XD=\#b@l2-YZD]Y=bTD6<.r,+@Na/Kj_D-.*1G'E7-cFJ>>K`GC)#ZI@j):78E"&%-i,#\s)1'RY-Kbi+F_\]i]R<@Eb`79>LFe<](8;lef`TqHVlct&&HFp>Sul`&^"2F+5cnZ)g2C(i`YmbY^gcS@-]+a`i=OfZ2nr6E^7Zu'+r:?JBrq[:6e?)^,mJ7STgdX%q&;s7]aLa5&OQu);nlQ)9Qpqi@kDq;JHTc+M2Nq[2XtQ;>jApZk?=Pl;DoUJbLTssPq'jhq=YbW*:r(.kj*$VEfu,-S:%o^fLJ\P"qfMQoeGA[BC_Ln7B*F<0oAD"?t):=j\c]E:R;l!-\%d[Z#Z7dngHiOdkZrT'VZ3o>/#!mYT#*#H)U*$]oniX0jUZrnAQC+5@+im4O_;@*Y_#ou'[1J0%k0M[9o_IEm_pUi#D&`qFgFT'$gee!F)psD04[,HN)uXt:u+=tO7h"U-Y/GS&AMQ'(J5l(R6*-/*=2GN9#$$l]BZ[%@[mWo'k5bQ[S!Bpf`jr`2f/cP-]XXq#!P]iYre:pPuR2=V)^1"3.s"="[Xk5?N_rmI@Cc\bUgUcK:7S4qILNlWeMiL@t"#mc(I'l/juYB=spp#AlPVO\Uc)Iq':0`.`@3q^_tInabP2\J'Gb,4B@qt#MIAq*H!cSR8XUImIg:3@2&O=j>959Nc*P0qR],5ZE8rb@#7_@ZW)'r&[4S8QZ2@_W?Yr>;UDDE>U[UK]iA3%2<&U,k!('`).fSF]sqs5\.pE_hpF3Na*cEG[6HR,'dKG-)T]j"<*nX_/k=;7#%mDGSACR7`aT(s5m7Y^T`qp=$6^8%#uPgFlM!Hhr"(fD$67=NIsM5)Q7VfcPV!^TM"Q5ch3R$h@M7$eH2oZ^LL\aU5a-YQUpMruRr:L1=#-\oT-lq5JukasU=cm\'Ilq:C;%o5d;bo`RjAd'Uf@DSdrme)tEZVtGl'NU*,=&94oSesTi[_(U2rA]C>q^>/2oRO$Qr;cq`aFgf%X\M`.Lf=CXjlX7p_CO:ZlZZrk=!hYq=ea8j.b!Od;B#a/GB6!u4H"5%bQ7&NlqHK2IS*;gO`\:fVcGi'"$^AA?D<.srp0cMPYE@9sVjjNoHa'\porE=$gb+XRLa1?QK_h]1C>VYjFIa>g+)+-0q0IBe$MbFqZ(%VSk0uFJ4i(=ZdH?DUX&mcL@V[O:c(Y,%Qn`64^fr%GmB&<>.mpSu&(:]q4c>=!HB0L]$q")`3BX]4ugZHp2?0GNJn)+JH[fc:C'O@'(LYCkoM\SG*kdeJ0$9Sep]NlS!OllYHubYldg`*L'k=4m35e8W(0Cu#eL$6Z%lJO=[Da(t?Ih7FZna'ulNoHDKa_b2cu34>`3?MbO*30*KFVFOm9,E=K7F:V,fC]RgSo>UCf0CPqU"VKI^,LZYaJ482Vkd2fH7[m&l$*ae%mWp#>V?G2m*9Woce&EhGfAfl;lLlI98,=d`ei[bE:M9[j5F%],JH~> +endstream +endobj +709 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 708 0 R +/Annots 710 0 R +>> +endobj +710 0 obj +[ +711 0 R +712 0 R +713 0 R +] +endobj +711 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 213.92 620.14 271.42 610.14 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 221 0 R +/H /I +>> +endobj +712 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 347.476 137.56 337.476 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +713 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 108.94 86.064 153.94 76.064 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 51 0 R +/H /I +>> +endobj +714 0 obj +<< /Length 1706 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHLgMYb*&:Ml+kUd)PX1HK#Cm,'?d;4D&h-H]A""htV;2Oq.<%`[XOe%:qRBcq%=9Jm\RH2S^3HJfr&*;3NbM3I=5OchW;;g7PKjFb/TpJ:=/e8:RN&.YR]^I4Vkb!0b1^N#tP<2K\X5H!12p^Yr6-[m!-V_-\Jtj&aR*VDIo&a6ALSru>YP\GcUg=mSI<`/,FS<%Tl3FNdb8%=e'QkuK)8A,6eA.mf>XFK>U?uKrTF85IH6j8/\Y)U8nQNq&j6/T9r%n\;bk'o/f*;t%Oe5*d38\X-c_E&8Ie9*=pdm14K;lQ@n%jU/75e8;A7]K?VeOR,.l>q:K%r>6e^g0`?5%Q+!HIH2=lMu&Fl^b-FEY5[.HY=r`<3HP1gId0dQB@WO`aB<1)=t+i?-\6V-$Yf[*?>D.8N`Ot5k+j\S,V0CfSXm4T6GAnt!KJA1K5oPmCg:-c0^V\7mQ[ZA'p3,pO=g3(&/a`>GQ">a))C_$#,E^aGNgHn$9ce.^u(?K'Nj?9s+nI39qigT<,Y"=M2r^+adp,#7m`,T5BP(bN+(J4p4r9>ZkA93V@GFo%>A8T8(*q[Qnm;D9Mp%H68$cW#S=mHMo&k3%HomCAWk"q89^]/^OW8M[S;Fc:G&Cs0^a-$p@`$!T2.qB"+EnnVpdGcP8'@kAIVEss;D-N#Ke#8>uR#;8qYcJ_(P2B'H"-3sK'8DAqT(^Z-0JsN>/VMZa.PG+?WTrDD!93@#ajeGa"S6ck`,BU^i/YOGj1ME\>GF\@Y\ml!:cVK'SFAfdHnBs+eA]nR^bVf&C%53?eP%3YCq8)r0\&gs44eBl(tU)\V#6:&'j%T$[^oYWO.cI)?N&q!PF%Kbpa\3GltWX2m+df_*HON\3;$9UJ9Lj,i,q\W/(mHYY2h"?]M[S1T"jgf-V]6OUn)AHr[H%&,JJ$&!g3GRf.W@/d=\?[.T6R+I*)pm&9VSV&[QN,D4cHJ61Xr9(V],Fm2@-#XaI!F'=roO^p1`B5gYURQim/;fka02.sJZo`lXXIr3C:^F8YiLpr`683;`K$;Y(O`K=MP-i_Qh`V[EZDkQ4/RkHA&I[o]!B\,QbphBMTiC0d]-/4*,=U6-GMdl:q-=7=@'u-!/qRK`Djk4h-pR@.?pi+/q*~> +endstream +endobj +715 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 714 0 R +/Annots 716 0 R +>> +endobj +716 0 obj +[ +717 0 R +718 0 R +] +endobj +717 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 362.52 691.866 408.08 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +718 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 459.74 639.894 502.24 629.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 261 0 R +/H /I +>> +endobj +719 0 obj +<< /Length 1165 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%#_/c#!&A@6WR%0>-RNcR`*7L^OVUIGkKX?N3#8n;P#"u*Eio]\[)r"O.W0?b*'getqp?A^W+Zk""RRlpRjZp\TL.PJIf?1Rp??,j)#7cpJ#i:1(k98\]V^ri0F,F"iJO9g^aZ(VL/Hs1rBn8Q6j/IMH;#9!5WI?cupZKVsg5Eut.iqp#%$Fl$g!E32L!*VT]NN^@=T'R'6:#B5nAi'0h:a??:EYGbprLF3pXA0$D&`\R)P3UFe8*4!l^)^gA.O;jMF,Jr:g$"mAi*&`1t%esRVDC`RhDFF9@7^sBO8$Q(_HnbPcuupMggHhYV[h\&b!OuS>&'7*MB4/\$,OM)62\=f#u$c=>#>n]rJl1%!7DDj,2iK,CsUB&4IduQ*Ynda;g\nWt1E#SaBkVpRZ-T4EN`/j5_pNM:/7mU:Io0N].l>[]O707[%U.8C+'SfFu2ql+Pu6?lT+M<%V$,dMXaX.?+Uu3MI-B9E"084hG:$7Hk.T+jle5FH%l0/4X:A81+5V9N9>`JJ^44L;q1-o5UDGob%#YL9DM#ue1u,nD1o9Z9Z-D$n>]OkFra=0+ZIJ_\m?(0O)b4j=5`R9lc,l,]sU%[7#J[1Q+b\cM5A5bfcEGF7Z_:Npioc4`a)npN\]`$Ur;A"GJPFeR5nnT2q$'6fXr92$Xk7"/NDT,)_+2?YGg@f&\)PA4UafbA^IBcDD7TiSgcRNk[jYZ;Z%UkopBi>'C9DBkIP]\nY.`2UM75^]F[0]`%H5fiYgS0?J3.g+@U\jI"'H>!cq^A_qgmVd-_Llb+[`n-:Toe]i,cg%DB]37aTZ3F_grY5A*Kt[:h]kq&T"5rtOJB:Xc,YIsZHY^RM$0C*Jgoj3UTLXiZNhL1*P82]NDB#!p\(+Un:)CchA[UD2(P?K3._0bp(Ql/pk-0;uh@lV,b)EAK+=ic`o!)`gLbU@csi4T*oFrKnW("7n&h!r~> +endstream +endobj +720 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 719 0 R +>> +endobj +721 0 obj +<< /Length 2717 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU6gN&fD&:Ml+nE.7'+c&5Wk&o[h[rQJJa*XdF%iWg=W5?>$(H^@bYm"tZ@6[6&bM]JpET#mIDQ.M;6gbk;a;hcXElQTqS,5H`1X1Fi74Fc4nJkaKm%ZHJ?Um]!:"(MnP&q_4^Ck8Q90Ho9@MgSlN=trt4?#IK?KWV4-jQ/A(o&L&.3uCWP^D6WP^&>ZiJU7U0PilH//7goJ&:j#V/h1jMUESiOZAQM/[k6o4?9.5l79;<=jFmjJi!$4?d&f-2d9gcn`X6_$M%UHIQB3A7_6XS1*9[Vh1*EdM-SC(g/=20/V,aYkXfJh8ie<@gQC'8,c\d?q58qhMhCp?Oql@!#%1BWCR6Il-utmPcu25h'q]LG#?hq?tHhS3'HA;%7tfO6X;gYmjtUHiNQ9Ki^KPoAA%f#QiF!)++"RA-Jb8D'C9F1ELKX=)\EKQb?R4'#SEK8PQBbIJ_V6f,VPioH"3>Acs-N[7BShc>n3u9`Vu6s&TM>Gi5>pP!1]"f+Nh;!F,C2PnF:pgKJGM]_Ol'N;]P\:L]BX3*a(8':#JhYpOGlFoEcuI@):O)@-aeK+B@p,8\8bgfmn:,Q7FY.IncYo5_/Bo_0CZRO+R*D(nV'mjB2nGG"5Z/)pj8OYbKgT2EuA6rL7i>2DMn?dV[8bkhZ>E59%S\#^r'qi[mq>bso1?K`Um0Oij(E,n10-JeQ=jDSV5V.;EfBk6tf'%:VZW*t^SZ)MdD*]9aEVTE1VfIt1CWG^Z0?]k&qBSgE\sC((_e==!GHUY$DR)so%/>o+H*cXOQ5aRi4(30UFl*06K7i8r"#&E*>ieqPq8k*2N=M20)e-<),::cFE6U!?bXH)V#b4rY/)'.g/sH[t^:ijdA9NhW8,$bk'.7D_G)SuSD'@]#Y'%O^mC'&Y?0-;fGMCQ[#cf(,6m,G@]MDuirCE?36p#:%K'="H$r'6Bfseoof:krm?UG2%]3ICj>NdT8BEP?Cc.FkW?V86PsH-Z7%J4J`]**tFhD&?P8V)Pq_@-VD]"q.._WPY`D:+/\+R?:%Uqki0oZ;'^u8YH>Pr[BJ&d,("hUm,+7seXM?5/^u#hgmAoC=i\AqjiXa!>M$ZsmiIeaol`VqV2H-^Gep$mm[#Pg1`>K0)Hqo=+]ih6SYe3r6S^ds\6rEndbTe!C>@V!1(6:HqC&ufI\8._O<SfV[,#ZaR6rBlg$5U*Qrch=:T)sHFdAB=Ma%nM'LK$8U`^LsNZRGFAg=CP"g?p>;hRr2so.N+&s.8A_$u/e@jj_fqqo1$/Ysn-j%d=ghbBQmjkGk_K:V>H>h.&ulYVj"u0!4?h^1oIqPPE?bbah*.Yi46aC\nYGcf@oYRR-1)h>k+:dY.PEi3ERUr??E"E$-csCTQ87aNdR2Y]<<<9,0SM!@r8T4XLCP^sO?ee`4kGW,5!duOX17!4Sff^"=2ZLWiSC0%)Sps1c\0/3e)VX(._Ndojc6Ku/I7Z]GqBXErh).52=l"T0LB-H$3gf!f;Xdo>\V/\o+da5!kmLhTf&g%Sn_"/1[;);pP^V.kQ6iA%X.($o<'AA0SH&Y$HC8p*ccs^g*QhXc(L3'%T^AW]9)+Hc2_"=`H2Xo$<=#\NE-_)Lgfn'23T8mLH7'l%S9L[91"N,-3.Wm*XSC71@57:,6cq@'8n7`%R]Lgr0BXLjG5Gl'.1NJb+71YksX`8;-,8@GN+X;?Hc153TK@\Z>_2-B^#BdJ\l_?D12%slYT:'IFt(FBR8E6=c`4EbZ_/^3mM+bFc3"d=!8VG9Zp5=i&PnPR4boHj6;Mi*3"d7#SHU:\+:l)JTu7GZb\"(R\:]*$e/)ncT3tAEK?^ZpP`k@"(INn_0Mfgh#4=t(PaJ.CKG=X?ci&tLkOcF"L_g0On&9$[X*/nB6Qio_IiIBRk&&ES])-OF]'V\)Bq9.j@6$i&\*=jZqJ43A'2DUb"7oWcQJcAhOi? +endstream +endobj +722 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 721 0 R +/Annots 723 0 R +>> +endobj +723 0 obj +[ +724 0 R +725 0 R +726 0 R +727 0 R +728 0 R +729 0 R +730 0 R +731 0 R +732 0 R +733 0 R +734 0 R +] +endobj +724 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 299.44 517.366 351.94 507.366 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +725 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 475.28 495.366 520.84 485.366 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +726 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 386.17 457.366 398.67 447.366 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 102 0 R +/H /I +>> +endobj +727 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 418.11 457.366 430.61 447.366 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 116 0 R +/H /I +>> +endobj +728 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 286.97 295.922 332.53 285.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +729 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 273.922 142.0 263.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 290 0 R +/H /I +>> +endobj +730 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.39 167.95 148.39 157.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 118 0 R +/H /I +>> +endobj +731 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 153.39 167.95 173.39 157.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 106 0 R +/H /I +>> +endobj +732 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 178.39 167.95 198.39 157.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 134 0 R +/H /I +>> +endobj +733 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 203.39 167.95 223.39 157.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 150 0 R +/H /I +>> +endobj +734 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 245.33 167.95 265.33 157.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 166 0 R +/H /I +>> +endobj +735 0 obj +<< /Length 1852 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU495iQE&AI=/BX4;&qaUSEYA3'0=(dLtRYV=qN:q?#M7QU7f1Qgh4j[6],1@L:keLumo%_S:$$2VWmdq'LW(-:Ba3!QbE6bp5WHK_aJ]jF+hUUV_=Ll>q4E&.]7Te^hJq%nr8p#MbM@.-b#HFUk,u>aEn!Xk#..mcUeYPsO5hYt6(dX,D\Q\N+Nj_hce0)4=p@ar$NJ!')$T%oS,[3J#3UkkT\R+&48QWD],h5]C3b$i2Mt`#F>lVK$4Zf('=:3q,,=c(>>$rd=a8j*?5o'57W_ODZdY_0`.MhqZ04m21IXp$*9ZY[^Q'n$lC':TCai.1Nlqa,oVCi0LlYcR%-aq='4'c]@.GF\Q%%>DLMQq=ANl'@)mW5W'km:V=O8apI3CK_@H;.Nl_[peC/#^:??b(%Bdm[S,#H.DWakdt6]de;.5=`BL`+]dRFg]okAQOpPKV(uFC2D;d%[pA`\HV>\:=MOBV,jnVgY;*I?gh/(m5J611'DaOa'5;2YWjj+UFYm+UBY+R98&76gS=Kk^N66'@j*rX7SZK>=\_MbF_[*Vck^+C?crKnI$UaTbR9>7'R-LG>Scofjk!@qdjl3eCgE0#?aUJL^[u(Od\j3n`dJb$'h219Q0JlH7S_eg3/"`4TX2",=#q2+j9+-<"Vg2G2taXD6el*c9rGeJ;;?00"@HcO@+j.7*F97@HR=9Si_C,&P\qEFS'qf0;C.]UjQQD:p74p1?Ro*di,;&Ej6fulPg.#_uc5a^4E(BHRl_=[d3s^G/YsZmmMI1hDR@5?+Q_Ta[f*!:%/l:9[P=7PEjuH=G/=+qS=^r]s[*Y'u3>qcRca$u+gRG=O[q]Mn[U&1T]^c^YO'e,PR+)I+]Cj"\T:8nG6p4-$?"-[%=C!HI.ll\rU$HVslR*Ebc\^fVC,rLaqki"TaZrVhu<,_W]o`6@.uU(.#@J(n`AjR9$DE3a#LfYg_tU2^K.7oUmMQ5j(e[)mTMWu(F1]f9*T9P0^.!uUH7W0c_eW@I=VR2XCCC*D;;)V7W5ZY#o48K(hD?fFJCLLPM18mRKbS'c86bR[SB/5LWY]o=G^bMO,U"nD8E@[eOZ5l2L`,gcRiQH&m(@9+X"Uhnd?"JQM?2rq4):ign0W(U,E+lK`!J-/W?F*DR3D#m9>7Q_K_Z57SGIQVk=?X=a3OP-p??]T4np,u?$15~> +endstream +endobj +736 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 735 0 R +/Annots 737 0 R +>> +endobj +737 0 obj +[ +738 0 R +739 0 R +740 0 R +741 0 R +742 0 R +743 0 R +744 0 R +745 0 R +746 0 R +747 0 R +748 0 R +749 0 R +750 0 R +] +endobj +738 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 680.866 143.66 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +739 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 384.15 658.866 426.65 648.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 362 0 R +/H /I +>> +endobj +740 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 548.894 212.0 538.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 271 0 R +/H /I +>> +endobj +741 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 224.0 548.894 278.0 538.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 298 0 R +/H /I +>> +endobj +742 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 290.0 548.894 338.0 538.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 302 0 R +/H /I +>> +endobj +743 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 350.0 548.894 380.0 538.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 277 0 R +/H /I +>> +endobj +744 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 392.0 548.894 422.0 538.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 306 0 R +/H /I +>> +endobj +745 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 440.0 548.894 482.0 538.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 330 0 R +/H /I +>> +endobj +746 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 539.034 206.0 529.034 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 300 0 R +/H /I +>> +endobj +747 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 224.0 539.034 272.0 529.034 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 296 0 R +/H /I +>> +endobj +748 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 425.202 194.0 415.202 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 273 0 R +/H /I +>> +endobj +749 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 300.37 212.0 290.37 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 275 0 R +/H /I +>> +endobj +750 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 181.538 182.0 171.538 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 277 0 R +/H /I +>> +endobj +751 0 obj +<< /Length 1786 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU4D/\/e&H88.TlhFs"0F'-QOpO*SstMQ`Ur\t3XY9W&I`cGQC9;4WAofSd$ljqZ3?]G7bNT5*8ji:B6gDo08fD]L[)Z8*nShMD.8ZB"25P"KQJL"E/8^/g6?5p>&DC1;E]XB,Eu+9\B/lGZa;+Cr=nM[3LcMo;H`m+CO@ca+kA]3M1,qJ4=9Li%3ldo)%qb/qkDIW'$FY7be84%Y_k;&">doVpGPs4'2Pi_G"cW;4U7):fLs#b[lB@YYd@#S4@3pUDbQ=M1,GsfAG&q>8h+4RP2K/\+j7#WGr<_A%bS-@Y,dP&nP8Z#-WD4?K&WOd]B>@`]4-!Y%p'%FNTSk,>l4W^F-#=]AugjUY*=O+1H*SBc`>Er$ts=Hr$&g'?@$"E!WB#V1?KhcglHM/00frh*[*^L\""n5/&MZCY-e0QEf;O9E1J3+_%#oOqj,;Ta@-Z+7.%h*_k,:B^or"8TOh`Y'9hLR_V24,iQ/?7h,i3o!RMJ--nn2(H0\4!;08CtNdm5I49<.;H8(4+;q;(=G?:H+./.Op]oA_rORZ'J[k_`p(GK:$OZk(-J#V*j!^Ofk@0CU^1I!<;'oM4ai=;^)">BWaB)dN'glG'e@T@T#P-.H4hrN+gAs&Na7(84L+GL9[8+N>9ab)OKgnY;XO+Leq"u.l5s0TX(S$HJZXY1Ou.<_a]I]B,Un/F$,&]AJfY!.O9aceLd\tXT.-2\^QdZe2iEi]?s:5]gIJIK8tO%b-k%ph>oD0)Xr3o]u`HJTDO-MiPp"!0Npoe^qoc.V6*d`Hgp/Y\/.V3[`u]hr[;nQW7ud$[q!*lMBo&NY5e#&;CLU6$LVd%>AaF@Qc//t?(,bZ0+n#DcpLjoH"n>iOn[S?RYH!A'3)RaOtST*%mfd1/7V6bk)I/:Lo-)MjS]%?g4Qq"HZ%*Gj]XCV)0qGhOAa2Gn4hEeHII23N42B/))@pK-eD+@Y\m>PHOo5Zt/kZsH#rgI4!iUH0>]dY$*RNkDK[KPf<7Ae?;n%[(NV$(hWjP(b$pG2^rWhLQ0s:~> +endstream +endobj +752 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 751 0 R +/Annots 753 0 R +>> +endobj +753 0 obj +[ +754 0 R +755 0 R +756 0 R +757 0 R +758 0 R +759 0 R +760 0 R +] +endobj +754 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 650.0 182.0 640.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 279 0 R +/H /I +>> +endobj +755 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 547.168 206.0 537.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 284 0 R +/H /I +>> +endobj +756 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 401.336 176.0 391.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +757 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 254.504 194.0 244.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 288 0 R +/H /I +>> +endobj +758 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 363.56 180.172 409.12 170.172 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +759 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 96.672 200.0 86.672 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 290 0 R +/H /I +>> +endobj +760 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 96.672 236.0 86.672 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +761 0 obj +<< /Length 1381 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU4gMZ%0&:Ml+W)&l_>F'JiMkWWZHdQ`E(F2a[@(QqQRC7c0P"SA<"8(VHq*n8XHq=kp0PSh&dI*CI.oOZ&-ahWc7`S4\3LYU5:DE$4e6^f#l>g$=J'5,p:O3DTp"#c(-Y>)>b'X*c-]dfBab/$N[I,R3ckD2]$iR;?"cch1Di\KcPRX?:p8J28M#7a@%:O[00SjNj!aA)"V47k'Po:S4B$+N0ok\VG00NH#S_(fDSB++Xt6TmA8d$G#?Vd3$tT8,,K_e*\ld7deoo];hmnATF][3C,u;\]:t;qZap(4!hrb>&re8'Adk32JH"u[5Bo6]:2Ao@#dqtc>-'5H'.4I!o]M,cC8'=m33YE''t/"l*TkO>]^s[(n,9:Z^J7DC/e%?5e&[Daa[Js2^"K,8.EL.esS!g9i70]dMZWOX0K3u<[PM3R[nLc#O_e_V%Z*GeO(^Ch>6i3O*?eMSWmsDcKmlegR$RS/73c%0I&1qE;2r!TO=jbLm\])^Qj'OjXR/!J,8XH(/^f;.!+Yl,,44%V2!PlkkOb`Bg+h:ma`gO.pZKaU6rspFYUOJ./T0=Wn<3'+F9XG;_o%GhHQ5[0TWno!1Ul\,Ts,F,R-P'rk'QC9,[@A90jXdZ"]6'H__nV)]-#m,b[lD-tfb,aV`U_NV\\%Z[GgOg);@Q=dU#(#rKnp@,dBc5SS+^dRf:[Qg4gcHjlBJc[WBrTAq.`:o[rLUU>'tFQ<)L]g5eT,H=g]$>8Kc2*Z3@Lg!*cXla@'3TcMrIH7he>bbEikV/,3;Naod`p%_OtYjf/?V!AOu[]&6N`W+C0ADR~> +endstream +endobj +762 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 761 0 R +/Annots 763 0 R +>> +endobj +763 0 obj +[ +764 0 R +765 0 R +766 0 R +767 0 R +768 0 R +769 0 R +770 0 R +771 0 R +772 0 R +773 0 R +774 0 R +775 0 R +776 0 R +777 0 R +] +endobj +764 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 626.028 206.0 616.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 292 0 R +/H /I +>> +endobj +765 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 218.0 626.028 272.0 616.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 298 0 R +/H /I +>> +endobj +766 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 284.0 626.028 332.0 616.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 302 0 R +/H /I +>> +endobj +767 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 512.196 200.0 502.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 294 0 R +/H /I +>> +endobj +768 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 512.196 266.0 502.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 298 0 R +/H /I +>> +endobj +769 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 278.0 512.196 326.0 502.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 302 0 R +/H /I +>> +endobj +770 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 338.0 512.196 368.0 502.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 306 0 R +/H /I +>> +endobj +771 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 371.364 200.0 361.364 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 296 0 R +/H /I +>> +endobj +772 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 371.364 236.0 361.364 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +773 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 268.532 206.0 258.532 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 298 0 R +/H /I +>> +endobj +774 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 218.0 268.532 272.0 258.532 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 284 0 R +/H /I +>> +endobj +775 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 290.0 268.532 326.0 258.532 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 326 0 R +/H /I +>> +endobj +776 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 149.7 206.0 139.7 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 300 0 R +/H /I +>> +endobj +777 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 218.0 149.7 242.0 139.7 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +778 0 obj +<< /Length 2008 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU4=]=*8&:WeDGb9LL2cG&4g26`ZJ3\h7^6er0t3cLJMJonKIOOsF#BNt3Q3>-,P4op6K:s/(*)Xk!HWU4VP##uns\rh?kL+Z1cjTIAt6l7&QFQ:$joS7:m<@&`O_Z2D+QN#d06;\2G\cQLI+'s$R%9t/$pZZtYJjQ7Slbg>c:qu)l#`T#A+E]FE5b:M,Olo6\XlHW4Lpg_]P=7>-7;FT'>K$iT4UOr3-iV"#Ftl?IJO@4-7=.0:Xo4AtgVp"![I-V[N1%i&$WhYuBZ.pt3%"a;pLMP,q=;0Qb!b()6.2erP5*$gJjjHR%D_GthBN/F58G3Cj:@\n.!3n0X@,611[*[49'rWI.K1;R*OPl;k%Uq/%sLQ8%b;*u1SdnY,eQ+2,%^\9b#cl_4OH9=L.d7p'SeHH_T+MK[t+B!;O@^X^q+B&X]U$2@sG8D4'0N.XiP'IWu9>OdZRfXjB8jO]A_":Y#%1c1J0fV0^]_2\dY7Kg3n;ncfAFb3mk%jW.]@t6UXmS/<">d.[nk+Pj6CR_;%@H@/DCiZU9GTXB;'u&e`Kl1agt,?1b(2fuD/D)VFU->)WX;_`J/h^XFcVN,*Hl+o5#+mE$++UOf/[58QS*Qns>/Nc12>E^%it@eu0Z?3fYL%\I?E8i!"_1#Q6Oh;_q>iS9$s!aH,oJ]Ej$g8JJXaN,Z!;UP%5q?Dc="Rl[(`Fpf>@t9"YXHQ26Dtn',Xm*2kVdr!gGoRO/2j9%G@K(4Fbg-'M@S?s7CL2[^&h)^kd#-..AC%dV;1fl?:%7fh0PHR*/)s*mg=Ck"d,7rp]aGik6Cs#C:KKHXe5'^B=tPqL?#SkSfsajLEKd7u(Ts9SjuA*Hi/P4lU.eXm:KE?[T%k/m+`gh)R*lq_CA6:JYQ/Pt8sUls:CAko'@^cCE9VL=@sRY4#@G$fs[QWLkmtGOqOF]P(P>Z>]ktF/9mGj/&Z?XV[UVpig73gOZ9Bb".MWD(g.i<9^od*)o*"WP@>>ROjJ//"pSrpP5W"DSdmGEVkA5)M.3Bd8UG&k,i.hb*8<0J5`jWGIhD.LnP[N*/H+eLA)Kpn2Qh3XVL>_Nq6uQ_t"Ub[fM8dr^6'udZ-JE*//i^Q%?PcDuM7r1Hq4MGX81KNkQs24&Go35pW#jEBbLp7'6SHSSiJ9P]/7Td+f,^!=SAT[/ahWHNFT7jh9q,Z,]@XVEat4ZBBDCdirM3=mdd/R6ht1o9<4R5a)s%4e@ZlR^IldT-I?La2#_V0EJ-OJVX!jXR3Tr@s]lg=X4A7$.J6Ojj2=j*1O+s.$fiIbI1!iPUV/bf0@BdSXM6gbr?d4`NRSDW=?MM@s47iY%,Jkd6DaGrq,[Y9bb'T4uTW]*Qnd[n`fq)oVDG1Ci4p!Uu?0LA_:N=U0);TrKpP,V^g,S<-I2Q6,2^N(Nd>-GfN;JkM)YPYrqd^NJS00~> +endstream +endobj +779 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 778 0 R +/Annots 780 0 R +>> +endobj +780 0 obj +[ +781 0 R +782 0 R +783 0 R +784 0 R +785 0 R +786 0 R +787 0 R +] +endobj +781 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 677.0 200.0 667.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 302 0 R +/H /I +>> +endobj +782 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 677.0 242.0 667.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 332 0 R +/H /I +>> +endobj +783 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 525.168 218.0 515.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 304 0 R +/H /I +>> +endobj +784 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 230.0 525.168 278.0 515.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 320 0 R +/H /I +>> +endobj +785 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 296.0 525.168 410.0 515.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 322 0 R +/H /I +>> +endobj +786 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 302.336 182.0 292.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 306 0 R +/H /I +>> +endobj +787 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 139.504 176.0 129.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 308 0 R +/H /I +>> +endobj +788 0 obj +<< /Length 1802 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<=`<%S&:Vs/cosJ+FqmlKY%"Y%[rI9@8STO-$V_^BMiNLV7B_`[#5[AHPSA1`aE$&[0+lGh3V1Kcr]WP,^[`u;fT%grSiG<]$*kQELTK->CS;Er6"nNpeq_`/A5-C%=hfW`LY5J$S!%Y[=`9d(RagEBqIn.2ajNKAmA+Ah\Y3$(+G_%Un+8Ihl6Il0[cI;P8g=6daaXc!C9^T6YV31O$%?%in^4t6&YPI+"#_X_PRCq>SI`W/o9Z7VPhDqZBYaf?Yodr\q'lJ9rk>oY+!`4L;?6GIF-qk<#;DSm*==AKfPg)/]-/OO.nOL$e=*8fEi\DfJjhDT,m]*.Zssu
$1IMM=o8^=4MfcfW3r;+n7H?qB\SbGHuRq)=*o>S#X7nYi)X.mV6K6?KhGqUqf2G*K,d`jC)Ql;9ubK5/_4d6Jh7]Fd]tM)^rBfca9l7.S`N!-i#m3F-7*U_JL/]Y.G1n#5Q]dp:=P:Cp#Q$@#frA-A*)9/<38eC#=Q'T-;=M!:7+[TF!+@pmpLY_\'2EO6UHbpLb?Sn#u"D+fr=<`:td!5fuH8CEIc5A.Z8n%K8`R0edd6OQ?f7F;4W=Hfpb19[Vr`R^R!Tkg\Jq"rBh+),IbECXV2B85`+CnB[^AqhS3^oBRSUtpfF6_asOH;@I(9%CL'Yp:OpJ>>EHcds7S5*Y0)+/5jfuTGo`te-5c71aa-m(i<,![mG[Z]'J$e@RLe:@8Ua3/[]`ZifBrrjF;@?I7O.Z+;$]Sq+'2XrU`EJ!!NnYQ!lhU6r-sCESaO*f!?*oRk'T;1E/iCoW@<*Bcdk@bHfOVWba+A1K*-E9-UG=o@%Bi_6N2ojO.K!dfKJB!qYdm&a%G-iX9Gh@]#~> +endstream +endobj +789 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 788 0 R +/Annots 790 0 R +>> +endobj +790 0 obj +[ +791 0 R +792 0 R +793 0 R +794 0 R +795 0 R +796 0 R +797 0 R +798 0 R +799 0 R +800 0 R +801 0 R +802 0 R +803 0 R +804 0 R +805 0 R +806 0 R +] +endobj +791 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 645.0 236.0 635.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 310 0 R +/H /I +>> +endobj +792 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 248.0 645.0 284.0 635.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 318 0 R +/H /I +>> +endobj +793 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 302.0 645.0 320.0 635.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 324 0 R +/H /I +>> +endobj +794 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 520.168 200.0 510.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 312 0 R +/H /I +>> +endobj +795 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 218.0 520.168 266.0 510.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 314 0 R +/H /I +>> +endobj +796 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 290.0 520.168 332.0 510.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 273 0 R +/H /I +>> +endobj +797 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 344.0 520.168 386.0 510.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 288 0 R +/H /I +>> +endobj +798 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 416.0 520.168 440.0 510.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 308 0 R +/H /I +>> +endobj +799 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 417.336 200.0 407.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 314 0 R +/H /I +>> +endobj +800 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 243.504 200.0 233.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 316 0 R +/H /I +>> +endobj +801 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 243.504 236.0 233.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 308 0 R +/H /I +>> +endobj +802 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 248.0 243.504 284.0 233.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 328 0 R +/H /I +>> +endobj +803 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 296.0 243.504 326.0 233.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 279 0 R +/H /I +>> +endobj +804 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 344.0 243.504 458.0 233.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 322 0 R +/H /I +>> +endobj +805 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 91.672 188.0 81.672 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 318 0 R +/H /I +>> +endobj +806 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.0 91.672 224.0 81.672 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 308 0 R +/H /I +>> +endobj +807 0 obj +<< /Length 1996 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU5>Ar7S'RnB3d(4uX>H6t;.>A:7j.\,ge4>j1;"oO,Bj7Me\tkS!^jc3Aj9lJe.S`n&-"P)crugoGi)(W:`8lV<;X>es+bi't+]#09g*&3k*[__5\9M0F1m&-Q2XEGff4J(jW]>`na,0WgXV:L),(--HjDLFaJ`HZ"?F)X,o(]C4F^s`VEm8+n]tL.l0#tdCm/OTEtFh)L0Hch>8:0:/YuQ]lW6A!Q=T,,^^H=4&Rt-bLQ!89WkPUUBOf(.@.>M][-ATGE+>iX-XZI<[g]T"2q\aism/*7#4#hn:QK2p;quitAQ]Hl8_-+*&3u^8;X)%g):glmt%T%6P$o33fhJX'>$7.+@eE7N3Z=I;C"cdaUL/mX:UTeK!poHj*jB%^@^H,m>9W(9M3]Q(;'801F5rZD49hOM5+YCrrG5h_F]6_6'-SV6kR,&$Eg"j^G9GRnEBMH8#r/];:"NF_%_sku1-=OfM$A)bV<(k>t2)V7])VDZ\D_g.0E>Xr`phh45HGoX]GiPOC'oeS&>^*^Q'O(d!"9cg#ZEZV52$2Q$!3BJMCG;NAZjhmb,pCe":e,Koii.i3VD'RTr0k^k5c&H'XVj#S^Y&%Dj85hU,G;YBOQ?PAhJTg[H_t(F+:bEVPfn.B,gOL+^@D3M=;CgehX0s7"rh1!6*#\ldqVM.$TjbUUr.eMg9i7*g3KTl2'UKHd2Dr!j%a51)fCOW_g4s:`$)/eYC?=m$!m6G/Mj'?sa0.,@Zh$\,h%*uLN<75p89/"_qT$,5H_0WEJXP8`FI`h(Pb\0t".(YKV%6U!mWLQYmG"N&3Qq]HeUDGmD\J'Tp/4E7rLh7KFP\tOWY=026H$!4q;#`GEJoE$Gc:/er`Np7E+Ime\3o&u-[B\]!>mKl8$>`-\bbR;Lc*G8H/HJe!tM^(oqqm:1S.A'B+D8O;'LFBaV\Yb5"$nZoL2%FhuM->]JV?shV`"p><]g8H&LPPXa.S1UOn`#Or;uHhqG"i1]&*1fN3sC-noLaBWD%(\g0;,+^6Tk[:mV_1Oq"lb%tRj;;K9NhRNYl71P?Ihfk1#61bEq:PO!VqmIt]QJgg[A:%VR@bT=V@Or5AFX=$1haXInoo!hkNs=ce*Y:3he3&>.IOJJH-,U;&eb_U,75cY^1i/k!VP<1OC*BJT!khNjFJF*(bgm,ha='7JBq?IQ*qJN$!D_W6%]N"E^WSMIYgV3-ug)ZsB]d+S1O/J4\(P[)tnC7"LK\Q(2`<5JNXA(l\j?PdhBCO+6FB_4M2jFI[PPFm(qidl,OnOfKA)YD@9Y/]/0VWZCdm%,_f;A^qDc/kGHYG@4Fl6``U@h55*d2-NL8^_eF>uRG9nJiqE+WkVnB8>oXO)BWD"Bt7p9]Q7K8IC'B%BjAsj*t'>SE-HO[ek^O-I,AI~> +endstream +endobj +808 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 807 0 R +/Annots 809 0 R +>> +endobj +809 0 obj +[ +810 0 R +811 0 R +812 0 R +813 0 R +814 0 R +815 0 R +816 0 R +817 0 R +818 0 R +819 0 R +820 0 R +821 0 R +] +endobj +810 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 516.028 200.0 506.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 320 0 R +/H /I +>> +endobj +811 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 516.028 236.0 506.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +812 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 260.0 516.028 284.0 506.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +813 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 302.0 516.028 338.0 506.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 328 0 R +/H /I +>> +endobj +814 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 356.0 516.028 404.0 506.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 316 0 R +/H /I +>> +endobj +815 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 506.168 242.0 496.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 279 0 R +/H /I +>> +endobj +816 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 260.0 506.168 374.0 496.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 322 0 R +/H /I +>> +endobj +817 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 398.0 506.168 446.0 496.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 290 0 R +/H /I +>> +endobj +818 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 387.336 266.0 377.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 322 0 R +/H /I +>> +endobj +819 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 213.504 170.0 203.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 324 0 R +/H /I +>> +endobj +820 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 182.0 213.504 206.0 203.504 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 308 0 R +/H /I +>> +endobj +821 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 110.672 188.0 100.672 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 326 0 R +/H /I +>> +endobj +822 0 obj +<< /Length 801 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatUq?#SFN'Sc)P'qTRN(O3CKO*I*?Z'),e8qi#MD1Z's0J$Bu:d3Crhp4l-S<0gcX`@pDpmgs0kOJ'Y2>D,B!%&6X[2U6m+(0WCLOgji![Gcl(T7F<%P`[U*J#q4aSZsRR@iT?R1iWH^X)?7`!c6(8(e/1!d/:!0S!#cj#Oh)icCGP/l2$+rIb)_6;ZgkAqDd+gPnkl2U@RsSL0P1sH=nd5CC27I?Kb*!`!lg(fM:G^8O]N[ppM[+P_CDH?b75jT3k;?C/X77o;]$eCT/*'(9gJAkV@%,Pnr"'39W?JhF9'V*4h[7'3f,5U;KZjg91`aos4ZnPTSI;Bld*,W]+qIUcZs/5DTa"f!O#?375DU+H/7olqIFC#)Q*%@5%=R/+13UbZ<8dj(6YGL4h6Z,Mp_r\J(hOU5rm8omIEa>T+I=:a5nR1lIB4]@otj"MPIN-Lq)P%B*L\VY!$O1a0p$+hgER^Ic([1i.HmZJ`&RK3;e5R=mqPjbLnnmfoeULiuI&T&oWulbdlCMS[<15a;8l7U<%W&UF,qbc`/;Q;G$&7@0H9->M/?UNT[s@Se+:*h1F"]<\ZV,\_Bpts!p@GNPAsd(L9!1"%(7?7]rM8*f<8s6'*ciNM[;%_rpPjRUA1Io2%u-tdIfW,6dFA~> +endstream +endobj +823 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 822 0 R +/Annots 824 0 R +>> +endobj +824 0 obj +[ +825 0 R +826 0 R +827 0 R +828 0 R +829 0 R +] +endobj +825 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 312.04 660.528 357.6 650.528 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +826 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 632.028 188.0 622.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 328 0 R +/H /I +>> +endobj +827 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 254.26 541.696 304.26 531.696 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 241 0 R +/H /I +>> +endobj +828 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 170.0 513.196 212.0 503.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 330 0 R +/H /I +>> +endobj +829 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 410.364 182.0 400.364 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 332 0 R +/H /I +>> +endobj +830 0 obj +<< /Length 2397 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU59lo&I&A@sBW:%Xme-s/h1"5BGle"p1a4AmXdZ0Rp6p_M5,bYjH?00ZS,UIGs"Crkt?OR6Clor'f]S1g(*kf,l8&Vb#d<'A=O-W:HA?.pM$6AeBBL`hXK@)Lp4e*)@3ZXde[]R8'H#rsN%33,#9FA%:7ELr[*ISi#h^P?"LtJiWS\_6_(lWmdhANs=7:u]?>ji.$gjN;-=?"<8#"E'1R'nlZks"(3qk'``(`#_ST??VDj6u`oh12=Q@sJBY;DR?3_Q*JQImhXdIXus2[ocZ+/irHoku?GV[+Ji&OND@ENXY\;\G.".Lk;,;Waf`+-+X?RIC"BgK#;9(I@DnlZZ0%B'KF_!U&QQjSj"J/S0+9teEPI%*O?BN-NQHR$K%X>%S)*ptX6cIDZ=g+Vl7Ll;GZF7FWs7T8ODnV?rDO!4-H9u+;%X]U.Ac(mZ89TX$*o*'n/0<.?)"0tTP.LY)Q%[\S:A>-!^#+_YcMl,rb@F_GZmRTK&^*4G@-i;ZC!^ZE_JFOW'N)qiC_HiaX\^JN@G<;4_:G:Qk_#@>iPaJ/+EuEft[hX>P)ajH7k1C\@33Xc-V$T2t.Ks2N>T63QZCVa%.o0Q-._WtarKc'E(Olph"Z$r&[?ITF28$i6e"EKhhE=KrC4)S0piF;`/07s!Bumb>E22DJ;$H`"=5a!QlnLtZc44:g9*XN..c)kYe9QqOL`+:kGm0^,U]t$5dIEuOLCWs0juk%Tmu95br@l`uE@eD\D%/=qr^q]/eSkVJ6\`)Ybt*s;W^EmgPV:p6S2>Yg)lZ:(0OK6X!6do.4;S:9Mqc?T[*".4$4@2/T[VfZ;JB6T^^&@9n%ELsa\(9K'K]+QkOWj4j7Z%ZFoHKs+k"J?nYcK2e+i5>S>h;eiqKlECL/iB4onK,JD)u&X+I;JCq[T!`mT\VZ?qi[-'OJS=rD&,0@F#B65M-mg;/PB0AV7L1>la`027P%L[9l(rMRAPJ!GC`R*K*@KQo%@U;0NCh*_1i`mE-lfdNI\3cRdHZ#LE*)J"'1Fd'TT1G:pmb"ok(XIjW'f/-qNqsB55n>`o[t8L*bt`EN\Do;'cJ;LK5XZgBDeef&W7"jh#4iYeMR(?9FH!AY(ce6l/]#1,EDs=&ij0V>?jHUQpL$F*=:\9<:St4>.&6RprMNWDHYrJeZIO/k1bae!tuf^:u"LD,6No*png;CscoD7R(QT]H:.cj3M#UHcl?W'X?9ckZ)ndFZ"s4*3HT:RobeeLfC1]n*2pr&MU[U/8D#XEE>&q6>&^mYue*qU-JQ(q7HFPXZH_H:pJqB3E";+iP-eei%%/[`gkd6$KX1fg5'gYrDE-_la9bk_UBaU$ENu4Yf1%8]5\^`B$/c]QiKDe#+O9GThq\kEK*jablEE6Pd20D[K'sfY+aq%82QDFFi"h8R%nq$+a9WG2YOgkuY1j9"bgj>_f:Z<$,()c\_!U&$H)W^$hV`3cR,>>,9/g:/.YTPHhQOW?AS3:S#&WSsAlh>L-1gp"&[J`r`Lo`!3gW9eicFOgSEIGKnUKA:_ +endstream +endobj +831 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 830 0 R +/Annots 832 0 R +>> +endobj +832 0 obj +[ +833 0 R +834 0 R +835 0 R +837 0 R +838 0 R +] +endobj +833 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 134.77 669.866 186.43 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +834 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 169.21 508.866 214.77 498.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +835 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 338.37 411.394 383.93 401.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 836 0 R +/H /I +>> +endobj +837 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 224.894 224.0 214.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 336 0 R +/H /I +>> +endobj +838 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 252.0 96.562 297.56 86.562 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +839 0 obj +<< /Length 1925 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!SlgMYe)&:O:SW9B-Ke;?fQK;*m34)G$\2XIi(,0g9BdCVfSP+mjDpZjo*G/^/@g5VZcV$IX6BO9!.S=)Cun$B\dmsL$Q[U[Q'p)Ik^7k=oE&M'_4(-8]@K[ssRIGR3&\GY[;q8MacJTXM3\RPT10A*$JWjaYr30>T/Ad_J:).cg4]P=%gap<]]R`h:XtQJeSbabIBabdlX>7IAfh5(W%SSeib1#:,%HT\o%G/0m;*:W3_V]qF]+&o:6r=$iqPeETu\0"Eb$k&5?"s\%2KK>GI'QSJuqIG,olrQ299f[8DT/Xh.LEh6W&j@#/KkpS&4NSO:5_Fn1AnABdcY5s"Y2+"j[ha=%lWtc1M/,ls#"9/6Ns7`^p*^-I-C79sNlGlm?XZ8+jl)@jPCAt#O-k3*;HqS#6jJ\2b>0F*5KCG`R*%TRYH;-?\,c'Teq('U/\jg#1T#mTlcZ;`qKCK+E6;lp:D+FbWAC]_N!eJS_NnUfkD]FAKDajKM12NI_]dT$?^:?`k"f;5@V_^J5$=&NlP45apHdXH64l99/Dl&@L'!+HMfW[C0]m5"+<5Z8Z&M2N\c:_X.KomDAi4$\JLP5:$ID1Z*_&mQNpfnOC+Q[=@b[\u.S]uN#a,??GSE5&Wl5I(]?Z>&^O=aWFQF_FCbHHOQ%e,A&'PBEKRKCi[36YRiBm_A[J;^VkB4Y5bl3(.+X;0<@)VOAI!9(B[+o"%\o/D>+,FePQ>F/LY(1JDR5)n*/b9lHQZ>Yh0Ter(crT?ZL%"@55_4d:/d9JER)GMTCuP5bF9:Z=5toMPM9)oPK#jALe)(qiNLBko)]K%;Q,@(boMU5/m8@R.=:7@m?jnU01E?7q(J!,:U!]-Yj>s%T(`02alZO!#?D0IF_fX;)0!U^XS3Nc1*RHD5Zot(3^LAIUuHG:E@_P>F9RfTui'ffdiJ&tnps.H)DIgC/8i=9bR#Y%?1MA;r='[75HIX&s[+_(J4/X\]k6^S6.#nX':8I>?1oXJDV+`i$Y7&h?XdWJ9tq1q"U6Pf]\?5\^N;I5!g)nkF/a&A004\$Bl4hGAhO6i6%Yj$^QRmt%V%ia8-*Kco2$XVBK['^Pf5\kJU2dZ0g7L,n\!cR:D^8]F9cfN>&rr(,]B8ts%lO9"Q[=]9Z8Cj)g\eHc!qk*XHA8sg8[0T5cZoL103_hXY8`->[Tk%F5g:HFJbp6.W5Th1H$l`Kq=%lYkLu5Or-%C-FBGgBJ5$r'1.#8D8m`2oeT?d00"j)L+K`~> +endstream +endobj +840 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 839 0 R +/Annots 841 0 R +>> +endobj +841 0 obj +[ +842 0 R +843 0 R +844 0 R +845 0 R +846 0 R +847 0 R +848 0 R +] +endobj +842 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 562.0 218.0 552.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 338 0 R +/H /I +>> +endobj +843 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 252.0 476.668 297.56 466.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +844 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 478.9 460.668 524.46 450.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +845 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 380.32 433.668 425.88 423.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +846 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 329.168 260.0 319.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 340 0 R +/H /I +>> +endobj +847 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 337.27 227.836 382.83 217.836 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +848 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 118.336 248.0 108.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 342 0 R +/H /I +>> +endobj +849 0 obj +<< /Length 2012 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#]D/\/e&H88.Tj_:JTN4LpWu)%OH!+!$XjO'fL8H%0M7/8`OseW`o'^piD!1'+E?*#QGEW1D8WaCH$h:`=VoBMtI\UfT_m%r\gQ_#uC,FS7,u:&$_fj2o$J&TVY)"Ci*mP_le$rGn>?s"QZEehkVqD9f^7EKk@b!X!YXIt)mhU^pP+[.Qk$-$*h3R;.,k;D>>SsRn[TE>="Ka>L5?sc654QCp>J%Du;.+^a,(=mm*Cl6u>]d_&MG9C[O*A[?P)2o$1]6qY.I;N_Go7i\>V(dB3e8Ua>DmqM.oVosT=!1!P4;Nb^LAXi;k[a`G*9-k=01KYSh)k`;)rSIa,FiRY'il\h8j\*79h0*j/D9d#V?DbB!SdeQ:13LCmu.PK:HNk$Mo&rMS,L@TX\Im4iZg!A93PgOtJFN._\/Vg(kMqmMF/7jUm*dd?B_.#*?c@GldNr&'Kh#P^'4Jal[f#lJ[sK)1"QW]2@TE4RfgB/.Io^a_psVF5t-,i$*0ptfb/0alh:uoLfPl_,dQMo+iJQ$WPU$CVm;>.*1%t5"'[X#0:2>T=eR=KkHZ@$!J\iSSXQUlu7Wo(1l.&>\>W#Y3^^jW3kZZ:.Ot2qnBX6^p!aFQ$<\u^Rk<@CidHT1s!]m*-)>'T%TAC)OSh.o7%7=HShe#"Nb#3Wg\>W;S/lF>A1r@M1PG\4K^+RtHl'tbpJ"K^?BTfPThF=?B+oh"WZlV"q[3kcXU:DO_.nE:)p@iI(b(V@`$6Le7SOriHYgKnN1)gRA$f(\p$b,U&*lpuEAi +endstream +endobj +850 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 849 0 R +/Annots 851 0 R +>> +endobj +851 0 obj +[ +852 0 R +853 0 R +854 0 R +855 0 R +856 0 R +857 0 R +858 0 R +859 0 R +860 0 R +861 0 R +862 0 R +863 0 R +] +endobj +852 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 252.0 684.5 297.56 674.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +853 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 403.92 668.5 449.48 658.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +854 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 363.38 641.5 415.88 631.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +855 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 559.0 236.0 549.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 344 0 R +/H /I +>> +endobj +856 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 475.85 484.668 521.41 474.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +857 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.26 457.668 447.82 447.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +858 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 356.99 392.668 401.99 382.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 98 0 R +/H /I +>> +endobj +859 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 252.0 343.668 297.0 333.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 94 0 R +/H /I +>> +endobj +860 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 315.168 194.0 305.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +861 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 252.0 229.836 297.56 219.836 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +862 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 417.52 202.836 463.08 192.836 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +863 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 390.86 93.836 436.42 83.836 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +864 0 obj +<< /Length 2335 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DlVlP-&HC$_iR/6%#Ko0A70sh=-7%^3Js&kUjF`Q?[O/<@k^an,IJ79o[s(":BrB'H@S+&th;@YW+2QOdbjH'KdsJoA9CGTa85G#UUhA!FPYbXhd,g4^fHgY)!CbeTA'R@*28u#O\V=>(QH'Vr)75KfaQ'Q]]B`-+p;Io\Sbt2?[t!^!&j[SD4Jf?:'T:?*1[i<_o]1_NOQ1iLWnkF\`c18QH6.A&7po>)eghn[R4T:;1LX!ZGoAA'rQ=`bjE)ZG?%'XlBa(:#H`)5\1I>eOGJ6@&h$kV^do_?nOPa9OL9cJ?ETX=4%XR`a^#gNS]cK3b6KsJ.*Xd,I)E%LXf<7%Bm4E[`%\Nm^6O>])if3ML/2qr=/Q`Pi]r\D,'?4O/jQVa%$1?h9d2Xo;%4Gi3,0k%j[l"THe\n_tbdPdi.7@e$Kh52]IJ4'2C`knjQT\3#IX$X6st'].tXb^@d$")'ft*j0\l7t[P[lTQWp-aWa)7W*tK(g(e'dMeSh%<0_i$J1YNK%@A#YR=YLF;L//C(djh$*:nIT%sLrnLnm9s1se%W3@"YV\lr)Hk"XB;5e:c9lA/1mYa"4&5UahWA^mr>h-Uth8,9\n+L'-aA#c>4P0I3'=gB/)H[1A9LgFTKf?(B3BSUQ[k&K]n+M4b;igfDgDoqsf@4'Wi8_O@p=9?Gn1eJJHoqme4@bW]9HSp__H'?q><5ge_mVDVCNd"YUDs'CU26^/WJ87?pFT\B/OE\r\c)2=bB"OMbh#$@=uP,^qS&l<<\LAaqlRWArWjEO+B'6aca/fKq4n,]M8bct[lso]GK!KEC"K]iX,bcjgQO,WW^J-oRs5C`Cr^4Z_rg3ipK+7T["AR,Bg1H.c\#U^+=r76AMsr+Lip<1T4^T<^^6@NV++PFemLF[OonNGKU]W%=05*BuB8G"]r)&ZaIk?L>J11[7"0s8u7I8rPT)?mK^a>9^[!]2cQ'8qkXcu(Y;_VF:*PM[]S0$+`!3U7KTaJ7(CRR_s;l\,Gmk81h`e1okcYiTUB\haq)!7BH[S8Qm+(^=crVYd;0"0N]I%=uj18jTti%Wb69m3+tLTDKXWR4^u_eK$AP*4&Jt[5T/>n48No9sG9tf>p"(qj+Egh8L]^I]h=.>r0])!/0Z6V_%B,u4r8FqRb.)p/b?uXj^D/Oi,2N#YZQ)GKiqu1ZWj8;;Ib4*IhetL1S=OUA[sZ'\I>D[+L?A5Rs/,OPbc<[ocea&Jn?ccj\h!$Vl7NVKi\[]?<>77mmkH^1@GTZC7c;C1*9MJh&+ZrrR0]IXZT@bj']Nm>(SXk.prZP/+-a3OEr~> +endstream +endobj +865 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 864 0 R +/Annots 866 0 R +>> +endobj +866 0 obj +[ +867 0 R +868 0 R +869 0 R +870 0 R +871 0 R +] +endobj +867 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 262.28 683.5 307.28 673.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 98 0 R +/H /I +>> +endobj +868 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 573.0 242.0 563.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 348 0 R +/H /I +>> +endobj +869 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 255.33 340.668 292.83 330.668 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +870 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 312.168 230.0 302.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 350 0 R +/H /I +>> +endobj +871 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 242.0 312.168 302.0 302.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 271 0 R +/H /I +>> +endobj +872 0 obj +<< /Length 2136 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<=`<%S&:Vs/&Ftte=OWus8k.j89mRoO2Js=hV@SPPgHoK!X!SI"SmVM$""gakf\6L_QaCGp&LZ9&r4"3m,9QmIq=Mo+@EdX'>^-8o[bJC'S?/cu9StSVX$We;j/eVQStB9%pbk,MfV+nD".;3dhG2@SNQ;Gj&@Nhg*oQ/5Hf7uM(XlAeDC4E6rV\n<&)hhuEY=6%3"f@3-5k.Mcc3)_hPA0t@saE/>\mph"(=]3jLL3PZm.[EF=es*J8L!8.\`PT;FCT5h[VO/3,G3`KgQ]BaW6^o"*(N!++guhNe;AaiD]Ncc]aNQV_[ZA#!QH&,B475R_B:gXr?MMSWkX=VYR.HN:.Ee!EUU`/uisg/mBS=!$SaoCO1CE&@7.fprTjn1m@=lH3'_@dt-3WE8?&s/L&XncR)YW`05mN@r@"jLb7t<'[g3$j=KY2A=B20"Z-WsjrJ9i-<_c>c475OA)T?*2UGOP8M1fgLd[Gr'bL)nVQ94to]`]"\CprJ)/pms;'l1^&2YLsQd?cjLW?Kf?mdXb'dsga#WIp\8K(!)0n7-Y#`=b4#fh`Ue-I'Tt@qJQ?!mT>NOYJ.68J[PP;H.:T%]`3IpcD&A:m_T50fUQWZ?+>g$X0rNWAYJ,Iu<;(3lmEJCYW3n:253h-cHZ$rV>J`5QWhgQ\Nd6SIa[NUBos3nZ@b!;DgQouNehAWgghK^3@3o,AJWj9J3W8U/ip*:B\3lt9^F^<+2^+b!gmc4Rol0ZQd"P3ADUq>Bs$3;&bsj12mJ1/!2"/lZ+p@I,]3q",^&;1P^PVYF^>>+/NWVD"".6]rr:t(kVuTG^59N[_X7(3%d&@.]Jeo-?MbLl[;n+rgNi@n6XFGYIE+7b2!3XU/`u1R(A*:52j*-a4!iMpOXEBpbFNTh9WSJu%$ch7te>qa.2o1.Q,hX:5p6MZ'$ek7pMZW<8Egrjr\\1"=,8e/H;25bHUYd1(%H@.sYmQFUT;cl^#*p1jbUeZ%QkDSrl"4%!NEBNm?,U\YRp[nNhmK;MA07F;E1b4"t=5I!rl9`"WZX/0`@3ac0Y<<4n"TZnG)YGM;J00<8lS?]D0TXWZcL\9S[o-!c+4jEJBAjL!A(I&FJ,E,[r?#TTK22~> +endstream +endobj +873 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 872 0 R +/Annots 874 0 R +>> +endobj +874 0 obj +[ +875 0 R +] +endobj +875 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 252.0 252.308 302.0 242.308 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 275 0 R +/H /I +>> +endobj +876 0 obj +<< /Length 2023 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DgN)%,&:N/3Y^Wl>2+2t=f28E%SYg+KWNE&H8b,,FOMqMp1*A""rVA-3;?//mG1qd9@nYbJ_=#.2`Cd>rPNeRP_?AQCHo\I6gBk<)pr-8VecKY457F[igA8=?4bW2)ld2tTGdLi.AQ^]^(KllJJS!i_j\&X=PjsM2la&Hu#On==m0::WNWc^2HH;.Nkjs$nNrT8DNoM%n,uidXO-KNQc,ubq&4dbs]ML>JXOs%9!ne3KFi#N@88f,'d':X:XWSEagli_lhjSZ`fq8-ApJsjZ;6K_3tRV9X.65EObC!>$M+$7?e5R7+F(AKL\4(3#*P*Pod/[q/#Y0*Ol7"#_Df4AZ7gcFn:f!5I`:0>*Q(^c5%XH[G..4/'G`a=Gr^<<:ZDRkA/:&rTe1N,U8m#7(j$s$Vk6mA%b%m;E7Q-jqe<49hdtUbgc)V!GFLpgI9Hr[ECT_sC`;f8okKG3dNoCf/[rkLgm`#EGDdcuqC,beoif$_5fIpkR>mVe;pjo;A>$hN"dRNmm#5N^%W2RLS9Elm(G^N7`bsO(``uK-03f+F0GnfmlX2pXtX,YT!Rh%ah.]K[h&iH69=`]e0fF9BabSV41dA55F[C]@^A70o7%N^E[)Nr;=>j0;Ds-`8.2HRKlOPQiJ\rfdi7k!c8':AimLIY,\.Mu9Aq00?^i00Y2nf7\4j@p@8jg1F+1&THTZA1acf&'XSE$p!dR6"Dl2<[?=Zq=5S&+=Z@^"q;_f3r3K*+IrmMJ$7#lr+T$tb%d@Lm"RVj;%g'FQG(d(h;,@-P0"t^._bEKT1:GZJNd2bW_dS4'dPmMbH:5*5pISCel%V*U7QiOp"VL>'5*Yh=,`q'uW)2Q\3IF%hNMeQka5>Nr&IR@WVs?f#$:iOH5Z`'t]NjD<1&8Sp3]82]ZZ;>]J>$hk3]E+dK<-s*aG`6*E!;2fg>WK9Xk9E`B;T!JX#Cj0b=EOUUGsG:Kk(aG;&iKX+cLD%-UTVXY,pd_>,[r>C-AcW]N.t/)rXV@HOpaDTZGjeMr$]XBi^o)@HCdsJ(4f""bYHZ7ZGL,If%AX>u`_9N[8(\*/Q0LJ=u)gMjQ*':H3D2b7EeR-7cj9qKNn8CAS1[JL+ZN +endstream +endobj +877 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 876 0 R +/Annots 878 0 R +>> +endobj +878 0 obj +[ +879 0 R +880 0 R +881 0 R +] +endobj +879 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 468.35 540.528 505.85 530.528 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +880 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 152.0 512.028 230.0 502.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 356 0 R +/H /I +>> +endobj +881 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 242.0 512.028 296.0 502.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 292 0 R +/H /I +>> +endobj +882 0 obj +<< /Length 335 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GasbT4&5r5&;5E1MCim<*3\mJ&OIZ]5tV=!Jg*hGO@t6LOgrI3BC>kP5XAgm4Sj55m:d7n;7m9i*M6OJ]N!gOU]k61lS7_`TIaJ&m_s:`($!&*`JV4n.Hf?-6kW4LBJfpaK%=%!q%).Y&)^gNP8OBN[c=gV&XWQ/ca5ba0/Z)O_fBiRaj9_+>Q%IMWG\`Z$s!r&*+6M_5)6ghB,sPfh[_rqXtpo1gO^Y;)#/hlT4pkNH7IMAeRS%NC7TWk.#tr@aq&>R)Yh\q?!i[P7_lG2N//ae0 +endstream +endobj +883 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 882 0 R +>> +endobj +884 0 obj +<< /Length 3076 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;f>B?9+&q9SY;!Gr=^$Q(7o6WLM*5)O$k$:CiM7_mE7\80m1q[gH`MN<3P@0gA*g5D^N+oSaO@8O_+JT&oc;B[Nr'[36Cb<%?H7oge9Fbu]/S5cd`:KsM?0bV,3Pb>l5NeN_?AJ=Gn7T&\T'7%%FH6[I[2mc%h1/^ZPrkI3DD6#H\NhS&*@@MlZp8l363!ldi89r`(C.cGQ_h#rl(@D,<&P]&eG,Y#n#EuX:YQ;4LQ?R]-&YuEhY1'OC#4?BZ2T%55CU=P,Ct`(1M#Pl09[7M!##::c0Ud0BPT+?Y`YZA$$"kGk$@#qaJ4kGQi/otPo9s\5pI%/@U=#XI_J`^[DO&uQhiV'9BBDIOX;>)"fBMo-cgs/?^fGa=V3D7$OVkUTEe?;#cgnk`eV5uq8i.ARe-JDF"C%J1fPp!6@OM*7$%+r'a5Z_O-4>m[CGN8I@0dAKa=%M+HBRejuRScYeEk,]V'"Y?/e_\oN5K(D:C7C@"*ockL:&H#@.,*\i8qP7$UrLHA->liA,RbEqAt#YuBd5IXmibbo@o0BamQtlQ9m1lCEnD_Xa[m$[&Et4e0SkaEe#3=+f:+>=M0CeRc8QgUmuMDdX]1[ATI#Cr0q8J_c=B&glSjNerh9[,u65s9#Ri)T=\O]+gckYl(9Zh>5`G)t;$,/Z-4VABB'4LrY/!jAmWf3]*.P]g-UuKaa95QTbI_T3=HEL4NJ8k0,=jT,.aHLE:L2lp3>3&O\,FqEO8s.S^&\oIDaFT?-`9ius+'$PJ[6C16IaZ*:YA/&XC^L`#sD]V1_.e6=[=$JI#\VC%HK!$ot[#1UK%PqkLXYOnRAYo7]SMKJB`U1k8m%T"sb\jiue@$qn)OG!qNb#+LNAes5Y,\$^D;I^D#/,[aES/b>h=$`flOj/d_VR)('^-N>Z'[^AlsN&(c,#l!0dF=_OIK:'@I?FJ,g]M+Cc<->YV-h`.V[Q.4]dMLSAXSM-$UXFX;FM"#&FA-K+Pd9b2LoiR"?BRqP!.HO6-&_&r`H:h?>*@ZG@MV-8@@YLIdP*Zp5#*!8*PV\&3O(C5DK@-bd)2E_fdCCT3"jThT3gauH?&76KS<)3>$4-'uW<,]Z"=B_R7:MT2s#E:rC.g!u;!_K!X/$h[Jt35Y]<+rFL1;FL+^$_p,dp:mUr&Z^1`s(ij^c6@WHUH!Y0<>pMheJ0X;T@pHPaTG5:9c00to\kFmbo1CEmZgqXm+d?2AGenH7?$Q[cbUXFb&t'cN,$"5\QVZ\;tn%"BW"Cif.@MdP)lL?>p@Z.LnGB'"3KdYIS*AK!J.Ct;^#f[1\BoN\>":KuPYmoQOA05n_l7;K6R0k"kEK2$me9?XP)O+);hle9YRn3KJ"q.(&o:pIpRYcT,bkh)R\Wtt#VK"i.G[tDr.[OpsSoIn+KVBD5K`ma$DfA_tQg3GPi49NI+!(g+(=YjIe+ej2S@Lfn#fGbqq9>7C?JJGltMUi0,t,bb>O4.42&J]F/2[Fqba#`mo"@?+>EN@edb6ab,]=N@tT@hk?Y;o97P*.EsMqhRmt4fDVTCP7n>0WDD,q"b];b&9(%.15Wj.jA/q\UW_f?a0/Rj*u!1U,SYn==`)J^am+Nh,\FW:4P;mDdBu*Se[c[DL\Igmq:.rn7g52%5+t,l$?&b71Qg4B[7&!*;!7J2aO5H1OOc#ALV\"tC*@`'IbnYNrLTXA0^_>J:8R>(o6*[XuS/1.J9N9@hZ@bI7Sg>P7(kgED#sG;X/cHlMSAIfZ2T^%h~> +endstream +endobj +885 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 884 0 R +/Annots 886 0 R +>> +endobj +886 0 obj +[ +887 0 R +888 0 R +889 0 R +890 0 R +] +endobj +887 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 161.16 691.866 206.16 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 96 0 R +/H /I +>> +endobj +888 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 169.2 161.266 214.76 151.266 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 541 0 R +/H /I +>> +endobj +889 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 347.79 161.266 393.35 151.266 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +890 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 415.29 161.266 460.85 151.266 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 891 0 R +/H /I +>> +endobj +892 0 obj +<< /Length 2131 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0EgMZ%0&:Ml+\GMMtX&Km.Z5,l[1`'u:\KPt$=D[\^Q=ubI/?0?EXT-!ZfbT>Ukr?Wt!G!j!17H9#-S'g<;9.q7Bj7rHii#"f]R@FRPLifk/^5/"JeeMC&C0=M)d6^qa\pHb%Rac$KH>t(BT^pZS0!5UTgm],aJ\b[k#Ik;4A&^Inskkp-Mpm\C)SNt9apY)HqB$.8Q&U\?$WXdN51X/EC1M$=q#gt\95l-f!`_9Z=$",1A/%CA)6fg&n7VU2@k?(Hd"6n#)qVFml8Qmfm2W_k-'ja:.f-WGA7MlMpaFTQ8$l:7#c1o1RNDhD@^>Y=\Dlc4c8"sO]`Wt7QfUm,96G_UDAG^lQVV[b['8D"a&Of*\C(To8n\h>q-`<0Ki")pGs;&?;D7ER8-miV7>(n'dQNlJ?5re;hgk*Q,"HIGlF#]/r3A!HhfWI$rodPJ5#GRN,/cq[WG.VY!G0C=3?hMSs=,T;?Le?'igC$Ch?3>OGZ1D0bqCi:9RI-FI1R*lanqL_8>Ih;;!V_b^h8=U*l355ZiFgVk5dE;C]j46dQZe9@C![;;9PLK($=hj3Ur@rS(,0O4WPR[?:]:lair["l!p=?I7pi9q/c*om=DA8EVg@OS%5M;Tlj>j`!2b>N7fgiHUWo)"9gghQoNYJM#V&1_gY2p1d7%bt^rpUbX7i]%g]-.A['sRC%JN!'*K_ZVi2F63+%p7gX=UKot.YRlq>!(6qXaLA*W(e@NCDlj6>seSK,R7$E3nE'()248E1g8t^hq%u+lK)F[M0WB!hVf$_HrZ!QX2=5>s,h46B"_.n])6[Bo,Df)"!8`EW6=hFTLD=JWucQjUYQ]3D.>P?D=!i`Co"HT]U5oKU#!;m2X.cSao?bFEbSuX[ok-ChiPCFQ6b9SkQsh!U=lY1'97cm1G[mbomM_\3\;K/THLqOUU0e=%iY9VE[0I4S>L>CRYd<8-+,Yl2mC=3UOCf/aY-HEd\3oO*UI\7n5?pASt56pY`FBOM&Nj(2?m4$:>GP$lQ/0V&jDK5UQd/Fa]Z+:]ls@HND>g#?(5"Ke8Q6,D1Xo@,_lR*9R<\isbfP\s#TfC2)LlO;`CL(emMoP@"(3JM<>1Z'5"KbYhJec3p0:-"":k+H3H!/lWTpR$9]/!+]Vl(G!CSL6_m+'a=Qq*=nT"]Q?[;KFC1H=LB&-`#J@4;AS$$MZ'hqcA(#WsC]=-nh/\cXfbC308pBoNTDA3<'74q9)D+W:""f8Heo_B+<>tu%j]QYa`duX(N;s2AVkRSLT8`$e;cQ?au:dJ\h7nEV#-pA5;9>3,[S$A$&gPLNuTU60lb:YKg`Kn9q?^uGFmsT!/PeUUOG&SE*q!%Ntl-<.qGO2hFk(V&u?i_r-g74GDEq5K(jG`"(d^3oH#)@]U$$a%q1Q(HZT.566-d!h[)2;W+^>=ti=t^;E#0hoT3p5OUIf]&Xc_g~> +endstream +endobj +893 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 892 0 R +/Annots 894 0 R +>> +endobj +894 0 obj +[ +895 0 R +] +endobj +895 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 261.2 99.78 306.76 89.78 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +896 0 obj +<< /Length 2513 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHNgMYb8&:O:S#jt%n^fF%lb.O^YN?j<#,?C6;E?80A>$Qa?Wk:lG^V3'-(=/)SP$A]T?USV=ZZBa@b[pbMo]3K^;9/'JS^-ld`9GKAgl=aODJ/%XG9b/s]"MNb]'+Nkjc6pU46H^dTc[V>H2WjH]Ku40q3]>N;_o/hr2K[!-2$GZaWI]J5<@7(,]M+MSW1Et@=BYA3$*14B497[eh8gfM-.i8.0B9MDWGSpGQ2D.Xq]f9#EJ*\4.UKMfu`m!6+=mgIfFfkp7-f,hb:1+msaVHW);fiKh:k?]U#5EdF6^bgg])F8?=Kh&2#7]u3\_cVeJKFLMdkbfO//L(`1UF"t5VBnb[9K[odR=Z/O9,od+6q1'Naig(ABs(e`Hd-ZDU6gOJ8Y<$$\&6M[8eRq?>cpQs@0qW[V%>?#C7jP&mTp4ul;oHt$oH)cd6f*I2qe::dIW6J%R>VabFr`_.M`Q0qSQno?P>`1G_c3(a^&o5qD1YZe5X>loYXYQ+"OJ7uiepZJ;CYa84][.eQ?gaen1UT*GkRd$PBI#D8T@Li.)mn';406Np<6^:?F2IlfM#ie0#SKiO"!Wkb_\E&859.!-o?"61=A-#DU2r.1rIucmJFg1i$GBuJ2H!Hb(Eqe2'8b_&1&2;K_]"_m!,=QThaPtl'`sre'Et^:@:;uR'-0-@tn1@6F*ubPM*+,bdk?>;M=pAV)U>8+PA"^WtkB3ja!l%lkD6E),^[O*;;]CAq[]ZG3[[+@Aldt8]6fM;?)F81DC1l*K1f39ERfY>=>2rE^[gejAnC(Pa4Nc)^dAU2Y=7&_1%C=$cBNVr;fP\BC\nNQE9"\e8d*E$![1Ag'\R9&:_/rVIg(s6[ZOAWt>urS0(D2"![&['qi%/nrJ2Ao,i]2OtY=7[4!U/B(LMYB4M;O#s_%ou.W*rK)n^Zb/ud728)6*0I)^gc@''kE*B:jdsj6R/!4Y9;4YT1@ZAFK<`Sb3s,j1CP=f'#!s-%m&Db:Js'B,:"_c_H*RDk(=N,<@JWf:VBbc2VEP@hTnqAql95'R'U4-"JhlR0'0Nt5gE#WmM4QZ!2dLY!l=J.`pZ+5YEKn/@2eY1:`GqtXR5WIF]Q7IN)1olsKklL)3Jnce,iVL:eE,GK?>.Nt@2mY_LZVRo,WnqPi=f:(jY]p5M6<#fj$BrEmjQ>4]RDk*h^>`Eq]tK,(Gg"r)m"Rf.]usUWk'3$ijZ9.L.F5&W6AJNBL1&MN)>o0<+Mu4=4_OOS=lbq([hk7m"Kc,UMDcLHb0`93XIRS7U?=VYWH`*\DUfS'g*@s[aJYBV\'o7Kf6_F\R5H!O'l]phC%DSiQtigGj&hq290oSQNeUOXYpQRGr#`8iG(1R'CqL62XqYKEE],8gd.F$oprXM&Q340mg1]Z+Hc>#B[g#j$K9OUQ9%mCGS35j].EZFe/0LZM9%]&Z-/^lf3'FpWUYZGd1_ob?Xq'9]gNEC@gI^)=K^,i!Cgsn=oN\&r>U()A@&Pt3]X8rh-%,[Z?^rV/1RR1O'\c1K_Vgo(i,\mU+r0Qga';$E.+,7)!G5pR&22B!Tp_$gTAg$CRbK$pEhu,Z";>bPYZVPJuLNg9/c!e9[f,o.;jn7h4I)>@&Q@b?3j0W&j?iQm0I(2qY'<5\K0-kVV$`[X@\-,t#H5qkMDVG%KA$h/QT5N4bb6omto^S$?=Lo:\E34NA>-,^@8_me/aZ7@$MB`$NREh/qS[:E/-OP\Ni%KFgeu/#u\,BXh4 +endstream +endobj +897 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 896 0 R +/Annots 898 0 R +>> +endobj +898 0 obj +[ +899 0 R +900 0 R +] +endobj +899 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 225.3 691.866 315.29 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 581 0 R +/H /I +>> +endobj +900 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 161.14 241.866 210.3 231.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 433 0 R +/H /I +>> +endobj +901 0 obj +<< /Length 1866 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;egN)%,&:N/3E;U>]`b#K_`On3+gbcG!Zt=WPN#sG\Z7mF.Kckg>qZ=>?]-8ACgs1"&GqgIM04tg@/t$q:jNU-QOO3ZjCd.BZ]*WI>q2[Ll0@pgiF\*-+=m^Su-<'ZHEuK=qr8IGLg$eLJO&ceS]O"ViD[Hj&Wa#fZ?9>Vkp.9Re*9+*6Cd"T:6Tg?5DsQ@U+.)[sMiHCb0=OpM^4h#n][778Gu*1T$1PR"E[e3-JSefL"[3/U%[jhQnI-IN'ZPda\\cA*A6nhQrt*u)BtRSQS>ZgY93t"f7pjD1C*L,e7$^P"W/BYjc[(qjH@dNZpiSV:30PqF;m:BEk^JI^7s,_U*hT#As(tLFNskd82O;?T:NE4aT_!T$a=r=<'Z[(:j7.ARF:*QN0$ZH,g;!!Gn$/ebF$T/._@m,m*,.UN?ic>I=^3(/Qi!9TKVEGkL_VKD4/o&>6nA>APK[r]N428/aQbHfke\q+9qM.1YXtD\qSc9]FgcYjV(5n%2GMpD]#0-&Jm&O5!'8eZi+UduQ[hT[p\)fS`>WM[icdE!r2g-nuQ]LJ^JM6p;Yl%LMfmd$o?bg=O]eM;^WUa_a60MW]TDu2)m4N;ceP^7,$]&?5gS"pT]_ILJ_G\+Q4P?+#NiEcI=,X!>+3dn^:TsY5bV_IV&MCNA8?,V#Pd-ckGQq31;^fh)0=D[sJ&IG*u`kIdL1P2?%NB\U/kh6VMTIM^5i5m1'5Wi)A*HLrkC+T#+4Ja1VdPK;PW9=MImF>DG18Z%'5m8*7E'H%p^rVbQD33kbG5'_)aEkNk]loW-4qO0^#l_'J7"2L7*Dgcl#66)^Qo+e]\nnWG1K']tfU0[(VcZ\$lNfU&-(<6(P,Z"99RhDM>hrTdC7[Y_#1BPdNp)&6I70sgGP4c#YReVu^-fh+'.IOJ=OMfG=ck37?B-\%YVBTsOSk=O+DpZOg,XfQ"hg/hp:Z[i'u3Y,$G$B1/\)F/VUW_YM]X+2$0HYDDF)X:-V>U2]pW.Php>+4Fd0QnEl(B@F6?M_2Ug;mWAC%A3dI'opFGVLn!-MD2U6M*`cBmB9n02q^:1AW].jX;Ab$^#f=oiU9toLiUn\DeA=HJPjBHB#LJWh7]P^d27/ss(;m$'9+AU`L:"FK`Yj4&t.@J=+P5'UsVU"]RFLLW$Eb1]>Ptuf1lKES$Z;\&o#FToq"PO5Lo=e+M+>K!)SYji^9dfCAhNi\T=duU,BbIJm*iiT(^>sLR?Qu7LrGekEn2D+HrH\~> +endstream +endobj +902 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 901 0 R +/Annots 903 0 R +>> +endobj +903 0 obj +[ +904 0 R +905 0 R +] +endobj +904 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 276.99 604.866 322.55 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +905 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 330.95 401.13 320.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +906 0 obj +<< /Length 2596 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>9lJcU&A@7.#e;brCmH0;,kdNb2X7#SK32gq`'(3DQ*^h=Ab]\e^\VC5lab:kQ,p8C>b+RfR4NiaS\g6HmWLYX'",?q(89b0G.E39lAb/FPZ";[s&H9!ccTY>.5%Bqr``CD=KPOb0KI]eOX^Yr5,`d;Z/t7[GmW>F6n)S#Ttb[oli,'tSZ6qH8"oV1F[F]qDM%A+A[0jgjj+kPmY-gW(ZMVTs;'ms"L"EHLAB)nK'P!5)$iM>W=J:W-NG&?M*i'6l\VYk/smW1,p6^Igg-O]&W4hi"k(k'2g`@o;MXQLu"Ees=k65ZsE?D$I@Z6'#%nX%KH2#OA82ARa1Hi-81.UH(kq0f,[N#O=e/_]Z:-.lPS,uq&48&@==<<#;lP0#J,T9@[o4M0U#0AOYYd,W\Il?f,Ge;>C)3[nom00].Pd8,7:6Ncg'B$9.T]c:SQ_f*ir44KH+%t#U'+fd"?s<"/LeN[O%^iG+RTIBIQrIih:7uX>KCJ^G<4U3T.`=U[Sto2j0p,i$0YgsG>K-.&\([%DZ8iAnD)]=#$Fl7JF;;PTD!S,rVRDY3b]*V!B!0JUA@-5J6/rO4/A.+jhsR[S_7!?4=ja>ao]7:L(t@C39"Z:HK/W+?95!=D&2Lff?o@oU!J;2@<.+*I`e$[;O%g9Z:AM4HZ=-sI8jtbDf?`66m8:j]@q]ML'eM32:J]V)F0b;TPtsBX8n\..A+d(0D$6&`n1(Xl/MW"4?)D_gD,!W5@[WY^T-G0(Ob\(<]0C7M2SVkLkt2H#siq;-1=%1s-#l.h3UB/qtfp/&f,DC6C5*>\hB6MV_$U4f):)sMa@DN-ga!H6%R8SB5MODI",oT]!lUMH8G-o>eUWSThn6GeF4%Cm(k>"ISsSC$UpAR_Uk/NCraO.^6FWqnf/hKeqH1O7%2&f/#N<5@afpM[iL*/Q&'k_T*:8d=B:LP[p?6rnnJJQU`8T;Y0M)g&%91X"0*51C'O"$PNOjO,[FV3a4;??HJPMC+Sb_#,']'S"1&u8lfZ&#&b<8RLjSJ<&9B2^Igb.YJ1cYSD8]"&oB?_QbuR.J1Cas3g(-3N)ug0LLXglA`r`Y/Utf+s%<7!c"`F4D/?r]KJ"HmX_NgT2Js/eki_'Sai,iT^QdU8S@Eu3qj`J17+hpO26+FjF]"%:U!]+cV7A=Ugj4YLa7ibGE$/9ksEo-0[&c"1mZ82cJl>L0n=hI?dr8^VZk@n@3.TPuP]2t#Qhoo?G.PFkT4Y[Oh;a#)nNG9kh_]!t^nh8KC9\G`?R:Ak0BrCt=>ATE.GFlb"!D,b7Ci^-]\n\H*GHrO+jVAD]pHs2lh&*$Qh\u'&Sa9R\dcu!PV$U*]#4dN-RFH53o*_Aah$ML27Mh=%WGW/Re>k>gGY\D`,%A7s=Cr\u$(H"hJ;R':U,N#V'^%M@0Y]+u%bSm;QpsE1Yr!5^YINW/D[m`j?aW@?p0`/+B>?OjT64KfgXcr*YNIm@r>%QjC[$uKqs&9iD2*eDHc+AJuJUVGjEWgBb.Sr-A'(:k,10%=EM?K@WXpq/5qtHF[j@%CCBA09,F!r/n\"IZrg^ec[K!SOiZA@3b^G\*&ke$9<["Ig.Tssdp6W6u_d'V:T1\1mB777O>e.c+aiW6r[B_JjGbFd+(GCB;.GWCg[-JaHp=m +endstream +endobj +907 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 906 0 R +/Annots 908 0 R +>> +endobj +908 0 obj +[ +909 0 R +911 0 R +913 0 R +915 0 R +917 0 R +918 0 R +] +endobj +909 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 488.9 691.866 534.46 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 910 0 R +/H /I +>> +endobj +911 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 315.32 647.866 360.88 637.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 912 0 R +/H /I +>> +endobj +913 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 415.04 647.866 460.6 637.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 914 0 R +/H /I +>> +endobj +915 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 240.02 625.866 285.58 615.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 916 0 R +/H /I +>> +endobj +917 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 151.16 582.866 202.82 572.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +918 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 258.45 539.866 304.01 529.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 916 0 R +/H /I +>> +endobj +919 0 obj +<< /Length 2760 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU6gN)%.&q/)-k\]?0S&:.T8WOp>?(7g(iej#GDT22IOcYAV$#fmm)UI#R;\ua/"=0gAWCi!Uf]YN_5(,_G?,$3t@5\FhSj-A\ht`J\]3sX9T3sD^iSD^bGX7QfQK@3)qp>)?-9&3u_`te`ib>/Vr<@2WGk^>q8L$!n=Y8R`e`;e5ni)XO:BH#!r?us=8l"NRk8u!'F('A\L8V/0rWW;A8ul%G-0sI&S0%.C^r5Mh4\,Ylg=_/6hH)pWK((/#sg-^Fn=`G^-b;AspT>sh/q/'!]26M"Lb2dY`GW$06=tUnH$+up\oDCcc->e^Xk98nPb>q-jMu@%G9I>'Al-t`ffS]g2q"HEPG@;+0Irle,Xr,E3Da'r^T"H_bq2F?FUCTZZPCF]H`J.M]hYk'#;O9%JgV(1!Wm2BaD@^@C-[_7:9(-'7@Qn$$)9<",ad;Q0.Y#@)L!]&;r)=E#4B7D_.+W/g7Q"Y*4m,eD,G30aH?X&b@p+g)%d5`k$NYND:)8i-9W=Sn%MAL$nF[QWp+&X<>;1-$,&;[5)`*)#)eAU@W!:^bKBPj'58M5Y):.O:$m7!5l4*g^uOF1hT^,;WP>OAJmbL0R]:XiUfla3&iSMG,"Wc28r2Sn[?6_(6Y45bNr\nA!abGd:\D%@IVg_iB_-Wo*fp1'9G=[oIH"tHCb6FBRN`gJ]!Op3%D)i_\1W(=LR3lJb/lV0l$\(NaWBp=$dgEL(5/tF8`u\IeroJm%8b,]X22&Qm%fV+df]fG950#a/pg%kr[]IQa9pVbbL.K.#!i*;\2)>)N9*SSY`\,=s);"TJ6t'1es\PIZ[dB]'o]N,)Ut>$"dfoXS]JF2n.@JaZ6TGc[on_@-71pN3L#bht+T"oO2DHLS'Y&dA"fb2$Kt&Y*)%%4h-24>Ob9\4BkS`E[KQAll$p*/&<]&)Mp*Y=ZgI\sP0QmGaK"]X*`Ia$KGUF)H7W$]F9;];DRm+&YR;S)jI7q/YoA1kS72oSVkX9r9?g_V'"fRei"T6HE7/hjX!LK[&o3;tJ%bB6L$S:(a\)Q,Mbg?YW?gViUOB=J+Q,aVmJ9Ze[16CG0>Ai,FZGTM$fApDi`ReL%ojtQ<3W(a#b>_.qHi$<>2P_-f:@nDc-dD0'2:$YGY5>(N">9;lKC>npPPtaO;_sVM%YPRi/Q:j)RUfcUS6?O(ePDRk_>Y#@GZ1hff)k$nfLE^YuE[5KG"H000Ul^*/61q(^,UfMhA9s:hUP"4doPgL4(\nS]tn[-^f<(b9"DNBgW!q["\^99C@CG][Iccdp\@\3\@p#04Z0Q,j:P3N-N=5<`t0Zd]$3GTV+p_lNnnb*!iJ/\i%p\M:+RT%d/\tZ;6E_<]3Au87>PUMAJV=1MD,STP91$@hF.>FK'\`!=0Z#d*YI"1\fbrId*Yi*F9dd6r/>CoiP,)[FYOc)X9s`#-$umU$qGuRX]S>N*m9aN&:isaZMurXrL6)!?tr_$KTC=Ck<'tVV"G8E:sC+qs0RoEB!j>`Z/_tNCuH66&RPUXcuTF$i7J091"CnqBhLh*fpuF^%]%;h01#entB:c7lB)":J2ZW2E;S(o']0kO(puE3LMllW,hV0Mf*e1M]q^o@;NG0:4E*,j=O,kj$+JELgmqC_2c%=6@$TFQ"#]1G<^OqVea4>bBZ)i:?[)oAd`DhsXG/"-+k;[nS/eB(("N('2BK+ju2.9Wb$8G;Zf4KeVdg:d)/;:1\AL3PLO+H,NkV>I4L4Gt3>4T)9Xb&b0:KXa@O\Dd3PG;=/Vcabouq\h'aMkN6J!("+O""D8H=ju(Ehq@L:VrmOtZLB1^oOdQL%\$IjC$1*"%ncO5h5KT?DHY#B\u?L]~> +endstream +endobj +920 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 919 0 R +/Annots 921 0 R +>> +endobj +921 0 obj +[ +922 0 R +923 0 R +924 0 R +] +endobj +922 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 335.31 659.866 380.87 649.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +923 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 482.8 659.866 528.36 649.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 916 0 R +/H /I +>> +endobj +924 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 383.63 444.894 429.19 434.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 925 0 R +/H /I +>> +endobj +926 0 obj +<< /Length 3285 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0F968iI'#*O1W-kjUALlbERTh\Y2jqh4VIDPA;t1hM&s=pq#nB4(j+"fQ8J\p05`]HbHnd'4)IL7nDX_#^2fJ/ZJN"L-#1NC;d9uLa)iW_$)bbX(Q].*$QOKb1i7l4h93tp7Y5QEM^:?n^`Q>g'8QkZ9%c\QkD)S[U8$s5d2V0Gcjtg;enl-HAYg=gJgE/BYM&"fm?1/cH_`J#4@-PB[\T$_XP"9?+r)SI:t`+MtQb=8eAM"1#J&iufTdKOJs@u+'X64?]_:k6\%iU]/M&Na]*]>"XEH'f04fd274E$LW[j5%*3BUWUj4+U=?An&giEYrUEeWFps6jU)rqlgKJg_OG(NaGO@9'XP*>7&Tm^$SITKo4=O22N1A4k$O!49L!I`:6@GPQ]NiH@+gguf`d[WK5HH7I/%M^8Z0`6El]<"BDIjd@oQ<-SKSGr\l@gcKmfeL*AJ-nb-TSs;_@JK4q1@Y2N(O2L4X=&rr_g"<#bj\-?#^2"nlQ&/.pIUKH+16!*%XKeOP,C0tE7eniDsU5<]b@+d,XgoG)%XF&Jm-QH)M>;J9:9Dt@+J&`Zo\a(Q)oOYNAN,Ioq6,caga[LTYo"OR,25ZCp_b=3$(P9KBO1k$7B(GfFKQK%!\&@>WtZ(8(j@[N_0lKRU5-$=\;cSlq8-Tm4j#`WOQ>k*Q6OoIO,5I"EV6h+\s/Wd+mh;@mdfL7CP-c=LR@-&c#0\m"@;V1PV\%YZOBbF18kQm*nA0opHWO)hDIE0A)cp]pOQ[g(#(#a$A!["9lh:_jn2bH*;$M7Eerl_*GpRee:4r=/WNC^@H%`EJ%Hq.5rVdiDcIqc#mFCOg"#j(E`l8lm0W&*]>r7QC$q4p>DD;KF`ehBQqO%gHc'fGupCiaFU9#QFGYpAHk=qTn!Y[Q+o=j'`ePnOVBa;W)pN9!G-=Xf_"+`hR\dbh*6[@*cj4Ur.Tp#@iJOk$i%9RQ$UDZ7HXn1(682LOAq=P,,9*,26:.UnTlLbTj[$W]1br>5<"P:0OS'R>ErGju?ab)$FA1pii=l-9`*DH+^YThE5+R44Q)DPTNJ/!XD3JJ]Xqms$K1+GQ_Me3i"cX*:,i#+B"T%$EsaIBfVV;E?3>0^&?H2%Jjh@$fZuH-5l$6]OATiXBh>.!nPm`A1%qW*+RK5Z^RjtUUs:k-7%mj[''d%+k/2L@$Ru_[G,Spa(CRf$gkBd<;h@ub??#.bb;GeQ9K`k?b_E6XSfS';[D*JNUA!BO7k>l=1eWS@Ce;:]'IbK,1MJ1]XY+=4nk['e::C)SM0V%o;hJZchh?P8iYS"_PW(AtGj%4q-O^[*d!?gdb.$>:O7ACn%m\%eAq"(/ldmGc?Y>so/N8pNH?:5@k>Z'KSejnQ:/9US2p*tV1Su^9h*)cp,gjQ;lEo'h<#-3?2Pb$TgbA4IQS69$X'Hn8g9G$'ji7#`gT%:bPWs08a]HGh#B4`V;)^X%H$!AgVs#V.[N_bR#Qmc"`'Gl@EW7NU>agu:-NT`hHMUD:S>9A9r")U#eO#tJp5D`-g_WmbBKF"!:[,7$JT4B(isHi1>!Xg\;TR,SL6ubVfUSb\XhE]O6`^MbKUO*)3+J'=6afu=*Y7m@2V9)/O^(EE+JaesLrU2LXGjTXhb)U<2/J7uJgbT>P.f8V)rX0&BHK%p:a=[1`aP*84oI?+Q?0U1e_+W-)b5Rh!=i`_H?lgsB*^5`Vj273jgP9-=!Gmsrr6/c0?8QOqqfm*7r^F^ITT`%e'7.B2%tLG6Ggb7J#tbgUr@Y/-.]Y_o+k_7I-J3ufer0=.\SLZUksn<`lJn_9,*j^">rsVVVLSqll?:glS$bREh4%A8:!AQ)AjG.t"@?l^pVN_kr@8]t3OX^%",qrlH=G#b;irDVH1sgib?0V<=UFa&9&?,"#AHjnK9`gjh#u1NK082fp9oAfO*iM5HRiPkg`jbD$of--h\tCi5LqX8iA@a_\ab;&LMfLgWS6P`*jg"Ti'6W[OK+_b%`UIZaE_3P>hcosSn*o0[O0P^@M7T-76a?ic!Sr0a+UYFauXnn[?eRIOeK\PPLB+ipT'eDQhm\6`0r2%OK9gnmAF$ILesV1G[l!h'#Qj[aAV!AG[fP-ggD5o8#+#2KD@Roop5Nqo_fVOX'$eC5SrGe/9G5dLh7"GJlBY>aio\A,i4LVp)+\K3[07~> +endstream +endobj +927 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 926 0 R +/Annots 928 0 R +>> +endobj +928 0 obj +[ +929 0 R +930 0 R +931 0 R +932 0 R +933 0 R +] +endobj +929 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 416.79 478.056 468.45 468.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +930 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 303.92 369.056 349.48 359.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 916 0 R +/H /I +>> +endobj +931 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 250.056 143.66 240.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +932 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 95.33 165.084 140.89 155.084 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 547 0 R +/H /I +>> +endobj +933 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.54 165.084 257.54 155.084 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 49 0 R +/H /I +>> +endobj +934 0 obj +<< /Length 1477 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D9on$e&A@P9cnPoM'hO]Ble#g]8-0J2)Q,`7F.ar8+sN.!5J6O$n&Y3k6]!Sl;X?iH.?a/ERlA7`D0u$VWJJtJ:87.D/rB:GBp?D?)H@Z6Pt<%#b@oX#cgUkTZJn&o8CM%/\[:[8))?R_Y%O4(5+h2\sJl!'eXD)&)SCo&//JrKGpnY$D[R8J%f,E+jjiq.hPgL<#jY)utckR[i,(cora(j%:9Gp#hVR+;d*dB%?&IJ`a&dChhB1m^PLDWiUG#):num\;rh(sehm7?U^;-rTs+$>HnNa*Hs$HN*O#6_7oCpZo:@dIU?ZkG:,N@@AZP@r.UM3D#"O!ARP>109t269V(q&R>'DV4C"Og+rcQ)D=o34dacc+:U5b@j/)^M1b.)9,\beJKf;u$Q9S9pRu&89(n!F[GC5Sl?lA_-S5KMb/u5'UV^>+q>R@Pdd6>>Y90"hHVp%_37[h_/;Z8p^"r'qYhTXVd*C.mt2>:A?;,'SE"t:.`rr1X33+;\(`RgHLq9FUui[Va5nj;2PK2cAjG.Zta&(]OFm^(JM;[L*TUfqsQ@kt7.5_t@b7l-$j2@][BD2oV*NbZfor!%T"mAR)kJ4[*FtBV[QYB`=,gQ"F\c]a+Plj54oH$XjT^XU80+D0bcuC`K>HE!m#a,.5lA83AT`UNlgk@TXc_N+a>k&5Z"%4JmP,.foEX=^.^+kU"mtG79%O*fi>cANFuFmE!OGbK>3QO$JF)B[iN+'\k/MV-q8cfOmql1BZp=^)rmHZlN.829Sl:gJj]=],P!./,CO+V&4I5pH2]Y!\rjjY;VEQ=bD+[atE?T4mDm6a4Sm;RT'ailVE[b42[u/ODucq4\$cg7MPLMn3&L!*M;6"f>2L!DbA%T=Ijr,)PGjoML/P=9"i77.L+4U0ls+DNL)n&r(/ii^l>M\VC6%'1frEgN<>]8CWB$Ac?O(qbB4P$eYS+Vl\/P&YL9[?:!7@<`>'l?0X@[tOj=eM#.#R(6T]f-(?t\._5#PL&_i:IdWGmEh,LW* +endstream +endobj +935 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 934 0 R +/Annots 936 0 R +>> +endobj +936 0 obj +[ +937 0 R +] +endobj +937 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 386.41 656.0 431.97 646.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 547 0 R +/H /I +>> +endobj +938 0 obj +<< /Length 1476 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`TgJ6fh&:N^l,+_&/4g>[`_HSZ%>"1Se2>#1q"S7^Mgms7CS;e0_Im94j`T?%)0XK17[F>\hj790JQh9b_B3ne#1#Up-I`Enq:`A@@?d9C^N%kZJ"0ciDJ$+^Hrs[q8emb-P.#mZVp9L!rb7#/2TQ);&-df/Iud2Zm#?8>W.0<-er/9>(Mf_L\O0X94!(Udga1T^cgsJ;"BCAe=;L/ZUjehjAPic@YC2WK5,aGOk?B=XK70*Uh8'1li>+$^)W=fLahOApAbscr#>lM&_8h"9qE[tqcg=M^_d")bNmk)"]d#)h"J-L6bj&A2=n./s[>3/Wp6\X#BNh!APGH@&GODSVgY(YY05"XQ?Y'qBoU$G/Y*T^8<]1MqZ@#-2&eqKD]oOGbA&RE)mrr-A/f:M6G8iE%971LBDimAl67(qdZ0IJf?2C$m^pWjHDIb>fp3B%NFi0e@l=oU9l^c]nTE!CK;$'g!Wuk<_)@P0$*"eU]W\(6rcoJ\U6;/!&PRTCi'6JWX276E#,[YJ<6gbg592J?Xliajm'-s1'?I@;qRb+#@VZCet/`K'BhciY.;?^n06_P@AWorWATF`5koMVpsa6!/T8R7Z9E]d'&1gB&-KPYKcL6K0iY\Ub3V1%JNZa>>pZ@9VsJLSK>Yc08(U_o&jkGh7JX9n76^*^jiQ80nJ9YqNMi1.UW=sk'G(HT.Fj^%3:m%@>'-8'3'/dJ`&%t##:IF<@$Bg)p$%%$u*M%R1s6Bp0>,k?@Q.,,2Q#];fFcU[cR/\\A%-*CE$QX+;>&u<]%RgOgG^O]_tN'UYm<\Um;M"UWY-,cL^`#i`JESGplnY3h.9dP6R8\g>QpSUKbW%D+*l,-"]$d[[^b[6Eb(N[Gq]WiQ6F.h9Pcad@sF;$D$2<&FN58L(L7,5paQ3\sYgY.P(4cOU=->fZBQ0oV-9:-$n(L:!Z^40>fJU["/`/BCE]0>9#Z*rLC1u4cDHk-KBM8GAg+uNdO^sk:1d[.E:Vh]rE,+)c+LP1AmkG>-3r/e9&e.L#bIS*cLho,.MNVSLl@Z5$&GDV6g'RB3jLl9+hM0A!1&sqj979jY4>__WSEDb"Pi%ZK0%uX)sMp:r2Y=jh#*qB3uk9MrL`C,I3\4?NeTuBtu]iA^7+)?Iq`:;Ck>)0QW@iE1@L(*?PN+NuQ'#jg5:E?%LR-TL8',V",DLW,,m$&,S=8&2G"r(='@K=!q3f'"YLQVLCfYt^8Zg7$4UN&d8cf*'Mds[`.r;b/jBqY~> +endstream +endobj +939 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 938 0 R +/Annots 940 0 R +>> +endobj +940 0 obj +[ +941 0 R +942 0 R +943 0 R +945 0 R +946 0 R +] +endobj +941 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 275.96 647.394 324.57 637.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 448 0 R +/H /I +>> +endobj +942 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 332.35 631.394 377.91 621.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +943 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 416.09 448.95 461.65 438.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 944 0 R +/H /I +>> +endobj +945 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 265.87 318.059 315.87 308.059 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 207 0 R +/H /I +>> +endobj +946 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 265.87 187.168 315.87 177.168 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 209 0 R +/H /I +>> +endobj +947 0 obj +<< /Length 957 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gaua=?#SFN'Sc)P'tsfbFlRCeG?nG&Q,?2cA)h$#Y#pXpEK+^6Qi[!";J:[+qbFn&5U,0s'fjmeE,Yq22483'sm`s<;KD'Bat3],]H]DdT!+pqeQb#E(TGY/WZAo1^F$)_oa&i4I=W5^)oQIK&QUu7i&,;@(Ro;nGfWmF-A<,b]@b,bUB?[PnRRARV9NrmDgc6mDb)5%_9M!B&$hgmC07ue@Et4PS#&]CFccV(e$Vdun`qMh9`-+MVa=%bn+;56j31oJNDRpCse:JV/[icGu#ZWU#Wp^'!8(;5n";mu&BCB-'*/l;BE:fn#:tZ?!Q-L>+[R#9i>HY]8k]0&;MHgjqh1uW/]/kp6_*lK8.Dg<,ZD(>t&'ubA`7tV=VXA%/*I]):3bbUU>gt/U_,,^^0E$!8nT2)^eRIW`??ue7-k^A7YUp?eO2KM\Blmg>]TD*np/NTM7!j3qGVbEH,^ih`_Y-"(h">WQppZ-f-Y<,:ELlb`mem5CW)Mg~> +endstream +endobj +948 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 947 0 R +/Annots 949 0 R +>> +endobj +949 0 obj +[ +950 0 R +951 0 R +952 0 R +953 0 R +954 0 R +955 0 R +956 0 R +] +endobj +950 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 265.87 693.0 315.87 683.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 211 0 R +/H /I +>> +endobj +951 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 265.87 562.109 315.87 552.109 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 213 0 R +/H /I +>> +endobj +952 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 265.87 431.218 315.87 421.218 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 237 0 R +/H /I +>> +endobj +953 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 265.87 300.327 315.87 290.327 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 239 0 R +/H /I +>> +endobj +954 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 265.87 169.436 315.87 159.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 241 0 R +/H /I +>> +endobj +955 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 180.34 103.964 230.34 93.964 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 245 0 R +/H /I +>> +endobj +956 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 216.15 87.964 266.15 77.964 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 247 0 R +/H /I +>> +endobj +957 0 obj +<< /Length 1787 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%f?Z4[W'ZJu..J/u-$S,-T]4!t9/6e%+a;SQ8B_nCUMBasD1L"1jDa-#NjlMb%kn8EDN-g$:8@3bT1[jJ42n)p8@hD6&a&sCaAho8:58dHn^K/:gN&T;'_Mh*/I@Djm#i>;k+Q(H*9fBfrouo-^F\^-?*VsPcp'OSgLf"(qQ7)c[;ue+3X5A%55Su9a:Ndr/Xe+778`:tM2ANu<#Q.4_""bF3e^8&n'V+#ce5oj.*8:e!hRV7hnQhs)%m$*P[;E%HKbKqW2(MCF'9^kIH(c.QscWi;Nc;-a@'A1!LOLQSr9d3LR:)3K;poq#aN[?R!I,uDOHG\]_'J'=V"4deLa;QoCe?5=Ua/VfaTGG(Glt!53P^,(l\\;l4J%KWt)k7+'snH]+NJrk!4:aVP4e./&$J1;QC?qLu>b)J&#AV*K=mEg+\0HVp+g9Et<\b2gF9J?dO*BLrqP$!\F0I6uZjiWd-^]Y="m0r+d+<0]"8;Y4J#E8`t9_c"OQoBOfAArZXTQ'`C+#(Na?TWmc%?QE;oBFsp+f=&j;Gnj/n38E.o*/UZDpu+H6;C.i";?7O%-BE4RE(i^g-O\>Kb'P@'E0RYa,N2#PM:m9A),sp#S-dd)9ck]&s!)J,7(4%Hb]atIdIR+%A47IUNTIi.G*F@KF'"'g)8+MJGu^:jO:Mh6;6)-;Gnj/"M?,H]E&_A!1VCD)(SIM?kQ9tFC#&)Z]^:$YsRW_%pqrO"I"AiU%8J]+!=%!bGMX5`^_=dJk85pd!5`774B+^@tfjKVbAcNW#[q&!P'0R?qLtk57mWg;C7b6e./$.J5g%oUNW`#n\1gCK1Wkjd[`:*I7GtDj&*dPk5rIBTQB>0#3bNV@fVGn64E\L$V?ao'$rX\h>f$fa,JQRNQc>,[ZlkuX0Ko?bE9J?c$*BJ*=qPI8Od]l-Yo/UB68O2OeGlobPLf$36aLRUi#po\^2kAP#rN12NJ13d?.JLQ@PR/Z;V*jDTTG2+Z-ljgK<$Pt+8O2Oeq/qqA\POMXQ7sTjSXY#cM)g*<$bpfgo,8b5-id_NE;F46!V:!A?ZA,EjS>1u7KaSU>^@(GB,p]c1RQboAJEV4K>QjXoDoV!pr,%V2h7,m4=E>*F5.3mjPen?n<5$O5YMhQnT!#:D`qtrk>;ULl@c,aUq>-kmj7MU0(Bg^2QKP]1e&/Vu,9*e"nbS"HGpTKU*&t&/pdu;o1TKZun)ir\ojj?*,l3H(%qnDKaolfT$_gGZA^:d:@,!SbL^*C8TATes)roX[GHN+(VilJD~> +endstream +endobj +958 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 957 0 R +/Annots 959 0 R +>> +endobj +959 0 obj +[ +960 0 R +961 0 R +962 0 R +963 0 R +964 0 R +] +endobj +960 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 160.32 716.5 210.32 706.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 249 0 R +/H /I +>> +endobj +961 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 207.81 700.5 257.81 690.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 251 0 R +/H /I +>> +endobj +962 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 209.48 684.5 259.48 674.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 253 0 R +/H /I +>> +endobj +963 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 213.65 671.0 424.602 661.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.iana.org/assignments/http-status-codes) +/S /URI >> +/H /I +>> +endobj +964 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 208.08 639.0 249.48 629.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +965 0 obj +<< /Length 2367 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gasar?#ppp&q/*0i,M\\E;@N'IDReFHe^OI9\S>%W3siRJ,Ia\[GSFL%m(g"pgon1$[XRlDCDF"QZ$)X[[%H:g9B,pS6*>(1oXLoh$i&/b=Y]>V`-`?;sl']CRllWD#qm)am(NZf"ZX1`L#&#Jd,IjVo^6rf[ML0e"6:hFc3$.V?:X7V"MboqhuMr^32DiYrfr']O9Ppd)JoAgNC[p,FB'jB&*VA$.*on"13'jdlJ=eKXiu"kWaG?[d[%q63SZ%Z>LMo[`6sP!,+lGBBU@$OP(8Dkb?Rrber]NEmS1IqY+=58a$cJSilK9Z7XRP\b>p,%[o\*&0;XNl.u&kp1oLKdt[)V)Z=Pkarm!juZ)Q]Scur!p&A;/\+4,\p#%h7_&('4fcT/l0ghM12hnoF0l9dl`gbd]2028j@nYhiX(eL0;lqMI+N7>D5R$2=Q+t(6>HuS7QqAY7bI-l<^d`\mO)(@(Ss('dU_>*f!E'K9O#eZ+kG)r$\q8]JA(!-L]ITF:!`9jT4#0EW.T(pPZS6M<5W&P]j;2rRCj`&O9Pt[ap!^_;:9T5raO#0l]M*@q;(Qs];SC/)hJ[$pBp@$&rAV=XW?nTT9S&F'i&\B%M(kUHst0a@cAabrmH=llR;_L'!L!1C%OYEShcB"'.Qg@\Y1p,sS+:R%`s/"_h]r,d:A']F/um4PG1(%HYOEb.Q5^pb1"(K,h[_FU+[l7K5bZ/a=$rtYT^d!UTSWiXh5!cE&IBHp!&/Y83[T`k[lCn"q@<*PWe1=Na"`@\aC^9jY3oKf]4eWCN/8));hPZRJm@JF-?1fG>'c%-$$\i]*0h;U]'N4oA:@`Y1D9f!_\4:4FR<`u/k:rMB8#cVR03%c596O,;?b%Rd<%C6OC>"K3flD\)T!FJhKXEU-JGCq5K*RW2HP,VY@Kbgc.\^JGck5H;Hi,BX%%YG'9VMr`Z;M7HZFI-@"7u`\Gd_S8sDk%9KL&c;jU)?*F1(qd0^X842o&Z^a=PQT=U5feL?a7*C.e*+S'Q^"fd9o(&YV7nA9hE-jN9_2Ao'AB/APkKt3HCdiG^HKN5Qn2:C97+-5rBG&P%YM8ou\Cp;;SFL\nj&%?'Z>ho_PL*_rg046+]ce+6&Oq/J.L7JdCGfoK*An$EN"JJXP5%A0(+lfZB;9e2_84*J>If6L@?O$]i8n@c8mX2[/XQ&`]#e\^?:uc(U,*!^_Q4r'b'YkgTUSN;/I+9(.Te,WOh=nluH?X)pePH)-iMXjS&6a-*7JUSUS+EUTR"Bp`EL=<[I8o9D1HU655YUWEF7=Z)'7T@sV,4N1&jJLNau:/fnPLSNFQ_04r-B<%]*#Xb4$F-faU)a5^agLqP+#&bY=4Hg%'N&\En[7HJs1C(-!hM8s+WcHYr_bi1hNA6rr1,i/OIb_Y8U-hPKEUd\<%JYa[oZXa1Zr*4h4f-kE_T/eaa7Y5c*)sN+4sA3pgAMo&(J0P#O+M*o0\[Ttd!h.P^InelYs-30!3_8* +endstream +endobj +966 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 965 0 R +>> +endobj +967 0 obj +<< /Length 721 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`P9lo#B&A@sBka9R.+kbFJVpce4!J[#d[)$SiR2gl35RH)F/2oqGIH?Mh/qje5(+uZ<5/#d#lb-&FiIh4O6NiQPJA=4]!4b;u/9F(1"N1lUaUUF$^3Ui"77+i&1nU"UK/6IZF6SRKf9J!iQB?#D`\-%h.G7`e"8e^bY[%>h=f?+POJ8F2l9e!9G&<7,"I"eQ,=531t-FpSpnGc`51H!9e/5&T30gejcWdpjTc_Wq?)NDE0cgVQPc/[G[HXWVf2e4V#q"rXa-ELd0`r.PInZ]niXC4.5Pu1D'[CO'8F84/o]-4n,GM(RheV;,]k]!mK/>D3$Z8fZC9.l;_85/UK'QaP=pEN2_7GB6#e%BW!?"KR1j9Y;a-"X_X7$4%&I[I-#32&<0Ji,=jgV/s`M!2G-UGX0bQob,Vcn,!:II`FipRgf/@MNQYSq4T1V+[VDRJFu48,,PO'OJ"R?m"-"`):C<5O67hU4Kq;U?AatO>_[1>Hhj7-N0WBRhFE687`dRs.!crS&/<&AMEQqS.S#al;LPskBYB&ab6\O)X^(!Gc+Mll'^8]7Qa_GVWB.0o!V@[m>A=cAVp""%jAGY(LKoEH;%j0lDa2]3)@m'T*r! +endstream +endobj +968 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 967 0 R +>> +endobj +969 0 obj +<< /Length 1033 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatn%>>O!-'Z],&.ImbmC4(6<-_S73,fWkGcnt.+,]<.<`[j\Im*kFad`#^qW4')eVl)NkTASOdO6=p,R>M;@31_cI*%rNLA,)3h;=H$9&RdGdYN!lTPb\2%+u"a9p5[_sSM)L4:sh?%@^'KXe):NH,h\6n`:NTaF<5jk0M3U(#sek5?'CnL*bEr5BYX)J.C-[KBCW3*bt&Y#iR6.UISgWL;d\DYdaZPYj7*c%-WCn3=kFm<,h\0&)G_O[+pmRub\X`Y7J82:3uVoEMkq*/Zl[Q3-"9u#R8,Dob?kdb:T!d'no2!N%&JE\)]Yjif3@2rCOmaeZ_#6P5iFi0DsgkIn#Qh^H![6821Z6klk,f@q`;f>\JX\B`PRtB*nA9`EZ\\lmaniu#L?E8Cm!F'FYT%NO7m)B*c+uiqIp6/61Lqr*L&4I$!-],HuR&LEhPe.Uds9)(BdK#i`'YM@@W5F6qJW]/]@&.a$Z1oBUq1pZh7(tKp7D?*FT=P/s\ZeUB&N+EBi6'_]VgNH^sP@?)?\4bD_fTN:[*\,)5H^VQ.P)Q=[U431C/SBASU9GX4Wf]V#+U?Pg+iJRGXOIG@;DA'W)8P")0M+IL;^LIJu)SbJY)`(KsE+=X%TDrnc#:/^g+Xo](h'Pb!k`BUX.%Ot*7#)[o7Gs;4t@S$)3#m'lip3/g!\P;N"TDRa!bD.1++kGKo-Cp;S?LZd^uk3?Z$OuA>Ce],kjMM!l`.7&SN"S,Oe`jDBR0'HL)61*Zf$j$02;;FI>TN-4n\Wa=U+O3kcQeW%-%0kHPSO%~> +endstream +endobj +970 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 969 0 R +>> +endobj +971 0 obj +<< /Length 6341 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb"/,968lH'#+6E9]]ed71942\$i`ff#*du3F6D4moGq\4ee`$cbl10fN*J"U.GFQ#)J%>fr?$T>WeDAYr;gF)OXmKoB%idWj:\/6aU.Ukt5S$-J>535r>Cj)&^Z2!>CQGPnrlh"uID"c$_X2\V9K8\K/OF]n/s*^3`nBXhFuO-mhH#sdPT5b:DIj_(S-&_2ecF*FSOCc=e8#$XFI@EScrncGK,UT+SB.hrZLpJ8d"rr/=F.n'Y@i@cZY#e+UNq9bkq=auap/39WM"U[bGM'AL"K3O>J)7[%VgjNKi`"D-Qd23%i#VU`pjpL:Oa'm7qS5iJ\FWUjlN:3p's8(chLXGiC7%07#EJJ.UTaO8=s$ia3e%?Z6_`'8R/[Bg/(,Kj5!.]HbRTZYhjjXeI6*-7JL=G>dC3KJ%ES`(&c;ua1#IF^D?AfVYHg\eCE>"h-Jh7q#"$29KKtkKY/KU"$,/)ArfoJP)?<`C!/S_6.m&9%3)U@Y4Q"rf$/Ti8j%Ie9*t!CN3TCU4SdqMB-;&!$]1LV;dgkdf>t;Ol7^2h(6#(Cec@;'O'-%b)+$%!C5`M5aFm`nJ%s%;_#)W.&Cl#ND`^&LW-%4AFs&_Vet=u&B3(ba!l1PoW(RpEe_Y#Z`iP,[sX(:^:beg0NE[n`I.-1%LK&s\<`'!<4A(gq)$B\G!ld+rqFYFGKgH7Vf.L8c1IS4AFRYR*SU"[JZrbg-VM]$`kVd9YJCXp,56EgYO7e"lO9TIcWm=OE@jLo*>+d85ObMEUBN;Yfu:Mqnnqi[GTS:\uD<6No9,qq3iPW:L;`i.Q?[Z7MV&><=B?-OI$K?)gVR#K8ZQYJ*3XV:SVS\I+!^9Vl:#T!;)@2e38dn,Xa6(3A9dL!ub,rS@A3#qUPZ:[oG(?id3[c2D?adQc>?'3nTpEire7$EL!t]`4I>l.`a)S*=jF9uo9ZXHdR4Y.GYB^+M+Ef0cTs9c66M`[Vt2]7W(O<\0or=##0fDe`PMQj;4?84a#MW8+d-cQX6%26q_d.Pco;G.L(FF%h#QK8h]R]Qg<7rM[iuful[DMr!.(2FQB`]r1o2CjH$iufl:'H0_`u*E@snY.Qn=R>!*/1c]k!r\,(&.Qeo4&S_+Nn_Jlr_+E6Uffe%I:;4[cTaf?brp=Qsmm:mRPt?\78<&#`BrkY%:bWtO$idS&D2BlU1.>2,lkF1JtGXDD78m2kug_ai^j8Sm$42_CFB?'+>^;_RQYZ]MRS3S,pt^R](q(+W]J4#%i3+'g]t8BAm1j[b;%3Uf"#.l@kP13s5MiRPiDB%JqJD(1-MV4Z^G%^T(iS-f?D4HIY?%Y/1OTSV:H7C:n.gAN"FmX9TEYVkOn'*9;RU-/%2Q&:sqgYKH12A3:iq5oH!80XMP9i44=kG`&T^'#A+8[sal>FS^\r^LVkE;B#$q4k$HX[AqhK0C"V:_n;h:Kou*o0[jXVJIs,>])M#E2=Ei1"hh\JiRW;!^J#kXRL:3MMUj3&Jl6AVA'gTM-6aoO9n25"[H6W[E?fp.O(aM`/UKXhSl-E(!`#c$\4t%@Z6qW<(&iEQ6L%LcJ'f;\oVWA@EDaq,P&)*<(:"/5ZK;n#Bm@cp9p36E$D8b1D8:hdUP;q=YiZKDL4+8(60ZKA`62SnAR>2G`b9'&"b*R.AHjPHIuW?q\$N]di2MLfJ36^:%O&kEEc^[7n.I?Pi#)A"s=/&>c#A(f6(Q+DJ9m%A2i>LRPY&$4Cg<#.SDk=kUHhV4QL)):r,gD,n;^X!0N&NXfP0;]f`,8i]1AKXZ)K]Jt8W/CP.IAH'f;X!QP,C5n4L7f/"o$$C!PV[puEh9!0s],3UdH9r3YZ$9qqc?mDUu19'dBUTm#+$dQe1;RWDERjV20@OnZR>?r#d_V/5AsDk9o;F4L(`bi;9D-0&/:;r+>i:=atrg(ilh%[!Tc?8lBPt/jke5/71is=r3PSW*6N5OXZ-d8#"_`;HCX!AR]F>S'#n##.ilf2bSC"XU,.c!\Np^<\CLTc'ESu%l(eUP/7\_[_r]6M!J'Ih]?qKC\C,hTfZ2K_%'[]G:&H-[`P\Acqd[l,-pAj0O).p>0F/h3+O\u*n=Z0R_/XYA].M3?)c2qcZ6'N'@+V*8gN1m2XRsFFuGKZF6p?*CGD/M]in9gFW&\"IU0/V#]WnLla!"@&#$>eYEIQ7,,Tb"^cJS.(R6a#sdR*94]YH!n^+.`)_-4T?g<7)pKfe=A#;X49JQ1h)4t,bg08Ri@!RKNSEbHM0,Rc\"-h8pq/fY3Ds'HVOXW=7Io$P,rds;/6_jBlbOLdm[9>F*NT:4bJV.7C?O]f*N]@k\%(G-.B6k0.poKK-/CD955_^&CN?ohRE*(S?ae4-;r`$Nqlq"daD%7dZn=S:A^U95aDFQUFZ@74F[rWb]:=8gf=bGS<`*FVe2D14ZN,7!*hd,nq86A["]<9NIAG(Ora?:KAngZB!08'G[q,(^a.!4K1Forc%(CoCJ3(Lo5['5st%kQjSV:WAUQ)!)t]>an,+gMMUH-m;?K@%AUF@Lr6[=E2HSUC+nCg2L_N$&Mm_LfU$L^/D02^Y6cPUanHI$1;HU5781)a0NlI9&'gob=roA<%a;.oRm&"J66rgr.rf4mCclubL^J\>qZq^,tYG#gn3Tj(U_J&!hY"!K+\_E`=fdJfnD!0]j93_YtB7(tm$/`Cl'Z(#3!6pue4f!ZNA7g2Zq)04a)+>c+:b8QO!`!:(N@&5sWb\G*"+S's@LC6?N2Q;LWqGSgP]ru^lmYV=D?Z7<=C_=Ha.O)U`ClDhUYp/0;@jV20EI+t'LE:+\knSm^:OFNP"I6nEs[71WCk[Ir9H9AfVC0_MkJKM/O/Rl,&VIN#Ch%c[Cn[AOX5O>&!\CU?UTa9l/RdI=7fl'RbW]lAdBhCf^Ze6n<2g,G!5sk[;G7T'L=\TqMV79/)I:STe,&:Hbej.&D[q-hT-C'A:ZYkK>:K&_eRZ8+jM@*Kn[i?..PKNWtYIFSPA8k@54KU-L&sJ+G.H+M_M'HN5`0&)[4R'U_Pu7RH`&tm8lgh9\gY0([Enqq3F\s6(c))eD?q;42j7Y[8Yqb_$U03c(=0_,'S`N+uUQZPYWZB.;;$142/)sbaRK,aiD`i+)O"8Y2*hmr5gaoSh/HX\!"rE'DUuL^C=#NeO1o27&3Igf\]raspAc[rQ4G2+D3@iQUIqi,J9g@`bWBK>e6,QLonZ#`qD%4)&fDD&W>+Ro`Xd8i9rgGZ0r2:Y#Hu+3Wn4Aa@'/KIGW..E\R8:X[G$1eqYfY:CJaS`2c8GMFRB294;%N#*T!GU36Zn[546tYnl]sPgfc4JrB;h@f-D$TO'MQ`Vi<^qfd?"dU*H+I.361;D1PlU+6JcRUCFMI%_&E5n3JL`+0@9-301s-Tj!]Hr(6m@@hrBTqV5BDV#nt.X.'a*mRdUns,*"YLe2!m(SZ+ih\-t&CmCr2pKWSaJH]Bo5I"+Od(3!??,3!p]?*0Palc<*aF>0&t60c:62>R4WY)<&N.Rh9W>61U$dK(,?gZ+NS,PVDuAeE`kcJTZl&s>X6$D)a8TnWat>]=@oOEthEZf&,,r*b`Ym2u-!*II3k="5E.^&Za]bt?R^@&5;Hj_'X`X[ioTlFdHKQmcCTXNcEtNlCla/9*In%&Fl:kjMb"csWkhd0O>V`;B5*3.U7dL7!93(PN&l)>*?P18WuYK2(GR.^]gbk;IO9$:_sT&5Eg1pM$o=6sbba(1MO$h6]8aGqp3PbZ#-fK1Q^G^-b$)r%&(17+2aD,\T`&*!-L.RS!B$%?obM&9KBV[l!iVVA4`iG$2Wp/q-Dm;1T=XdHX4)#SY2<11;/6D"tVd-\qZ7W=LL=I[]BV8BRra;S)??Fa%T%.O=HI`Q(^\dZ@5hf=0Sn'9a&?ok`4Xj1"RQr?%n'm8a~> +endstream +endobj +972 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 971 0 R +/Annots 973 0 R +>> +endobj +973 0 obj +[ +974 0 R +975 0 R +976 0 R +977 0 R +978 0 R +979 0 R +980 0 R +981 0 R +982 0 R +983 0 R +984 0 R +985 0 R +986 0 R +987 0 R +988 0 R +989 0 R +990 0 R +991 0 R +992 0 R +993 0 R +994 0 R +995 0 R +996 0 R +997 0 R +998 0 R +999 0 R +1000 0 R +1001 0 R +] +endobj +974 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 482.78 665.894 531.58 655.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2006/REC-xml-20060816/) +/S /URI >> +/H /I +>> +endobj +975 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 654.894 379.75 644.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2006/REC-xml-20060816/) +/S /URI >> +/H /I +>> +endobj +976 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 289.22 627.894 454.12 617.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2004/REC-xml-infoset-20040204/) +/S /URI >> +/H /I +>> +endobj +977 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 397.25 589.894 539.08 579.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2006/REC-xml-names-20060816/) +/S /URI >> +/H /I +>> +endobj +978 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 578.894 224.78 568.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2006/REC-xml-names-20060816/) +/S /URI >> +/H /I +>> +endobj +979 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 239.77 551.894 483.82 541.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2119.txt) +/S /URI >> +/H /I +>> +endobj +980 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 261.71 524.894 453.26 514.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2277.txt) +/S /URI >> +/H /I +>> +endobj +981 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 243.92 486.894 415.19 476.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2616.txt) +/S /URI >> +/H /I +>> +endobj +982 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 311.15 459.894 508.54 449.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2617.txt) +/S /URI >> +/H /I +>> +endobj +983 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 448.894 251.44 438.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2617.txt) +/S /URI >> +/H /I +>> +endobj +984 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 319.48 432.894 500.48 422.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3339.txt) +/S /URI >> +/H /I +>> +endobj +985 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 241.43 405.894 432.15 395.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3629.txt) +/S /URI >> +/H /I +>> +endobj +986 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 378.64 378.894 528.24 368.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3986.txt) +/S /URI >> +/H /I +>> +endobj +987 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 367.894 253.93 357.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3986.txt) +/S /URI >> +/H /I +>> +endobj +988 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 340.32 351.894 534.91 341.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc4122.txt) +/S /URI >> +/H /I +>> +endobj +989 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 340.894 238.09 330.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc4122.txt) +/S /URI >> +/H /I +>> +endobj +990 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 393.37 293.922 524.1 283.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2291.txt) +/S /URI >> +/H /I +>> +endobj +991 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 282.922 435.85 272.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2291.txt) +/S /URI >> +/H /I +>> +endobj +992 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 453.35 255.922 531.32 245.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +993 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 244.922 350.31 234.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +994 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 307.53 228.922 456.87 218.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2781.txt) +/S /URI >> +/H /I +>> +endobj +995 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 358.65 201.922 442.44 191.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3023.txt) +/S /URI >> +/H /I +>> +endobj +996 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 450.87 174.922 501.89 164.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3253.txt) +/S /URI >> +/H /I +>> +endobj +997 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 163.922 472.24 153.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3253.txt) +/S /URI >> +/H /I +>> +endobj +998 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 340.59 136.922 521.04 126.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3648.txt) +/S /URI >> +/H /I +>> +endobj +999 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 125.922 358.91 115.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3648.txt) +/S /URI >> +/H /I +>> +endobj +1000 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 417.81 109.922 534.38 99.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3744.txt) +/S /URI >> +/H /I +>> +endobj +1001 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 98.922 403.35 88.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3744.txt) +/S /URI >> +/H /I +>> +endobj +1002 0 obj +<< /Length 596 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat$u8WV=S'YaHG4O'jj^t=Y\a%&0X#8I^5Zk4:X*\pYiW5o$CK_Y;o.?5D`CP7#uHiCHuH)h@e>K8u8-BPn`!X5;`oT+T3\gJoD<'XL#@<'r%Dkf/Ja!JA5S9)BVpf.6qeJm=PZ&r/DA$>[_o_lO@T:7lL40pZE.?9"_f45DoaU,j/bdKehbN7Nfc*KG?Xm/5%B?J4C`p>p1cJY-D&tI+rK3F^:qU2aELbhcc+`S4i\>K/m$=_.t'L)OGU8>DV&$"sd63Ugr3BdX6/iJY#S-iI*D(LT?/g")B;PCdkWu@<17q%a"\e!D7/o\s>%6;g@b4So&-[VijOYB?fm#Ca1=1;C88E-c8al*R_6bKh?qU)A/a_ZlP0g[l!lTZI]M8W +endstream +endobj +1003 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1002 0 R +/Annots 1004 0 R +>> +endobj +1004 0 obj +[ +1005 0 R +1006 0 R +] +endobj +1005 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 360.33 719.0 514.38 709.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3864.txt) +/S /URI >> +/H /I +>> +endobj +1006 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 708.0 247.82 698.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3864.txt) +/S /URI >> +/H /I +>> +endobj +1007 0 obj +<< /Length 462 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GarW6b>,r/&4Q?hMV4+aeC`'a0e:Zg;?a#+!>]MgVDb+lBga$-mphf^:=Ub^n`7u`d@D.Df%l8cE=mrhbJFfp"L.k20WK1&F$puiU(l1%=I%cHIT$Y_83O'Sg7abA.Wf,-dC^gbRWLjc".a9X:]!Hq_PAXg-*!b'1V>4BE>ZRRP-V?Xgm;0X))R&6%=N,ij"*o)^/H\bm+$E5d8K>CB"4h!eM56XBA=L"10eE.$^"s?Mioj-2:$8U@dZ;0l,>Zs_7css-.F`6l*K"ipPiu-=he">K]OF\"M9Hn]"gHDUd2?&3bT05@)Rj/7p&otegP9Ko,,[U>"5E(,2:X2ll(Z,iGA&W-0^\6cKa-FCi!p,3=5>90^;KQ_>[9'POQ92"T1J?Y7A_=.E8I8&rMfARc-;?2'bBE'Eq`o +endstream +endobj +1008 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1007 0 R +/Annots 1009 0 R +>> +endobj +1009 0 obj +[ +1010 0 R +] +endobj +1010 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 631.866 210.36 621.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ldusseault@commerce.net) +/S /URI >> +/H /I +>> +endobj +1011 0 obj +<< /Length 2492 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHMgN)%,&:O:SB_7P5,S]\k4h6MW4)I$lUoA^/8b)kDOG/E7(m$TA^[G'b`*F";Q$1mOMZH,r`PJ.P]?oZag#C7sk4k6*]31cZZeI9KB#C5s>IlMtZt%;L_V%XIqs`[`H9-BofJ^r>GmSX9#eWJ6%n@oL;8eo1V6KKI(^LT2drWG5tUDWNRnH*Uk4W,f7Q/_)Nd74JpN'5ZqCX+q9;%*nJ$q-bJ4(kY/LO#H?bj"1icM(EDo`YrH,2JDJ]W![Aen4D^O2i:n:&`El-Hl0Q,GkGNcn]KpA`Sl3Dg&D.ts+@))c:\USSiZpAS0t#+ErmA:3+)3G'f?+pQD'c`.ISXd7u:iiF$-h`ZfAlCdA31Z)]#e^-%-hcM5(TAa'%@Wu8SUbUCW;S:/C)''LRcWX!s^<6Zo5/#g>sJ]j34KhRG5=uD[S#gbG%%*SfkNrshL=$[Z630kMGa\%j5]rG*W0c]l-SeWp,3Sm28)$)Z0]R"1A'45j6KoF+58e5;@%06*VY`]\SV.?P.X*ODhm_]mJr4Z4I>Kk4L:oBgp,W2s'hVc)JL9PZW&J`+2OsM)V1Y"+CEWm5MC!Z6$L\$FuielltK""ZI_Ue"*6n*kb5I\]:cHSilR,VHkJL(f1j@(A*JIPX+:BVCqZ4"[<82[=ne)OOLaQ8*X2E?O4O-.6tm"*Q!J)CII2S`gM6;%%3s3FLt'Hj7KA+^N]hMAEj-Sf$ppsste+aUN;#$p?p+\u+59>Hd-$!.ekS)eEQW9sBhXo"'L1@gE[XrDUHFsgCJXO!+X>&9Z$132Zn[cL@QCY=#CO*BaB6j"oSm6uUSabI^Ecu,D^VHuKJcmQ#(NG,OPiIp-3DrKuo90I'qNW.bc^h?T9?bMt:?2T.8TC*;7He>%RqhIm2._C*EqScbLpdW22_/c\]h:_P\2O:sgI'FM.I.^SYHZ0pd/rW^=h\VhU*KfL_!9%?[Fp1VLXQ,,9/YgF]7gWhQ,>N=l9q4KF8.,r2Db,n)DuqoX*m]lXJ?bBP1ZStGEkL!;X_Q:qBLTkD.\V4+<=^^6Beb3=_mf#AnF=UH6Q<],L$7PqD*IUIk]!oA^@'F-mMu#Co.IZ#%7idK.#_i(,5HV$!)5eD?(--"f"9ispGDUXD"n!Tf:f`6Q(]1,!@T;:tF:n!m0]%[$7n+hu=2M1-9oBM3DQlLa@tYG(Ia=4Tk$#Z$m^9@B;L6S+gcuIH6]Ltf7U-7Q6IdK_s)Iu[9_pRU8E^0nNC^]#.^Y_32?mXZ6;'ru2[U$b]1CDQP'4h71VLE@fL$"pN#+<3:+(nXAS,VTCXOH[)k1eV$&=-$]1E<:INcZ&:]FT`*a1="gc!O9TtMoYTu8sK[#C_49SSDk6c]r[-]/.BVDkg?"K61F9+],]LdV:ZRltQnV+V<+XWEaiN#q?nV*6t7RmPIGKOQ8lF5n#(=eUBSqQ9k%Sh+R,@qFq4,:``*aqS!(d-`[2%"qhT?EkCsD,aLKkpH0*'1$WekM;]o5misBQnnEj:>f=VlU_OE?$i[5N^7YniStsQ#n+5>GO)uGj*#g3hC/4r>AgA9BuZ--DS#M1\?=qH]1r.pf.ZHj'Q#cTp'3RI;)b@3E`GFV\4ibPp2ZWi7JnqU\RlFPu;U(cM55+&Stg;f(3h3hZp_]3Y*g*G!#q7I34/D?'DgBZo;RA$kb~> +endstream +endobj +1012 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1011 0 R +>> +endobj +1013 0 obj +<< /Length 1385 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0CD/\/e&H;*)+o"]n,,Re3Y=@!8=lQ-o\n.kG%;>WiQHlomM/k$o:B#[@(J@7ubdo_9#Y=7U*:LGu4nSDn11*$G22Q%K[hI6C9rMLC5AC=e>k:XM:sW7A`lI[\>jO,&RH7DfkO<_P"[o\La:`]?RZa"s<\Qr"F8E^E[a,09,O)D0ENaY#`*7*u%fX;s=fM$ab>d#+jg*a*e[J:q&dcO7`;-DGlo-[\,.*6Y%o?*qY@pZFS?j'kpoc*\PN[2\"r$7bb"Mb?V?qK\=a@cLV=:J)["VQ3("Yg/I75!t$p5RP06lrR:+AtmEXK$#pS^V`MR50FHJ`nNK>5Q('NVZC:%gskcOQ6ut^cP8sE9Q5qB/tf.PhVqS7Z9h8(\:u/M7d%iJAaot^Wt,aWZ4)'ol?+M'$`Ld"lGcJE\bgAMk%Nn-KrKh_X*EK#[q;Xou$25Vj0E1=-9Q<7d1b@[7YA7`s5e.kp?.pAdongY?l%N4FcmANn6a7YQL6EhEMQr>@u]*2m1\+"qP]Sk24sfINcp[_.'P\'WJE-eMe5%@5mQ46FuZp5W]IP3+TPniVn],^,iJ"4]&V^'UfNWpsW>$Z(7P_VW&6lMUq<]6pRlpfQ('T_PYQI2E\93P=$IgE%&bgle?]o+#u2#cB#P:\Q$4NT29Rhi0b9S_p9NH)%W9EO#M(/N\l?]2#867~> +endstream +endobj +1014 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1013 0 R +/Annots 1015 0 R +>> +endobj +1015 0 obj +[ +1016 0 R +] +endobj +1016 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 208.09 621.56 250.59 611.56 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 362 0 R +/H /I +>> +endobj +1017 0 obj +<< /Length 1987 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU59lo&Y%))O>BQ@fB;lc]>8nN!9i_5e4dnHmPLEqWi$&MlM>]iVAEIWC&8hqL0?,rLr0-mr4G^+H5eh>Vk5J1,n_Sn@!XLB"^A9SV\!r`QTWu6Ck"n=F>0.6'po',tnl&3Yp'En@4q$0hs`NQVHHSoC7r-#dQfY>,B23g&3RprR9^!XBnmpAc&9\/\-#gjn+N^l'I+1KuXYG,`MhYp=)]`./VI\h[K"/Jo\>tJc1V)J;DL;*+`\$2)G&0lO5%VEe8o>0^VO,EH[%-'9]$>]MK[Xno^TB;Zp@7nMi4p@0&tILH#NtdQqs3ot5Gt/81F^tR/0S2$/\oe@rs?'tXFVZ*FcVXVcV9+/Dsi$h-QP/8ORRXj4EpgG,f6B=F>c.0=jDA%:4&YKRMmc0F'G[m3kk22Zc7_QW1Nc1n%FU-Jel8M1nEWccma[mA^"!Fq$6os,*7R+m2cF+S.3!dUb-Dkf>AeF_b3*7jg4AegAk`2+5fooEK8%AkpB`*3WWu>H/o$.Kh6On/lM_c_L^?#0YCT^(htmI^F*S]AV&5PV%WMW.1A":kIH(VnlcRMHOff7k8,OA!$:GAb-rko3TbgNRsFVh0Gmbp;mENS'oU-jmJ[S(=%e&B#HMp0gu:5.Y\@o?6`'s,qNps:aW4P\MJ=3VLqC>*#M`0J<[WMj*6!I>.WK\KB'[sN."Y>pe64X:LIWfQ%[0ZeWN(XXChSnUT$E9QYf0cGe^2R,ralc3^^p#LQDnr.E+$#&9!Zg\T:L,kJ_IZ;^hsPoat(]9RO$iC"U&A`(6RF9$I/X@VZufmkbTSjF2IE#j%Of\f"&$uqWRutNg6Ik`GrD7+Nm`Il!*t@c_5-dJjQ&bga#+)X'`hI7LFP^+4SV_A!6@%fPK:5\>j``D*>UTO$E9M*qV-'IQU'E,+9(J#;/CV%B-?!TLTP)NZ_`I<*(E]IA`0!gO1aX6?<)[Ct?CM#FbsS<&$78&-d^#j>"].C20MX0#:d;]M*'g>J.jdlaf'UO[E(#ZE8i0IO6ubG#[6eL:P3P#=5.C3mHhdqsk^j>',uGa-d+t!S8;-'4Vc67M6u3/ep7VJ&\\8e=:>R"O,oOap!e&g2Qg8I^(3:mn+^Qa-)X>8G.u6lj<[YAUg0MUgsrq1,g491<`$XVTe!dfHFPgR6SXY,jR_5@VC$!aOo1TLK.Atk`'W9h[.%uq1dE<-B3b>qgNVV!r3s^%T#UDdjR?Qm]?5[Nh(4#$lR3gQl!>J>6iG_@.9V=l*_Ngj6")qp$!^mUE:(h1"&443UM.+^A&KY4'BN~> +endstream +endobj +1018 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1017 0 R +>> +endobj +1019 0 obj +<< /Length 1137 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm:9lJc?%)(h*(qm9W>0jK+4'o0Zedillf+!_O*nhSJ7nJXd;CBT0qX5e]7C6QC`A$?s.2)IOpdk7&4SWk9>0+/n.p`]*CGeACUOU>l?%!JLOc@#hMRV0cab;mbcEQ?mo94Dnou=tik*WoH*uWo(j@/STb)X_Rdpu1^i$r.CZ[''QaItJtiZ!jY>KA6je-Mcg5qp0a;^6Oj=)HT+A.-878@)IGA]cF:Pd1WnNkr!*K;-/e`6-fOEhh"$9jCKgf,$&/UY5XKiR.8%NJ$Blk9^^kY#;6tJ2l\0Aj%$hNLtu#N:iWt"VkXM\6122&k+E8\JbYf+Mdn(+Di+4jQ02#k1NRS_V9f-6uHkTnK(J"k?&g8Y#)`in>^088$]5Q\T8`E%Mj]Pj\p5Bl"J!F@%l9/p-ruk,<#3rAr\[kejjOL`j95:hYBX:-/%Bg=+FXT5=PoF-j`nA[Fa(IlgQ?2!M,q-9"in,UJG=bVNl"-Xq0O&Wr7fC-;''**,pb/ZSAB.k]+iG#gMPTUaH&bjq]4^)rE[J%8diHb-61R'enB.*15"SfOQJrHY$#UAn2.1M#f9R9<>YO=?U$%Qq)A#KC,*EH)TV?9Q/5)#MBs]8!SKrV#os1/VcJjGk%YT^lEhWnuPq::oc+SfL7TF?fmE#DR?tk<$c)S>mU+J-!7/m?B'Q5t&o*n!hY0F,1f^Ihd?n'dU6n9r05h\,A@]=.g6;s.R+q#Dt&-5pYd3tB)HuU3VpBHaTC084"%o%oW&K9bg$!klb,O=EQc7d5qbEGefTA3rcPds6r[MUMWkXLPIc7^t&.EOKY85OhYlP`4GcrB8T9#Lc>JQS"C[%P2DKC[UB2mNMa,!<5i731Pk4ST@3-IG`~> +endstream +endobj +1020 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1019 0 R +/Annots 1021 0 R +>> +endobj +1021 0 obj +[ +1022 0 R +1023 0 R +1024 0 R +] +endobj +1022 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 297.5 691.866 343.06 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +1023 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.0 570.146 254.0 560.146 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 547 0 R +/H /I +>> +endobj +1024 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.0 510.986 254.0 500.986 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +1025 0 obj +<< /Length 2416 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^h/h:0&:`#5iQ98#2[O+!`mD1Q+.]c1?kUnWS#-_5#crlf>g!WEIt&u-nZFH@g?["*q@^oY[/8V8IaY^SUVEr5a6RqSB=^'CS`Q!s\F@gcJhHBnm\o1=#0q>PI!Tq_3H<>lf7j0)2_obE98Y]`XffFl]&u[$=if-Mo@]PGLP(#qF3B3:6V%""_9hY\eY1HJf13nU/4IG&Tmu[e`LDq]H6q_o4^Q42?GmtM8=&pHOu)laTl3:*ZI66EOAo!ED5Ff4rJFSt1RUQ,Y#D19^m5P[#AV7$@.8VEA?E&W?RVjWaf(neFr@Q4',1qe7Bin,Nd/jr$4//l\e2icChCH6_D7[V6/=%XPO[^XQ^gY8$0S/+nU6a*55E^i?ZVZ]h*Kqjjk@qpp"L=1h2)Wut/sO4SjnkUQ@DZ.op``1"c9R%LTd.EV!<(7J[uV%ur'&OA.#A(hp"p@C)F*6``6=1%oXtId3l]d_DZ/:3Ej5E;U;CmPV,QG[OU*2mPmEBQW>WliaYS<"KE^l%3G'#r)X?4`9re&b6-%jp\D-]8CX4LqIma1\U)8kU\QBGYJr]srER_ihE@:@(FX;rVIVaojMJ"SAH^2`aNaicj*SjZofm!>GCA[i%LEm9FY'^VKkS=0nHN3VBC)*U;<&79E&8:`]+JhrX2>Xg-Jc@4*>o66"tJm;oGE=a8]if&4b30G>61#\4p%6C@gMUuWX^Je;-$1!A8^+_SNEd#V&J,F291'NV96<#lOt=#.UZY9Qe021!9WVBR7iHXHX)K%BO.d#+XChoGh27G^"7[tpTn(VF[TI_>Kq,f,(qFA/IHQYdN8sK6hlT[1?5daJc?Bm7UGe@!$MPZb$l5#SLco;":l!-CdMkWSI':H,iO\M.2)>HCET;eJ6;Ckl?-G@#[P$!HTEl;@Gk:V0\L,mQ$HeX_4\NSs(m+[ISU\m.WtHX#qIS2&m,8L82nn`XQQ@WP&P8OQ3eQ2M5[^r!VTedr?Lf)K,6t1=D^dVth$rZ#i4RaVKFICB1I^B*$'q?5B+e:J["`F[KsGM&Z,KFJN%ECjbk@)N/6:SsDEu,Ze;+rgP/cUni]Fe'7Ymc$ZiXW\Y.l?:!YUgP,0-cMd7Dm;Q9#8=[%9;qE,a4/$=X]Aa!;W]iZ9H6]?=o$2\QQ=:EPL,@[fYf07XhVmoP-"mpn@kHWQ"`,=.337#S.-M?%3Xi-[?(W7M:#ms7fo]7[5eW:=QHlH1OUns$.m[FLj*7&lK,@\LhP,k%hQ1;`FG+S2o;Xq`iohWHO=1A%6thJ)t"^H1i[hXIjcR,nc=]q?N*C.9&^(Dm;QjS*8GRj/5M`lc"F!YD%ji&ia.28:efU@4:8a!Z'faZ%8<2^S]'VLW@6<6.RBLGN;1a!9]L)lc%O/bJU8=PM;h+a%R3tIi`+sYW8]EnY%a:TSIMQOta;*Onoh]du;$Qq"&q$`?LMHI6]%&Ork"WaM7gOc4'bM^,Bb9j=_OK-1?;L5^)mE"LSX5jd:_hpSjJ*-W9Jc<4X@AK=SF[:8N.lPQ)LqVGUSuad`/g5129VlT>4!Du.NL'sN>Y3P:ARtXn*m"PoVm^-.Y@Uq"ArJ/?r4T1Bu62"^ +endstream +endobj +1026 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1025 0 R +/Annots 1027 0 R +>> +endobj +1027 0 obj +[ +1028 0 R +1029 0 R +] +endobj +1028 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 255.31 669.866 300.31 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +1029 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 272.81 348.894 318.37 338.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +1030 0 obj +<< /Length 2867 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0F?$"c/&q0MXd.epW9LY-Z.>k["c8Zo+e#?7n;X3fjVtVMk-4MML%sAHnQ@:\T3PS7660.jTna+3_btiq_&-%8g'#M9!:VC-im#?q[fB7(cZX,liI>?dHB`hhWBpAD$cf;*fXbhUXgRu?19kKNQb/Vr\`(??P4:NAbC'Npo%>'<]CW,c'l3BD:da>.(gBE:\iJs_Z&GL%Qc=AFMi#=+u0=;'['/I9O(BQQ4sW.SS5@&^Y$-Jr0;g`5;Ah3lm.*%u(&PJ]gNi$\_7k>R'uK33s\5IL7Kcj(2,XGO>A5ZZ=5U0+Dc:.i?\[+];AKL%bFHH.dj9$+B3sMZRO0&VWAQh"`\?,?>NB%@qC;sj!YkQ"j(ghc>idh?6&Up`6s6=*-QQm2<;hd8IC=d-ohu+H6sqqRD\=LTX1^&\gZ,qTiCp)nW)qSWWaZHXE2$sI"nBW`PHCDfR/5Yh\5Nf"X=@80UblO;^[&H(K:n_F(*0r@@6/nUikj?T(W!3e@PJJgWejW%r[t8$E@il&ua0%iLWV!d>LB_>%A=-`+E&7#(D?UNs1-N=J(U*c,76I.;".Y7ej//hgBB?ec,u70IjB_\&@_[a!KLRQK/Gai/IN<(Wg\`86>\qIn2:%=9QCij2,216j@`?>E\qXYqu'E#osS@B]Hhl:2^jc]G=FQNe,e:(SpN_dS=aR<_8B0ArMM[MS8=4TrQ%P_75c`G=QB'*T\m*@P/rbs\6OKd8J>;EAF7B"G8ke*1?b`A,pf:_Jc6"HhC>jJT%jBdmiVg_ChV-TUV2>7i<(>sWWL$ke)WkTuI"p0B^87Up.qW>^$@_;&t1!?Va=/5SctR855b02STh71,#+0E]XGT(Ae-At.:\PjJLbMKbh]p@I5)bP2(k\q7nsHhr3B^EtF7S@Z&CQu0fZJE1SKe,+:<9mJfI#`3nkf2P]ELQO=6=?hgg/o%1"b*LHso+R@j1SIOGR<%T89L\>MKK)A_\+,:_p5e1\K[)E^eC`3t$C*CO&W8$9-O)78b-S_B$uN2WIlJ>m-a#E14(Do-@jp!%`$sI^R>cJUr`8u%([>0@#2>:P6r,C^+icbS@$p^(1/MBqr5Y76K2LhY5p$irc/JlVGm%I(f6n@iCJ6/4"p8`S&"PpqA>/j!r'MIfXdg*jZ!$9KpOd]ijO8ZM>=k;QQHpU;Sde)$5=&n%]DP\Z;XGDI>^8A"R3"\RWK0lH?NK:+FRbBV,p'j9IgUhVCrk,%/32;C.)bPYuH%YS%GNP=u2q5bt2d-_(ngpc/+&bG_0IYb72?:1e-Y9[Xs15)F];WGOFC'jI8!>5m(58LW5[$H_f+>d^URc8$2<;-H*T!5r6[O[GJdLF2b92lOjukok?3/;T[ZSeZp4/`49k39UqJ:I+QqT<1SL3.APSTX<@0]^]OUM+t#.=S'7/,%J(,'Jn__ge?_C`r.!0@h7Hr#$>dTnW=YX:a_N)n*+,-#=hD!AYdmP3cup<<;Zp"Pg?2*TP0^W;M3El1b-c;q&jn'4UmOSU8dmf1"JF8b2V\f\76bNl/+,(4fbgV9tA"u8ho`[F$2X=TFZaXOKqpFmJ,61Y_kGdhOIU!ta[&jp*d-_TK>>^*Yq/WCWA.GG'>B.D;g\$7Rc"FXnNVTN/Y\&0'a/etN?gO8->\gBgX=C/e1WBm7^!GS061*S/b+RRR/5($:W.%Vm)D-_N)5U%Js9J49@BFfTN6@"8f328eP*8g2UM#09u;A4)ngnPLGVsCsfrCa(I@c&O`49HkK\EfnUB42#Snp%oV>1F;-EVtRH':.rUcS[$q=R0iA&ncVYJSD",ccmdi?XN_^I%?UMQc=L"KE4tb#PEW5lfLKYXK2/sl%Tp@ism@h?ump'EnG&6D0Q14P#MD,L;tMOal6HToZbk]*^S71l1uG-4GD)#LHbK0j("="EO=tBN;V8&^%$*daS"Q[^o-igJf#cXY4'coFLKGT.F7)Nfl9`I+<#Rj^M%or!sg-[V*-T@Q&4nga4o/(cZc"]a^"]N3*h[Xa72Lc;rC`4)2`eL?WUGNh(;ilZbcqDb.a1rY8+*dBMNJ0gbqha:nHA)q$C[:&N>"VCF%J=*G5f&FX0_AKK,7uY!oK3m7qn=*d-H"*,Y;ZU9Gm^USJ,%S0kC3LG7o"(~> +endstream +endobj +1031 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1030 0 R +/Annots 1032 0 R +>> +endobj +1032 0 obj +[ +1033 0 R +1034 0 R +] +endobj +1033 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 330.29 528.866 375.29 518.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 92 0 R +/H /I +>> +endobj +1034 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 359.99 389.566 405.55 379.566 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 925 0 R +/H /I +>> +endobj +1035 0 obj +<< /Length 3175 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0Fa`?-,'#"/m(r"(KYgC9VCcRBZNa!fCI?=?'D6l0fu#P/>C/!G(rCKJ!B1e<9Xuji]4Q[X@bkAMCGRCHbt*?Hm*nYO'r4rp3eGd(Ng.=gkr0/[O->2j`+[m*)*Jl2I1,-HegQ9K*s+]2>[+hred;5KKk>Ypt[5^Ugs`s9.=jR5@b+uY)Aje\<-ir2c;**-ACt(lqh8_<488/gi>L2=T+dp=oOXsKf_h(=CabuXVL8uC,VQUKS`cDI=Z;I>bOX#:i0aE<[uZ9I/e#i_nni(e4l>e0@Xc>Dd8AYPfR@IQ\l+sJ*jrk>9ge&na:1B6.Xp-3SJ$r73]P\G6D0-L@.lCmIo#sAo@Ec4'Kj?jhpFrKAPCg_LQQe,0#(WY7G!d)*Zb_pV72TA;.HM6YO$-)>q9j[7B[\Xd&ed@GEfCP?-DuU4f-doV[R8A_=Ll^d;tnZnZ^g.[_MgW&7=B'NHA[bepO`,iH^Q_0A\G/p.8fUDh/Jr]TK$Kf*Je[;.04s\aCHXVa5-G43A0:JB-8@d>2TS?r`F(48MuPsu5V4D:i;0)EPd4AMB!\,f-iEA1o8Q)h".hO^0g5?55$f'"\=,*EVM;E8Xa8@!jcmi,A5?JpNrAQ=Rb@?T!^6IF8:KZPWnh]$-C/!(J_/.F"#<7rJX-VJ?_PSDA]/!C#ZW[]bqG!MeI$9\[s.I5_uWQ!p!6[goG@c@!gk`T)&?E4*mpJNq.55D]XHlK/_(khq1l(5$a,;DUB,Xk>,*NCKUT^B_!"T7om>Cl9Q9X`m/oAZ;Jfjpc.7@kqcnQ\9(@<2+=.UNq&+H^Pdb*i!G-a"`;sj`_ocW>n(V13`;CgskXs5NEMAWdY/VDWT2XW&L6S=FT3I5*n#ZHZT`k36$=)4rs/i!l#V$Q&HQA_7#i@VlZY6@S)\!4mIa[W(0(8,#Dp]V#M?&]=L\N0+t]Uk%baAF\1hUQT?ZAIu4rVMu.n:>[F&<,Zf\+fGq%97dH.@4rW8A"X(u`dbU-!/on"SV_H+'8Ag?e6l=pV$iL0I?Sm%+QYJ<37*T9sQhEPfG0FIAYlMY)]mW]#VHSL@gmM_)5[dXd$.u]7TXLoq%Z#$NZ@W7PIiuIBcZ2.#r,tDFn8_lqnOT@9NiG+N[&s-$XO$M6qVQg#_>5X2;r9,#SJ8BP:MLlIbM"us7Y%gCAVHnA05ES"X'1_uP0Hr@9c6TF*RRp00,>8t1RG+c3o^Q'%Y#&'?sb7!]+GaDpF7Jtf-*)4*I$pTDTEXfmH3upFaWpZ;mkbA;^p7s_^"V'Vk0/k!P.P5oh)2Obri+6tbaEI^abonBM,Kc!25k5[D909(9-jA#\@&L*TX(SVFgQUTu5fu5#Ai_SV9.[^kI_mE_A$)&rnD2,_NDe$uU8ea``%87N&=@gW+n/9QJ+;smfhg5f0PJZ;/sa5(3&mWQ/!=AZ,q5-9AVo9c0Krj8BhP%4U=7+OI(^t?>i],Qs$DEcrGi'QF=k[0j[E?8kfr+X7Bs9YQDr?L@=+6F"$XpCn(LWHKe@lS3,q4Ek@lRZ`PKEe%"+dqIEL#+PqV,:.U5Zn`&hJsIbjf=r:$nN4sDo@a[/(]aPjK_[F%Nm]Fs*n_\!RncD?ms^+:q`g4D9@0TAcMZNr-eS'^LWB;1P1'SV%k,BB%%]1.=BZk1"Nn*u\N2YGOFc1Ck+CgajtRFb%XK=`,)U0C/FV0Fs;V"AAlaFjdUdFQJYp#5VijF51A+HDtPt/:s*(X\2qYAL@N_.smgffAYZ9bi%7d]OFrnpeB[?nPX_%Bp_pod`&0e!N;S9!TlFCUGWP1*rF42g3=LJkd*lH3`.qGpKO,F(XD-K=R@,.T9.;Rh$>p*%5l>Zj$d11nf!Z2Gb:S33Ts'WS/OR/l)-9rOAQpp?r/6dOOq%#QBn*r@2[rjA%l&ENfY%Ao$a_&7E`G3""`=h5P4n+j2l,!H0I\f2(1(D:MHFnILVAq_hBt-"Eq8\>QX^`-q%3N_YN_t=go\_B`#S$:o2l7a@K@$CiFl:UM`lD9u2G##LHX@iX$FL*TK;XgCO-CBKs5k':9+O4"t5#8q?c]/;q27Yn]qIV&WKhjN9AmaoYcfK2V:0L>42a(A8=Z14e(&B^.8.SO1VJ>cS]'8hJ_a[D.]tePI4E@@F4UJt[d!L0I(/R"HJk721iWZi;))2LXBT=T@DO +endstream +endobj +1036 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1035 0 R +/Annots 1037 0 R +>> +endobj +1037 0 obj +[ +1038 0 R +1039 0 R +1040 0 R +1041 0 R +1042 0 R +1043 0 R +1044 0 R +1045 0 R +1046 0 R +1047 0 R +1048 0 R +1049 0 R +1050 0 R +1051 0 R +1052 0 R +] +endobj +1038 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.87 669.866 420.37 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 207 0 R +/H /I +>> +endobj +1039 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 439.81 669.866 457.31 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 373 0 R +/H /I +>> +endobj +1040 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 266.42 604.394 311.42 594.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 102 0 R +/H /I +>> +endobj +1041 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 229.76 473.394 284.76 463.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 320 0 R +/H /I +>> +endobj +1042 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 303.38 446.394 348.94 436.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +1043 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 374.21 446.394 419.21 436.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 144 0 R +/H /I +>> +endobj +1044 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 482.5 409.394 527.5 399.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 83 0 R +/H /I +>> +endobj +1045 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 149.77 360.394 195.33 350.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +1046 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 216.43 360.394 258.93 350.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 360 0 R +/H /I +>> +endobj +1047 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 191.7 349.394 241.7 339.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 279 0 R +/H /I +>> +endobj +1048 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 125.6 311.394 168.1 301.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 375 0 R +/H /I +>> +endobj +1049 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 230.68 236.394 275.68 226.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +1050 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 176.394 164.5 166.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 180 0 R +/H /I +>> +endobj +1051 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 175.03 149.394 230.03 139.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 306 0 R +/H /I +>> +endobj +1052 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 398.61 149.394 453.61 139.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 296 0 R +/H /I +>> +endobj +1053 0 obj +<< /Length 2688 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>9lo&K'#!I0W,f.K'm'0K0mf?KE@"O>RoHJWfr\ot&.+7s;ZrEi/UN!G2G$L,-378@h9*]i!3jlLqXnVXn!@sA\.'46D:sj/r>U*gH/gGlpua1@(WIX\Z,11Zgb-$O-ad*9<&cuOAOLBf-G^I>,H/Lt/6[4$]S*e1/(\M!ZcGrH5V(@gqSTQ_pc2%-UI0(qLs01HNj^RY<[aQ!"09V"oDP!2L:.iL:(f/pok#qm,=sGQ]]5$M8k?=/CjF',`aH?k5?YM-Z6fh?!uf2IWrlP0oKhq`Z)n38Zd))Ueu#Q!=XuiJpX;>"]sLeltZ/QaUmKa(\8a$!n#Qa#U"hW3%?'l0HWf^WKYn(SY/4Er]idH69tF2pkRJBSsPqnm8m,USCW0=%,HoWNmC`A(XQS^CH?B0uTgWd2,U<0l1??"A3YWX(OUeni7saYFD,,:`cN#Xk&M.L*pc>DP3Vj)kHW`QQd#Ne3Oc[^SL,E+dCC;%>EUJ@E+;g(lnrH`XB3AEm(!Mfg[f7=o02ja2L\2rk*6eQ+Q#PQQoDp5'5/NH`O=mTKdI(Y7\0.I/C4\UlMK(N>jj^`MI2GfBC17pO#^)_f+`VM$M5:+2t2?K;n>51\11UU[PHdNVjfA7ceYLKb\TT.Ru)uF)GC)$(28UQ?'o.['Go>@,,k/;%MW"??DkCB.d=^U+Hh?)Ek^hj\.,'TV.h/f4o+OJr3`S+n)mL)1sr_Lq@GWK3i(Jcr!Ah_L8o`gf`"@gI)14fPk?6jK)/(;s2.YRN!<_Sle=Nj$&@&>3Q_cUcbus%LOT,btMmSd8Ta'18=_0EAcqmfXuhqZaWu&T_SB8c[k4Y1)?7>DL,L;A79fQh?L"MpeNbnuGpPVD@2a:(5^?AL@I,%&e.nX02uci6_9J\lsEq.77uZ#Suk)@o3\Xp^T2X=,h]8XG(b!N4K..d]$"_s*lVo`CBUH9ok:1f^e-F2>]4U:,u;&NF]*ZdBr7]:8m$L@5E!K95`Y]?T_%@Oq(]#qja%0X'XDZ$VX'?QTG=3g=m9FeP8kfGH,3%C[O!C!kI.N7G;+Y)hm7$*a4n$qTI:^g.Co!>P%"'$(ro+rkA2D$Zmb@,HL!2CiU6(/iEA84l$3$5R[9o>KoVfTG7T$*%.atQZ\4[\Ukm[NXWk[Z1mdbZq8SSg/92ZLplW!JXc>J,TuB5JRrC9;I1"+7C!)A9@[Kt(#Su!!G5AGmr&cZ8$V;?(Oo%C_-kBAV'c/rh$KX__glQ`rp![8/kT&n*\O,MSK&k[",7::(%p'lg6b>!q&qQcOS4m0`ll7;%64e:n"[rtg'2aH/(aHC\jMRmK+W2-[q:UR+_(93j2;IV*_'RI--?/7L?Mo-EFO05,1gn +endstream +endobj +1054 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1053 0 R +/Annots 1055 0 R +>> +endobj +1055 0 obj +[ +1056 0 R +1057 0 R +1058 0 R +1059 0 R +1060 0 R +1061 0 R +1062 0 R +1063 0 R +1064 0 R +1065 0 R +1066 0 R +1067 0 R +1068 0 R +1069 0 R +1070 0 R +1071 0 R +] +endobj +1056 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 199.2 705.5 244.2 695.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 83 0 R +/H /I +>> +endobj +1057 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 493.0 678.5 505.5 668.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 144 0 R +/H /I +>> +endobj +1058 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 524.94 678.5 537.44 668.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 162 0 R +/H /I +>> +endobj +1059 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 409.72 619.5 454.72 609.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +1060 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 337.62 592.5 380.12 582.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 334 0 R +/H /I +>> +endobj +1061 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 433.86 576.5 483.86 566.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 348 0 R +/H /I +>> +endobj +1062 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 489.95 539.5 534.95 529.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 92 0 R +/H /I +>> +endobj +1063 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 107.0 480.5 152.0 470.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +1064 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 177.83 453.5 223.39 443.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +1065 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 283.33 442.5 333.33 432.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 176 0 R +/H /I +>> +endobj +1066 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 377.028 137.0 367.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +1067 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 313.35 356.028 358.91 346.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +1068 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 251.95 302.028 301.95 292.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 207 0 R +/H /I +>> +endobj +1069 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 110.6 259.028 160.6 249.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 209 0 R +/H /I +>> +endobj +1070 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 255.32 238.028 309.21 228.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +1071 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 430.55 195.028 480.55 185.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 241 0 R +/H /I +>> +endobj +1072 0 obj +<< /Length 591 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GarnU9lldX&A@sBE3s7U,"M]TPX3rkK#C2Q;s31+)E3G[3Q1IfY=KM;3&&)5>B]]5^"r2_(RISO"jCU#ra%;IAJ'12rW4_^D(]7R+!_p,oBqs5P,Mg+)qn"_B1EF@r_fjYfnq=1@C=#C$*jt`k:T\G:>:"C"ZNY^Y,uC?b70+f,.kX9n\?P*6+_s]Q-P]&.TV-1EXc2bqlB72mME(:XZnKotk2*jJK=+-6&mg9@C=O>rErP)TZVJ-Vc0=oZl9iRS.>Er[;%WI\Il\V]mHO(dpIlRZ8_9qiaLo!ccgBKYOkdcWqs.$Z'dZH,OknXV$XjD[>Bq=<@o"RnnK0la(jZ~> +endstream +endobj +1073 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1072 0 R +>> +endobj +1074 0 obj +<< /Length 1955 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%%bAQ&o']&X:ZqQ!Nebd`3$mpC5OKL?q0sbQ*TI&;=.SD6;4-RE$1,d*u=](:m+HU-*Et8dg\lU!)X*;%XPIR3jhQAtR\+1P8r:s*RM.^PbqJ#B!&'icWs4[;:XM;ETcd(oXgH:^;,<$*rF5$e*#hrnO/0@1h?L54>$TnTVPUqT#&8P3O<^`f/9X0F[GFj,E;+X+X7(B*K6I+"DY/)+FKB+LXe0RD,J7R[%cAN'M,^Qe;Oi']E.-OW69=mUX05)(0X#V(4aCGIIG^%)C4n3Yb:cRs4Ms@>E>s;jpS?E6K*:Gn%FAE\KH4_4Ck05+R=s,N59dkpTrmO0eigi5_k%i0Me@jFVo?AfWb)M6.;;k&[Xg2rW]^IHPtc7_9!F*`^QK0)"XMIpHA%'eIbM,;D].u#'sr^d4Oteu]C#rug_b-COJ$[7:N^.b9QCchQ#%rH)f3`l2fOU-5f7_Re*?qsBPIt5t52[(do*#eLgH5M0UX]X%SlA.<[YH9Ma)c#:GgQ0+p?I6,'p%WKE)_o1K3Bqo(f/ICJN@SkJEd+l0JZ$-MZ"Y(4#-QstqZkIWeeM36@2na$YjNf^>CWFPV`iuI:.E_@1`&\1Js*^LWP#=[DHmEgf9j?gBt\e7?P,fcpq@rr3*-!f/i^kHn*-P5k8;fen-2VZjc7oV_fP"-m0W1h@]M_tY!Zu8$QGb1=LW=`'ZF6j3Kp3C7`\gAS*)LBj#kdC%]9]rQ;^Zeig(1sVh86cCs*/q40YlmZbc.lg%+8ZCZ[PJgIa^_.n"3`nu\^=f"`a6qo+iHlda>3fbio204u$]WA$g:PJ6g5E+2%T_fU-"3M)`:On7WrY5VaU==Yr[3J53O#Oluqp[*<#9I#4Jl.^iN1\cbm]TU8J'M>H]!k%Ku8hmB('.f9'!J3XSJU'4@Tqs7&*2s.CMA%V(C&fN5<%pTm80+?glB>3tAlWiF,uQH_:kWjo-Rc?$"e?h0]Qn&0,tOW>5qq(u6X3IP%":Th$/jP_N8f7Ni-W2-:,*.q'b[He/LjIm'kY=?EJa`nLr@Z0iMM>2WQZh,Z`6"D&uc]j.5EiC-7H6#"U.f'<@VT\EM=aW0gJS1d]<`^/QG)feD;P&?r_4+"]>e[U_&r=4@(\m*M>[_eg_E@W]8*5W>Ha,;E(2Acr(U#cE]3-X=?uR5cOjH'X=-=',6)K!=GWCX&R:).A"V1n/P_%FC4P"j=TJ&N'Dq1prC.A6*9,eL/qcPhE"tLpW+a4j9$lK'JgR`TF\q$HkOc/X/hGNKZ15[V4CXF"6ti`pId5"JrU2Qhb`K9:fgDRDQEn#J_m%,_p9Rf[W:et7U)):T%#(F,q,A@=-RfKe<($dRn6b?3In[KX2AE^\=*"3@XH'#U=9Xj=WE-^[1rJ2)\)O_o&,'#oYga.A+](Z.,H&aXrM"j4S!d:r1FXZ">RsegnD:?;bATIVEFC#r6kG=pe`tG'eOWrJXl^_&rCHO=4l8;NPB_jlT;$jq72`$$330="Wg8qLW!X]YnBgJc1A#f^*sSoAmNa%4?7snO2n[tQ/NYorIho0I(Qu%H+`rrD=2H-:Jj^Ok';:H!-Kj8I9@H\di](;Y[%-"2kP!e3BtW+q;f^2eo(W99?/64<''_+_tiQLFd/E>I"((5;93cDoQXMA^o0+<`=LWO:=DT8qg)%]+I^BGY+:o[ms508&+TQX&-~> +endstream +endobj +1075 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1074 0 R +/Annots 1076 0 R +>> +endobj +1076 0 obj +[ +1077 0 R +1078 0 R +1079 0 R +] +endobj +1077 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 488.89 604.866 535.374 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.ietf.org/ipr) +/S /URI >> +/H /I +>> +endobj +1078 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 593.866 144.783 583.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.ietf.org/ipr) +/S /URI >> +/H /I +>> +endobj +1079 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 247.26 550.866 313.4 540.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ietf-ipr@ietf.org) +/S /URI >> +/H /I +>> +endobj +1080 0 obj +<< /Length 3083 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm?968iI'#*O1kVg^P=M,`&b;rD;1J[:/PhFl]R0+\]%P'<6V5R)cn$g,gLmBKP'O@]JC_kCTLIOK/nm@;Vs*/0g9G0BjgqTSiLKA:]i?+O,%aPQ+KF'eG4O3r9\5$,qce#M=g"3H->:SY[ChsDp4LoS?l0*#r=i=oV>U"&c4u&r(O(/5N47OeI;#8OH*Jrq@^g3H/@/@SkNa&(Mkrg=k4EJU5KrglbUVVR6=FP,mRD+Y>0a+n+q8+%^RM_'1*-Y)Q1_EL5_g3;$8PM%DK9?T%b]UMQh;9U',Yj9kl/s86UPJVuQY4-b_uZ4blf6pP]$;I;qUPlP^mR5U7EZnN&[0$Zl<2A.kD:^($#PFrkJL$+U3I\CQa_[$g^_c0t[;7VS;+o>b[4gcs'">RFSE]^igSiZ(J]9g6u1+_X-b5aY$5epPH";8PYchS&&?p,u16O&<1[(2rA]SNM+A15Vdu*#XJf*>.^"8P0:R6?9^8qNqs#c8MGs!@;A0"ra:%!C'"D/^S(Xllsm,Ed:5Q($/+!tD#;5[[+lTXSq?".Wd/&\ddkWViEnO^N)5D&>K3gAk:Eb;T)d\sgNtUkn37GZ0!ZVE9S`"p=Nq/&RJL`-nGo]Y42\bMk6/^1F?"nQKUN/?hgq6jRH[W3*JUP>)2CKJC/A@8$2r*3J4J<*HPs;n3@G,>d>2R7c8;UFgh")[e84Z^M+9K$^?.mu]&S>&3BtcF62^:5o'BCS&Vk[J1c_'>aENAI"?gFJ'dkp#Zq@crFnCnbD)IFgR%+m.?l11s-&.X*iW9OfJAf!YdYAiXU<[/dWGcT4`mJZ01Ed#e_CSG(_RsuN`Z4KR*2a#*IhYT_I3K0bq3%+>T/8&bGQ>JC/)b(Bb0pEZH$>=a#YeCKf(r>id#8JU5EGdb;aUZsna9Seebtt&pHK`\R`*$K[gMF/8,1iM9SeWMup-oT_$rD\B5mi,P%:-]-VBHWV"-%sD3X<#I#k(hR=,X+.mB+l+3e;qkF..g@rp%rt8*?>A^=m_V!MAl3dEFkQV9Q5cNZs1lMf]pHl9's*G\K^9X;UaJ]Cbm4bHT@-[^tU0K<*A.:_k<,E&F+O"u1k`aKnnLb="ufiL):G2rW()1e`&\*R7WNhP9\69QriW`.6_IYU+g0mjY-bS'p8e^#S\\9-f.N'd?@L3-&+Te-FMSBgr079`_C;m?'!<>\hI6kLI(Riugat;-#J*0BXk);_G#1sh%)MlWqpf]glOZ^Y:Xr)d"%?aQsMs3[5S<"OSRjm7cpqGMSW$/5[2'nV46,S;2^W$tf5"<9S$RS\O&_5YPHb2F*_^Ja'RXlaXg2TPJ>G@T98IAmPeK(G3@-m?jVs"j%g?Z/!RC'p#eWX$N;t.(ir:kE)Os$@m!YWWB/)=qi!c$&1.f1#e52D1U-,In)]+YH\c`=t)3$jO8YBSgp)B#;<2#$&iAHn`&rsP4,T=Xb)fXVgOY>C>K/9\a:H18!U:-3L2EO#P`'dX*lO!?4=@1Sr6HUXj4V`A2c2?3TZ`SG)jMgZ@fN44'kk)j(]ZF)G4B6DI9L\@956dkV%:BU4bh3N-(m;A.PS\bgJqp[7n+hn_`PAPR>no:a.)dXT)+=V&%OR^B@lG0@E0.K9Tc8dG_f>pT+::B,ZZLJ5Pr)X^RM +endstream +endobj +1081 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1080 0 R +/Annots 1082 0 R +>> +endobj +1082 0 obj +[ +] +endobj +1083 0 obj +<< /Length 4877 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatmA>E@Ms(4OT5n7)glW8aB'[qm\L'MTMle*[9++5F+V0f$#I[\lRorK<e+TJGBHeqfL(8c2EYh2GeFtqnO!dJbKpEJFNLllf&^[2o.4HPn50Do1C059<_nKA0jSI]D^j`Yl4I'rE;\+fHhdt*0(#cPmRiQ2pQ`PG$Q/tL\o-ZI1["U%k3E&V8:MdEjYWKNTP3K*"/?kFg9NZ7?BJA<0!7dPIUU?.[#E`[h"fJpN4G6TMHgV*Ae`[&1_&p+Pldt07s(UBWMAF$ki&l`^Ol/]g:;r??38T(gAoW%%Jc"o17GsG?1'U\\6U@d#dl7]tk&Pi3h_iAf@"gjGN2J(YJPr6VEt#YMZ`]IjDTZC3@fTT`NFLEpnpd"He*2+k2EN;XU594iEY4mPL^:kO)BDlIXGbYs3*V_(%OuXAY!sLUl::iKq7QZIDab+($Z:[!^I99'JAB_"(fE4P&'sb8'Z?l9*mpan5D`\P1`p@UX[gQXoKMXQ4,=_0HF1Gb&;u(7Qm9VX*j+i.LS*J=H2H'kER:o6p%,H0Q._g6f.t@B)/%e\;0k&d-__`ba!-[m1n5qM-j3]bO\Jqg.&-GhDINYH993K++K!+'Nj%@0\&d[CWr[[cu1auiZ62<%;L;-C&VnEcYEen.>ptORR@AP.C:g74ko;;^h:Cs-"R[2!l*8ka5RnIcLCB*A/a2d>FX7s;cYYq[?4E&eGS7dp<.QC5@tX3'Q@4gA:6\NB7UJTN)r`/rBc4G@8J1iQ\TARbZZIFXgRG*ZO)5$L_7qEg8\#)nhsD&#hbk&&>nEsXtO71hri(M2bu'mS;kRo]jt-NTfJfFj>-O\Qm)0AOJk)?!)f0a$-&DIOatS@nP'(,j)a=;Ka$TF@D`!$3FE5t6(^`$Pc"f:*FX#Xd:.5(kNXq@MV<_9b70Ua75L(F^k;6hl8:iGXY,KGC#A9=9MhXn#BhTKV#j?GU!,Pa)=G)nq'A0e"U^kmsls9g^3)1hEMWW:'e#3IaXI^`S_(AUpZ>nSK_EYVI>N%VT#eh^+`u5%J#"!h,FqGKFHK&'@BCR[2!BtM#T=]:_u]i9cuA*Y;m"R8fd&f;I&GLId1*"*]naXdg*&R0FE&E0]B=!Lfe&aMOB/.kf_PkX/C0l\aZ3p+ZeMk]R_ZNr9_/.>h:Tl>@;74_[E.O3.WM:N.oNDjjhMDVV7^:G:Ok`C*>_nV7V;/nG6)Mei7opt0`5$apVoj]aGBcH2J_?J`Pq.d<.'FsV0c?dKbh]F[gZ>].Z?Zi[S',NX4LQ@=DeMG)9Q$<]Z/',)gkS8Y#udpq\'e2=*)[NK<3RcmZkgZ>.CChPC\:\6)sD+!cV2g!r.MaX3.fYSoH6#;tQ;]a\Jh'm85#J!V%.?CTB_Afe+DA%B?A)!),jUHnL;()4)`>0`hDr?qCTSYkoZ"`4T,c'=L-2N;fh_q[:#ODWo*O`l^mg'kqGON;iYWMk*@5%EE1@3;9kHR,K?&%22mlYdV^WWiCruLf1$i[aD]D(3.Cm*uqeplkfBqeC*s..3mM*>AHG90s6qLnP!H#/o4'@qZrZ#ud:=T=r42MjV+rHEq.i4k2SHiG,[d0orc%%AUC/U,Lg\Q`c(EoO\V>:?jl#DFVSmc-^&3#:4J9_0f-p9Y#>+XZP[9tT?t29^D4bu8jQ;7!XTSB]@-_H#NJi`RJ$]d=Dd;M?;==iD25Yd.j76pfD],5`&c>:GI6,4IbYfnJnH1B`t&rSsuB90CA@T]Zo=JsimUDk"+%`*h/1@+]6A$Ic@.+qDe\'llI*X7,65=L'G>fPu_(Ud8.[B5-jXmNf`@=\YKr7/2sP#Jsf`K2#"fZiS.i:/e=eh'&Z6H'=\X;$T&f=@FIQ"Ll2OBnn;1dLLVT)e#h]pKI,SA<ka:pb!&/?#$"lg!DKHjB8-Ysnk`T@=@Bf52h<^>f%J:KW*fpP0`cpQ3_!a,$38%.<.G9q2D%r&ol/a6WClu7,2+q,*A$Ep45bj8l7I8gI.Ei$BE2rV2nFhQTf/@?M^T:DX"\I96&#KeY:FN-Q8`%SAa0A@W#]=i5<2('Li!jFsigm%(OX#<1UHcm&kMESM<$MZWWgJ/,6`V-0:1lH2"V9E94'$[IPILk\k$V'.7ON=SOq2L]+b=BhQC]Z]l20%NE!6^K6c+(BY1fCUo&sTIsY+%Nju'(&V@"S$5du3Q]nlr_0/i'NiY%\V@kLe.XCr-(nstI*#;f#1?;M!)5g#oB;F(6#BqJ-C0hlU0G=Fgl=';NWp4+WBDc5rq`2C#ZPB,lg"G=Fce$CF,Uh[QJTrA0^qZDG?J?II"]]'H^@`emiu)i$[73a4O\\W+P%u>Lmd(XD&89O3MtSL!G,DFcnAc0Z^@IT/^XqCotlDj[s:?9RIR#_gWqF(!IaSoZ`ka)Amm@Rb`n&Y%1?08htiap0P&;hU6bSBKtV(V_cb\+D0/W.G\-_6Ta]hVQ$IbL-PYS7Bk>d%k;8Qj$bLkf,-+I(,?0r_IdC$1aLJP'Eg6ud0]7RuK*m>bnL>C7j,D/n6rE)aFHJ*I1CIX4;B6je`6Tuqmu=@Vh]#XM&$CjMXs3e[\0Ci>j!qt@`YJ1K+*$sFGTf9$.4Z7M:VmM03Q:P*J>OJfGCej/!DPqWmKUjA!LebQ77@7Vn3'l=dh"I;ABG7l1[T?sbP@(MUo_@Ydt.RgL:C+";H@1>-5`CiaBPpn4`iRr:8e7F7cctUZL5_5KS_c(@72^f'bh]"H5[shN%a=-9D#35e-iW0^O]^Gmn`nW?HCZueO&rGMOl7;`#)t3>l!($TQ]2``uKKWC.=Sp[/8C4>,#(Sl32JL^RAa/IPkV_e?Kb=C>f^-I+S%-qP1[S=bV[b[;:6u<@Y"`32&d1(k(BY_2tKc1hqkB]V8`jB!Y:G$M"#3$\@1_?=imqnVZkq#EA!`r-dQ>j3M$$=i%jK]V8^lB18Y#\i72i]gG15$)&.&8X'rc4c'^^es,lF_ipAl:$g$>(k8n]n.Pta\6J9`fgYHRIfNg(2R.N$ekQ/RKM++L\S*WT@f&@NM[N4:rlnUI>k>Ss@RIaM@*4p=r>KT%aim+&i.S`$1iX@RQ^URhd*Lg[IQJ\&U-raXbc+:".o+E$NT^l^<)6e0=%)6R%*(MhHF-FBpc.,ck'rH:ja-OALUWtk+32.oGg*?&BC75kM8rcYj'KVXn=bD4IQS&6RlV_f]>YL3+6Ygt)P#OcW4:f;$CEmS:SHMRtN1T*'$U_Do)ZO>n#Q*aK#K4Ok#CpK,V=3UE3P?(#qR(;a`A$BCIU8,ji$gNn4V4F[.6^Y?6CBDtP[""dBdGi*iH*cs!AJBrre;h9bAsq7rh&>6="f$iIW(Y1:I][3JeQ#p4\)4^KbceiFKM,Di+Z2TAI9He<2]]>-MbT""-o!]NLq-/DtD]l-=phVY_%f)FRqrLg?4TD]kB*QL>~> +endstream +endobj +1084 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1083 0 R +/Annots 1085 0 R +>> +endobj +1085 0 obj +[ +] +endobj +1086 0 obj +<< /Length 552 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU0a\K`-&A@fgHu'<%[c?muA?,it73sK_`?A%k.VM+^.XiJONK^rMU/dIOm(@W4c"(FHe(.r/&He8nD_4TP(*hUUNOCN7]+\+*EU9M"BO=Mq*Yp]gpm[2p`&;j8WERD]Y/$UiYRjTCe#CT(T737`aUEA6mtgDSCbj4liO!#dMsU,SUKe,%(E;.Aq2uYkU-n']c^!]sgA1u<5;:1~> +endstream +endobj +1087 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 1086 0 R +/Annots 1088 0 R +>> +endobj +1088 0 obj +[ +] +endobj +1091 0 obj +<< + /Title (\376\377\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\157\0\146\0\40\0\164\0\150\0\151\0\163\0\40\0\115\0\145\0\155\0\157) + /Parent 1089 0 R + /Next 1093 0 R + /A 1090 0 R +>> endobj +1093 0 obj +<< + /Title (\376\377\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\116\0\157\0\164\0\151\0\143\0\145) + /Parent 1089 0 R + /Prev 1091 0 R + /Next 1095 0 R + /A 1092 0 R +>> endobj +1095 0 obj +<< + /Title (\376\377\0\101\0\142\0\163\0\164\0\162\0\141\0\143\0\164) + /Parent 1089 0 R + /Prev 1093 0 R + /Next 1097 0 R + /A 1094 0 R +>> endobj +1097 0 obj +<< + /Title (\376\377\0\124\0\141\0\142\0\154\0\145\0\40\0\157\0\146\0\40\0\103\0\157\0\156\0\164\0\145\0\156\0\164\0\163) + /Parent 1089 0 R + /Prev 1095 0 R + /Next 1099 0 R + /A 1096 0 R +>> endobj +1099 0 obj +<< + /Title (\376\377\0\61\0\40\0\111\0\156\0\164\0\162\0\157\0\144\0\165\0\143\0\164\0\151\0\157\0\156) + /Parent 1089 0 R + /Prev 1097 0 R + /Next 1100 0 R + /A 1098 0 R +>> endobj +1100 0 obj +<< + /Title (\376\377\0\62\0\40\0\116\0\157\0\164\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\157\0\156\0\166\0\145\0\156\0\164\0\151\0\157\0\156\0\163) + /Parent 1089 0 R + /Prev 1099 0 R + /Next 1101 0 R + /A 15 0 R +>> endobj +1101 0 obj +<< + /Title (\376\377\0\63\0\40\0\124\0\145\0\162\0\155\0\151\0\156\0\157\0\154\0\157\0\147\0\171) + /Parent 1089 0 R + /Prev 1100 0 R + /Next 1103 0 R + /A 17 0 R +>> endobj +1103 0 obj +<< + /Title (\376\377\0\64\0\40\0\104\0\141\0\164\0\141\0\40\0\115\0\157\0\144\0\145\0\154\0\40\0\146\0\157\0\162\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1089 0 R + /First 1104 0 R + /Last 1110 0 R + /Prev 1101 0 R + /Next 1112 0 R + /Count -6 + /A 1102 0 R +>> endobj +1104 0 obj +<< + /Title (\376\377\0\64\0\56\0\61\0\40\0\124\0\150\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\115\0\157\0\144\0\145\0\154) + /Parent 1103 0 R + /Next 1105 0 R + /A 21 0 R +>> endobj +1105 0 obj +<< + /Title (\376\377\0\64\0\56\0\62\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163\0\40\0\141\0\156\0\144\0\40\0\110\0\124\0\124\0\120\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\163) + /Parent 1103 0 R + /Prev 1104 0 R + /Next 1107 0 R + /A 23 0 R +>> endobj +1107 0 obj +<< + /Title (\376\377\0\64\0\56\0\63\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\126\0\141\0\154\0\165\0\145\0\163) + /Parent 1103 0 R + /First 1108 0 R + /Last 1108 0 R + /Prev 1105 0 R + /Next 1109 0 R + /Count -1 + /A 1106 0 R +>> endobj +1108 0 obj +<< + /Title (\376\377\0\64\0\56\0\63\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\167\0\151\0\164\0\150\0\40\0\115\0\151\0\170\0\145\0\144\0\40\0\103\0\157\0\156\0\164\0\145\0\156\0\164) + /Parent 1107 0 R + /A 27 0 R +>> endobj +1109 0 obj +<< + /Title (\376\377\0\64\0\56\0\64\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\116\0\141\0\155\0\145\0\163) + /Parent 1103 0 R + /Prev 1107 0 R + /Next 1110 0 R + /A 29 0 R +>> endobj +1110 0 obj +<< + /Title (\376\377\0\64\0\56\0\65\0\40\0\123\0\157\0\165\0\162\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163\0\40\0\141\0\156\0\144\0\40\0\117\0\165\0\164\0\160\0\165\0\164\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1103 0 R + /Prev 1109 0 R + /A 31 0 R +>> endobj +1112 0 obj +<< + /Title (\376\377\0\65\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163\0\40\0\157\0\146\0\40\0\127\0\145\0\142\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1089 0 R + /First 1114 0 R + /Last 1116 0 R + /Prev 1103 0 R + /Next 1118 0 R + /Count -2 + /A 1111 0 R +>> endobj +1114 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\40\0\110\0\124\0\124\0\120\0\40\0\125\0\122\0\114\0\40\0\116\0\141\0\155\0\145\0\163\0\160\0\141\0\143\0\145\0\40\0\115\0\157\0\144\0\145\0\154) + /Parent 1112 0 R + /Next 1116 0 R + /A 1113 0 R +>> endobj +1116 0 obj +<< + /Title (\376\377\0\65\0\56\0\62\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1112 0 R + /Prev 1114 0 R + /A 1115 0 R +>> endobj +1118 0 obj +<< + /Title (\376\377\0\66\0\40\0\114\0\157\0\143\0\153\0\151\0\156\0\147) + /Parent 1089 0 R + /First 1120 0 R + /Last 1131 0 R + /Prev 1112 0 R + /Next 1133 0 R + /Count -8 + /A 1117 0 R +>> endobj +1120 0 obj +<< + /Title (\376\377\0\66\0\56\0\61\0\40\0\114\0\157\0\143\0\153\0\40\0\115\0\157\0\144\0\145\0\154) + /Parent 1118 0 R + /Next 1122 0 R + /A 1119 0 R +>> endobj +1122 0 obj +<< + /Title (\376\377\0\66\0\56\0\62\0\40\0\105\0\170\0\143\0\154\0\165\0\163\0\151\0\166\0\145\0\40\0\166\0\163\0\56\0\40\0\123\0\150\0\141\0\162\0\145\0\144\0\40\0\114\0\157\0\143\0\153\0\163) + /Parent 1118 0 R + /Prev 1120 0 R + /Next 1123 0 R + /A 1121 0 R +>> endobj +1123 0 obj +<< + /Title (\376\377\0\66\0\56\0\63\0\40\0\122\0\145\0\161\0\165\0\151\0\162\0\145\0\144\0\40\0\123\0\165\0\160\0\160\0\157\0\162\0\164) + /Parent 1118 0 R + /Prev 1122 0 R + /Next 1125 0 R + /A 45 0 R +>> endobj +1125 0 obj +<< + /Title (\376\377\0\66\0\56\0\64\0\40\0\114\0\157\0\143\0\153\0\40\0\103\0\162\0\145\0\141\0\164\0\157\0\162\0\40\0\141\0\156\0\144\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\163) + /Parent 1118 0 R + /Prev 1123 0 R + /Next 1127 0 R + /A 1124 0 R +>> endobj +1127 0 obj +<< + /Title (\376\377\0\66\0\56\0\65\0\40\0\114\0\157\0\143\0\153\0\40\0\124\0\157\0\153\0\145\0\156\0\163) + /Parent 1118 0 R + /Prev 1125 0 R + /Next 1129 0 R + /A 1126 0 R +>> endobj +1129 0 obj +<< + /Title (\376\377\0\66\0\56\0\66\0\40\0\114\0\157\0\143\0\153\0\40\0\124\0\151\0\155\0\145\0\157\0\165\0\164) + /Parent 1118 0 R + /Prev 1127 0 R + /Next 1130 0 R + /A 1128 0 R +>> endobj +1130 0 obj +<< + /Title (\376\377\0\66\0\56\0\67\0\40\0\114\0\157\0\143\0\153\0\40\0\103\0\141\0\160\0\141\0\142\0\151\0\154\0\151\0\164\0\171\0\40\0\104\0\151\0\163\0\143\0\157\0\166\0\145\0\162\0\171) + /Parent 1118 0 R + /Prev 1129 0 R + /Next 1131 0 R + /A 53 0 R +>> endobj +1131 0 obj +<< + /Title (\376\377\0\66\0\56\0\70\0\40\0\101\0\143\0\164\0\151\0\166\0\145\0\40\0\114\0\157\0\143\0\153\0\40\0\104\0\151\0\163\0\143\0\157\0\166\0\145\0\162\0\171) + /Parent 1118 0 R + /Prev 1130 0 R + /A 55 0 R +>> endobj +1133 0 obj +<< + /Title (\376\377\0\67\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153) + /Parent 1089 0 R + /First 1134 0 R + /Last 1145 0 R + /Prev 1118 0 R + /Next 1147 0 R + /Count -9 + /A 1132 0 R +>> endobj +1134 0 obj +<< + /Title (\376\377\0\67\0\56\0\61\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\163\0\40\0\141\0\156\0\144\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1133 0 R + /Next 1135 0 R + /A 59 0 R +>> endobj +1135 0 obj +<< + /Title (\376\377\0\67\0\56\0\62\0\40\0\101\0\166\0\157\0\151\0\144\0\151\0\156\0\147\0\40\0\114\0\157\0\163\0\164\0\40\0\125\0\160\0\144\0\141\0\164\0\145\0\163) + /Parent 1133 0 R + /Prev 1134 0 R + /Next 1137 0 R + /A 61 0 R +>> endobj +1137 0 obj +<< + /Title (\376\377\0\67\0\56\0\63\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\163\0\40\0\141\0\156\0\144\0\40\0\125\0\156\0\155\0\141\0\160\0\160\0\145\0\144\0\40\0\125\0\122\0\114\0\163) + /Parent 1133 0 R + /Prev 1135 0 R + /Next 1139 0 R + /A 1136 0 R +>> endobj +1139 0 obj +<< + /Title (\376\377\0\67\0\56\0\64\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\163\0\40\0\141\0\156\0\144\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 1133 0 R + /Prev 1137 0 R + /Next 1141 0 R + /A 1138 0 R +>> endobj +1141 0 obj +<< + /Title (\376\377\0\67\0\56\0\65\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\163\0\40\0\141\0\156\0\144\0\40\0\164\0\150\0\145\0\40\0\111\0\146\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1133 0 R + /First 1142 0 R + /Last 1143 0 R + /Prev 1139 0 R + /Next 1144 0 R + /Count -2 + /A 1140 0 R +>> endobj +1142 0 obj +<< + /Title (\376\377\0\67\0\56\0\65\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\40\0\141\0\156\0\144\0\40\0\103\0\117\0\120\0\131) + /Parent 1141 0 R + /Next 1143 0 R + /A 69 0 R +>> endobj +1143 0 obj +<< + /Title (\376\377\0\67\0\56\0\65\0\56\0\62\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\104\0\145\0\154\0\145\0\164\0\151\0\156\0\147\0\40\0\141\0\40\0\115\0\145\0\155\0\142\0\145\0\162\0\40\0\157\0\146\0\40\0\141\0\40\0\114\0\157\0\143\0\153\0\145\0\144\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156) + /Parent 1141 0 R + /Prev 1142 0 R + /A 71 0 R +>> endobj +1144 0 obj +<< + /Title (\376\377\0\67\0\56\0\66\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\163\0\40\0\141\0\156\0\144\0\40\0\103\0\117\0\120\0\131\0\57\0\115\0\117\0\126\0\105) + /Parent 1133 0 R + /Prev 1141 0 R + /Next 1145 0 R + /A 73 0 R +>> endobj +1145 0 obj +<< + /Title (\376\377\0\67\0\56\0\67\0\40\0\122\0\145\0\146\0\162\0\145\0\163\0\150\0\151\0\156\0\147\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\163) + /Parent 1133 0 R + /Prev 1144 0 R + /A 75 0 R +>> endobj +1147 0 obj +<< + /Title (\376\377\0\70\0\40\0\107\0\145\0\156\0\145\0\162\0\141\0\154\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164\0\40\0\141\0\156\0\144\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\40\0\110\0\141\0\156\0\144\0\154\0\151\0\156\0\147) + /Parent 1089 0 R + /First 1149 0 R + /Last 1162 0 R + /Prev 1133 0 R + /Next 1164 0 R + /Count -9 + /A 1146 0 R +>> endobj +1149 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\40\0\120\0\162\0\145\0\143\0\145\0\144\0\145\0\156\0\143\0\145\0\40\0\151\0\156\0\40\0\105\0\162\0\162\0\157\0\162\0\40\0\110\0\141\0\156\0\144\0\154\0\151\0\156\0\147) + /Parent 1147 0 R + /Next 1150 0 R + /A 1148 0 R +>> endobj +1150 0 obj +<< + /Title (\376\377\0\70\0\56\0\62\0\40\0\125\0\163\0\145\0\40\0\157\0\146\0\40\0\130\0\115\0\114) + /Parent 1147 0 R + /Prev 1149 0 R + /Next 1152 0 R + /A 81 0 R +>> endobj +1152 0 obj +<< + /Title (\376\377\0\70\0\56\0\63\0\40\0\125\0\122\0\114\0\40\0\110\0\141\0\156\0\144\0\154\0\151\0\156\0\147) + /Parent 1147 0 R + /First 1153 0 R + /Last 1153 0 R + /Prev 1150 0 R + /Next 1154 0 R + /Count -1 + /A 1151 0 R +>> endobj +1153 0 obj +<< + /Title (\376\377\0\70\0\56\0\63\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\157\0\162\0\162\0\145\0\143\0\164\0\40\0\125\0\122\0\114\0\40\0\110\0\141\0\156\0\144\0\154\0\151\0\156\0\147) + /Parent 1152 0 R + /A 85 0 R +>> endobj +1154 0 obj +<< + /Title (\376\377\0\70\0\56\0\64\0\40\0\122\0\145\0\161\0\165\0\151\0\162\0\145\0\144\0\40\0\102\0\157\0\144\0\151\0\145\0\163\0\40\0\151\0\156\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164\0\163) + /Parent 1147 0 R + /Prev 1152 0 R + /Next 1156 0 R + /A 90 0 R +>> endobj +1156 0 obj +<< + /Title (\376\377\0\70\0\56\0\65\0\40\0\110\0\124\0\124\0\120\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\163\0\40\0\146\0\157\0\162\0\40\0\125\0\163\0\145\0\40\0\151\0\156\0\40\0\127\0\145\0\142\0\104\0\101\0\126) + /Parent 1147 0 R + /Prev 1154 0 R + /Next 1158 0 R + /A 1155 0 R +>> endobj +1158 0 obj +<< + /Title (\376\377\0\70\0\56\0\66\0\40\0\105\0\124\0\141\0\147) + /Parent 1147 0 R + /Prev 1156 0 R + /Next 1160 0 R + /A 1157 0 R +>> endobj +1160 0 obj +<< + /Title (\376\377\0\70\0\56\0\67\0\40\0\111\0\156\0\143\0\154\0\165\0\144\0\151\0\156\0\147\0\40\0\105\0\162\0\162\0\157\0\162\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\40\0\102\0\157\0\144\0\151\0\145\0\163) + /Parent 1147 0 R + /Prev 1158 0 R + /Next 1162 0 R + /A 1159 0 R +>> endobj +1162 0 obj +<< + /Title (\376\377\0\70\0\56\0\70\0\40\0\111\0\155\0\160\0\141\0\143\0\164\0\40\0\157\0\146\0\40\0\116\0\141\0\155\0\145\0\163\0\160\0\141\0\143\0\145\0\40\0\117\0\160\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163\0\40\0\157\0\156\0\40\0\103\0\141\0\143\0\150\0\145\0\40\0\126\0\141\0\154\0\151\0\144\0\141\0\164\0\157\0\162\0\163) + /Parent 1147 0 R + /Prev 1160 0 R + /A 1161 0 R +>> endobj +1164 0 obj +<< + /Title (\376\377\0\71\0\40\0\110\0\124\0\124\0\120\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\163\0\40\0\146\0\157\0\162\0\40\0\104\0\151\0\163\0\164\0\162\0\151\0\142\0\165\0\164\0\145\0\144\0\40\0\101\0\165\0\164\0\150\0\157\0\162\0\151\0\156\0\147) + /Parent 1089 0 R + /First 1166 0 R + /Last 1232 0 R + /Prev 1147 0 R + /Next 1236 0 R + /Count -50 + /A 1163 0 R +>> endobj +1166 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\40\0\120\0\122\0\117\0\120\0\106\0\111\0\116\0\104\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1164 0 R + /First 1167 0 R + /Last 1173 0 R + /Next 1175 0 R + /Count -6 + /A 1165 0 R +>> endobj +1167 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\61\0\40\0\120\0\122\0\117\0\120\0\106\0\111\0\116\0\104\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1166 0 R + /Next 1169 0 R + /A 104 0 R +>> endobj +1169 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\62\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163\0\40\0\146\0\157\0\162\0\40\0\125\0\163\0\145\0\40\0\151\0\156\0\40\0\47\0\160\0\162\0\157\0\160\0\163\0\164\0\141\0\164\0\47\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1166 0 R + /Prev 1167 0 R + /Next 1170 0 R + /A 1168 0 R +>> endobj +1170 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\63\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\116\0\141\0\155\0\145\0\144\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1166 0 R + /Prev 1169 0 R + /Next 1171 0 R + /A 108 0 R +>> endobj +1171 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\64\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\163\0\151\0\156\0\147\0\40\0\47\0\160\0\162\0\157\0\160\0\156\0\141\0\155\0\145\0\47\0\40\0\164\0\157\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\145\0\40\0\101\0\154\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\116\0\141\0\155\0\145\0\163) + /Parent 1166 0 R + /Prev 1170 0 R + /Next 1172 0 R + /A 110 0 R +>> endobj +1172 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\65\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\163\0\151\0\156\0\147\0\40\0\123\0\157\0\55\0\143\0\141\0\154\0\154\0\145\0\144\0\40\0\47\0\141\0\154\0\154\0\160\0\162\0\157\0\160\0\47) + /Parent 1166 0 R + /Prev 1171 0 R + /Next 1173 0 R + /A 112 0 R +>> endobj +1173 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\66\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\163\0\151\0\156\0\147\0\40\0\47\0\141\0\154\0\154\0\160\0\162\0\157\0\160\0\47\0\40\0\167\0\151\0\164\0\150\0\40\0\47\0\151\0\156\0\143\0\154\0\165\0\144\0\145\0\47) + /Parent 1166 0 R + /Prev 1172 0 R + /A 114 0 R +>> endobj +1175 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\40\0\120\0\122\0\117\0\120\0\120\0\101\0\124\0\103\0\110\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1164 0 R + /First 1177 0 R + /Last 1178 0 R + /Prev 1166 0 R + /Next 1180 0 R + /Count -2 + /A 1174 0 R +>> endobj +1177 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\56\0\61\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163\0\40\0\146\0\157\0\162\0\40\0\125\0\163\0\145\0\40\0\151\0\156\0\40\0\47\0\160\0\162\0\157\0\160\0\163\0\164\0\141\0\164\0\47\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1175 0 R + /Next 1178 0 R + /A 1176 0 R +>> endobj +1178 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\56\0\62\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\120\0\122\0\117\0\120\0\120\0\101\0\124\0\103\0\110) + /Parent 1175 0 R + /Prev 1177 0 R + /A 120 0 R +>> endobj +1180 0 obj +<< + /Title (\376\377\0\71\0\56\0\63\0\40\0\115\0\113\0\103\0\117\0\114\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1164 0 R + /First 1181 0 R + /Last 1182 0 R + /Prev 1175 0 R + /Next 1183 0 R + /Count -2 + /A 1179 0 R +>> endobj +1181 0 obj +<< + /Title (\376\377\0\71\0\56\0\63\0\56\0\61\0\40\0\115\0\113\0\103\0\117\0\114\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1180 0 R + /Next 1182 0 R + /A 124 0 R +>> endobj +1182 0 obj +<< + /Title (\376\377\0\71\0\56\0\63\0\56\0\62\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\113\0\103\0\117\0\114) + /Parent 1180 0 R + /Prev 1181 0 R + /A 126 0 R +>> endobj +1183 0 obj +<< + /Title (\376\377\0\71\0\56\0\64\0\40\0\107\0\105\0\124\0\54\0\40\0\110\0\105\0\101\0\104\0\40\0\146\0\157\0\162\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 1164 0 R + /Prev 1180 0 R + /Next 1185 0 R + /A 128 0 R +>> endobj +1185 0 obj +<< + /Title (\376\377\0\71\0\56\0\65\0\40\0\120\0\117\0\123\0\124\0\40\0\146\0\157\0\162\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 1164 0 R + /Prev 1183 0 R + /Next 1187 0 R + /A 1184 0 R +>> endobj +1187 0 obj +<< + /Title (\376\377\0\71\0\56\0\66\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\122\0\145\0\161\0\165\0\151\0\162\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 1164 0 R + /First 1189 0 R + /Last 1191 0 R + /Prev 1185 0 R + /Next 1193 0 R + /Count -2 + /A 1186 0 R +>> endobj +1189 0 obj +<< + /Title (\376\377\0\71\0\56\0\66\0\56\0\61\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\146\0\157\0\162\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 1187 0 R + /Next 1191 0 R + /A 1188 0 R +>> endobj +1191 0 obj +<< + /Title (\376\377\0\71\0\56\0\66\0\56\0\62\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\104\0\105\0\114\0\105\0\124\0\105) + /Parent 1187 0 R + /Prev 1189 0 R + /A 1190 0 R +>> endobj +1193 0 obj +<< + /Title (\376\377\0\71\0\56\0\67\0\40\0\120\0\125\0\124\0\40\0\122\0\145\0\161\0\165\0\151\0\162\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 1164 0 R + /First 1195 0 R + /Last 1196 0 R + /Prev 1187 0 R + /Next 1198 0 R + /Count -2 + /A 1192 0 R +>> endobj +1195 0 obj +<< + /Title (\376\377\0\71\0\56\0\67\0\56\0\61\0\40\0\120\0\125\0\124\0\40\0\146\0\157\0\162\0\40\0\116\0\157\0\156\0\55\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1193 0 R + /Next 1196 0 R + /A 1194 0 R +>> endobj +1196 0 obj +<< + /Title (\376\377\0\71\0\56\0\67\0\56\0\62\0\40\0\120\0\125\0\124\0\40\0\146\0\157\0\162\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 1193 0 R + /Prev 1195 0 R + /A 142 0 R +>> endobj +1198 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\40\0\103\0\117\0\120\0\131\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1164 0 R + /First 1199 0 R + /Last 1208 0 R + /Prev 1193 0 R + /Next 1210 0 R + /Count -8 + /A 1197 0 R +>> endobj +1199 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\61\0\40\0\103\0\117\0\120\0\131\0\40\0\146\0\157\0\162\0\40\0\116\0\157\0\156\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1198 0 R + /Next 1201 0 R + /A 146 0 R +>> endobj +1201 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\62\0\40\0\103\0\117\0\120\0\131\0\40\0\146\0\157\0\162\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1198 0 R + /Prev 1199 0 R + /Next 1203 0 R + /A 1200 0 R +>> endobj +1203 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\63\0\40\0\103\0\117\0\120\0\131\0\40\0\146\0\157\0\162\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 1198 0 R + /Prev 1201 0 R + /Next 1204 0 R + /A 1202 0 R +>> endobj +1204 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\64\0\40\0\103\0\117\0\120\0\131\0\40\0\141\0\156\0\144\0\40\0\117\0\166\0\145\0\162\0\167\0\162\0\151\0\164\0\151\0\156\0\147\0\40\0\104\0\145\0\163\0\164\0\151\0\156\0\141\0\164\0\151\0\157\0\156\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1198 0 R + /Prev 1203 0 R + /Next 1205 0 R + /A 152 0 R +>> endobj +1205 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\65\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1198 0 R + /Prev 1204 0 R + /Next 1206 0 R + /A 154 0 R +>> endobj +1206 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\66\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\117\0\120\0\131\0\40\0\167\0\151\0\164\0\150\0\40\0\117\0\166\0\145\0\162\0\167\0\162\0\151\0\164\0\145) + /Parent 1198 0 R + /Prev 1205 0 R + /Next 1207 0 R + /A 156 0 R +>> endobj +1207 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\67\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\117\0\120\0\131\0\40\0\167\0\151\0\164\0\150\0\40\0\116\0\157\0\40\0\117\0\166\0\145\0\162\0\167\0\162\0\151\0\164\0\145) + /Parent 1198 0 R + /Prev 1206 0 R + /Next 1208 0 R + /A 158 0 R +>> endobj +1208 0 obj +<< + /Title (\376\377\0\71\0\56\0\70\0\56\0\70\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\117\0\120\0\131\0\40\0\157\0\146\0\40\0\141\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156) + /Parent 1198 0 R + /Prev 1207 0 R + /A 160 0 R +>> endobj +1210 0 obj +<< + /Title (\376\377\0\71\0\56\0\71\0\40\0\115\0\117\0\126\0\105\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1164 0 R + /First 1212 0 R + /Last 1218 0 R + /Prev 1198 0 R + /Next 1220 0 R + /Count -6 + /A 1209 0 R +>> endobj +1212 0 obj +<< + /Title (\376\377\0\71\0\56\0\71\0\56\0\61\0\40\0\115\0\117\0\126\0\105\0\40\0\146\0\157\0\162\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1210 0 R + /Next 1214 0 R + /A 1211 0 R +>> endobj +1214 0 obj +<< + /Title (\376\377\0\71\0\56\0\71\0\56\0\62\0\40\0\115\0\117\0\126\0\105\0\40\0\146\0\157\0\162\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 1210 0 R + /Prev 1212 0 R + /Next 1215 0 R + /A 1213 0 R +>> endobj +1215 0 obj +<< + /Title (\376\377\0\71\0\56\0\71\0\56\0\63\0\40\0\115\0\117\0\126\0\105\0\40\0\141\0\156\0\144\0\40\0\164\0\150\0\145\0\40\0\117\0\166\0\145\0\162\0\167\0\162\0\151\0\164\0\145\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1210 0 R + /Prev 1214 0 R + /Next 1216 0 R + /A 168 0 R +>> endobj +1216 0 obj +<< + /Title (\376\377\0\71\0\56\0\71\0\56\0\64\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1210 0 R + /Prev 1215 0 R + /Next 1217 0 R + /A 170 0 R +>> endobj +1217 0 obj +<< + /Title (\376\377\0\71\0\56\0\71\0\56\0\65\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\117\0\126\0\105\0\40\0\157\0\146\0\40\0\141\0\40\0\116\0\157\0\156\0\55\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156) + /Parent 1210 0 R + /Prev 1216 0 R + /Next 1218 0 R + /A 172 0 R +>> endobj +1218 0 obj +<< + /Title (\376\377\0\71\0\56\0\71\0\56\0\66\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\117\0\126\0\105\0\40\0\157\0\146\0\40\0\141\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156) + /Parent 1210 0 R + /Prev 1217 0 R + /A 174 0 R +>> endobj +1220 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\40\0\114\0\117\0\103\0\113\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1164 0 R + /First 1221 0 R + /Last 1230 0 R + /Prev 1210 0 R + /Next 1232 0 R + /Count -9 + /A 1219 0 R +>> endobj +1221 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\61\0\40\0\103\0\162\0\145\0\141\0\164\0\151\0\156\0\147\0\40\0\141\0\40\0\114\0\157\0\143\0\153\0\40\0\157\0\156\0\40\0\141\0\156\0\40\0\105\0\170\0\151\0\163\0\164\0\151\0\156\0\147\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 1220 0 R + /Next 1223 0 R + /A 178 0 R +>> endobj +1223 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\62\0\40\0\122\0\145\0\146\0\162\0\145\0\163\0\150\0\151\0\156\0\147\0\40\0\114\0\157\0\143\0\153\0\163) + /Parent 1220 0 R + /Prev 1221 0 R + /Next 1224 0 R + /A 1222 0 R +>> endobj +1224 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\63\0\40\0\104\0\145\0\160\0\164\0\150\0\40\0\141\0\156\0\144\0\40\0\114\0\157\0\143\0\153\0\151\0\156\0\147) + /Parent 1220 0 R + /Prev 1223 0 R + /Next 1225 0 R + /A 182 0 R +>> endobj +1225 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\64\0\40\0\114\0\157\0\143\0\153\0\151\0\156\0\147\0\40\0\125\0\156\0\155\0\141\0\160\0\160\0\145\0\144\0\40\0\125\0\122\0\114\0\163) + /Parent 1220 0 R + /Prev 1224 0 R + /Next 1226 0 R + /A 184 0 R +>> endobj +1226 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\65\0\40\0\114\0\157\0\143\0\153\0\40\0\103\0\157\0\155\0\160\0\141\0\164\0\151\0\142\0\151\0\154\0\151\0\164\0\171\0\40\0\124\0\141\0\142\0\154\0\145) + /Parent 1220 0 R + /Prev 1225 0 R + /Next 1227 0 R + /A 186 0 R +>> endobj +1227 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\66\0\40\0\114\0\117\0\103\0\113\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\163) + /Parent 1220 0 R + /Prev 1226 0 R + /Next 1228 0 R + /A 188 0 R +>> endobj +1228 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\67\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\123\0\151\0\155\0\160\0\154\0\145\0\40\0\114\0\157\0\143\0\153\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164) + /Parent 1220 0 R + /Prev 1227 0 R + /Next 1229 0 R + /A 190 0 R +>> endobj +1229 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\70\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\122\0\145\0\146\0\162\0\145\0\163\0\150\0\151\0\156\0\147\0\40\0\141\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153) + /Parent 1220 0 R + /Prev 1228 0 R + /Next 1230 0 R + /A 195 0 R +>> endobj +1230 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\60\0\56\0\71\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\165\0\154\0\164\0\151\0\55\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\114\0\157\0\143\0\153\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164) + /Parent 1220 0 R + /Prev 1229 0 R + /A 197 0 R +>> endobj +1232 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\61\0\40\0\125\0\116\0\114\0\117\0\103\0\113\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1164 0 R + /First 1233 0 R + /Last 1234 0 R + /Prev 1220 0 R + /Count -2 + /A 1231 0 R +>> endobj +1233 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\61\0\56\0\61\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1232 0 R + /Next 1234 0 R + /A 201 0 R +>> endobj +1234 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\61\0\56\0\62\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\116\0\114\0\117\0\103\0\113) + /Parent 1232 0 R + /Prev 1233 0 R + /A 203 0 R +>> endobj +1236 0 obj +<< + /Title (\376\377\0\61\0\60\0\40\0\110\0\124\0\124\0\120\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\163\0\40\0\146\0\157\0\162\0\40\0\104\0\151\0\163\0\164\0\162\0\151\0\142\0\165\0\164\0\145\0\144\0\40\0\101\0\165\0\164\0\150\0\157\0\162\0\151\0\156\0\147) + /Parent 1089 0 R + /First 1238 0 R + /Last 1266 0 R + /Prev 1164 0 R + /Next 1268 0 R + /Count -18 + /A 1235 0 R +>> endobj +1238 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\61\0\40\0\104\0\101\0\126\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1236 0 R + /Next 1240 0 R + /A 1237 0 R +>> endobj +1240 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\62\0\40\0\104\0\145\0\160\0\164\0\150\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1236 0 R + /Prev 1238 0 R + /Next 1242 0 R + /A 1239 0 R +>> endobj +1242 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\63\0\40\0\104\0\145\0\163\0\164\0\151\0\156\0\141\0\164\0\151\0\157\0\156\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1236 0 R + /Prev 1240 0 R + /Next 1244 0 R + /A 1241 0 R +>> endobj +1244 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\40\0\111\0\146\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1236 0 R + /First 1246 0 R + /Last 1260 0 R + /Prev 1242 0 R + /Next 1262 0 R + /Count -11 + /A 1243 0 R +>> endobj +1246 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\61\0\40\0\120\0\165\0\162\0\160\0\157\0\163\0\145) + /Parent 1244 0 R + /Next 1248 0 R + /A 1245 0 R +>> endobj +1248 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\62\0\40\0\123\0\171\0\156\0\164\0\141\0\170) + /Parent 1244 0 R + /Prev 1246 0 R + /Next 1250 0 R + /A 1247 0 R +>> endobj +1250 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\63\0\40\0\114\0\151\0\163\0\164\0\40\0\105\0\166\0\141\0\154\0\165\0\141\0\164\0\151\0\157\0\156) + /Parent 1244 0 R + /Prev 1248 0 R + /Next 1252 0 R + /A 1249 0 R +>> endobj +1252 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\64\0\40\0\115\0\141\0\164\0\143\0\150\0\151\0\156\0\147\0\40\0\123\0\164\0\141\0\164\0\145\0\40\0\124\0\157\0\153\0\145\0\156\0\163\0\40\0\141\0\156\0\144\0\40\0\105\0\124\0\141\0\147\0\163) + /Parent 1244 0 R + /Prev 1250 0 R + /Next 1253 0 R + /A 1251 0 R +>> endobj +1253 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\65\0\40\0\111\0\146\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\40\0\141\0\156\0\144\0\40\0\116\0\157\0\156\0\55\0\104\0\101\0\126\0\55\0\101\0\167\0\141\0\162\0\145\0\40\0\120\0\162\0\157\0\170\0\151\0\145\0\163) + /Parent 1244 0 R + /Prev 1252 0 R + /Next 1255 0 R + /A 223 0 R +>> endobj +1255 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\66\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\116\0\157\0\55\0\164\0\141\0\147\0\40\0\120\0\162\0\157\0\144\0\165\0\143\0\164\0\151\0\157\0\156) + /Parent 1244 0 R + /Prev 1253 0 R + /Next 1256 0 R + /A 1254 0 R +>> endobj +1256 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\67\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\163\0\151\0\156\0\147\0\40\0\42\0\116\0\157\0\164\0\42\0\40\0\167\0\151\0\164\0\150\0\40\0\116\0\157\0\55\0\164\0\141\0\147\0\40\0\120\0\162\0\157\0\144\0\165\0\143\0\164\0\151\0\157\0\156) + /Parent 1244 0 R + /Prev 1255 0 R + /Next 1257 0 R + /A 227 0 R +>> endobj +1257 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\70\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\141\0\165\0\163\0\151\0\156\0\147\0\40\0\141\0\40\0\103\0\157\0\156\0\144\0\151\0\164\0\151\0\157\0\156\0\40\0\164\0\157\0\40\0\101\0\154\0\167\0\141\0\171\0\163\0\40\0\105\0\166\0\141\0\154\0\165\0\141\0\164\0\145\0\40\0\164\0\157\0\40\0\124\0\162\0\165\0\145) + /Parent 1244 0 R + /Prev 1256 0 R + /Next 1258 0 R + /A 229 0 R +>> endobj +1258 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\71\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\124\0\141\0\147\0\147\0\145\0\144\0\40\0\114\0\151\0\163\0\164\0\40\0\111\0\146\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\40\0\151\0\156\0\40\0\103\0\117\0\120\0\131) + /Parent 1244 0 R + /Prev 1257 0 R + /Next 1259 0 R + /A 231 0 R +>> endobj +1259 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\61\0\60\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\141\0\164\0\143\0\150\0\151\0\156\0\147\0\40\0\114\0\157\0\143\0\153\0\40\0\124\0\157\0\153\0\145\0\156\0\163\0\40\0\167\0\151\0\164\0\150\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\114\0\157\0\143\0\153\0\163) + /Parent 1244 0 R + /Prev 1258 0 R + /Next 1260 0 R + /A 233 0 R +>> endobj +1260 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\64\0\56\0\61\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\141\0\164\0\143\0\150\0\151\0\156\0\147\0\40\0\105\0\124\0\141\0\147\0\163\0\40\0\157\0\156\0\40\0\125\0\156\0\155\0\141\0\160\0\160\0\145\0\144\0\40\0\125\0\122\0\114\0\163) + /Parent 1244 0 R + /Prev 1259 0 R + /A 235 0 R +>> endobj +1262 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\65\0\40\0\114\0\157\0\143\0\153\0\55\0\124\0\157\0\153\0\145\0\156\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1236 0 R + /Prev 1244 0 R + /Next 1264 0 R + /A 1261 0 R +>> endobj +1264 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\66\0\40\0\117\0\166\0\145\0\162\0\167\0\162\0\151\0\164\0\145\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1236 0 R + /Prev 1262 0 R + /Next 1266 0 R + /A 1263 0 R +>> endobj +1266 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\67\0\40\0\124\0\151\0\155\0\145\0\157\0\165\0\164\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 1236 0 R + /Prev 1264 0 R + /A 1265 0 R +>> endobj +1268 0 obj +<< + /Title (\376\377\0\61\0\61\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\40\0\105\0\170\0\164\0\145\0\156\0\163\0\151\0\157\0\156\0\163\0\40\0\164\0\157\0\40\0\110\0\124\0\124\0\120\0\57\0\61\0\56\0\61) + /Parent 1089 0 R + /First 1270 0 R + /Last 1278 0 R + /Prev 1236 0 R + /Next 1280 0 R + /Count -5 + /A 1267 0 R +>> endobj +1270 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\61\0\40\0\62\0\60\0\67\0\40\0\115\0\165\0\154\0\164\0\151\0\55\0\123\0\164\0\141\0\164\0\165\0\163) + /Parent 1268 0 R + /Next 1272 0 R + /A 1269 0 R +>> endobj +1272 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\62\0\40\0\64\0\62\0\62\0\40\0\125\0\156\0\160\0\162\0\157\0\143\0\145\0\163\0\163\0\141\0\142\0\154\0\145\0\40\0\105\0\156\0\164\0\151\0\164\0\171) + /Parent 1268 0 R + /Prev 1270 0 R + /Next 1274 0 R + /A 1271 0 R +>> endobj +1274 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\63\0\40\0\64\0\62\0\63\0\40\0\114\0\157\0\143\0\153\0\145\0\144) + /Parent 1268 0 R + /Prev 1272 0 R + /Next 1276 0 R + /A 1273 0 R +>> endobj +1276 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\64\0\40\0\64\0\62\0\64\0\40\0\106\0\141\0\151\0\154\0\145\0\144\0\40\0\104\0\145\0\160\0\145\0\156\0\144\0\145\0\156\0\143\0\171) + /Parent 1268 0 R + /Prev 1274 0 R + /Next 1278 0 R + /A 1275 0 R +>> endobj +1278 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\65\0\40\0\65\0\60\0\67\0\40\0\111\0\156\0\163\0\165\0\146\0\146\0\151\0\143\0\151\0\145\0\156\0\164\0\40\0\123\0\164\0\157\0\162\0\141\0\147\0\145) + /Parent 1268 0 R + /Prev 1276 0 R + /A 1277 0 R +>> endobj +1280 0 obj +<< + /Title (\376\377\0\61\0\62\0\40\0\125\0\163\0\145\0\40\0\157\0\146\0\40\0\110\0\124\0\124\0\120\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1089 0 R + /First 1281 0 R + /Last 1282 0 R + /Prev 1268 0 R + /Next 1284 0 R + /Count -2 + /A 1279 0 R +>> endobj +1281 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\40\0\64\0\61\0\62\0\40\0\120\0\162\0\145\0\143\0\157\0\156\0\144\0\151\0\164\0\151\0\157\0\156\0\40\0\106\0\141\0\151\0\154\0\145\0\144) + /Parent 1280 0 R + /Next 1282 0 R + /A 257 0 R +>> endobj +1282 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\62\0\40\0\64\0\61\0\64\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164\0\55\0\125\0\122\0\111\0\40\0\124\0\157\0\157\0\40\0\114\0\157\0\156\0\147) + /Parent 1280 0 R + /Prev 1281 0 R + /A 259 0 R +>> endobj +1284 0 obj +<< + /Title (\376\377\0\61\0\63\0\40\0\115\0\165\0\154\0\164\0\151\0\55\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145) + /Parent 1089 0 R + /First 1285 0 R + /Last 1287 0 R + /Prev 1280 0 R + /Next 1289 0 R + /Count -3 + /A 1283 0 R +>> endobj +1285 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\163) + /Parent 1284 0 R + /Next 1286 0 R + /A 263 0 R +>> endobj +1286 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\62\0\40\0\110\0\141\0\156\0\144\0\154\0\151\0\156\0\147\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\145\0\144\0\40\0\103\0\150\0\151\0\154\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1284 0 R + /Prev 1285 0 R + /Next 1287 0 R + /A 265 0 R +>> endobj +1287 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\63\0\40\0\111\0\156\0\164\0\145\0\162\0\156\0\141\0\154\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1284 0 R + /Prev 1286 0 R + /A 267 0 R +>> endobj +1289 0 obj +<< + /Title (\376\377\0\61\0\64\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164\0\40\0\104\0\145\0\146\0\151\0\156\0\151\0\164\0\151\0\157\0\156\0\163) + /Parent 1089 0 R + /First 1291 0 R + /Last 1349 0 R + /Prev 1284 0 R + /Next 1351 0 R + /Count -30 + /A 1288 0 R +>> endobj +1291 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\40\0\141\0\143\0\164\0\151\0\166\0\145\0\154\0\157\0\143\0\153\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Next 1293 0 R + /A 1290 0 R +>> endobj +1293 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\40\0\141\0\154\0\154\0\160\0\162\0\157\0\160\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1291 0 R + /Next 1295 0 R + /A 1292 0 R +>> endobj +1295 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\63\0\40\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1293 0 R + /Next 1297 0 R + /A 1294 0 R +>> endobj +1297 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\64\0\40\0\144\0\145\0\160\0\164\0\150\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1295 0 R + /Next 1299 0 R + /A 1296 0 R +>> endobj +1299 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\65\0\40\0\145\0\162\0\162\0\157\0\162\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1297 0 R + /Next 1301 0 R + /A 1298 0 R +>> endobj +1301 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\66\0\40\0\145\0\170\0\143\0\154\0\165\0\163\0\151\0\166\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1299 0 R + /Next 1303 0 R + /A 1300 0 R +>> endobj +1303 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\67\0\40\0\150\0\162\0\145\0\146\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1301 0 R + /Next 1305 0 R + /A 1302 0 R +>> endobj +1305 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\70\0\40\0\151\0\156\0\143\0\154\0\165\0\144\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1303 0 R + /Next 1307 0 R + /A 1304 0 R +>> endobj +1307 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\71\0\40\0\154\0\157\0\143\0\141\0\164\0\151\0\157\0\156\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1305 0 R + /Next 1309 0 R + /A 1306 0 R +>> endobj +1309 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\60\0\40\0\154\0\157\0\143\0\153\0\145\0\156\0\164\0\162\0\171\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1307 0 R + /Next 1311 0 R + /A 1308 0 R +>> endobj +1311 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\61\0\40\0\154\0\157\0\143\0\153\0\151\0\156\0\146\0\157\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1309 0 R + /Next 1313 0 R + /A 1310 0 R +>> endobj +1313 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\62\0\40\0\154\0\157\0\143\0\153\0\162\0\157\0\157\0\164\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1311 0 R + /Next 1315 0 R + /A 1312 0 R +>> endobj +1315 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\63\0\40\0\154\0\157\0\143\0\153\0\163\0\143\0\157\0\160\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1313 0 R + /Next 1317 0 R + /A 1314 0 R +>> endobj +1317 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\64\0\40\0\154\0\157\0\143\0\153\0\164\0\157\0\153\0\145\0\156\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1315 0 R + /Next 1319 0 R + /A 1316 0 R +>> endobj +1319 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\65\0\40\0\154\0\157\0\143\0\153\0\164\0\171\0\160\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1317 0 R + /Next 1321 0 R + /A 1318 0 R +>> endobj +1321 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\66\0\40\0\155\0\165\0\154\0\164\0\151\0\163\0\164\0\141\0\164\0\165\0\163\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1319 0 R + /Next 1323 0 R + /A 1320 0 R +>> endobj +1323 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\67\0\40\0\157\0\167\0\156\0\145\0\162\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1321 0 R + /Next 1325 0 R + /A 1322 0 R +>> endobj +1325 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\70\0\40\0\160\0\162\0\157\0\160\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1323 0 R + /Next 1327 0 R + /A 1324 0 R +>> endobj +1327 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\71\0\40\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\165\0\160\0\144\0\141\0\164\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1325 0 R + /Next 1329 0 R + /A 1326 0 R +>> endobj +1329 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\60\0\40\0\160\0\162\0\157\0\160\0\146\0\151\0\156\0\144\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1327 0 R + /Next 1331 0 R + /A 1328 0 R +>> endobj +1331 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\61\0\40\0\160\0\162\0\157\0\160\0\156\0\141\0\155\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1329 0 R + /Next 1333 0 R + /A 1330 0 R +>> endobj +1333 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\62\0\40\0\160\0\162\0\157\0\160\0\163\0\164\0\141\0\164\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1331 0 R + /Next 1335 0 R + /A 1332 0 R +>> endobj +1335 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\63\0\40\0\162\0\145\0\155\0\157\0\166\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1333 0 R + /Next 1337 0 R + /A 1334 0 R +>> endobj +1337 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\64\0\40\0\162\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1335 0 R + /Next 1339 0 R + /A 1336 0 R +>> endobj +1339 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\65\0\40\0\162\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\144\0\145\0\163\0\143\0\162\0\151\0\160\0\164\0\151\0\157\0\156\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1337 0 R + /Next 1341 0 R + /A 1338 0 R +>> endobj +1341 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\66\0\40\0\163\0\145\0\164\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1339 0 R + /Next 1343 0 R + /A 1340 0 R +>> endobj +1343 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\67\0\40\0\163\0\150\0\141\0\162\0\145\0\144\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1341 0 R + /Next 1345 0 R + /A 1342 0 R +>> endobj +1345 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\70\0\40\0\163\0\164\0\141\0\164\0\165\0\163\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1343 0 R + /Next 1347 0 R + /A 1344 0 R +>> endobj +1347 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\71\0\40\0\164\0\151\0\155\0\145\0\157\0\165\0\164\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1345 0 R + /Next 1349 0 R + /A 1346 0 R +>> endobj +1349 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\63\0\60\0\40\0\167\0\162\0\151\0\164\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1289 0 R + /Prev 1347 0 R + /A 1348 0 R +>> endobj +1351 0 obj +<< + /Title (\376\377\0\61\0\65\0\40\0\104\0\101\0\126\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1089 0 R + /First 1353 0 R + /Last 1372 0 R + /Prev 1289 0 R + /Next 1375 0 R + /Count -12 + /A 1350 0 R +>> endobj +1353 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\61\0\40\0\143\0\162\0\145\0\141\0\164\0\151\0\157\0\156\0\144\0\141\0\164\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Next 1355 0 R + /A 1352 0 R +>> endobj +1355 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\62\0\40\0\144\0\151\0\163\0\160\0\154\0\141\0\171\0\156\0\141\0\155\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Prev 1353 0 R + /Next 1357 0 R + /A 1354 0 R +>> endobj +1357 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\63\0\40\0\147\0\145\0\164\0\143\0\157\0\156\0\164\0\145\0\156\0\164\0\154\0\141\0\156\0\147\0\165\0\141\0\147\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Prev 1355 0 R + /Next 1359 0 R + /A 1356 0 R +>> endobj +1359 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\64\0\40\0\147\0\145\0\164\0\143\0\157\0\156\0\164\0\145\0\156\0\164\0\154\0\145\0\156\0\147\0\164\0\150\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Prev 1357 0 R + /Next 1361 0 R + /A 1358 0 R +>> endobj +1361 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\65\0\40\0\147\0\145\0\164\0\143\0\157\0\156\0\164\0\145\0\156\0\164\0\164\0\171\0\160\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Prev 1359 0 R + /Next 1363 0 R + /A 1360 0 R +>> endobj +1363 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\66\0\40\0\147\0\145\0\164\0\145\0\164\0\141\0\147\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Prev 1361 0 R + /Next 1365 0 R + /A 1362 0 R +>> endobj +1365 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\67\0\40\0\147\0\145\0\164\0\154\0\141\0\163\0\164\0\155\0\157\0\144\0\151\0\146\0\151\0\145\0\144\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Prev 1363 0 R + /Next 1367 0 R + /A 1364 0 R +>> endobj +1367 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\70\0\40\0\154\0\157\0\143\0\153\0\144\0\151\0\163\0\143\0\157\0\166\0\145\0\162\0\171\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /First 1368 0 R + /Last 1368 0 R + /Prev 1365 0 R + /Next 1370 0 R + /Count -1 + /A 1366 0 R +>> endobj +1368 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\70\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\104\0\101\0\126\0\72\0\154\0\157\0\143\0\153\0\144\0\151\0\163\0\143\0\157\0\166\0\145\0\162\0\171) + /Parent 1367 0 R + /A 352 0 R +>> endobj +1370 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\71\0\40\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\164\0\171\0\160\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /Prev 1367 0 R + /Next 1372 0 R + /A 1369 0 R +>> endobj +1372 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\61\0\60\0\40\0\163\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144\0\154\0\157\0\143\0\153\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 1351 0 R + /First 1373 0 R + /Last 1373 0 R + /Prev 1370 0 R + /Count -1 + /A 1371 0 R +>> endobj +1373 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\61\0\60\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144\0\154\0\157\0\143\0\153) + /Parent 1372 0 R + /A 358 0 R +>> endobj +1375 0 obj +<< + /Title (\376\377\0\61\0\66\0\40\0\120\0\162\0\145\0\143\0\157\0\156\0\144\0\151\0\164\0\151\0\157\0\156\0\57\0\120\0\157\0\163\0\164\0\143\0\157\0\156\0\144\0\151\0\164\0\151\0\157\0\156\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 1089 0 R + /Prev 1351 0 R + /Next 1377 0 R + /A 1374 0 R +>> endobj +1377 0 obj +<< + /Title (\376\377\0\61\0\67\0\40\0\130\0\115\0\114\0\40\0\105\0\170\0\164\0\145\0\156\0\163\0\151\0\142\0\151\0\154\0\151\0\164\0\171\0\40\0\151\0\156\0\40\0\104\0\101\0\126) + /Parent 1089 0 R + /Prev 1375 0 R + /Next 1379 0 R + /A 1376 0 R +>> endobj +1379 0 obj +<< + /Title (\376\377\0\61\0\70\0\40\0\104\0\101\0\126\0\40\0\103\0\157\0\155\0\160\0\154\0\151\0\141\0\156\0\143\0\145\0\40\0\103\0\154\0\141\0\163\0\163\0\145\0\163) + /Parent 1089 0 R + /First 1380 0 R + /Last 1383 0 R + /Prev 1377 0 R + /Next 1385 0 R + /Count -3 + /A 1378 0 R +>> endobj +1380 0 obj +<< + /Title (\376\377\0\61\0\70\0\56\0\61\0\40\0\103\0\154\0\141\0\163\0\163\0\40\0\61) + /Parent 1379 0 R + /Next 1381 0 R + /A 369 0 R +>> endobj +1381 0 obj +<< + /Title (\376\377\0\61\0\70\0\56\0\62\0\40\0\103\0\154\0\141\0\163\0\163\0\40\0\62) + /Parent 1379 0 R + /Prev 1380 0 R + /Next 1383 0 R + /A 371 0 R +>> endobj +1383 0 obj +<< + /Title (\376\377\0\61\0\70\0\56\0\63\0\40\0\103\0\154\0\141\0\163\0\163\0\40\0\63) + /Parent 1379 0 R + /Prev 1381 0 R + /A 1382 0 R +>> endobj +1385 0 obj +<< + /Title (\376\377\0\61\0\71\0\40\0\111\0\156\0\164\0\145\0\162\0\156\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\151\0\172\0\141\0\164\0\151\0\157\0\156\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 1089 0 R + /Prev 1379 0 R + /Next 1387 0 R + /A 1384 0 R +>> endobj +1387 0 obj +<< + /Title (\376\377\0\62\0\60\0\40\0\123\0\145\0\143\0\165\0\162\0\151\0\164\0\171\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 1089 0 R + /First 1388 0 R + /Last 1396 0 R + /Prev 1385 0 R + /Next 1397 0 R + /Count -8 + /A 1386 0 R +>> endobj +1388 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\61\0\40\0\101\0\165\0\164\0\150\0\145\0\156\0\164\0\151\0\143\0\141\0\164\0\151\0\157\0\156\0\40\0\157\0\146\0\40\0\103\0\154\0\151\0\145\0\156\0\164\0\163) + /Parent 1387 0 R + /Next 1389 0 R + /A 379 0 R +>> endobj +1389 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\62\0\40\0\104\0\145\0\156\0\151\0\141\0\154\0\40\0\157\0\146\0\40\0\123\0\145\0\162\0\166\0\151\0\143\0\145) + /Parent 1387 0 R + /Prev 1388 0 R + /Next 1390 0 R + /A 381 0 R +>> endobj +1390 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\63\0\40\0\123\0\145\0\143\0\165\0\162\0\151\0\164\0\171\0\40\0\164\0\150\0\162\0\157\0\165\0\147\0\150\0\40\0\117\0\142\0\163\0\143\0\165\0\162\0\151\0\164\0\171) + /Parent 1387 0 R + /Prev 1389 0 R + /Next 1391 0 R + /A 383 0 R +>> endobj +1391 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\64\0\40\0\120\0\162\0\151\0\166\0\141\0\143\0\171\0\40\0\111\0\163\0\163\0\165\0\145\0\163\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\145\0\144\0\40\0\164\0\157\0\40\0\114\0\157\0\143\0\153\0\163) + /Parent 1387 0 R + /Prev 1390 0 R + /Next 1392 0 R + /A 385 0 R +>> endobj +1392 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\65\0\40\0\120\0\162\0\151\0\166\0\141\0\143\0\171\0\40\0\111\0\163\0\163\0\165\0\145\0\163\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\145\0\144\0\40\0\164\0\157\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1387 0 R + /Prev 1391 0 R + /Next 1393 0 R + /A 387 0 R +>> endobj +1393 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\66\0\40\0\111\0\155\0\160\0\154\0\151\0\143\0\141\0\164\0\151\0\157\0\156\0\163\0\40\0\157\0\146\0\40\0\130\0\115\0\114\0\40\0\105\0\156\0\164\0\151\0\164\0\151\0\145\0\163) + /Parent 1387 0 R + /Prev 1392 0 R + /Next 1395 0 R + /A 389 0 R +>> endobj +1395 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\67\0\40\0\122\0\151\0\163\0\153\0\163\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\145\0\144\0\40\0\167\0\151\0\164\0\150\0\40\0\114\0\157\0\143\0\153\0\40\0\124\0\157\0\153\0\145\0\156\0\163) + /Parent 1387 0 R + /Prev 1393 0 R + /Next 1396 0 R + /A 1394 0 R +>> endobj +1396 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\70\0\40\0\110\0\157\0\163\0\164\0\151\0\156\0\147\0\40\0\115\0\141\0\154\0\151\0\143\0\151\0\157\0\165\0\163\0\40\0\103\0\157\0\156\0\164\0\145\0\156\0\164) + /Parent 1387 0 R + /Prev 1395 0 R + /A 393 0 R +>> endobj +1397 0 obj +<< + /Title (\376\377\0\62\0\61\0\40\0\111\0\101\0\116\0\101\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 1089 0 R + /First 1398 0 R + /Last 1408 0 R + /Prev 1387 0 R + /Next 1409 0 R + /Count -11 + /A 395 0 R +>> endobj +1398 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\61\0\40\0\116\0\145\0\167\0\40\0\125\0\122\0\111\0\40\0\123\0\143\0\150\0\145\0\155\0\145\0\163) + /Parent 1397 0 R + /Next 1399 0 R + /A 397 0 R +>> endobj +1399 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\62\0\40\0\130\0\115\0\114\0\40\0\116\0\141\0\155\0\145\0\163\0\160\0\141\0\143\0\145\0\163) + /Parent 1397 0 R + /Prev 1398 0 R + /Next 1400 0 R + /A 399 0 R +>> endobj +1400 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\40\0\115\0\145\0\163\0\163\0\141\0\147\0\145\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\40\0\106\0\151\0\145\0\154\0\144\0\163) + /Parent 1397 0 R + /First 1401 0 R + /Last 1407 0 R + /Prev 1399 0 R + /Next 1408 0 R + /Count -7 + /A 401 0 R +>> endobj +1401 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\56\0\61\0\40\0\104\0\101\0\126) + /Parent 1400 0 R + /Next 1402 0 R + /A 403 0 R +>> endobj +1402 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\56\0\62\0\40\0\104\0\145\0\160\0\164\0\150) + /Parent 1400 0 R + /Prev 1401 0 R + /Next 1403 0 R + /A 405 0 R +>> endobj +1403 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\56\0\63\0\40\0\104\0\145\0\163\0\164\0\151\0\156\0\141\0\164\0\151\0\157\0\156) + /Parent 1400 0 R + /Prev 1402 0 R + /Next 1404 0 R + /A 407 0 R +>> endobj +1404 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\56\0\64\0\40\0\111\0\146) + /Parent 1400 0 R + /Prev 1403 0 R + /Next 1405 0 R + /A 409 0 R +>> endobj +1405 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\56\0\65\0\40\0\114\0\157\0\143\0\153\0\55\0\124\0\157\0\153\0\145\0\156) + /Parent 1400 0 R + /Prev 1404 0 R + /Next 1406 0 R + /A 411 0 R +>> endobj +1406 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\56\0\66\0\40\0\117\0\166\0\145\0\162\0\167\0\162\0\151\0\164\0\145) + /Parent 1400 0 R + /Prev 1405 0 R + /Next 1407 0 R + /A 413 0 R +>> endobj +1407 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\63\0\56\0\67\0\40\0\124\0\151\0\155\0\145\0\157\0\165\0\164) + /Parent 1400 0 R + /Prev 1406 0 R + /A 415 0 R +>> endobj +1408 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\64\0\40\0\110\0\124\0\124\0\120\0\40\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\103\0\157\0\144\0\145\0\163) + /Parent 1397 0 R + /Prev 1400 0 R + /A 417 0 R +>> endobj +1409 0 obj +<< + /Title (\376\377\0\62\0\62\0\40\0\101\0\143\0\153\0\156\0\157\0\167\0\154\0\145\0\144\0\147\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 1089 0 R + /Prev 1397 0 R + /Next 1410 0 R + /A 419 0 R +>> endobj +1410 0 obj +<< + /Title (\376\377\0\62\0\63\0\40\0\103\0\157\0\156\0\164\0\162\0\151\0\142\0\165\0\164\0\157\0\162\0\163\0\40\0\164\0\157\0\40\0\124\0\150\0\151\0\163\0\40\0\123\0\160\0\145\0\143\0\151\0\146\0\151\0\143\0\141\0\164\0\151\0\157\0\156) + /Parent 1089 0 R + /Prev 1409 0 R + /Next 1411 0 R + /A 421 0 R +>> endobj +1411 0 obj +<< + /Title (\376\377\0\62\0\64\0\40\0\101\0\165\0\164\0\150\0\157\0\162\0\163\0\40\0\157\0\146\0\40\0\122\0\106\0\103\0\40\0\62\0\65\0\61\0\70) + /Parent 1089 0 R + /Prev 1410 0 R + /Next 1412 0 R + /A 423 0 R +>> endobj +1412 0 obj +<< + /Title (\376\377\0\62\0\65\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 1089 0 R + /First 1413 0 R + /Last 1414 0 R + /Prev 1411 0 R + /Next 1415 0 R + /Count -2 + /A 425 0 R +>> endobj +1413 0 obj +<< + /Title (\376\377\0\62\0\65\0\56\0\61\0\40\0\116\0\157\0\162\0\155\0\141\0\164\0\151\0\166\0\145\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 1412 0 R + /Next 1414 0 R + /A 427 0 R +>> endobj +1414 0 obj +<< + /Title (\376\377\0\62\0\65\0\56\0\62\0\40\0\111\0\156\0\146\0\157\0\162\0\155\0\141\0\164\0\151\0\166\0\145\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 1412 0 R + /Prev 1413 0 R + /A 429 0 R +>> endobj +1415 0 obj +<< + /Title (\376\377\0\101\0\165\0\164\0\150\0\157\0\162\0\47\0\163\0\40\0\101\0\144\0\144\0\162\0\145\0\163\0\163) + /Parent 1089 0 R + /Prev 1412 0 R + /Next 1417 0 R + /A 431 0 R +>> endobj +1417 0 obj +<< + /Title (\376\377\0\101\0\40\0\116\0\157\0\164\0\145\0\163\0\40\0\157\0\156\0\40\0\120\0\162\0\157\0\143\0\145\0\163\0\163\0\151\0\156\0\147\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 1089 0 R + /First 1418 0 R + /Last 1421 0 R + /Prev 1415 0 R + /Next 1422 0 R + /Count -4 + /A 1416 0 R +>> endobj +1418 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\40\0\116\0\157\0\164\0\145\0\163\0\40\0\157\0\156\0\40\0\105\0\155\0\160\0\164\0\171\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 1417 0 R + /Next 1419 0 R + /A 435 0 R +>> endobj +1419 0 obj +<< + /Title (\376\377\0\101\0\56\0\62\0\40\0\116\0\157\0\164\0\145\0\163\0\40\0\157\0\156\0\40\0\111\0\154\0\154\0\145\0\147\0\141\0\154\0\40\0\130\0\115\0\114\0\40\0\120\0\162\0\157\0\143\0\145\0\163\0\163\0\151\0\156\0\147) + /Parent 1417 0 R + /Prev 1418 0 R + /Next 1420 0 R + /A 437 0 R +>> endobj +1420 0 obj +<< + /Title (\376\377\0\101\0\56\0\63\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\130\0\115\0\114\0\40\0\123\0\171\0\156\0\164\0\141\0\170\0\40\0\105\0\162\0\162\0\157\0\162) + /Parent 1417 0 R + /Prev 1419 0 R + /Next 1421 0 R + /A 439 0 R +>> endobj +1421 0 obj +<< + /Title (\376\377\0\101\0\56\0\64\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\156\0\145\0\170\0\160\0\145\0\143\0\164\0\145\0\144\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 1417 0 R + /Prev 1420 0 R + /A 441 0 R +>> endobj +1422 0 obj +<< + /Title (\376\377\0\102\0\40\0\116\0\157\0\164\0\145\0\163\0\40\0\157\0\156\0\40\0\110\0\124\0\124\0\120\0\40\0\103\0\154\0\151\0\145\0\156\0\164\0\40\0\103\0\157\0\155\0\160\0\141\0\164\0\151\0\142\0\151\0\154\0\151\0\164\0\171) + /Parent 1089 0 R + /Prev 1417 0 R + /Next 1424 0 R + /A 443 0 R +>> endobj +1424 0 obj +<< + /Title (\376\377\0\103\0\40\0\124\0\150\0\145\0\40\0\47\0\157\0\160\0\141\0\161\0\165\0\145\0\154\0\157\0\143\0\153\0\164\0\157\0\153\0\145\0\156\0\47\0\40\0\123\0\143\0\150\0\145\0\155\0\145\0\40\0\141\0\156\0\144\0\40\0\125\0\122\0\111\0\163) + /Parent 1089 0 R + /Prev 1422 0 R + /Next 1426 0 R + /A 1423 0 R +>> endobj +1426 0 obj +<< + /Title (\376\377\0\104\0\40\0\114\0\157\0\143\0\153\0\55\0\156\0\165\0\154\0\154\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1089 0 R + /First 1427 0 R + /Last 1427 0 R + /Prev 1424 0 R + /Next 1428 0 R + /Count -1 + /A 1425 0 R +>> endobj +1427 0 obj +<< + /Title (\376\377\0\104\0\56\0\61\0\40\0\107\0\165\0\151\0\144\0\141\0\156\0\143\0\145\0\40\0\146\0\157\0\162\0\40\0\103\0\154\0\151\0\145\0\156\0\164\0\163\0\40\0\125\0\163\0\151\0\156\0\147\0\40\0\114\0\117\0\103\0\113\0\40\0\164\0\157\0\40\0\103\0\162\0\145\0\141\0\164\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 1426 0 R + /A 452 0 R +>> endobj +1428 0 obj +<< + /Title (\376\377\0\105\0\40\0\107\0\165\0\151\0\144\0\141\0\156\0\143\0\145\0\40\0\146\0\157\0\162\0\40\0\103\0\154\0\151\0\145\0\156\0\164\0\163\0\40\0\104\0\145\0\163\0\151\0\162\0\151\0\156\0\147\0\40\0\164\0\157\0\40\0\101\0\165\0\164\0\150\0\145\0\156\0\164\0\151\0\143\0\141\0\164\0\145) + /Parent 1089 0 R + /Prev 1426 0 R + /Next 1429 0 R + /A 454 0 R +>> endobj +1429 0 obj +<< + /Title (\376\377\0\106\0\40\0\123\0\165\0\155\0\155\0\141\0\162\0\171\0\40\0\157\0\146\0\40\0\103\0\150\0\141\0\156\0\147\0\145\0\163\0\40\0\146\0\162\0\157\0\155\0\40\0\122\0\106\0\103\0\40\0\62\0\65\0\61\0\70) + /Parent 1089 0 R + /First 1430 0 R + /Last 1432 0 R + /Prev 1428 0 R + /Next 1433 0 R + /Count -3 + /A 456 0 R +>> endobj +1430 0 obj +<< + /Title (\376\377\0\106\0\56\0\61\0\40\0\103\0\150\0\141\0\156\0\147\0\145\0\163\0\40\0\146\0\157\0\162\0\40\0\102\0\157\0\164\0\150\0\40\0\103\0\154\0\151\0\145\0\156\0\164\0\40\0\141\0\156\0\144\0\40\0\123\0\145\0\162\0\166\0\145\0\162\0\40\0\111\0\155\0\160\0\154\0\145\0\155\0\145\0\156\0\164\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 1429 0 R + /Next 1431 0 R + /A 458 0 R +>> endobj +1431 0 obj +<< + /Title (\376\377\0\106\0\56\0\62\0\40\0\103\0\150\0\141\0\156\0\147\0\145\0\163\0\40\0\146\0\157\0\162\0\40\0\123\0\145\0\162\0\166\0\145\0\162\0\40\0\111\0\155\0\160\0\154\0\145\0\155\0\145\0\156\0\164\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 1429 0 R + /Prev 1430 0 R + /Next 1432 0 R + /A 460 0 R +>> endobj +1432 0 obj +<< + /Title (\376\377\0\106\0\56\0\63\0\40\0\117\0\164\0\150\0\145\0\162\0\40\0\103\0\150\0\141\0\156\0\147\0\145\0\163) + /Parent 1429 0 R + /Prev 1431 0 R + /A 462 0 R +>> endobj +1433 0 obj +<< + /Title (\376\377\0\111\0\156\0\164\0\145\0\154\0\154\0\145\0\143\0\164\0\165\0\141\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\141\0\156\0\144\0\40\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\123\0\164\0\141\0\164\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 1089 0 R + /Prev 1429 0 R + /Next 1434 0 R + /A 464 0 R +>> endobj +1434 0 obj +<< + /Title (\376\377\0\111\0\156\0\144\0\145\0\170) + /Parent 1089 0 R + /Prev 1433 0 R + /A 466 0 R +>> endobj +1435 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F5 +/BaseFont /Times-Roman +/Encoding /WinAnsiEncoding >> +endobj +1436 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F6 +/BaseFont /Times-Italic +/Encoding /WinAnsiEncoding >> +endobj +1437 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F9 +/BaseFont /Courier +/Encoding /WinAnsiEncoding >> +endobj +1438 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F7 +/BaseFont /Times-Bold +/Encoding /WinAnsiEncoding >> +endobj +1 0 obj +<< /Type /Pages +/Count 104 +/Kids [6 0 R 10 0 R 87 0 R 192 0 R 281 0 R 364 0 R 445 0 R 468 0 R 495 0 R 503 0 R 510 0 R 515 0 R 517 0 R 521 0 R 523 0 R 528 0 R 530 0 R 537 0 R 543 0 R 551 0 R 553 0 R 557 0 R 561 0 R 568 0 R 572 0 R 576 0 R 587 0 R 594 0 R 600 0 R 607 0 R 612 0 R 614 0 R 616 0 R 620 0 R 627 0 R 632 0 R 636 0 R 641 0 R 643 0 R 647 0 R 652 0 R 654 0 R 656 0 R 661 0 R 663 0 R 667 0 R 669 0 R 671 0 R 673 0 R 675 0 R 679 0 R 681 0 R 686 0 R 693 0 R 701 0 R 705 0 R 709 0 R 715 0 R 720 0 R 722 0 R 736 0 R 752 0 R 762 0 R 779 0 R 789 0 R 808 0 R 823 0 R 831 0 R 840 0 R 850 0 R 865 0 R 873 0 R 877 0 R 883 0 R 885 0 R 893 0 R 897 0 R 902 0 R 907 0 R 920 0 R 927 0 R 935 0 R 939 0 R 948 0 R 958 0 R 966 0 R 968 0 R 970 0 R 972 0 R 1003 0 R 1008 0 R 1012 0 R 1014 0 R 1018 0 R 1020 0 R 1026 0 R 1031 0 R 1036 0 R 1054 0 R 1073 0 R 1075 0 R 1081 0 R 1084 0 R 1087 0 R ] >> +endobj +2 0 obj +<< /Type /Catalog +/Pages 1 0 R + /Outlines 1089 0 R + /PageMode /UseOutlines + /Names << /Dests << /Names [ (rfc.status) [ 6 0 R /XYZ 67.0 520.084 null ] (rfc.copyrightnotice) [ 6 0 R /XYZ 67.0 429.95 null ] (rfc.abstract) [ 6 0 R /XYZ 67.0 372.816 null ] (rfc.toc) [ 10 0 R /XYZ 67.0 725.0 null ] (rfc.section.1) [ 468 0 R /XYZ 67.0 725.0 null ] (intro) [ 468 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2291.1) [ 468 0 R /XYZ 67.0 504.866 null ] (rfc.xref.RFC2291.2) [ 468 0 R /XYZ 67.0 483.866 null ] (rfc.xref.RFC3253.1) [ 468 0 R /XYZ 67.0 472.866 null ] (rfc.xref.REC-XML.1) [ 468 0 R /XYZ 67.0 278.866 null ] (rfc.section.2) [ 495 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2616.1) [ 495 0 R /XYZ 67.0 686.866 null ] (rfc.xref.RFC2616.2) [ 495 0 R /XYZ 67.0 664.866 null ] (rfc.xref.RFC2119.1) [ 495 0 R /XYZ 67.0 621.866 null ] (rfc.section.3) [ 503 0 R /XYZ 67.0 725.0 null ] (rfc.iref.1) [ 503 0 R /XYZ 67.0 697.866 null ] (rfc.iref.2) [ 503 0 R /XYZ 67.0 697.866 null ] (rfc.xref.RFC3986.1) [ 503 0 R /XYZ 67.0 686.866 null ] (rfc.iref.3) [ 503 0 R /XYZ 67.0 665.866 null ] (rfc.iref.4) [ 503 0 R /XYZ 67.0 665.866 null ] (rfc.iref.5) [ 503 0 R /XYZ 67.0 611.866 null ] (rfc.xref.RFC3986.2) [ 503 0 R /XYZ 67.0 600.866 null ] (rfc.iref.6) [ 503 0 R /XYZ 67.0 579.866 null ] (rfc.iref.7) [ 503 0 R /XYZ 67.0 536.866 null ] (rfc.iref.8) [ 503 0 R /XYZ 67.0 504.866 null ] (rfc.iref.9) [ 503 0 R /XYZ 67.0 472.866 null ] (rfc.iref.10) [ 503 0 R /XYZ 67.0 440.866 null ] (rfc.iref.11) [ 503 0 R /XYZ 67.0 408.866 null ] (rfc.iref.12) [ 503 0 R /XYZ 67.0 387.866 null ] (rfc.iref.13) [ 503 0 R /XYZ 67.0 344.866 null ] (rfc.iref.14) [ 503 0 R /XYZ 67.0 301.866 null ] (rfc.iref.15) [ 503 0 R /XYZ 67.0 280.866 null ] (rfc.section.4) [ 510 0 R /XYZ 67.0 725.0 null ] (data.model.for.resource.properties) [ 510 0 R /XYZ 67.0 725.0 null ] (rfc.section.4.1) [ 510 0 R /XYZ 67.0 690.866 null ] (rfc.section.4.2) [ 510 0 R /XYZ 67.0 487.894 null ] (rfc.section.4.3) [ 510 0 R /XYZ 67.0 391.922 null ] (property_values) [ 510 0 R /XYZ 67.0 391.922 null ] (rfc.figure.u.1) [ 510 0 R /XYZ 67.0 151.95 null ] (rfc.xref.REC-XML-INFOSET.1) [ 510 0 R /XYZ 67.0 99.09 null ] (rfc.section.4.3.1) [ 515 0 R /XYZ 67.0 260.0 null ] (rfc.figure.u.2) [ 515 0 R /XYZ 67.0 219.109 null ] (rfc.figure.u.3) [ 517 0 R /XYZ 67.0 673.14 null ] (rfc.section.4.4) [ 517 0 R /XYZ 67.0 262.38 null ] (rfc.xref.RFC3986.3) [ 517 0 R /XYZ 67.0 163.408 null ] (rfc.section.4.5) [ 521 0 R /XYZ 67.0 681.0 null ] (rfc.section.5) [ 523 0 R /XYZ 67.0 725.0 null ] (collections.of.web.resources) [ 523 0 R /XYZ 67.0 725.0 null ] (rfc.section.5.1) [ 523 0 R /XYZ 67.0 626.866 null ] (http.url.namespace.model) [ 523 0 R /XYZ 67.0 626.866 null ] (rfc.xref.RFC2616.3) [ 523 0 R /XYZ 67.0 473.894 null ] (rfc.xref.RFC3986.4) [ 523 0 R /XYZ 67.0 473.894 null ] (rfc.section.5.2) [ 523 0 R /XYZ 67.0 434.894 null ] (collection.resources) [ 523 0 R /XYZ 67.0 434.894 null ] (rfc.section.6) [ 530 0 R /XYZ 67.0 725.0 null ] (locking) [ 530 0 R /XYZ 67.0 725.0 null ] (rfc.section.6.1) [ 530 0 R /XYZ 67.0 593.866 null ] (lock-model) [ 530 0 R /XYZ 67.0 593.866 null ] (rfc.section.6.2) [ 530 0 R /XYZ 67.0 239.894 null ] (exclusive-lock) [ 530 0 R /XYZ 67.0 239.894 null ] (rfc.section.6.3) [ 537 0 R /XYZ 67.0 442.0 null ] (rfc.section.6.4) [ 537 0 R /XYZ 67.0 303.028 null ] (lock-creator) [ 537 0 R /XYZ 67.0 303.028 null ] (rfc.xref.RFC3744.1) [ 537 0 R /XYZ 67.0 225.056 null ] (rfc.section.6.5) [ 537 0 R /XYZ 67.0 133.056 null ] (lock-tokens) [ 537 0 R /XYZ 67.0 133.056 null ] (rfc.xref.RFC4122.1) [ 543 0 R /XYZ 67.0 580.0 null ] (rfc.section.6.6) [ 543 0 R /XYZ 67.0 498.0 null ] (lock-timeout) [ 543 0 R /XYZ 67.0 498.0 null ] (rfc.section.6.7) [ 543 0 R /XYZ 67.0 176.028 null ] (rfc.section.6.8) [ 551 0 R /XYZ 67.0 692.0 null ] (rfc.section.7) [ 553 0 R /XYZ 67.0 725.0 null ] (write-lock) [ 553 0 R /XYZ 67.0 725.0 null ] (rfc.section.7.1) [ 553 0 R /XYZ 67.0 391.866 null ] (rfc.section.7.2) [ 553 0 R /XYZ 67.0 317.894 null ] (rfc.section.7.3) [ 557 0 R /XYZ 67.0 574.0 null ] (lock-unmapped-urls) [ 557 0 R /XYZ 67.0 574.0 null ] (rfc.xref.RFC2616.4) [ 557 0 R /XYZ 67.0 528.028 null ] (rfc.xref.RFC2518.1) [ 561 0 R /XYZ 67.0 720.0 null ] (rfc.section.7.4) [ 561 0 R /XYZ 67.0 659.0 null ] (write.locks.and.collections) [ 561 0 R /XYZ 67.0 659.0 null ] (rfc.section.7.5) [ 561 0 R /XYZ 67.0 119.028 null ] (write.locks.and.the.if.request.header) [ 561 0 R /XYZ 67.0 119.028 null ] (rfc.section.7.5.1) [ 568 0 R /XYZ 67.0 562.0 null ] (rfc.figure.u.4) [ 568 0 R /XYZ 67.0 542.109 null ] (rfc.figure.u.5) [ 568 0 R /XYZ 67.0 455.809 null ] (rfc.section.7.5.2) [ 568 0 R /XYZ 67.0 348.949 null ] (rfc.figure.u.6) [ 568 0 R /XYZ 67.0 297.058 null ] (rfc.figure.u.7) [ 568 0 R /XYZ 67.0 240.338 null ] (rfc.figure.u.8) [ 572 0 R /XYZ 67.0 699.0 null ] (rfc.figure.u.9) [ 572 0 R /XYZ 67.0 647.14 null ] (rfc.figure.u.10) [ 572 0 R /XYZ 67.0 585.42 null ] (rfc.section.7.6) [ 572 0 R /XYZ 67.0 505.7 null ] (rfc.section.7.7) [ 572 0 R /XYZ 67.0 333.728 null ] (rfc.section.8) [ 576 0 R /XYZ 67.0 725.0 null ] (response-handling) [ 576 0 R /XYZ 67.0 725.0 null ] (rfc.section.8.1) [ 576 0 R /XYZ 67.0 690.866 null ] (error-precedence) [ 576 0 R /XYZ 67.0 690.866 null ] (rfc.section.8.2) [ 576 0 R /XYZ 67.0 616.894 null ] (rfc.xref.REC-XML.2) [ 576 0 R /XYZ 67.0 581.922 null ] (rfc.xref.REC-XML.3) [ 576 0 R /XYZ 67.0 452.922 null ] (rfc.xref.REC-XML-NAMES.1) [ 576 0 R /XYZ 67.0 452.922 null ] (rfc.section.8.3) [ 576 0 R /XYZ 67.0 315.922 null ] (url-handling) [ 576 0 R /XYZ 67.0 315.922 null ] (rfc.xref.RFC2518.2) [ 576 0 R /XYZ 67.0 291.95 null ] (rfc.xref.RFC3986.5) [ 576 0 R /XYZ 67.0 269.95 null ] (rfc.figure.u.11) [ 576 0 R /XYZ 67.0 151.95 null ] (rfc.xref.RFC3986.6) [ 576 0 R /XYZ 67.0 122.09 null ] (rfc.xref.RFC3986.7) [ 576 0 R /XYZ 67.0 122.09 null ] (rfc.xref.RFC3986.8) [ 576 0 R /XYZ 67.0 122.09 null ] (rfc.xref.RFC3986.9) [ 576 0 R /XYZ 67.0 122.09 null ] (rfc.xref.RFC2616.5) [ 587 0 R /XYZ 67.0 706.5 null ] (rfc.section.8.3.1) [ 587 0 R /XYZ 67.0 645.0 null ] (rfc.figure.u.12) [ 587 0 R /XYZ 67.0 593.109 null ] (rfc.xref.RFC3986.10) [ 587 0 R /XYZ 67.0 462.529 null ] (rfc.section.8.4) [ 587 0 R /XYZ 67.0 412.529 null ] (rfc.section.8.5) [ 587 0 R /XYZ 67.0 327.557 null ] (http-headers) [ 587 0 R /XYZ 67.0 327.557 null ] (rfc.xref.RFC2616.6) [ 587 0 R /XYZ 67.0 281.585 null ] (rfc.xref.RFC2616.7) [ 587 0 R /XYZ 67.0 281.585 null ] (rfc.section.8.6) [ 587 0 R /XYZ 67.0 232.585 null ] (etag) [ 587 0 R /XYZ 67.0 232.585 null ] (rfc.xref.RFC2616.8) [ 587 0 R /XYZ 67.0 99.613 null ] (rfc.section.8.7) [ 594 0 R /XYZ 67.0 556.0 null ] (including.error.reponse.bodies) [ 594 0 R /XYZ 67.0 556.0 null ] (rfc.xref.RFC3253.2) [ 594 0 R /XYZ 67.0 510.028 null ] (rfc.section.8.8) [ 594 0 R /XYZ 67.0 438.028 null ] (cache-control) [ 594 0 R /XYZ 67.0 438.028 null ] (rfc.xref.RFC2616.9) [ 594 0 R /XYZ 67.0 414.056 null ] (rfc.xref.RFC2616.10) [ 594 0 R /XYZ 67.0 414.056 null ] (rfc.xref.RFC2616.11) [ 594 0 R /XYZ 67.0 414.056 null ] (rfc.section.9) [ 600 0 R /XYZ 67.0 725.0 null ] (http.methods.for.distributed.authoring) [ 600 0 R /XYZ 67.0 725.0 null ] (rfc.section.9.1) [ 600 0 R /XYZ 67.0 690.866 null ] (METHOD_PROPFIND) [ 600 0 R /XYZ 67.0 690.866 null ] (rfc.xref.RFC3253.3) [ 600 0 R /XYZ 67.0 370.894 null ] (rfc.xref.RFC3744.2) [ 600 0 R /XYZ 67.0 370.894 null ] (rfc.xref.RFC2616.12) [ 600 0 R /XYZ 67.0 100.894 null ] (rfc.section.9.1.1) [ 600 0 R /XYZ 67.0 73.894 null ] (rfc.section.9.1.2) [ 607 0 R /XYZ 67.0 635.109 null ] (PROPFIND-multistatus) [ 607 0 R /XYZ 67.0 635.109 null ] (rfc.section.9.1.3) [ 607 0 R /XYZ 67.0 471.218 null ] (rfc.figure.u.13) [ 607 0 R /XYZ 67.0 451.327 null ] (rfc.figure.u.14) [ 607 0 R /XYZ 67.0 276.287 null ] (rfc.section.9.1.4) [ 612 0 R /XYZ 67.0 525.68 null ] (rfc.figure.u.15) [ 612 0 R /XYZ 67.0 505.789 null ] (rfc.figure.u.16) [ 612 0 R /XYZ 67.0 380.049 null ] (rfc.section.9.1.5) [ 614 0 R /XYZ 67.0 394.4 null ] (rfc.figure.u.17) [ 614 0 R /XYZ 67.0 342.509 null ] (rfc.figure.u.18) [ 614 0 R /XYZ 67.0 206.909 null ] (rfc.section.9.1.6) [ 620 0 R /XYZ 67.0 547.0 null ] (rfc.figure.u.19) [ 620 0 R /XYZ 67.0 527.109 null ] (rfc.xref.RFC3253.4) [ 620 0 R /XYZ 67.0 330.069 null ] (rfc.section.9.2) [ 620 0 R /XYZ 67.0 302.069 null ] (METHOD_PROPPATCH) [ 620 0 R /XYZ 67.0 302.069 null ] (rfc.xref.RFC2616.13) [ 627 0 R /XYZ 67.0 720.0 null ] (rfc.section.9.2.1) [ 627 0 R /XYZ 67.0 682.0 null ] (PROPPATCH-status) [ 627 0 R /XYZ 67.0 682.0 null ] (rfc.section.9.2.2) [ 627 0 R /XYZ 67.0 432.109 null ] (rfc.figure.u.20) [ 627 0 R /XYZ 67.0 412.218 null ] (rfc.figure.u.21) [ 627 0 R /XYZ 67.0 178.018 null ] (rfc.section.9.3) [ 632 0 R /XYZ 67.0 503.82 null ] (METHOD_MKCOL) [ 632 0 R /XYZ 67.0 503.82 null ] (rfc.xref.RFC2616.14) [ 632 0 R /XYZ 67.0 306.848 null ] (rfc.section.9.3.1) [ 632 0 R /XYZ 67.0 268.848 null ] (rfc.section.9.3.2) [ 636 0 R /XYZ 67.0 650.0 null ] (rfc.figure.u.22) [ 636 0 R /XYZ 67.0 609.109 null ] (rfc.figure.u.23) [ 636 0 R /XYZ 67.0 552.389 null ] (rfc.section.9.4) [ 636 0 R /XYZ 67.0 498.529 null ] (rfc.xref.RFC2616.15) [ 636 0 R /XYZ 67.0 463.557 null ] (rfc.section.9.5) [ 636 0 R /XYZ 67.0 370.557 null ] (METHOD_POST) [ 636 0 R /XYZ 67.0 370.557 null ] (rfc.section.9.6) [ 636 0 R /XYZ 67.0 296.585 null ] (METHOD_DELETE) [ 636 0 R /XYZ 67.0 296.585 null ] (rfc.xref.RFC2616.16) [ 636 0 R /XYZ 67.0 272.613 null ] (rfc.section.9.6.1) [ 636 0 R /XYZ 67.0 159.613 null ] (delete-collections) [ 636 0 R /XYZ 67.0 159.613 null ] (rfc.section.9.6.2) [ 641 0 R /XYZ 67.0 489.0 null ] (DELETE-example) [ 641 0 R /XYZ 67.0 489.0 null ] (rfc.figure.u.24) [ 641 0 R /XYZ 67.0 469.109 null ] (rfc.figure.u.25) [ 641 0 R /XYZ 67.0 412.389 null ] (rfc.section.9.7) [ 641 0 R /XYZ 67.0 174.069 null ] (METHOD_PUT) [ 641 0 R /XYZ 67.0 174.069 null ] (rfc.section.9.7.1) [ 641 0 R /XYZ 67.0 144.097 null ] (put-resources) [ 641 0 R /XYZ 67.0 144.097 null ] (rfc.section.9.7.2) [ 643 0 R /XYZ 67.0 563.0 null ] (rfc.section.9.8) [ 643 0 R /XYZ 67.0 483.109 null ] (METHOD_COPY) [ 643 0 R /XYZ 67.0 483.109 null ] (rfc.xref.RFC2616.17) [ 643 0 R /XYZ 67.0 362.137 null ] (rfc.section.9.8.1) [ 643 0 R /XYZ 67.0 324.137 null ] (rfc.section.9.8.2) [ 643 0 R /XYZ 67.0 211.246 null ] (copy.for.properties) [ 643 0 R /XYZ 67.0 211.246 null ] (rfc.section.9.8.3) [ 647 0 R /XYZ 67.0 682.0 null ] (copy.for.collections) [ 647 0 R /XYZ 67.0 682.0 null ] (rfc.section.9.8.4) [ 647 0 R /XYZ 67.0 181.109 null ] (rfc.xref.RFC3253.5) [ 647 0 R /XYZ 67.0 118.218 null ] (rfc.section.9.8.5) [ 652 0 R /XYZ 67.0 617.0 null ] (rfc.section.9.8.6) [ 652 0 R /XYZ 67.0 249.109 null ] (rfc.figure.u.26) [ 652 0 R /XYZ 67.0 186.218 null ] (rfc.figure.u.27) [ 652 0 R /XYZ 67.0 119.638 null ] (rfc.section.9.8.7) [ 654 0 R /XYZ 67.0 709.0 null ] (rfc.figure.u.28) [ 654 0 R /XYZ 67.0 646.109 null ] (rfc.figure.u.29) [ 654 0 R /XYZ 67.0 569.669 null ] (rfc.section.9.8.8) [ 654 0 R /XYZ 67.0 516.809 null ] (rfc.figure.u.30) [ 654 0 R /XYZ 67.0 496.918 null ] (rfc.figure.u.31) [ 654 0 R /XYZ 67.0 420.478 null ] (rfc.section.9.9) [ 654 0 R /XYZ 67.0 183.298 null ] (METHOD_MOVE) [ 654 0 R /XYZ 67.0 183.298 null ] (rfc.xref.RFC2616.18) [ 656 0 R /XYZ 67.0 624.0 null ] (rfc.section.9.9.1) [ 656 0 R /XYZ 67.0 586.0 null ] (move-properties) [ 656 0 R /XYZ 67.0 586.0 null ] (rfc.section.9.9.2) [ 656 0 R /XYZ 67.0 420.109 null ] (move-collections) [ 656 0 R /XYZ 67.0 420.109 null ] (rfc.section.9.9.3) [ 661 0 R /XYZ 67.0 682.0 null ] (rfc.section.9.9.4) [ 661 0 R /XYZ 67.0 613.109 null ] (rfc.section.9.9.5) [ 661 0 R /XYZ 67.0 244.218 null ] (rfc.figure.u.32) [ 661 0 R /XYZ 67.0 170.327 null ] (rfc.figure.u.33) [ 661 0 R /XYZ 67.0 103.747 null ] (rfc.section.9.9.6) [ 663 0 R /XYZ 67.0 678.28 null ] (rfc.figure.u.34) [ 663 0 R /XYZ 67.0 658.389 null ] (rfc.figure.u.35) [ 663 0 R /XYZ 67.0 562.229 null ] (rfc.section.9.10) [ 663 0 R /XYZ 67.0 317.909 null ] (METHOD_LOCK) [ 663 0 R /XYZ 67.0 317.909 null ] (rfc.xref.RFC2616.19) [ 663 0 R /XYZ 67.0 218.937 null ] (rfc.section.9.10.1) [ 663 0 R /XYZ 67.0 180.937 null ] (rfc.section.9.10.2) [ 667 0 R /XYZ 67.0 650.0 null ] (refreshing-locks) [ 667 0 R /XYZ 67.0 650.0 null ] (rfc.section.9.10.3) [ 667 0 R /XYZ 67.0 495.109 null ] (rfc.section.9.10.4) [ 667 0 R /XYZ 67.0 243.218 null ] (rfc.section.9.10.5) [ 667 0 R /XYZ 67.0 152.327 null ] (rfc.table.u.1) [ 667 0 R /XYZ 67.0 111.436 null ] (rfc.section.9.10.6) [ 669 0 R /XYZ 67.0 582.69 null ] (rfc.section.9.10.7) [ 669 0 R /XYZ 67.0 353.799 null ] (rfc.figure.u.36) [ 669 0 R /XYZ 67.0 333.908 null ] (rfc.figure.u.37) [ 669 0 R /XYZ 67.0 119.428 null ] (rfc.section.9.10.8) [ 671 0 R /XYZ 67.0 376.64 null ] (rfc.figure.u.38) [ 671 0 R /XYZ 67.0 356.749 null ] (rfc.figure.u.39) [ 671 0 R /XYZ 67.0 240.869 null ] (rfc.section.9.10.9) [ 673 0 R /XYZ 67.0 541.68 null ] (rfc.figure.u.40) [ 673 0 R /XYZ 67.0 521.789 null ] (rfc.figure.u.41) [ 673 0 R /XYZ 67.0 297.449 null ] (rfc.section.9.11) [ 675 0 R /XYZ 67.0 617.0 null ] (METHOD_UNLOCK) [ 675 0 R /XYZ 67.0 617.0 null ] (rfc.xref.RFC2616.20) [ 675 0 R /XYZ 67.0 412.028 null ] (rfc.section.9.11.1) [ 675 0 R /XYZ 67.0 374.028 null ] (rfc.section.9.11.2) [ 675 0 R /XYZ 67.0 210.137 null ] (rfc.figure.u.42) [ 675 0 R /XYZ 67.0 190.246 null ] (rfc.figure.u.43) [ 675 0 R /XYZ 67.0 84.226 null ] (rfc.section.10) [ 681 0 R /XYZ 67.0 725.0 null ] (http.headers.for.distributed.authoring) [ 681 0 R /XYZ 67.0 725.0 null ] (rfc.section.10.1) [ 681 0 R /XYZ 67.0 637.866 null ] (HEADER_DAV) [ 681 0 R /XYZ 67.0 637.866 null ] (rfc.figure.u.44) [ 681 0 R /XYZ 67.0 613.894 null ] (rfc.xref.RFC2616.21) [ 681 0 R /XYZ 67.0 579.314 null ] (rfc.xref.RFC3986.11) [ 681 0 R /XYZ 67.0 530.014 null ] (rfc.xref.RFC2616.22) [ 681 0 R /XYZ 67.0 400.714 null ] (rfc.section.10.2) [ 681 0 R /XYZ 67.0 221.714 null ] (HEADER_Depth) [ 681 0 R /XYZ 67.0 221.714 null ] (rfc.figure.u.45) [ 681 0 R /XYZ 67.0 197.742 null ] (rfc.section.10.3) [ 686 0 R /XYZ 67.0 445.0 null ] (HEADER_Destination) [ 686 0 R /XYZ 67.0 445.0 null ] (rfc.figure.u.46) [ 686 0 R /XYZ 67.0 389.028 null ] (rfc.xref.RFC3986.12) [ 686 0 R /XYZ 67.0 359.168 null ] (rfc.section.10.4) [ 686 0 R /XYZ 67.0 266.168 null ] (HEADER_If) [ 686 0 R /XYZ 67.0 266.168 null ] (rfc.xref.RFC2616.23) [ 686 0 R /XYZ 67.0 231.196 null ] (rfc.section.10.4.1) [ 686 0 R /XYZ 67.0 193.196 null ] (if.header.purpose) [ 686 0 R /XYZ 67.0 193.196 null ] (rfc.section.10.4.2) [ 693 0 R /XYZ 67.0 639.0 null ] (if.header.syntax) [ 693 0 R /XYZ 67.0 639.0 null ] (rfc.figure.u.47) [ 693 0 R /XYZ 67.0 619.109 null ] (rfc.xref.RFC2616.24) [ 693 0 R /XYZ 67.0 545.089 null ] (rfc.xref.RFC2616.25) [ 693 0 R /XYZ 67.0 280.349 null ] (rfc.section.10.4.3) [ 693 0 R /XYZ 67.0 253.349 null ] (if.header.evaluation) [ 693 0 R /XYZ 67.0 253.349 null ] (rfc.section.10.4.4) [ 701 0 R /XYZ 67.0 693.0 null ] (if.header.matching.function) [ 701 0 R /XYZ 67.0 693.0 null ] (rfc.xref.RFC2616.26) [ 701 0 R /XYZ 67.0 609.109 null ] (rfc.section.10.4.5) [ 701 0 R /XYZ 67.0 507.109 null ] (rfc.section.10.4.6) [ 701 0 R /XYZ 67.0 384.218 null ] (if.header.evaluation.example.no-tag) [ 701 0 R /XYZ 67.0 384.218 null ] (rfc.figure.u.48) [ 701 0 R /XYZ 67.0 364.327 null ] (rfc.figure.u.49) [ 701 0 R /XYZ 67.0 270.747 null ] (rfc.section.10.4.7) [ 701 0 R /XYZ 67.0 148.867 null ] (rfc.figure.u.50) [ 701 0 R /XYZ 67.0 128.976 null ] (rfc.section.10.4.8) [ 705 0 R /XYZ 67.0 671.0 null ] (rfc.figure.u.51) [ 705 0 R /XYZ 67.0 608.109 null ] (rfc.section.10.4.9) [ 705 0 R /XYZ 67.0 496.389 null ] (rfc.figure.u.52) [ 705 0 R /XYZ 67.0 476.498 null ] (rfc.section.10.4.10) [ 705 0 R /XYZ 67.0 309.338 null ] (rfc.figure.u.53) [ 705 0 R /XYZ 67.0 289.447 null ] (rfc.section.10.4.11) [ 705 0 R /XYZ 67.0 169.007 null ] (rfc.figure.u.54) [ 705 0 R /XYZ 67.0 128.116 null ] (rfc.figure.u.55) [ 709 0 R /XYZ 67.0 678.0 null ] (rfc.section.10.5) [ 709 0 R /XYZ 67.0 598.14 null ] (HEADER_Lock-Token) [ 709 0 R /XYZ 67.0 598.14 null ] (rfc.figure.u.56) [ 709 0 R /XYZ 67.0 574.168 null ] (rfc.section.10.6) [ 709 0 R /XYZ 67.0 462.308 null ] (HEADER_Overwrite) [ 709 0 R /XYZ 67.0 462.308 null ] (rfc.figure.u.57) [ 709 0 R /XYZ 67.0 438.336 null ] (rfc.xref.RFC2616.27) [ 709 0 R /XYZ 67.0 364.476 null ] (rfc.section.10.7) [ 709 0 R /XYZ 67.0 261.476 null ] (HEADER_Timeout) [ 709 0 R /XYZ 67.0 261.476 null ] (rfc.figure.u.58) [ 709 0 R /XYZ 67.0 237.504 null ] (rfc.section.11) [ 715 0 R /XYZ 67.0 725.0 null ] (status.code.extensions.to.http11) [ 715 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2616.28) [ 715 0 R /XYZ 67.0 697.866 null ] (rfc.section.11.1) [ 715 0 R /XYZ 67.0 669.866 null ] (STATUS_207) [ 715 0 R /XYZ 67.0 669.866 null ] (rfc.section.11.2) [ 715 0 R /XYZ 67.0 606.894 null ] (STATUS_422) [ 715 0 R /XYZ 67.0 606.894 null ] (rfc.section.11.3) [ 715 0 R /XYZ 67.0 510.922 null ] (STATUS_423) [ 715 0 R /XYZ 67.0 510.922 null ] (rfc.section.11.4) [ 715 0 R /XYZ 67.0 436.95 null ] (STATUS_424) [ 715 0 R /XYZ 67.0 436.95 null ] (rfc.section.11.5) [ 715 0 R /XYZ 67.0 351.978 null ] (STATUS_507) [ 715 0 R /XYZ 67.0 351.978 null ] (rfc.section.12) [ 720 0 R /XYZ 67.0 725.0 null ] (http-status-codes) [ 720 0 R /XYZ 67.0 725.0 null ] (rfc.section.12.1) [ 720 0 R /XYZ 67.0 625.866 null ] (rfc.section.12.2) [ 720 0 R /XYZ 67.0 540.894 null ] (rfc.section.13) [ 722 0 R /XYZ 67.0 725.0 null ] (multi-status.response) [ 722 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2616.29) [ 722 0 R /XYZ 67.0 501.366 null ] (rfc.section.13.1) [ 722 0 R /XYZ 67.0 432.866 null ] (rfc.section.13.2) [ 722 0 R /XYZ 67.0 347.894 null ] (rfc.xref.RFC2518.3) [ 722 0 R /XYZ 67.0 301.922 null ] (rfc.section.13.3) [ 722 0 R /XYZ 67.0 197.922 null ] (rfc.section.14) [ 736 0 R /XYZ 67.0 725.0 null ] (xml.element.definitions) [ 736 0 R /XYZ 67.0 725.0 null ] (rfc.xref.REC-XML.4) [ 736 0 R /XYZ 67.0 697.866 null ] (rfc.section.14.1) [ 736 0 R /XYZ 67.0 625.866 null ] (ELEMENT_activelock) [ 736 0 R /XYZ 67.0 625.866 null ] (rfc.figure.u.59) [ 736 0 R /XYZ 67.0 559.894 null ] (rfc.section.14.2) [ 736 0 R /XYZ 67.0 513.174 null ] (ELEMENT_allprop) [ 736 0 R /XYZ 67.0 513.174 null ] (rfc.figure.u.60) [ 736 0 R /XYZ 67.0 436.202 null ] (rfc.section.14.3) [ 736 0 R /XYZ 67.0 399.342 null ] (ELEMENT_collection) [ 736 0 R /XYZ 67.0 399.342 null ] (rfc.figure.u.61) [ 736 0 R /XYZ 67.0 311.37 null ] (rfc.section.14.4) [ 736 0 R /XYZ 67.0 274.51 null ] (ELEMENT_depth) [ 736 0 R /XYZ 67.0 274.51 null ] (rfc.figure.u.62) [ 736 0 R /XYZ 67.0 192.538 null ] (rfc.section.14.5) [ 736 0 R /XYZ 67.0 155.678 null ] (ELEMENT_error) [ 736 0 R /XYZ 67.0 155.678 null ] (rfc.figure.u.63) [ 752 0 R /XYZ 67.0 661.0 null ] (rfc.section.14.6) [ 752 0 R /XYZ 67.0 624.14 null ] (ELEMENT_exclusive) [ 752 0 R /XYZ 67.0 624.14 null ] (rfc.figure.u.64) [ 752 0 R /XYZ 67.0 558.168 null ] (rfc.section.14.7) [ 752 0 R /XYZ 67.0 521.308 null ] (ELEMENT_href) [ 752 0 R /XYZ 67.0 521.308 null ] (rfc.figure.u.65) [ 752 0 R /XYZ 67.0 412.336 null ] (rfc.section.14.8) [ 752 0 R /XYZ 67.0 375.476 null ] (ELEMENT_include) [ 752 0 R /XYZ 67.0 375.476 null ] (rfc.figure.u.66) [ 752 0 R /XYZ 67.0 265.504 null ] (rfc.section.14.9) [ 752 0 R /XYZ 67.0 228.644 null ] (ELEMENT_location) [ 752 0 R /XYZ 67.0 228.644 null ] (rfc.xref.RFC2616.30) [ 752 0 R /XYZ 67.0 186.172 null ] (rfc.figure.u.67) [ 752 0 R /XYZ 67.0 107.672 null ] (rfc.section.14.10) [ 762 0 R /XYZ 67.0 703.0 null ] (ELEMENT_lockentry) [ 762 0 R /XYZ 67.0 703.0 null ] (rfc.figure.u.68) [ 762 0 R /XYZ 67.0 637.028 null ] (rfc.section.14.11) [ 762 0 R /XYZ 67.0 600.168 null ] (ELEMENT_lockinfo) [ 762 0 R /XYZ 67.0 600.168 null ] (rfc.figure.u.69) [ 762 0 R /XYZ 67.0 523.196 null ] (rfc.section.14.12) [ 762 0 R /XYZ 67.0 486.336 null ] (ELEMENT_lockroot) [ 762 0 R /XYZ 67.0 486.336 null ] (rfc.figure.u.70) [ 762 0 R /XYZ 67.0 382.364 null ] (rfc.section.14.13) [ 762 0 R /XYZ 67.0 345.504 null ] (ELEMENT_lockscope) [ 762 0 R /XYZ 67.0 345.504 null ] (rfc.figure.u.71) [ 762 0 R /XYZ 67.0 279.532 null ] (rfc.section.14.14) [ 762 0 R /XYZ 67.0 242.672 null ] (ELEMENT_locktoken) [ 762 0 R /XYZ 67.0 242.672 null ] (rfc.figure.u.72) [ 762 0 R /XYZ 67.0 160.7 null ] (rfc.section.14.15) [ 762 0 R /XYZ 67.0 123.84 null ] (ELEMENT_locktype) [ 762 0 R /XYZ 67.0 123.84 null ] (rfc.figure.u.73) [ 779 0 R /XYZ 67.0 688.0 null ] (rfc.section.14.16) [ 779 0 R /XYZ 67.0 651.14 null ] (ELEMENT_multistatus) [ 779 0 R /XYZ 67.0 651.14 null ] (rfc.figure.u.74) [ 779 0 R /XYZ 67.0 536.168 null ] (rfc.section.14.17) [ 779 0 R /XYZ 67.0 499.308 null ] (ELEMENT_owner) [ 779 0 R /XYZ 67.0 499.308 null ] (rfc.figure.u.75) [ 779 0 R /XYZ 67.0 313.336 null ] (rfc.section.14.18) [ 779 0 R /XYZ 67.0 276.476 null ] (ELEMENT_prop) [ 779 0 R /XYZ 67.0 276.476 null ] (rfc.figure.u.76) [ 779 0 R /XYZ 67.0 150.504 null ] (rfc.section.14.19) [ 779 0 R /XYZ 67.0 113.644 null ] (ELEMENT_propertyupdate) [ 779 0 R /XYZ 67.0 113.644 null ] (rfc.figure.u.77) [ 789 0 R /XYZ 67.0 656.0 null ] (rfc.section.14.20) [ 789 0 R /XYZ 67.0 619.14 null ] (ELEMENT_propfind) [ 789 0 R /XYZ 67.0 619.14 null ] (rfc.figure.u.78) [ 789 0 R /XYZ 67.0 531.168 null ] (rfc.section.14.21) [ 789 0 R /XYZ 67.0 494.308 null ] (ELEMENT_propname) [ 789 0 R /XYZ 67.0 494.308 null ] (rfc.figure.u.79) [ 789 0 R /XYZ 67.0 428.336 null ] (rfc.section.14.22) [ 789 0 R /XYZ 67.0 391.476 null ] (ELEMENT_propstat) [ 789 0 R /XYZ 67.0 391.476 null ] (rfc.figure.u.80) [ 789 0 R /XYZ 67.0 254.504 null ] (rfc.section.14.23) [ 789 0 R /XYZ 67.0 217.644 null ] (ELEMENT_remove) [ 789 0 R /XYZ 67.0 217.644 null ] (rfc.figure.u.81) [ 789 0 R /XYZ 67.0 102.672 null ] (rfc.section.14.24) [ 808 0 R /XYZ 67.0 708.0 null ] (ELEMENT_response) [ 808 0 R /XYZ 67.0 708.0 null ] (rfc.figure.u.82) [ 808 0 R /XYZ 67.0 527.028 null ] (rfc.section.14.25) [ 808 0 R /XYZ 67.0 480.308 null ] (ELEMENT_responsedescription) [ 808 0 R /XYZ 67.0 480.308 null ] (rfc.figure.u.83) [ 808 0 R /XYZ 67.0 398.336 null ] (rfc.section.14.26) [ 808 0 R /XYZ 67.0 361.476 null ] (ELEMENT_set) [ 808 0 R /XYZ 67.0 361.476 null ] (rfc.figure.u.84) [ 808 0 R /XYZ 67.0 224.504 null ] (rfc.section.14.27) [ 808 0 R /XYZ 67.0 187.644 null ] (ELEMENT_shared) [ 808 0 R /XYZ 67.0 187.644 null ] (rfc.figure.u.85) [ 808 0 R /XYZ 67.0 121.672 null ] (rfc.section.14.28) [ 808 0 R /XYZ 67.0 84.812 null ] (ELEMENT_status) [ 823 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2616.31) [ 823 0 R /XYZ 67.0 666.528 null ] (rfc.figure.u.86) [ 823 0 R /XYZ 67.0 643.028 null ] (rfc.section.14.29) [ 823 0 R /XYZ 67.0 606.168 null ] (ELEMENT_timeout) [ 823 0 R /XYZ 67.0 606.168 null ] (rfc.figure.u.87) [ 823 0 R /XYZ 67.0 524.196 null ] (rfc.section.14.30) [ 823 0 R /XYZ 67.0 487.336 null ] (ELEMENT_write) [ 823 0 R /XYZ 67.0 487.336 null ] (rfc.figure.u.88) [ 823 0 R /XYZ 67.0 421.364 null ] (rfc.section.15) [ 831 0 R /XYZ 67.0 725.0 null ] (dav.properties) [ 831 0 R /XYZ 67.0 725.0 null ] (rfc.xref.REC-XML.5) [ 831 0 R /XYZ 67.0 675.866 null ] (rfc.xref.RFC2616.32) [ 831 0 R /XYZ 67.0 514.866 null ] (rfc.section.15.1) [ 831 0 R /XYZ 67.0 475.866 null ] (PROPERTY_creationdate) [ 831 0 R /XYZ 67.0 475.866 null ] (rfc.xref.RFC3339.1) [ 831 0 R /XYZ 67.0 417.394 null ] (rfc.xref.RFC3339.2) [ 831 0 R /XYZ 67.0 417.394 null ] (rfc.figure.u.89) [ 831 0 R /XYZ 67.0 235.894 null ] (rfc.section.15.2) [ 831 0 R /XYZ 67.0 199.034 null ] (PROPERTY_displayname) [ 831 0 R /XYZ 67.0 199.034 null ] (rfc.xref.RFC2518.4) [ 831 0 R /XYZ 67.0 113.562 null ] (rfc.figure.u.90) [ 840 0 R /XYZ 67.0 573.0 null ] (rfc.section.15.3) [ 840 0 R /XYZ 67.0 536.14 null ] (PROPERTY_getcontentlanguage) [ 840 0 R /XYZ 67.0 536.14 null ] (rfc.xref.RFC2616.33) [ 840 0 R /XYZ 67.0 493.668 null ] (rfc.xref.RFC2616.34) [ 840 0 R /XYZ 67.0 466.668 null ] (rfc.xref.RFC2518.5) [ 840 0 R /XYZ 67.0 439.668 null ] (rfc.figure.u.91) [ 840 0 R /XYZ 67.0 340.168 null ] (rfc.section.15.4) [ 840 0 R /XYZ 67.0 303.308 null ] (PROPERTY_getcontentlength) [ 840 0 R /XYZ 67.0 303.308 null ] (rfc.xref.RFC2616.35) [ 840 0 R /XYZ 67.0 233.836 null ] (rfc.figure.u.92) [ 840 0 R /XYZ 67.0 129.336 null ] (rfc.section.15.5) [ 840 0 R /XYZ 67.0 92.476 null ] (PROPERTY_getcontenttype) [ 840 0 R /XYZ 67.0 92.476 null ] (rfc.xref.RFC2616.36) [ 850 0 R /XYZ 67.0 701.5 null ] (rfc.xref.RFC2616.37) [ 850 0 R /XYZ 67.0 674.5 null ] (rfc.figure.u.93) [ 850 0 R /XYZ 67.0 570.0 null ] (rfc.section.15.6) [ 850 0 R /XYZ 67.0 533.14 null ] (PROPERTY_getetag) [ 850 0 R /XYZ 67.0 533.14 null ] (rfc.xref.RFC2616.38) [ 850 0 R /XYZ 67.0 490.668 null ] (rfc.xref.RFC2616.39) [ 850 0 R /XYZ 67.0 463.668 null ] (rfc.xref.RFC2616.40) [ 850 0 R /XYZ 67.0 371.668 null ] (rfc.figure.u.94) [ 850 0 R /XYZ 67.0 326.168 null ] (rfc.section.15.7) [ 850 0 R /XYZ 67.0 289.308 null ] (PROPERTY_getlastmodified) [ 850 0 R /XYZ 67.0 289.308 null ] (rfc.xref.RFC2616.41) [ 850 0 R /XYZ 67.0 246.836 null ] (rfc.xref.RFC2616.42) [ 850 0 R /XYZ 67.0 208.836 null ] (rfc.xref.RFC2616.43) [ 850 0 R /XYZ 67.0 99.836 null ] (rfc.figure.u.95) [ 865 0 R /XYZ 67.0 584.0 null ] (rfc.section.15.8) [ 865 0 R /XYZ 67.0 547.14 null ] (PROPERTY_lockdiscovery) [ 865 0 R /XYZ 67.0 547.14 null ] (rfc.figure.u.96) [ 865 0 R /XYZ 67.0 323.168 null ] (rfc.section.15.8.1) [ 865 0 R /XYZ 67.0 287.308 null ] (rfc.figure.u.97) [ 865 0 R /XYZ 67.0 267.417 null ] (rfc.figure.u.98) [ 865 0 R /XYZ 67.0 141.677 null ] (rfc.section.15.9) [ 873 0 R /XYZ 67.0 414.78 null ] (PROPERTY_resourcetype) [ 873 0 R /XYZ 67.0 414.78 null ] (rfc.figure.u.99) [ 873 0 R /XYZ 67.0 168.808 null ] (rfc.section.15.10) [ 873 0 R /XYZ 67.0 85.368 null ] (PROPERTY_supportedlock) [ 877 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.100) [ 877 0 R /XYZ 67.0 523.028 null ] (rfc.section.15.10.1) [ 877 0 R /XYZ 67.0 487.168 null ] (rfc.figure.u.101) [ 877 0 R /XYZ 67.0 467.277 null ] (rfc.figure.u.102) [ 877 0 R /XYZ 67.0 341.537 null ] (rfc.section.16) [ 885 0 R /XYZ 67.0 725.0 null ] (precondition.postcondition.xml.elements) [ 885 0 R /XYZ 67.0 725.0 null ] (rfc.iref.114) [ 885 0 R /XYZ 67.0 654.866 null ] (rfc.iref.115) [ 885 0 R /XYZ 67.0 654.866 null ] (rfc.figure.u.103) [ 885 0 R /XYZ 67.0 340.866 null ] (rfc.xref.RFC3744.3) [ 885 0 R /XYZ 67.0 167.266 null ] (rfc.xref.RFC3744.4) [ 885 0 R /XYZ 67.0 167.266 null ] (rfc.xref.RFC3253.6) [ 885 0 R /XYZ 67.0 167.266 null ] (rfc.xref.RFC3648.1) [ 885 0 R /XYZ 67.0 167.266 null ] (rfc.iref.116) [ 885 0 R /XYZ 67.0 114.266 null ] (rfc.iref.117) [ 885 0 R /XYZ 67.0 114.266 null ] (rfc.iref.118) [ 893 0 R /XYZ 67.0 655.0 null ] (rfc.iref.119) [ 893 0 R /XYZ 67.0 655.0 null ] (rfc.figure.u.104) [ 893 0 R /XYZ 67.0 542.0 null ] (rfc.iref.120) [ 893 0 R /XYZ 67.0 512.14 null ] (rfc.iref.121) [ 893 0 R /XYZ 67.0 512.14 null ] (rfc.figure.u.105) [ 893 0 R /XYZ 67.0 410.14 null ] (rfc.iref.122) [ 893 0 R /XYZ 67.0 380.28 null ] (rfc.iref.123) [ 893 0 R /XYZ 67.0 380.28 null ] (rfc.iref.124) [ 893 0 R /XYZ 67.0 311.28 null ] (rfc.iref.125) [ 893 0 R /XYZ 67.0 311.28 null ] (rfc.iref.126) [ 893 0 R /XYZ 67.0 220.28 null ] (rfc.iref.127) [ 893 0 R /XYZ 67.0 220.28 null ] (rfc.iref.128) [ 893 0 R /XYZ 67.0 151.28 null ] (rfc.iref.129) [ 893 0 R /XYZ 67.0 151.28 null ] (rfc.xref.RFC3253.7) [ 893 0 R /XYZ 67.0 105.78 null ] (rfc.section.17) [ 897 0 R /XYZ 67.0 725.0 null ] (xml-extensibility) [ 897 0 R /XYZ 67.0 725.0 null ] (rfc.xref.REC-XML-NAMES.2) [ 897 0 R /XYZ 67.0 697.866 null ] (rfc.section.18) [ 902 0 R /XYZ 67.0 725.0 null ] (dav.compliance.classes) [ 902 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2616.44) [ 902 0 R /XYZ 67.0 610.866 null ] (rfc.section.18.1) [ 902 0 R /XYZ 67.0 561.866 null ] (rfc.section.18.2) [ 902 0 R /XYZ 67.0 477.894 null ] (rfc.section.18.3) [ 902 0 R /XYZ 67.0 360.922 null ] (compliance-class-3) [ 902 0 R /XYZ 67.0 360.922 null ] (rfc.xref.RFC2518.6) [ 902 0 R /XYZ 67.0 336.95 null ] (rfc.figure.u.106) [ 902 0 R /XYZ 67.0 271.95 null ] (rfc.section.19) [ 907 0 R /XYZ 67.0 725.0 null ] (internationalization.considerations) [ 907 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2277.1) [ 907 0 R /XYZ 67.0 697.866 null ] (rfc.xref.RFC3629.1) [ 907 0 R /XYZ 67.0 653.866 null ] (rfc.xref.RFC2781.1) [ 907 0 R /XYZ 67.0 653.866 null ] (rfc.xref.RFC3023.1) [ 907 0 R /XYZ 67.0 631.866 null ] (rfc.xref.REC-XML.6) [ 907 0 R /XYZ 67.0 588.866 null ] (rfc.xref.RFC3023.2) [ 907 0 R /XYZ 67.0 545.866 null ] (rfc.section.20) [ 920 0 R /XYZ 67.0 725.0 null ] (security.considerations) [ 920 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2616.45) [ 920 0 R /XYZ 67.0 665.866 null ] (rfc.xref.RFC3023.3) [ 920 0 R /XYZ 67.0 665.866 null ] (rfc.section.20.1) [ 920 0 R /XYZ 67.0 604.866 null ] (rfc.xref.RFC2617.1) [ 920 0 R /XYZ 67.0 450.894 null ] (rfc.section.20.2) [ 920 0 R /XYZ 67.0 389.894 null ] (rfc.section.20.3) [ 920 0 R /XYZ 67.0 203.922 null ] (rfc.section.20.4) [ 920 0 R /XYZ 67.0 107.95 null ] (rfc.section.20.5) [ 927 0 R /XYZ 67.0 615.0 null ] (rfc.section.20.6) [ 927 0 R /XYZ 67.0 508.028 null ] (rfc.xref.REC-XML.7) [ 927 0 R /XYZ 67.0 484.056 null ] (rfc.xref.RFC3023.4) [ 927 0 R /XYZ 67.0 375.056 null ] (rfc.xref.REC-XML.8) [ 927 0 R /XYZ 67.0 267.056 null ] (rfc.section.20.7) [ 927 0 R /XYZ 67.0 206.056 null ] (risks.connected.with.lock.tokens) [ 927 0 R /XYZ 67.0 206.056 null ] (rfc.xref.RFC4122.2) [ 927 0 R /XYZ 67.0 182.084 null ] (rfc.xref.RFC4122.3) [ 927 0 R /XYZ 67.0 160.084 null ] (rfc.xref.RFC4122.4) [ 935 0 R /XYZ 67.0 662.0 null ] (rfc.section.20.8) [ 935 0 R /XYZ 67.0 612.0 null ] (rfc.section.21) [ 939 0 R /XYZ 67.0 725.0 null ] (rfc.section.21.1) [ 939 0 R /XYZ 67.0 690.866 null ] (rfc.xref.RFC2518.7) [ 939 0 R /XYZ 67.0 637.394 null ] (rfc.section.21.2) [ 939 0 R /XYZ 67.0 552.894 null ] (rfc.section.21.3) [ 939 0 R /XYZ 67.0 478.922 null ] (rfc.xref.RFC3864.1) [ 939 0 R /XYZ 67.0 454.95 null ] (rfc.section.21.3.1) [ 939 0 R /XYZ 67.0 427.95 null ] (rfc.section.21.3.2) [ 939 0 R /XYZ 67.0 297.059 null ] (rfc.section.21.3.3) [ 939 0 R /XYZ 67.0 166.168 null ] (rfc.section.21.3.4) [ 948 0 R /XYZ 67.0 672.0 null ] (rfc.section.21.3.5) [ 948 0 R /XYZ 67.0 541.109 null ] (rfc.section.21.3.6) [ 948 0 R /XYZ 67.0 410.218 null ] (rfc.section.21.3.7) [ 948 0 R /XYZ 67.0 279.327 null ] (rfc.section.21.4) [ 948 0 R /XYZ 67.0 147.436 null ] (STATUS_102) [ 958 0 R /XYZ 67.0 656.0 null ] (rfc.iref.133) [ 958 0 R /XYZ 67.0 656.0 null ] (rfc.iref.134) [ 958 0 R /XYZ 67.0 656.0 null ] (rfc.xref.RFC2518.8) [ 958 0 R /XYZ 67.0 645.0 null ] (rfc.section.22) [ 966 0 R /XYZ 67.0 725.0 null ] (rfc.section.23) [ 968 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.107) [ 968 0 R /XYZ 67.0 697.866 null ] (rfc.figure.u.108) [ 968 0 R /XYZ 67.0 643.37 null ] (rfc.figure.u.109) [ 968 0 R /XYZ 67.0 588.874 null ] (rfc.section.24) [ 970 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.110) [ 970 0 R /XYZ 67.0 697.866 null ] (rfc.figure.u.111) [ 970 0 R /XYZ 67.0 634.496 null ] (rfc.figure.u.112) [ 970 0 R /XYZ 67.0 571.126 null ] (rfc.figure.u.113) [ 970 0 R /XYZ 67.0 507.756 null ] (rfc.figure.u.114) [ 970 0 R /XYZ 67.0 435.512 null ] (rfc.references) [ 972 0 R /XYZ 67.0 725.0 null ] (rfc.references.1) [ 972 0 R /XYZ 67.0 690.866 null ] (REC-XML) [ 972 0 R /XYZ 67.0 671.894 null ] (REC-XML-INFOSET) [ 972 0 R /XYZ 67.0 633.894 null ] (REC-XML-NAMES) [ 972 0 R /XYZ 67.0 595.894 null ] (RFC2119) [ 972 0 R /XYZ 67.0 557.894 null ] (RFC2277) [ 972 0 R /XYZ 67.0 530.894 null ] (RFC2616) [ 972 0 R /XYZ 67.0 503.894 null ] (RFC2617) [ 972 0 R /XYZ 67.0 476.894 null ] (RFC3339) [ 972 0 R /XYZ 67.0 438.894 null ] (RFC3629) [ 972 0 R /XYZ 67.0 411.894 null ] (RFC3986) [ 972 0 R /XYZ 67.0 384.894 null ] (RFC4122) [ 972 0 R /XYZ 67.0 357.894 null ] (rfc.references.2) [ 972 0 R /XYZ 67.0 318.894 null ] (RFC2291) [ 972 0 R /XYZ 67.0 299.922 null ] (RFC2518) [ 972 0 R /XYZ 67.0 261.922 null ] (RFC2781) [ 972 0 R /XYZ 67.0 234.922 null ] (RFC3023) [ 972 0 R /XYZ 67.0 207.922 null ] (RFC3253) [ 972 0 R /XYZ 67.0 180.922 null ] (RFC3648) [ 972 0 R /XYZ 67.0 142.922 null ] (RFC3744) [ 972 0 R /XYZ 67.0 115.922 null ] (RFC3864) [ 972 0 R /XYZ 67.0 88.922 null ] (rfc.authors) [ 1008 0 R /XYZ 67.0 725.0 null ] (rfc.section.A) [ 1012 0 R /XYZ 67.0 725.0 null ] (xml-appendix) [ 1012 0 R /XYZ 67.0 725.0 null ] (rfc.section.A.1) [ 1012 0 R /XYZ 67.0 690.866 null ] (rfc.section.A.2) [ 1012 0 R /XYZ 67.0 616.894 null ] (rfc.section.A.3) [ 1012 0 R /XYZ 67.0 488.922 null ] (rfc.figure.u.115) [ 1012 0 R /XYZ 67.0 443.95 null ] (rfc.section.A.4) [ 1012 0 R /XYZ 67.0 248.65 null ] (rfc.figure.u.116) [ 1012 0 R /XYZ 67.0 159.678 null ] (rfc.figure.u.117) [ 1014 0 R /XYZ 67.0 699.0 null ] (rfc.figure.u.118) [ 1014 0 R /XYZ 67.0 563.56 null ] (rfc.section.B) [ 1018 0 R /XYZ 67.0 725.0 null ] (rfc.section.C) [ 1020 0 R /XYZ 67.0 725.0 null ] (opaquelocktoken.lock.token.uri.scheme) [ 1020 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.9) [ 1020 0 R /XYZ 67.0 697.866 null ] (rfc.figure.u.119) [ 1020 0 R /XYZ 67.0 600.866 null ] (rfc.xref.RFC4122.5) [ 1020 0 R /XYZ 67.0 586.006 null ] (rfc.xref.RFC3986.13) [ 1020 0 R /XYZ 67.0 526.846 null ] (rfc.section.D) [ 1026 0 R /XYZ 67.0 725.0 null ] (lock-null) [ 1026 0 R /XYZ 67.0 725.0 null ] (rfc.section.D.1) [ 1026 0 R /XYZ 67.0 389.866 null ] (rfc.xref.RFC2518.10) [ 1026 0 R /XYZ 67.0 354.894 null ] (rfc.section.E) [ 1031 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.120) [ 1031 0 R /XYZ 67.0 481.866 null ] (rfc.xref.RFC2617.2) [ 1031 0 R /XYZ 67.0 395.566 null ] (rfc.figure.u.121) [ 1031 0 R /XYZ 67.0 190.566 null ] (rfc.section.F) [ 1036 0 R /XYZ 67.0 725.0 null ] (rfc.section.F.1) [ 1036 0 R /XYZ 67.0 647.866 null ] (rfc.xref.RFC3253.8) [ 1036 0 R /XYZ 67.0 452.394 null ] (rfc.xref.RFC3253.9) [ 1036 0 R /XYZ 67.0 366.394 null ] (rfc.section.F.2) [ 1036 0 R /XYZ 67.0 113.894 null ] (rfc.iref.135) [ 1054 0 R /XYZ 67.0 695.5 null ] (rfc.xref.RFC2518.11) [ 1054 0 R /XYZ 67.0 459.5 null ] (rfc.section.F.3) [ 1054 0 R /XYZ 67.0 418.0 null ] (PROPERTY_source) [ 1054 0 R /XYZ 67.0 362.028 null ] (rfc.iref.136) [ 1054 0 R /XYZ 67.0 362.028 null ] (rfc.iref.137) [ 1054 0 R /XYZ 67.0 362.028 null ] (rfc.xref.RFC2518.12) [ 1054 0 R /XYZ 67.0 362.028 null ] (rfc.xref.RFC2518.13) [ 1054 0 R /XYZ 67.0 287.028 null ] (HEADER_Status-URI) [ 1054 0 R /XYZ 67.0 244.028 null ] (rfc.xref.RFC2518.14) [ 1054 0 R /XYZ 67.0 244.028 null ] (rfc.xref.RFC2518.15) [ 1054 0 R /XYZ 67.0 233.028 null ] (rfc.copyright) [ 1054 0 R /XYZ 67.0 171.028 null ] (rfc.ipr) [ 1075 0 R /XYZ 67.0 725.0 null ] (rfc.index) [ 1081 0 R /XYZ 67.0 725.0 null ] ] >> >> + >> +endobj +3 0 obj +<< +/Font << /F5 1435 0 R /F6 1436 0 R /F9 1437 0 R /F7 1438 0 R >> +/ProcSet [ /PDF /ImageC /Text ] >> +endobj +13 0 obj +<< +/S /GoTo +/D [468 0 R /XYZ 67.0 725.0 null] +>> +endobj +15 0 obj +<< +/S /GoTo +/D [495 0 R /XYZ 67.0 725.0 null] +>> +endobj +17 0 obj +<< +/S /GoTo +/D [503 0 R /XYZ 67.0 725.0 null] +>> +endobj +19 0 obj +<< +/S /GoTo +/D [510 0 R /XYZ 67.0 725.0 null] +>> +endobj +21 0 obj +<< +/S /GoTo +/D [510 0 R /XYZ 67.0 690.866 null] +>> +endobj +23 0 obj +<< +/S /GoTo +/D [510 0 R /XYZ 67.0 487.894 null] +>> +endobj +25 0 obj +<< +/S /GoTo +/D [510 0 R /XYZ 67.0 391.922 null] +>> +endobj +27 0 obj +<< +/S /GoTo +/D [515 0 R /XYZ 67.0 260.0 null] +>> +endobj +29 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 262.38 null] +>> +endobj +31 0 obj +<< +/S /GoTo +/D [521 0 R /XYZ 67.0 681.0 null] +>> +endobj +33 0 obj +<< +/S /GoTo +/D [523 0 R /XYZ 67.0 725.0 null] +>> +endobj +35 0 obj +<< +/S /GoTo +/D [523 0 R /XYZ 67.0 626.866 null] +>> +endobj +37 0 obj +<< +/S /GoTo +/D [523 0 R /XYZ 67.0 434.894 null] +>> +endobj +39 0 obj +<< +/S /GoTo +/D [530 0 R /XYZ 67.0 725.0 null] +>> +endobj +41 0 obj +<< +/S /GoTo +/D [530 0 R /XYZ 67.0 593.866 null] +>> +endobj +43 0 obj +<< +/S /GoTo +/D [530 0 R /XYZ 67.0 239.894 null] +>> +endobj +45 0 obj +<< +/S /GoTo +/D [537 0 R /XYZ 67.0 442.0 null] +>> +endobj +47 0 obj +<< +/S /GoTo +/D [537 0 R /XYZ 67.0 303.028 null] +>> +endobj +49 0 obj +<< +/S /GoTo +/D [537 0 R /XYZ 67.0 133.056 null] +>> +endobj +51 0 obj +<< +/S /GoTo +/D [543 0 R /XYZ 67.0 498.0 null] +>> +endobj +53 0 obj +<< +/S /GoTo +/D [543 0 R /XYZ 67.0 176.028 null] +>> +endobj +55 0 obj +<< +/S /GoTo +/D [551 0 R /XYZ 67.0 692.0 null] +>> +endobj +57 0 obj +<< +/S /GoTo +/D [553 0 R /XYZ 67.0 725.0 null] +>> +endobj +59 0 obj +<< +/S /GoTo +/D [553 0 R /XYZ 67.0 391.866 null] +>> +endobj +61 0 obj +<< +/S /GoTo +/D [553 0 R /XYZ 67.0 317.894 null] +>> +endobj +63 0 obj +<< +/S /GoTo +/D [557 0 R /XYZ 67.0 574.0 null] +>> +endobj +65 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 659.0 null] +>> +endobj +67 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 119.028 null] +>> +endobj +69 0 obj +<< +/S /GoTo +/D [568 0 R /XYZ 67.0 562.0 null] +>> +endobj +71 0 obj +<< +/S /GoTo +/D [568 0 R /XYZ 67.0 348.949 null] +>> +endobj +73 0 obj +<< +/S /GoTo +/D [572 0 R /XYZ 67.0 505.7 null] +>> +endobj +75 0 obj +<< +/S /GoTo +/D [572 0 R /XYZ 67.0 333.728 null] +>> +endobj +77 0 obj +<< +/S /GoTo +/D [576 0 R /XYZ 67.0 725.0 null] +>> +endobj +79 0 obj +<< +/S /GoTo +/D [576 0 R /XYZ 67.0 690.866 null] +>> +endobj +81 0 obj +<< +/S /GoTo +/D [576 0 R /XYZ 67.0 616.894 null] +>> +endobj +83 0 obj +<< +/S /GoTo +/D [576 0 R /XYZ 67.0 315.922 null] +>> +endobj +85 0 obj +<< +/S /GoTo +/D [587 0 R /XYZ 67.0 645.0 null] +>> +endobj +90 0 obj +<< +/S /GoTo +/D [587 0 R /XYZ 67.0 412.529 null] +>> +endobj +92 0 obj +<< +/S /GoTo +/D [587 0 R /XYZ 67.0 327.557 null] +>> +endobj +94 0 obj +<< +/S /GoTo +/D [587 0 R /XYZ 67.0 232.585 null] +>> +endobj +96 0 obj +<< +/S /GoTo +/D [594 0 R /XYZ 67.0 556.0 null] +>> +endobj +98 0 obj +<< +/S /GoTo +/D [594 0 R /XYZ 67.0 438.028 null] +>> +endobj +100 0 obj +<< +/S /GoTo +/D [600 0 R /XYZ 67.0 725.0 null] +>> +endobj +102 0 obj +<< +/S /GoTo +/D [600 0 R /XYZ 67.0 690.866 null] +>> +endobj +104 0 obj +<< +/S /GoTo +/D [600 0 R /XYZ 67.0 73.894 null] +>> +endobj +106 0 obj +<< +/S /GoTo +/D [607 0 R /XYZ 67.0 635.109 null] +>> +endobj +108 0 obj +<< +/S /GoTo +/D [607 0 R /XYZ 67.0 471.218 null] +>> +endobj +110 0 obj +<< +/S /GoTo +/D [612 0 R /XYZ 67.0 525.68 null] +>> +endobj +112 0 obj +<< +/S /GoTo +/D [614 0 R /XYZ 67.0 394.4 null] +>> +endobj +114 0 obj +<< +/S /GoTo +/D [620 0 R /XYZ 67.0 547.0 null] +>> +endobj +116 0 obj +<< +/S /GoTo +/D [620 0 R /XYZ 67.0 302.069 null] +>> +endobj +118 0 obj +<< +/S /GoTo +/D [627 0 R /XYZ 67.0 682.0 null] +>> +endobj +120 0 obj +<< +/S /GoTo +/D [627 0 R /XYZ 67.0 432.109 null] +>> +endobj +122 0 obj +<< +/S /GoTo +/D [632 0 R /XYZ 67.0 503.82 null] +>> +endobj +124 0 obj +<< +/S /GoTo +/D [632 0 R /XYZ 67.0 268.848 null] +>> +endobj +126 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 650.0 null] +>> +endobj +128 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 498.529 null] +>> +endobj +130 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 370.557 null] +>> +endobj +132 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 296.585 null] +>> +endobj +134 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 159.613 null] +>> +endobj +136 0 obj +<< +/S /GoTo +/D [641 0 R /XYZ 67.0 489.0 null] +>> +endobj +138 0 obj +<< +/S /GoTo +/D [641 0 R /XYZ 67.0 174.069 null] +>> +endobj +140 0 obj +<< +/S /GoTo +/D [641 0 R /XYZ 67.0 144.097 null] +>> +endobj +142 0 obj +<< +/S /GoTo +/D [643 0 R /XYZ 67.0 563.0 null] +>> +endobj +144 0 obj +<< +/S /GoTo +/D [643 0 R /XYZ 67.0 483.109 null] +>> +endobj +146 0 obj +<< +/S /GoTo +/D [643 0 R /XYZ 67.0 324.137 null] +>> +endobj +148 0 obj +<< +/S /GoTo +/D [643 0 R /XYZ 67.0 211.246 null] +>> +endobj +150 0 obj +<< +/S /GoTo +/D [647 0 R /XYZ 67.0 682.0 null] +>> +endobj +152 0 obj +<< +/S /GoTo +/D [647 0 R /XYZ 67.0 181.109 null] +>> +endobj +154 0 obj +<< +/S /GoTo +/D [652 0 R /XYZ 67.0 617.0 null] +>> +endobj +156 0 obj +<< +/S /GoTo +/D [652 0 R /XYZ 67.0 249.109 null] +>> +endobj +158 0 obj +<< +/S /GoTo +/D [654 0 R /XYZ 67.0 709.0 null] +>> +endobj +160 0 obj +<< +/S /GoTo +/D [654 0 R /XYZ 67.0 516.809 null] +>> +endobj +162 0 obj +<< +/S /GoTo +/D [654 0 R /XYZ 67.0 183.298 null] +>> +endobj +164 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 586.0 null] +>> +endobj +166 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 420.109 null] +>> +endobj +168 0 obj +<< +/S /GoTo +/D [661 0 R /XYZ 67.0 682.0 null] +>> +endobj +170 0 obj +<< +/S /GoTo +/D [661 0 R /XYZ 67.0 613.109 null] +>> +endobj +172 0 obj +<< +/S /GoTo +/D [661 0 R /XYZ 67.0 244.218 null] +>> +endobj +174 0 obj +<< +/S /GoTo +/D [663 0 R /XYZ 67.0 678.28 null] +>> +endobj +176 0 obj +<< +/S /GoTo +/D [663 0 R /XYZ 67.0 317.909 null] +>> +endobj +178 0 obj +<< +/S /GoTo +/D [663 0 R /XYZ 67.0 180.937 null] +>> +endobj +180 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 650.0 null] +>> +endobj +182 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 495.109 null] +>> +endobj +184 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 243.218 null] +>> +endobj +186 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 152.327 null] +>> +endobj +188 0 obj +<< +/S /GoTo +/D [669 0 R /XYZ 67.0 582.69 null] +>> +endobj +190 0 obj +<< +/S /GoTo +/D [669 0 R /XYZ 67.0 353.799 null] +>> +endobj +195 0 obj +<< +/S /GoTo +/D [671 0 R /XYZ 67.0 376.64 null] +>> +endobj +197 0 obj +<< +/S /GoTo +/D [673 0 R /XYZ 67.0 541.68 null] +>> +endobj +199 0 obj +<< +/S /GoTo +/D [675 0 R /XYZ 67.0 617.0 null] +>> +endobj +201 0 obj +<< +/S /GoTo +/D [675 0 R /XYZ 67.0 374.028 null] +>> +endobj +203 0 obj +<< +/S /GoTo +/D [675 0 R /XYZ 67.0 210.137 null] +>> +endobj +205 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 725.0 null] +>> +endobj +207 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 637.866 null] +>> +endobj +209 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 221.714 null] +>> +endobj +211 0 obj +<< +/S /GoTo +/D [686 0 R /XYZ 67.0 445.0 null] +>> +endobj +213 0 obj +<< +/S /GoTo +/D [686 0 R /XYZ 67.0 266.168 null] +>> +endobj +215 0 obj +<< +/S /GoTo +/D [686 0 R /XYZ 67.0 193.196 null] +>> +endobj +217 0 obj +<< +/S /GoTo +/D [693 0 R /XYZ 67.0 639.0 null] +>> +endobj +219 0 obj +<< +/S /GoTo +/D [693 0 R /XYZ 67.0 253.349 null] +>> +endobj +221 0 obj +<< +/S /GoTo +/D [701 0 R /XYZ 67.0 693.0 null] +>> +endobj +223 0 obj +<< +/S /GoTo +/D [701 0 R /XYZ 67.0 507.109 null] +>> +endobj +225 0 obj +<< +/S /GoTo +/D [701 0 R /XYZ 67.0 384.218 null] +>> +endobj +227 0 obj +<< +/S /GoTo +/D [701 0 R /XYZ 67.0 148.867 null] +>> +endobj +229 0 obj +<< +/S /GoTo +/D [705 0 R /XYZ 67.0 671.0 null] +>> +endobj +231 0 obj +<< +/S /GoTo +/D [705 0 R /XYZ 67.0 496.389 null] +>> +endobj +233 0 obj +<< +/S /GoTo +/D [705 0 R /XYZ 67.0 309.338 null] +>> +endobj +235 0 obj +<< +/S /GoTo +/D [705 0 R /XYZ 67.0 169.007 null] +>> +endobj +237 0 obj +<< +/S /GoTo +/D [709 0 R /XYZ 67.0 598.14 null] +>> +endobj +239 0 obj +<< +/S /GoTo +/D [709 0 R /XYZ 67.0 462.308 null] +>> +endobj +241 0 obj +<< +/S /GoTo +/D [709 0 R /XYZ 67.0 261.476 null] +>> +endobj +243 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 725.0 null] +>> +endobj +245 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 669.866 null] +>> +endobj +247 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 606.894 null] +>> +endobj +249 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 510.922 null] +>> +endobj +251 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 436.95 null] +>> +endobj +253 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 351.978 null] +>> +endobj +255 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 725.0 null] +>> +endobj +257 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 625.866 null] +>> +endobj +259 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 540.894 null] +>> +endobj +261 0 obj +<< +/S /GoTo +/D [722 0 R /XYZ 67.0 725.0 null] +>> +endobj +263 0 obj +<< +/S /GoTo +/D [722 0 R /XYZ 67.0 432.866 null] +>> +endobj +265 0 obj +<< +/S /GoTo +/D [722 0 R /XYZ 67.0 347.894 null] +>> +endobj +267 0 obj +<< +/S /GoTo +/D [722 0 R /XYZ 67.0 197.922 null] +>> +endobj +269 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 725.0 null] +>> +endobj +271 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 625.866 null] +>> +endobj +273 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 513.174 null] +>> +endobj +275 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 399.342 null] +>> +endobj +277 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 274.51 null] +>> +endobj +279 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 155.678 null] +>> +endobj +284 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 624.14 null] +>> +endobj +286 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 521.308 null] +>> +endobj +288 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 375.476 null] +>> +endobj +290 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 228.644 null] +>> +endobj +292 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 703.0 null] +>> +endobj +294 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 600.168 null] +>> +endobj +296 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 486.336 null] +>> +endobj +298 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 345.504 null] +>> +endobj +300 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 242.672 null] +>> +endobj +302 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 123.84 null] +>> +endobj +304 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 651.14 null] +>> +endobj +306 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 499.308 null] +>> +endobj +308 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 276.476 null] +>> +endobj +310 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 113.644 null] +>> +endobj +312 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 619.14 null] +>> +endobj +314 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 494.308 null] +>> +endobj +316 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 391.476 null] +>> +endobj +318 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 217.644 null] +>> +endobj +320 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 708.0 null] +>> +endobj +322 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 480.308 null] +>> +endobj +324 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 361.476 null] +>> +endobj +326 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 187.644 null] +>> +endobj +328 0 obj +<< +/S /GoTo +/D [823 0 R /XYZ 67.0 725.0 null] +>> +endobj +330 0 obj +<< +/S /GoTo +/D [823 0 R /XYZ 67.0 606.168 null] +>> +endobj +332 0 obj +<< +/S /GoTo +/D [823 0 R /XYZ 67.0 487.336 null] +>> +endobj +334 0 obj +<< +/S /GoTo +/D [831 0 R /XYZ 67.0 725.0 null] +>> +endobj +336 0 obj +<< +/S /GoTo +/D [831 0 R /XYZ 67.0 475.866 null] +>> +endobj +338 0 obj +<< +/S /GoTo +/D [831 0 R /XYZ 67.0 199.034 null] +>> +endobj +340 0 obj +<< +/S /GoTo +/D [840 0 R /XYZ 67.0 536.14 null] +>> +endobj +342 0 obj +<< +/S /GoTo +/D [840 0 R /XYZ 67.0 303.308 null] +>> +endobj +344 0 obj +<< +/S /GoTo +/D [840 0 R /XYZ 67.0 92.476 null] +>> +endobj +346 0 obj +<< +/S /GoTo +/D [850 0 R /XYZ 67.0 533.14 null] +>> +endobj +348 0 obj +<< +/S /GoTo +/D [850 0 R /XYZ 67.0 289.308 null] +>> +endobj +350 0 obj +<< +/S /GoTo +/D [865 0 R /XYZ 67.0 547.14 null] +>> +endobj +352 0 obj +<< +/S /GoTo +/D [865 0 R /XYZ 67.0 287.308 null] +>> +endobj +354 0 obj +<< +/S /GoTo +/D [873 0 R /XYZ 67.0 414.78 null] +>> +endobj +356 0 obj +<< +/S /GoTo +/D [877 0 R /XYZ 67.0 725.0 null] +>> +endobj +358 0 obj +<< +/S /GoTo +/D [877 0 R /XYZ 67.0 487.168 null] +>> +endobj +360 0 obj +<< +/S /GoTo +/D [885 0 R /XYZ 67.0 725.0 null] +>> +endobj +362 0 obj +<< +/S /GoTo +/D [897 0 R /XYZ 67.0 725.0 null] +>> +endobj +367 0 obj +<< +/S /GoTo +/D [902 0 R /XYZ 67.0 725.0 null] +>> +endobj +369 0 obj +<< +/S /GoTo +/D [902 0 R /XYZ 67.0 561.866 null] +>> +endobj +371 0 obj +<< +/S /GoTo +/D [902 0 R /XYZ 67.0 477.894 null] +>> +endobj +373 0 obj +<< +/S /GoTo +/D [902 0 R /XYZ 67.0 360.922 null] +>> +endobj +375 0 obj +<< +/S /GoTo +/D [907 0 R /XYZ 67.0 725.0 null] +>> +endobj +377 0 obj +<< +/S /GoTo +/D [920 0 R /XYZ 67.0 725.0 null] +>> +endobj +379 0 obj +<< +/S /GoTo +/D [920 0 R /XYZ 67.0 604.866 null] +>> +endobj +381 0 obj +<< +/S /GoTo +/D [920 0 R /XYZ 67.0 389.894 null] +>> +endobj +383 0 obj +<< +/S /GoTo +/D [920 0 R /XYZ 67.0 203.922 null] +>> +endobj +385 0 obj +<< +/S /GoTo +/D [920 0 R /XYZ 67.0 107.95 null] +>> +endobj +387 0 obj +<< +/S /GoTo +/D [927 0 R /XYZ 67.0 615.0 null] +>> +endobj +389 0 obj +<< +/S /GoTo +/D [927 0 R /XYZ 67.0 508.028 null] +>> +endobj +391 0 obj +<< +/S /GoTo +/D [927 0 R /XYZ 67.0 206.056 null] +>> +endobj +393 0 obj +<< +/S /GoTo +/D [935 0 R /XYZ 67.0 612.0 null] +>> +endobj +395 0 obj +<< +/S /GoTo +/D [939 0 R /XYZ 67.0 725.0 null] +>> +endobj +397 0 obj +<< +/S /GoTo +/D [939 0 R /XYZ 67.0 690.866 null] +>> +endobj +399 0 obj +<< +/S /GoTo +/D [939 0 R /XYZ 67.0 552.894 null] +>> +endobj +401 0 obj +<< +/S /GoTo +/D [939 0 R /XYZ 67.0 478.922 null] +>> +endobj +403 0 obj +<< +/S /GoTo +/D [939 0 R /XYZ 67.0 427.95 null] +>> +endobj +405 0 obj +<< +/S /GoTo +/D [939 0 R /XYZ 67.0 297.059 null] +>> +endobj +407 0 obj +<< +/S /GoTo +/D [939 0 R /XYZ 67.0 166.168 null] +>> +endobj +409 0 obj +<< +/S /GoTo +/D [948 0 R /XYZ 67.0 672.0 null] +>> +endobj +411 0 obj +<< +/S /GoTo +/D [948 0 R /XYZ 67.0 541.109 null] +>> +endobj +413 0 obj +<< +/S /GoTo +/D [948 0 R /XYZ 67.0 410.218 null] +>> +endobj +415 0 obj +<< +/S /GoTo +/D [948 0 R /XYZ 67.0 279.327 null] +>> +endobj +417 0 obj +<< +/S /GoTo +/D [948 0 R /XYZ 67.0 147.436 null] +>> +endobj +419 0 obj +<< +/S /GoTo +/D [966 0 R /XYZ 67.0 725.0 null] +>> +endobj +421 0 obj +<< +/S /GoTo +/D [968 0 R /XYZ 67.0 725.0 null] +>> +endobj +423 0 obj +<< +/S /GoTo +/D [970 0 R /XYZ 67.0 725.0 null] +>> +endobj +425 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 725.0 null] +>> +endobj +427 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 690.866 null] +>> +endobj +429 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 318.894 null] +>> +endobj +431 0 obj +<< +/S /GoTo +/D [1008 0 R /XYZ 67.0 725.0 null] +>> +endobj +433 0 obj +<< +/S /GoTo +/D [1012 0 R /XYZ 67.0 725.0 null] +>> +endobj +435 0 obj +<< +/S /GoTo +/D [1012 0 R /XYZ 67.0 690.866 null] +>> +endobj +437 0 obj +<< +/S /GoTo +/D [1012 0 R /XYZ 67.0 616.894 null] +>> +endobj +439 0 obj +<< +/S /GoTo +/D [1012 0 R /XYZ 67.0 488.922 null] +>> +endobj +441 0 obj +<< +/S /GoTo +/D [1012 0 R /XYZ 67.0 248.65 null] +>> +endobj +443 0 obj +<< +/S /GoTo +/D [1018 0 R /XYZ 67.0 725.0 null] +>> +endobj +448 0 obj +<< +/S /GoTo +/D [1020 0 R /XYZ 67.0 725.0 null] +>> +endobj +450 0 obj +<< +/S /GoTo +/D [1026 0 R /XYZ 67.0 725.0 null] +>> +endobj +452 0 obj +<< +/S /GoTo +/D [1026 0 R /XYZ 67.0 389.866 null] +>> +endobj +454 0 obj +<< +/S /GoTo +/D [1031 0 R /XYZ 67.0 725.0 null] +>> +endobj +456 0 obj +<< +/S /GoTo +/D [1036 0 R /XYZ 67.0 725.0 null] +>> +endobj +458 0 obj +<< +/S /GoTo +/D [1036 0 R /XYZ 67.0 647.866 null] +>> +endobj +460 0 obj +<< +/S /GoTo +/D [1036 0 R /XYZ 67.0 113.894 null] +>> +endobj +462 0 obj +<< +/S /GoTo +/D [1054 0 R /XYZ 67.0 418.0 null] +>> +endobj +464 0 obj +<< +/S /GoTo +/D [1075 0 R /XYZ 67.0 725.0 null] +>> +endobj +466 0 obj +<< +/S /GoTo +/D [1081 0 R /XYZ 67.0 725.0 null] +>> +endobj +471 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 299.922 null] +>> +endobj +474 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 180.922 null] +>> +endobj +487 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 671.894 null] +>> +endobj +498 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 503.894 null] +>> +endobj +501 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 557.894 null] +>> +endobj +506 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 384.894 null] +>> +endobj +513 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 633.894 null] +>> +endobj +541 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 115.922 null] +>> +endobj +547 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 357.894 null] +>> +endobj +564 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 261.922 null] +>> +endobj +581 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 595.894 null] +>> +endobj +836 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 438.894 null] +>> +endobj +891 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 142.922 null] +>> +endobj +910 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 530.894 null] +>> +endobj +912 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 411.894 null] +>> +endobj +914 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 234.922 null] +>> +endobj +916 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 207.922 null] +>> +endobj +925 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 476.894 null] +>> +endobj +944 0 obj +<< +/S /GoTo +/D [972 0 R /XYZ 67.0 88.922 null] +>> +endobj +1089 0 obj +<< + /First 1091 0 R + /Last 1434 0 R +>> endobj +1090 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 520.084 null] +>> +endobj +1092 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 429.95 null] +>> +endobj +1094 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 372.816 null] +>> +endobj +1096 0 obj +<< +/S /GoTo +/D [10 0 R /XYZ 67.0 725.0 null] +>> +endobj +1098 0 obj +<< +/S /GoTo +/D [468 0 R /XYZ 67.0 725.0 null] +>> +endobj +1102 0 obj +<< +/S /GoTo +/D [510 0 R /XYZ 67.0 725.0 null] +>> +endobj +1106 0 obj +<< +/S /GoTo +/D [510 0 R /XYZ 67.0 391.922 null] +>> +endobj +1111 0 obj +<< +/S /GoTo +/D [523 0 R /XYZ 67.0 725.0 null] +>> +endobj +1113 0 obj +<< +/S /GoTo +/D [523 0 R /XYZ 67.0 626.866 null] +>> +endobj +1115 0 obj +<< +/S /GoTo +/D [523 0 R /XYZ 67.0 434.894 null] +>> +endobj +1117 0 obj +<< +/S /GoTo +/D [530 0 R /XYZ 67.0 725.0 null] +>> +endobj +1119 0 obj +<< +/S /GoTo +/D [530 0 R /XYZ 67.0 593.866 null] +>> +endobj +1121 0 obj +<< +/S /GoTo +/D [530 0 R /XYZ 67.0 239.894 null] +>> +endobj +1124 0 obj +<< +/S /GoTo +/D [537 0 R /XYZ 67.0 303.028 null] +>> +endobj +1126 0 obj +<< +/S /GoTo +/D [537 0 R /XYZ 67.0 133.056 null] +>> +endobj +1128 0 obj +<< +/S /GoTo +/D [543 0 R /XYZ 67.0 498.0 null] +>> +endobj +1132 0 obj +<< +/S /GoTo +/D [553 0 R /XYZ 67.0 725.0 null] +>> +endobj +1136 0 obj +<< +/S /GoTo +/D [557 0 R /XYZ 67.0 574.0 null] +>> +endobj +1138 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 659.0 null] +>> +endobj +1140 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 119.028 null] +>> +endobj +1146 0 obj +<< +/S /GoTo +/D [576 0 R /XYZ 67.0 725.0 null] +>> +endobj +1148 0 obj +<< +/S /GoTo +/D [576 0 R /XYZ 67.0 690.866 null] +>> +endobj +1151 0 obj +<< +/S /GoTo +/D [576 0 R /XYZ 67.0 315.922 null] +>> +endobj +1155 0 obj +<< +/S /GoTo +/D [587 0 R /XYZ 67.0 327.557 null] +>> +endobj +1157 0 obj +<< +/S /GoTo +/D [587 0 R /XYZ 67.0 232.585 null] +>> +endobj +1159 0 obj +<< +/S /GoTo +/D [594 0 R /XYZ 67.0 556.0 null] +>> +endobj +1161 0 obj +<< +/S /GoTo +/D [594 0 R /XYZ 67.0 438.028 null] +>> +endobj +1163 0 obj +<< +/S /GoTo +/D [600 0 R /XYZ 67.0 725.0 null] +>> +endobj +1165 0 obj +<< +/S /GoTo +/D [600 0 R /XYZ 67.0 690.866 null] +>> +endobj +1168 0 obj +<< +/S /GoTo +/D [607 0 R /XYZ 67.0 635.109 null] +>> +endobj +1174 0 obj +<< +/S /GoTo +/D [620 0 R /XYZ 67.0 302.069 null] +>> +endobj +1176 0 obj +<< +/S /GoTo +/D [627 0 R /XYZ 67.0 682.0 null] +>> +endobj +1179 0 obj +<< +/S /GoTo +/D [632 0 R /XYZ 67.0 503.82 null] +>> +endobj +1184 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 370.557 null] +>> +endobj +1186 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 296.585 null] +>> +endobj +1188 0 obj +<< +/S /GoTo +/D [636 0 R /XYZ 67.0 159.613 null] +>> +endobj +1190 0 obj +<< +/S /GoTo +/D [641 0 R /XYZ 67.0 489.0 null] +>> +endobj +1192 0 obj +<< +/S /GoTo +/D [641 0 R /XYZ 67.0 174.069 null] +>> +endobj +1194 0 obj +<< +/S /GoTo +/D [641 0 R /XYZ 67.0 144.097 null] +>> +endobj +1197 0 obj +<< +/S /GoTo +/D [643 0 R /XYZ 67.0 483.109 null] +>> +endobj +1200 0 obj +<< +/S /GoTo +/D [643 0 R /XYZ 67.0 211.246 null] +>> +endobj +1202 0 obj +<< +/S /GoTo +/D [647 0 R /XYZ 67.0 682.0 null] +>> +endobj +1209 0 obj +<< +/S /GoTo +/D [654 0 R /XYZ 67.0 183.298 null] +>> +endobj +1211 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 586.0 null] +>> +endobj +1213 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 420.109 null] +>> +endobj +1219 0 obj +<< +/S /GoTo +/D [663 0 R /XYZ 67.0 317.909 null] +>> +endobj +1222 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 650.0 null] +>> +endobj +1231 0 obj +<< +/S /GoTo +/D [675 0 R /XYZ 67.0 617.0 null] +>> +endobj +1235 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 725.0 null] +>> +endobj +1237 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 637.866 null] +>> +endobj +1239 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 221.714 null] +>> +endobj +1241 0 obj +<< +/S /GoTo +/D [686 0 R /XYZ 67.0 445.0 null] +>> +endobj +1243 0 obj +<< +/S /GoTo +/D [686 0 R /XYZ 67.0 266.168 null] +>> +endobj +1245 0 obj +<< +/S /GoTo +/D [686 0 R /XYZ 67.0 193.196 null] +>> +endobj +1247 0 obj +<< +/S /GoTo +/D [693 0 R /XYZ 67.0 639.0 null] +>> +endobj +1249 0 obj +<< +/S /GoTo +/D [693 0 R /XYZ 67.0 253.349 null] +>> +endobj +1251 0 obj +<< +/S /GoTo +/D [701 0 R /XYZ 67.0 693.0 null] +>> +endobj +1254 0 obj +<< +/S /GoTo +/D [701 0 R /XYZ 67.0 384.218 null] +>> +endobj +1261 0 obj +<< +/S /GoTo +/D [709 0 R /XYZ 67.0 598.14 null] +>> +endobj +1263 0 obj +<< +/S /GoTo +/D [709 0 R /XYZ 67.0 462.308 null] +>> +endobj +1265 0 obj +<< +/S /GoTo +/D [709 0 R /XYZ 67.0 261.476 null] +>> +endobj +1267 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 725.0 null] +>> +endobj +1269 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 669.866 null] +>> +endobj +1271 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 606.894 null] +>> +endobj +1273 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 510.922 null] +>> +endobj +1275 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 436.95 null] +>> +endobj +1277 0 obj +<< +/S /GoTo +/D [715 0 R /XYZ 67.0 351.978 null] +>> +endobj +1279 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 725.0 null] +>> +endobj +1283 0 obj +<< +/S /GoTo +/D [722 0 R /XYZ 67.0 725.0 null] +>> +endobj +1288 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 725.0 null] +>> +endobj +1290 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 625.866 null] +>> +endobj +1292 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 513.174 null] +>> +endobj +1294 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 399.342 null] +>> +endobj +1296 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 274.51 null] +>> +endobj +1298 0 obj +<< +/S /GoTo +/D [736 0 R /XYZ 67.0 155.678 null] +>> +endobj +1300 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 624.14 null] +>> +endobj +1302 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 521.308 null] +>> +endobj +1304 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 375.476 null] +>> +endobj +1306 0 obj +<< +/S /GoTo +/D [752 0 R /XYZ 67.0 228.644 null] +>> +endobj +1308 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 703.0 null] +>> +endobj +1310 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 600.168 null] +>> +endobj +1312 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 486.336 null] +>> +endobj +1314 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 345.504 null] +>> +endobj +1316 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 242.672 null] +>> +endobj +1318 0 obj +<< +/S /GoTo +/D [762 0 R /XYZ 67.0 123.84 null] +>> +endobj +1320 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 651.14 null] +>> +endobj +1322 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 499.308 null] +>> +endobj +1324 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 276.476 null] +>> +endobj +1326 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 113.644 null] +>> +endobj +1328 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 619.14 null] +>> +endobj +1330 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 494.308 null] +>> +endobj +1332 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 391.476 null] +>> +endobj +1334 0 obj +<< +/S /GoTo +/D [789 0 R /XYZ 67.0 217.644 null] +>> +endobj +1336 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 708.0 null] +>> +endobj +1338 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 480.308 null] +>> +endobj +1340 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 361.476 null] +>> +endobj +1342 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 187.644 null] +>> +endobj +1344 0 obj +<< +/S /GoTo +/D [808 0 R /XYZ 67.0 84.812 null] +>> +endobj +1346 0 obj +<< +/S /GoTo +/D [823 0 R /XYZ 67.0 606.168 null] +>> +endobj +1348 0 obj +<< +/S /GoTo +/D [823 0 R /XYZ 67.0 487.336 null] +>> +endobj +1350 0 obj +<< +/S /GoTo +/D [831 0 R /XYZ 67.0 725.0 null] +>> +endobj +1352 0 obj +<< +/S /GoTo +/D [831 0 R /XYZ 67.0 475.866 null] +>> +endobj +1354 0 obj +<< +/S /GoTo +/D [831 0 R /XYZ 67.0 199.034 null] +>> +endobj +1356 0 obj +<< +/S /GoTo +/D [840 0 R /XYZ 67.0 536.14 null] +>> +endobj +1358 0 obj +<< +/S /GoTo +/D [840 0 R /XYZ 67.0 303.308 null] +>> +endobj +1360 0 obj +<< +/S /GoTo +/D [840 0 R /XYZ 67.0 92.476 null] +>> +endobj +1362 0 obj +<< +/S /GoTo +/D [850 0 R /XYZ 67.0 533.14 null] +>> +endobj +1364 0 obj +<< +/S /GoTo +/D [850 0 R /XYZ 67.0 289.308 null] +>> +endobj +1366 0 obj +<< +/S /GoTo +/D [865 0 R /XYZ 67.0 547.14 null] +>> +endobj +1369 0 obj +<< +/S /GoTo +/D [873 0 R /XYZ 67.0 414.78 null] +>> +endobj +1371 0 obj +<< +/S /GoTo +/D [873 0 R /XYZ 67.0 85.368 null] +>> +endobj +1374 0 obj +<< +/S /GoTo +/D [885 0 R /XYZ 67.0 725.0 null] +>> +endobj +1376 0 obj +<< +/S /GoTo +/D [897 0 R /XYZ 67.0 725.0 null] +>> +endobj +1378 0 obj +<< +/S /GoTo +/D [902 0 R /XYZ 67.0 725.0 null] +>> +endobj +1382 0 obj +<< +/S /GoTo +/D [902 0 R /XYZ 67.0 360.922 null] +>> +endobj +1384 0 obj +<< +/S /GoTo +/D [907 0 R /XYZ 67.0 725.0 null] +>> +endobj +1386 0 obj +<< +/S /GoTo +/D [920 0 R /XYZ 67.0 725.0 null] +>> +endobj +1394 0 obj +<< +/S /GoTo +/D [927 0 R /XYZ 67.0 206.056 null] +>> +endobj +1416 0 obj +<< +/S /GoTo +/D [1012 0 R /XYZ 67.0 725.0 null] +>> +endobj +1423 0 obj +<< +/S /GoTo +/D [1020 0 R /XYZ 67.0 725.0 null] +>> +endobj +1425 0 obj +<< +/S /GoTo +/D [1026 0 R /XYZ 67.0 725.0 null] +>> +endobj +xref +0 1439 +0000000000 65535 f +0000396086 00000 n +0000396983 00000 n +0000431375 00000 n +0000000015 00000 n +0000000071 00000 n +0000001444 00000 n +0000001564 00000 n +0000001589 00000 n +0000001770 00000 n +0000003970 00000 n +0000004092 00000 n +0000004371 00000 n +0000431495 00000 n +0000004506 00000 n +0000431560 00000 n +0000004641 00000 n +0000431625 00000 n +0000004775 00000 n +0000431690 00000 n +0000004909 00000 n +0000431755 00000 n +0000005044 00000 n +0000431822 00000 n +0000005179 00000 n +0000431889 00000 n +0000005314 00000 n +0000431956 00000 n +0000005449 00000 n +0000432021 00000 n +0000005584 00000 n +0000432087 00000 n +0000005719 00000 n +0000432152 00000 n +0000005854 00000 n +0000432217 00000 n +0000005988 00000 n +0000432284 00000 n +0000006123 00000 n +0000432351 00000 n +0000006258 00000 n +0000432416 00000 n +0000006393 00000 n +0000432483 00000 n +0000006528 00000 n +0000432550 00000 n +0000006663 00000 n +0000432615 00000 n +0000006798 00000 n +0000432682 00000 n +0000006933 00000 n +0000432749 00000 n +0000007068 00000 n +0000432814 00000 n +0000007203 00000 n +0000432881 00000 n +0000007338 00000 n +0000432946 00000 n +0000007473 00000 n +0000433011 00000 n +0000007607 00000 n +0000433078 00000 n +0000007742 00000 n +0000433145 00000 n +0000007877 00000 n +0000433210 00000 n +0000008012 00000 n +0000433275 00000 n +0000008147 00000 n +0000433342 00000 n +0000008282 00000 n +0000433407 00000 n +0000008417 00000 n +0000433474 00000 n +0000008552 00000 n +0000433539 00000 n +0000008687 00000 n +0000433606 00000 n +0000008822 00000 n +0000433671 00000 n +0000008957 00000 n +0000433738 00000 n +0000009091 00000 n +0000433805 00000 n +0000009225 00000 n +0000433872 00000 n +0000009358 00000 n +0000012044 00000 n +0000012167 00000 n +0000012589 00000 n +0000433937 00000 n +0000012720 00000 n +0000434004 00000 n +0000012851 00000 n +0000434071 00000 n +0000012982 00000 n +0000434138 00000 n +0000013113 00000 n +0000434203 00000 n +0000013244 00000 n +0000434270 00000 n +0000013376 00000 n +0000434336 00000 n +0000013511 00000 n +0000434404 00000 n +0000013646 00000 n +0000434471 00000 n +0000013781 00000 n +0000434539 00000 n +0000013916 00000 n +0000434607 00000 n +0000014051 00000 n +0000434674 00000 n +0000014186 00000 n +0000434740 00000 n +0000014321 00000 n +0000434806 00000 n +0000014455 00000 n +0000434874 00000 n +0000014590 00000 n +0000434940 00000 n +0000014725 00000 n +0000435008 00000 n +0000014860 00000 n +0000435075 00000 n +0000014995 00000 n +0000435143 00000 n +0000015130 00000 n +0000435209 00000 n +0000015265 00000 n +0000435277 00000 n +0000015400 00000 n +0000435345 00000 n +0000015535 00000 n +0000435413 00000 n +0000015670 00000 n +0000435481 00000 n +0000015805 00000 n +0000435547 00000 n +0000015940 00000 n +0000435615 00000 n +0000016075 00000 n +0000435683 00000 n +0000016210 00000 n +0000435749 00000 n +0000016345 00000 n +0000435817 00000 n +0000016480 00000 n +0000435885 00000 n +0000016615 00000 n +0000435953 00000 n +0000016750 00000 n +0000436019 00000 n +0000016884 00000 n +0000436087 00000 n +0000017019 00000 n +0000436153 00000 n +0000017154 00000 n +0000436221 00000 n +0000017289 00000 n +0000436287 00000 n +0000017424 00000 n +0000436355 00000 n +0000017559 00000 n +0000436423 00000 n +0000017694 00000 n +0000436489 00000 n +0000017829 00000 n +0000436557 00000 n +0000017964 00000 n +0000436623 00000 n +0000018099 00000 n +0000436691 00000 n +0000018234 00000 n +0000436759 00000 n +0000018369 00000 n +0000436826 00000 n +0000018504 00000 n +0000436894 00000 n +0000018639 00000 n +0000436962 00000 n +0000018774 00000 n +0000437028 00000 n +0000018909 00000 n +0000437096 00000 n +0000019043 00000 n +0000437164 00000 n +0000019177 00000 n +0000437232 00000 n +0000019310 00000 n +0000437299 00000 n +0000019443 00000 n +0000021953 00000 n +0000022079 00000 n +0000022444 00000 n +0000437367 00000 n +0000022577 00000 n +0000437434 00000 n +0000022710 00000 n +0000437501 00000 n +0000022843 00000 n +0000437567 00000 n +0000022976 00000 n +0000437635 00000 n +0000023109 00000 n +0000437703 00000 n +0000023242 00000 n +0000437769 00000 n +0000023377 00000 n +0000437837 00000 n +0000023512 00000 n +0000437905 00000 n +0000023647 00000 n +0000437971 00000 n +0000023782 00000 n +0000438039 00000 n +0000023918 00000 n +0000438107 00000 n +0000024054 00000 n +0000438173 00000 n +0000024189 00000 n +0000438241 00000 n +0000024324 00000 n +0000438307 00000 n +0000024459 00000 n +0000438375 00000 n +0000024594 00000 n +0000438443 00000 n +0000024729 00000 n +0000438511 00000 n +0000024864 00000 n +0000438577 00000 n +0000024999 00000 n +0000438645 00000 n +0000025134 00000 n +0000438713 00000 n +0000025269 00000 n +0000438781 00000 n +0000025403 00000 n +0000438848 00000 n +0000025538 00000 n +0000438916 00000 n +0000025673 00000 n +0000438984 00000 n +0000025808 00000 n +0000439050 00000 n +0000025943 00000 n +0000439118 00000 n +0000026078 00000 n +0000439186 00000 n +0000026213 00000 n +0000439254 00000 n +0000026348 00000 n +0000439321 00000 n +0000026483 00000 n +0000439389 00000 n +0000026618 00000 n +0000439455 00000 n +0000026753 00000 n +0000439523 00000 n +0000026888 00000 n +0000439591 00000 n +0000027023 00000 n +0000439657 00000 n +0000027158 00000 n +0000439725 00000 n +0000027293 00000 n +0000439793 00000 n +0000027428 00000 n +0000439861 00000 n +0000027563 00000 n +0000439927 00000 n +0000027698 00000 n +0000439995 00000 n +0000027833 00000 n +0000440063 00000 n +0000027968 00000 n +0000440131 00000 n +0000028102 00000 n +0000440198 00000 n +0000028235 00000 n +0000030220 00000 n +0000030346 00000 n +0000030687 00000 n +0000440266 00000 n +0000030820 00000 n +0000440333 00000 n +0000030953 00000 n +0000440401 00000 n +0000031086 00000 n +0000440469 00000 n +0000031219 00000 n +0000440537 00000 n +0000031352 00000 n +0000440603 00000 n +0000031485 00000 n +0000440671 00000 n +0000031618 00000 n +0000440739 00000 n +0000031751 00000 n +0000440807 00000 n +0000031884 00000 n +0000440875 00000 n +0000032017 00000 n +0000440942 00000 n +0000032150 00000 n +0000441009 00000 n +0000032283 00000 n +0000441077 00000 n +0000032416 00000 n +0000441145 00000 n +0000032549 00000 n +0000441213 00000 n +0000032682 00000 n +0000441280 00000 n +0000032815 00000 n +0000441348 00000 n +0000032948 00000 n +0000441416 00000 n +0000033081 00000 n +0000441484 00000 n +0000033214 00000 n +0000441550 00000 n +0000033347 00000 n +0000441618 00000 n +0000033480 00000 n +0000441686 00000 n +0000033613 00000 n +0000441754 00000 n +0000033746 00000 n +0000441820 00000 n +0000033879 00000 n +0000441888 00000 n +0000034011 00000 n +0000441956 00000 n +0000034144 00000 n +0000442022 00000 n +0000034279 00000 n +0000442090 00000 n +0000034414 00000 n +0000442158 00000 n +0000034548 00000 n +0000442225 00000 n +0000034682 00000 n +0000442293 00000 n +0000034817 00000 n +0000442360 00000 n +0000034952 00000 n +0000442427 00000 n +0000035087 00000 n +0000442495 00000 n +0000035222 00000 n +0000442562 00000 n +0000035357 00000 n +0000442630 00000 n +0000035492 00000 n +0000442697 00000 n +0000035626 00000 n +0000442763 00000 n +0000035760 00000 n +0000442831 00000 n +0000035894 00000 n +0000442897 00000 n +0000036027 00000 n +0000038267 00000 n +0000038393 00000 n +0000038726 00000 n +0000442963 00000 n +0000038859 00000 n +0000443029 00000 n +0000038994 00000 n +0000443097 00000 n +0000039129 00000 n +0000443165 00000 n +0000039264 00000 n +0000443233 00000 n +0000039399 00000 n +0000443299 00000 n +0000039533 00000 n +0000443365 00000 n +0000039668 00000 n +0000443433 00000 n +0000039803 00000 n +0000443501 00000 n +0000039938 00000 n +0000443569 00000 n +0000040073 00000 n +0000443636 00000 n +0000040208 00000 n +0000443702 00000 n +0000040343 00000 n +0000443770 00000 n +0000040478 00000 n +0000443838 00000 n +0000040613 00000 n +0000443904 00000 n +0000040747 00000 n +0000443970 00000 n +0000040882 00000 n +0000444038 00000 n +0000041016 00000 n +0000444106 00000 n +0000041151 00000 n +0000444174 00000 n +0000041287 00000 n +0000444241 00000 n +0000041423 00000 n +0000444309 00000 n +0000041559 00000 n +0000444377 00000 n +0000041695 00000 n +0000444443 00000 n +0000041831 00000 n +0000444511 00000 n +0000041967 00000 n +0000444579 00000 n +0000042103 00000 n +0000444647 00000 n +0000042238 00000 n +0000444715 00000 n +0000042373 00000 n +0000444781 00000 n +0000042508 00000 n +0000444847 00000 n +0000042643 00000 n +0000444913 00000 n +0000042778 00000 n +0000444979 00000 n +0000042913 00000 n +0000445047 00000 n +0000043048 00000 n +0000445115 00000 n +0000043183 00000 n +0000445182 00000 n +0000043318 00000 n +0000445249 00000 n +0000043451 00000 n +0000445318 00000 n +0000043584 00000 n +0000445387 00000 n +0000043716 00000 n +0000445456 00000 n +0000043848 00000 n +0000445524 00000 n +0000043978 00000 n +0000044971 00000 n +0000045097 00000 n +0000045198 00000 n +0000445591 00000 n +0000045331 00000 n +0000445658 00000 n +0000045466 00000 n +0000445725 00000 n +0000045601 00000 n +0000445794 00000 n +0000045736 00000 n +0000445861 00000 n +0000045871 00000 n +0000445928 00000 n +0000046006 00000 n +0000445997 00000 n +0000046141 00000 n +0000446066 00000 n +0000046276 00000 n +0000446133 00000 n +0000046411 00000 n +0000446200 00000 n +0000046545 00000 n +0000049497 00000 n +0000049623 00000 n +0000049812 00000 n +0000446267 00000 n +0000049951 00000 n +0000050090 00000 n +0000446335 00000 n +0000050229 00000 n +0000050366 00000 n +0000050504 00000 n +0000050642 00000 n +0000050780 00000 n +0000050919 00000 n +0000051058 00000 n +0000051196 00000 n +0000051335 00000 n +0000051472 00000 n +0000051611 00000 n +0000051750 00000 n +0000446403 00000 n +0000051889 00000 n +0000052027 00000 n +0000052166 00000 n +0000052305 00000 n +0000052444 00000 n +0000052583 00000 n +0000052722 00000 n +0000053833 00000 n +0000053959 00000 n +0000054004 00000 n +0000446471 00000 n +0000054143 00000 n +0000054282 00000 n +0000446539 00000 n +0000054419 00000 n +0000056600 00000 n +0000056726 00000 n +0000056771 00000 n +0000446607 00000 n +0000056910 00000 n +0000057049 00000 n +0000057187 00000 n +0000060236 00000 n +0000060362 00000 n +0000060391 00000 n +0000446675 00000 n +0000060526 00000 n +0000062367 00000 n +0000062477 00000 n +0000065239 00000 n +0000065365 00000 n +0000065394 00000 n +0000065533 00000 n +0000066632 00000 n +0000066742 00000 n +0000069739 00000 n +0000069865 00000 n +0000069902 00000 n +0000070041 00000 n +0000070180 00000 n +0000072429 00000 n +0000072539 00000 n +0000075585 00000 n +0000075711 00000 n +0000075764 00000 n +0000075902 00000 n +0000076040 00000 n +0000076178 00000 n +0000076316 00000 n +0000079527 00000 n +0000079653 00000 n +0000079690 00000 n +0000079824 00000 n +0000446743 00000 n +0000079963 00000 n +0000083174 00000 n +0000083300 00000 n +0000083353 00000 n +0000083488 00000 n +0000446811 00000 n +0000083623 00000 n +0000083758 00000 n +0000083897 00000 n +0000084650 00000 n +0000084760 00000 n +0000087424 00000 n +0000087550 00000 n +0000087579 00000 n +0000087717 00000 n +0000090613 00000 n +0000090739 00000 n +0000090768 00000 n +0000090907 00000 n +0000093444 00000 n +0000093570 00000 n +0000093615 00000 n +0000446879 00000 n +0000093750 00000 n +0000093885 00000 n +0000094021 00000 n +0000096826 00000 n +0000096952 00000 n +0000096981 00000 n +0000097118 00000 n +0000099542 00000 n +0000099668 00000 n +0000099697 00000 n +0000099835 00000 n +0000102821 00000 n +0000102947 00000 n +0000103024 00000 n +0000103163 00000 n +0000103302 00000 n +0000446947 00000 n +0000103439 00000 n +0000103578 00000 n +0000103714 00000 n +0000103851 00000 n +0000103988 00000 n +0000106869 00000 n +0000106995 00000 n +0000107048 00000 n +0000107182 00000 n +0000107320 00000 n +0000107458 00000 n +0000107595 00000 n +0000110441 00000 n +0000110567 00000 n +0000110612 00000 n +0000110751 00000 n +0000110890 00000 n +0000111029 00000 n +0000114083 00000 n +0000114209 00000 n +0000114262 00000 n +0000114401 00000 n +0000114540 00000 n +0000114679 00000 n +0000114816 00000 n +0000117081 00000 n +0000117207 00000 n +0000117244 00000 n +0000117383 00000 n +0000117519 00000 n +0000119498 00000 n +0000119608 00000 n +0000121892 00000 n +0000122002 00000 n +0000124178 00000 n +0000124304 00000 n +0000124333 00000 n +0000124470 00000 n +0000127307 00000 n +0000127433 00000 n +0000127486 00000 n +0000127625 00000 n +0000127764 00000 n +0000127903 00000 n +0000128038 00000 n +0000130379 00000 n +0000130505 00000 n +0000130542 00000 n +0000130676 00000 n +0000130812 00000 n +0000133552 00000 n +0000133678 00000 n +0000133707 00000 n +0000133845 00000 n +0000136304 00000 n +0000136430 00000 n +0000136467 00000 n +0000136605 00000 n +0000136744 00000 n +0000139431 00000 n +0000139541 00000 n +0000142415 00000 n +0000142541 00000 n +0000142570 00000 n +0000142708 00000 n +0000145743 00000 n +0000145869 00000 n +0000145906 00000 n +0000146044 00000 n +0000146183 00000 n +0000148782 00000 n +0000148892 00000 n +0000151233 00000 n +0000151343 00000 n +0000154512 00000 n +0000154638 00000 n +0000154675 00000 n +0000154809 00000 n +0000154947 00000 n +0000157519 00000 n +0000157629 00000 n +0000160329 00000 n +0000160455 00000 n +0000160484 00000 n +0000160623 00000 n +0000163589 00000 n +0000163699 00000 n +0000166600 00000 n +0000166710 00000 n +0000168782 00000 n +0000168892 00000 n +0000171142 00000 n +0000171252 00000 n +0000173743 00000 n +0000173869 00000 n +0000173898 00000 n +0000174036 00000 n +0000174877 00000 n +0000174987 00000 n +0000177541 00000 n +0000177667 00000 n +0000177704 00000 n +0000177843 00000 n +0000177980 00000 n +0000180794 00000 n +0000180920 00000 n +0000180973 00000 n +0000181111 00000 n +0000181250 00000 n +0000181388 00000 n +0000181524 00000 n +0000184139 00000 n +0000184265 00000 n +0000184326 00000 n +0000184460 00000 n +0000184597 00000 n +0000184733 00000 n +0000184872 00000 n +0000185011 00000 n +0000187525 00000 n +0000187651 00000 n +0000187680 00000 n +0000187818 00000 n +0000190433 00000 n +0000190559 00000 n +0000190588 00000 n +0000190726 00000 n +0000193130 00000 n +0000193256 00000 n +0000193301 00000 n +0000193438 00000 n +0000193575 00000 n +0000193711 00000 n +0000195511 00000 n +0000195637 00000 n +0000195674 00000 n +0000195813 00000 n +0000195952 00000 n +0000197211 00000 n +0000197321 00000 n +0000200132 00000 n +0000200258 00000 n +0000200367 00000 n +0000200506 00000 n +0000200645 00000 n +0000200784 00000 n +0000200923 00000 n +0000201062 00000 n +0000201198 00000 n +0000201335 00000 n +0000201472 00000 n +0000201609 00000 n +0000201746 00000 n +0000201883 00000 n +0000203829 00000 n +0000203955 00000 n +0000204080 00000 n +0000204217 00000 n +0000204356 00000 n +0000204493 00000 n +0000204630 00000 n +0000204767 00000 n +0000204904 00000 n +0000205041 00000 n +0000205178 00000 n +0000205315 00000 n +0000205452 00000 n +0000205589 00000 n +0000205724 00000 n +0000205861 00000 n +0000207741 00000 n +0000207867 00000 n +0000207944 00000 n +0000208077 00000 n +0000208214 00000 n +0000208351 00000 n +0000208488 00000 n +0000208627 00000 n +0000208762 00000 n +0000208897 00000 n +0000210372 00000 n +0000210498 00000 n +0000210631 00000 n +0000210768 00000 n +0000210905 00000 n +0000211042 00000 n +0000211179 00000 n +0000211316 00000 n +0000211453 00000 n +0000211590 00000 n +0000211727 00000 n +0000211864 00000 n +0000212001 00000 n +0000212138 00000 n +0000212275 00000 n +0000212408 00000 n +0000212541 00000 n +0000214643 00000 n +0000214769 00000 n +0000214846 00000 n +0000214979 00000 n +0000215112 00000 n +0000215249 00000 n +0000215386 00000 n +0000215523 00000 n +0000215660 00000 n +0000215797 00000 n +0000217693 00000 n +0000217819 00000 n +0000217968 00000 n +0000218101 00000 n +0000218234 00000 n +0000218367 00000 n +0000218504 00000 n +0000218641 00000 n +0000218778 00000 n +0000218915 00000 n +0000219052 00000 n +0000219189 00000 n +0000219326 00000 n +0000219463 00000 n +0000219600 00000 n +0000219737 00000 n +0000219874 00000 n +0000220009 00000 n +0000220144 00000 n +0000222234 00000 n +0000222360 00000 n +0000222477 00000 n +0000222614 00000 n +0000222751 00000 n +0000222888 00000 n +0000223025 00000 n +0000223162 00000 n +0000223299 00000 n +0000223436 00000 n +0000223573 00000 n +0000223710 00000 n +0000223847 00000 n +0000223984 00000 n +0000224121 00000 n +0000225015 00000 n +0000225141 00000 n +0000225202 00000 n +0000225340 00000 n +0000225477 00000 n +0000225616 00000 n +0000225753 00000 n +0000225890 00000 n +0000228381 00000 n +0000228507 00000 n +0000228568 00000 n +0000228707 00000 n +0000228846 00000 n +0000447015 00000 n +0000228985 00000 n +0000229122 00000 n +0000229258 00000 n +0000231277 00000 n +0000231403 00000 n +0000231480 00000 n +0000231613 00000 n +0000231751 00000 n +0000231889 00000 n +0000232028 00000 n +0000232165 00000 n +0000232304 00000 n +0000232441 00000 n +0000234547 00000 n +0000234673 00000 n +0000234790 00000 n +0000234924 00000 n +0000235059 00000 n +0000235194 00000 n +0000235327 00000 n +0000235466 00000 n +0000235605 00000 n +0000235743 00000 n +0000235879 00000 n +0000236016 00000 n +0000236154 00000 n +0000236293 00000 n +0000236430 00000 n +0000238859 00000 n +0000238985 00000 n +0000239046 00000 n +0000239180 00000 n +0000239313 00000 n +0000239451 00000 n +0000239588 00000 n +0000239725 00000 n +0000241955 00000 n +0000242081 00000 n +0000242110 00000 n +0000242247 00000 n +0000244364 00000 n +0000244490 00000 n +0000244535 00000 n +0000244673 00000 n +0000244810 00000 n +0000244947 00000 n +0000245375 00000 n +0000245485 00000 n +0000248655 00000 n +0000248781 00000 n +0000248834 00000 n +0000248972 00000 n +0000249110 00000 n +0000249249 00000 n +0000447083 00000 n +0000249388 00000 n +0000251613 00000 n +0000251739 00000 n +0000251768 00000 n +0000251902 00000 n +0000254509 00000 n +0000254635 00000 n +0000254672 00000 n +0000254810 00000 n +0000254948 00000 n +0000256908 00000 n +0000257034 00000 n +0000257071 00000 n +0000257210 00000 n +0000257347 00000 n +0000260037 00000 n +0000260163 00000 n +0000260232 00000 n +0000447151 00000 n +0000260370 00000 n +0000447219 00000 n +0000260509 00000 n +0000447287 00000 n +0000260647 00000 n +0000447355 00000 n +0000260786 00000 n +0000260925 00000 n +0000261064 00000 n +0000263918 00000 n +0000264044 00000 n +0000264089 00000 n +0000264228 00000 n +0000264366 00000 n +0000447423 00000 n +0000264505 00000 n +0000267884 00000 n +0000268010 00000 n +0000268071 00000 n +0000268210 00000 n +0000268349 00000 n +0000268486 00000 n +0000268624 00000 n +0000268762 00000 n +0000270333 00000 n +0000270459 00000 n +0000270488 00000 n +0000270623 00000 n +0000272193 00000 n +0000272319 00000 n +0000272380 00000 n +0000272519 00000 n +0000272658 00000 n +0000447491 00000 n +0000272795 00000 n +0000272934 00000 n +0000273073 00000 n +0000274123 00000 n +0000274249 00000 n +0000274326 00000 n +0000274461 00000 n +0000274600 00000 n +0000274739 00000 n +0000274878 00000 n +0000275017 00000 n +0000275155 00000 n +0000275292 00000 n +0000277173 00000 n +0000277299 00000 n +0000277360 00000 n +0000277495 00000 n +0000277630 00000 n +0000277765 00000 n +0000277964 00000 n +0000278099 00000 n +0000280560 00000 n +0000280670 00000 n +0000281484 00000 n +0000281594 00000 n +0000282721 00000 n +0000282831 00000 n +0000289266 00000 n +0000289392 00000 n +0000289639 00000 n +0000289835 00000 n +0000290030 00000 n +0000290234 00000 n +0000290436 00000 n +0000290637 00000 n +0000290828 00000 n +0000291019 00000 n +0000291210 00000 n +0000291401 00000 n +0000291591 00000 n +0000291782 00000 n +0000291973 00000 n +0000292164 00000 n +0000292354 00000 n +0000292545 00000 n +0000292735 00000 n +0000292925 00000 n +0000293115 00000 n +0000293306 00000 n +0000293496 00000 n +0000293687 00000 n +0000293878 00000 n +0000294069 00000 n +0000294259 00000 n +0000294450 00000 n +0000294640 00000 n +0000294831 00000 n +0000295020 00000 n +0000295710 00000 n +0000295839 00000 n +0000295879 00000 n +0000296067 00000 n +0000296254 00000 n +0000296810 00000 n +0000296939 00000 n +0000296970 00000 n +0000297154 00000 n +0000299741 00000 n +0000299853 00000 n +0000301333 00000 n +0000301462 00000 n +0000301493 00000 n +0000301631 00000 n +0000303713 00000 n +0000303825 00000 n +0000305057 00000 n +0000305186 00000 n +0000305235 00000 n +0000305374 00000 n +0000305512 00000 n +0000305650 00000 n +0000308161 00000 n +0000308290 00000 n +0000308330 00000 n +0000308469 00000 n +0000308609 00000 n +0000311571 00000 n +0000311700 00000 n +0000311740 00000 n +0000311879 00000 n +0000312019 00000 n +0000315289 00000 n +0000315418 00000 n +0000315575 00000 n +0000315715 00000 n +0000315855 00000 n +0000315995 00000 n +0000316135 00000 n +0000316275 00000 n +0000316415 00000 n +0000316552 00000 n +0000316692 00000 n +0000316832 00000 n +0000316970 00000 n +0000317108 00000 n +0000317247 00000 n +0000317385 00000 n +0000317525 00000 n +0000317665 00000 n +0000320448 00000 n +0000320577 00000 n +0000320743 00000 n +0000320876 00000 n +0000321010 00000 n +0000321146 00000 n +0000321281 00000 n +0000321417 00000 n +0000321553 00000 n +0000321688 00000 n +0000321821 00000 n +0000321957 00000 n +0000322093 00000 n +0000322229 00000 n +0000322369 00000 n +0000322509 00000 n +0000322647 00000 n +0000322787 00000 n +0000322927 00000 n +0000323612 00000 n +0000323724 00000 n +0000325774 00000 n +0000325903 00000 n +0000325952 00000 n +0000326130 00000 n +0000326306 00000 n +0000326483 00000 n +0000329661 00000 n +0000329790 00000 n +0000329812 00000 n +0000334784 00000 n +0000334913 00000 n +0000334935 00000 n +0000335581 00000 n +0000335710 00000 n +0000447558 00000 n +0000447615 00000 n +0000335732 00000 n +0000447682 00000 n +0000335933 00000 n +0000447748 00000 n +0000336134 00000 n +0000447815 00000 n +0000336288 00000 n +0000447881 00000 n +0000336494 00000 n +0000336682 00000 n +0000336927 00000 n +0000447948 00000 n +0000337107 00000 n +0000337467 00000 n +0000337734 00000 n +0000448015 00000 n +0000338017 00000 n +0000338276 00000 n +0000338594 00000 n +0000338801 00000 n +0000448084 00000 n +0000339127 00000 n +0000448151 00000 n +0000339452 00000 n +0000448220 00000 n +0000339703 00000 n +0000448289 00000 n +0000339932 00000 n +0000448356 00000 n +0000340134 00000 n +0000448425 00000 n +0000340303 00000 n +0000340581 00000 n +0000448494 00000 n +0000340800 00000 n +0000448563 00000 n +0000341085 00000 n +0000448632 00000 n +0000341276 00000 n +0000341473 00000 n +0000341745 00000 n +0000448699 00000 n +0000341977 00000 n +0000342196 00000 n +0000342457 00000 n +0000448766 00000 n +0000342705 00000 n +0000448833 00000 n +0000343001 00000 n +0000448900 00000 n +0000343286 00000 n +0000343672 00000 n +0000343958 00000 n +0000344367 00000 n +0000344637 00000 n +0000448969 00000 n +0000344875 00000 n +0000449036 00000 n +0000345253 00000 n +0000345528 00000 n +0000449105 00000 n +0000345710 00000 n +0000345951 00000 n +0000346228 00000 n +0000449174 00000 n +0000346511 00000 n +0000449243 00000 n +0000346812 00000 n +0000449312 00000 n +0000346962 00000 n +0000449379 00000 n +0000347271 00000 n +0000449448 00000 n +0000347675 00000 n +0000449515 00000 n +0000348060 00000 n +0000348303 00000 n +0000449584 00000 n +0000348546 00000 n +0000348926 00000 n +0000349278 00000 n +0000349744 00000 n +0000350081 00000 n +0000449653 00000 n +0000350430 00000 n +0000449722 00000 n +0000350695 00000 n +0000351059 00000 n +0000449789 00000 n +0000351289 00000 n +0000351530 00000 n +0000351755 00000 n +0000351961 00000 n +0000449857 00000 n +0000352232 00000 n +0000449926 00000 n +0000352476 00000 n +0000449995 00000 n +0000352759 00000 n +0000450064 00000 n +0000353009 00000 n +0000450131 00000 n +0000353222 00000 n +0000450200 00000 n +0000353487 00000 n +0000353795 00000 n +0000450269 00000 n +0000354026 00000 n +0000354261 00000 n +0000450338 00000 n +0000354574 00000 n +0000450407 00000 n +0000354822 00000 n +0000355076 00000 n +0000355459 00000 n +0000355665 00000 n +0000355969 00000 n +0000356290 00000 n +0000450474 00000 n +0000356583 00000 n +0000450543 00000 n +0000356818 00000 n +0000450610 00000 n +0000357050 00000 n +0000357304 00000 n +0000357609 00000 n +0000357815 00000 n +0000358147 00000 n +0000450679 00000 n +0000358440 00000 n +0000358680 00000 n +0000450748 00000 n +0000359032 00000 n +0000359268 00000 n +0000359508 00000 n +0000359772 00000 n +0000360054 00000 n +0000360277 00000 n +0000360586 00000 n +0000360918 00000 n +0000450815 00000 n +0000361258 00000 n +0000361494 00000 n +0000361689 00000 n +0000450882 00000 n +0000361906 00000 n +0000450949 00000 n +0000362296 00000 n +0000451018 00000 n +0000362470 00000 n +0000451087 00000 n +0000362672 00000 n +0000451154 00000 n +0000362910 00000 n +0000451223 00000 n +0000363139 00000 n +0000451292 00000 n +0000363306 00000 n +0000451359 00000 n +0000363483 00000 n +0000451428 00000 n +0000363713 00000 n +0000364036 00000 n +0000451495 00000 n +0000364380 00000 n +0000364678 00000 n +0000365072 00000 n +0000365538 00000 n +0000365904 00000 n +0000366353 00000 n +0000451564 00000 n +0000366721 00000 n +0000451632 00000 n +0000366952 00000 n +0000451701 00000 n +0000367178 00000 n +0000451770 00000 n +0000367423 00000 n +0000451837 00000 n +0000367784 00000 n +0000451906 00000 n +0000367990 00000 n +0000451975 00000 n +0000368260 00000 n +0000452044 00000 n +0000368447 00000 n +0000452112 00000 n +0000368699 00000 n +0000452181 00000 n +0000368953 00000 n +0000369258 00000 n +0000369505 00000 n +0000452248 00000 n +0000369756 00000 n +0000370045 00000 n +0000370254 00000 n +0000370591 00000 n +0000452315 00000 n +0000370829 00000 n +0000452382 00000 n +0000371131 00000 n +0000452451 00000 n +0000371376 00000 n +0000452520 00000 n +0000371619 00000 n +0000452589 00000 n +0000371880 00000 n +0000452657 00000 n +0000372111 00000 n +0000452726 00000 n +0000372342 00000 n +0000452794 00000 n +0000372597 00000 n +0000452863 00000 n +0000372822 00000 n +0000452932 00000 n +0000373065 00000 n +0000453001 00000 n +0000373314 00000 n +0000453068 00000 n +0000373574 00000 n +0000453137 00000 n +0000373828 00000 n +0000453206 00000 n +0000374082 00000 n +0000453275 00000 n +0000374342 00000 n +0000453344 00000 n +0000374602 00000 n +0000453412 00000 n +0000374856 00000 n +0000453480 00000 n +0000375128 00000 n +0000453549 00000 n +0000375364 00000 n +0000453618 00000 n +0000375594 00000 n +0000453687 00000 n +0000375884 00000 n +0000453755 00000 n +0000376138 00000 n +0000453824 00000 n +0000376392 00000 n +0000453893 00000 n +0000376646 00000 n +0000453962 00000 n +0000376888 00000 n +0000454029 00000 n +0000377142 00000 n +0000454098 00000 n +0000377462 00000 n +0000454167 00000 n +0000377686 00000 n +0000454236 00000 n +0000377928 00000 n +0000454304 00000 n +0000378170 00000 n +0000454373 00000 n +0000378418 00000 n +0000454442 00000 n +0000378638 00000 n +0000454509 00000 n +0000378887 00000 n +0000454578 00000 n +0000379127 00000 n +0000454647 00000 n +0000379377 00000 n +0000454715 00000 n +0000379669 00000 n +0000454784 00000 n +0000379949 00000 n +0000454852 00000 n +0000380217 00000 n +0000454920 00000 n +0000380443 00000 n +0000454989 00000 n +0000380717 00000 n +0000381023 00000 n +0000455057 00000 n +0000381354 00000 n +0000455125 00000 n +0000381610 00000 n +0000381905 00000 n +0000455193 00000 n +0000382241 00000 n +0000455260 00000 n +0000382593 00000 n +0000455327 00000 n +0000382855 00000 n +0000383150 00000 n +0000383304 00000 n +0000455394 00000 n +0000383474 00000 n +0000455463 00000 n +0000383629 00000 n +0000455530 00000 n +0000383959 00000 n +0000384261 00000 n +0000384523 00000 n +0000384753 00000 n +0000385037 00000 n +0000385361 00000 n +0000385715 00000 n +0000455597 00000 n +0000386010 00000 n +0000386329 00000 n +0000386591 00000 n +0000386869 00000 n +0000387071 00000 n +0000387284 00000 n +0000387582 00000 n +0000387724 00000 n +0000387894 00000 n +0000388100 00000 n +0000388252 00000 n +0000388451 00000 n +0000388645 00000 n +0000388811 00000 n +0000389025 00000 n +0000389241 00000 n +0000389562 00000 n +0000389789 00000 n +0000390013 00000 n +0000390246 00000 n +0000390491 00000 n +0000455666 00000 n +0000390690 00000 n +0000391039 00000 n +0000391307 00000 n +0000391615 00000 n +0000391892 00000 n +0000392189 00000 n +0000455734 00000 n +0000392505 00000 n +0000455802 00000 n +0000392838 00000 n +0000393111 00000 n +0000393504 00000 n +0000393885 00000 n +0000394228 00000 n +0000394632 00000 n +0000394959 00000 n +0000395146 00000 n +0000395523 00000 n +0000395642 00000 n +0000395754 00000 n +0000395867 00000 n +0000395975 00000 n +trailer +<< +/Size 1439 +/Root 2 0 R +/Info 4 0 R +>> +startxref +455870 +%%EOF diff --git a/dav/SabreDAV/docs/rfc5051.txt b/dav/SabreDAV/docs/rfc5051.txt new file mode 100644 index 000000000..0a4479cad --- /dev/null +++ b/dav/SabreDAV/docs/rfc5051.txt @@ -0,0 +1,395 @@ + + + + + + +Network Working Group M. Crispin +Request for Comments: 5051 University of Washington +Category: Standards Track October 2007 + + + i;unicode-casemap - Simple Unicode Collation Algorithm + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Abstract + + This document describes "i;unicode-casemap", a simple case- + insensitive collation for Unicode strings. It provides equality, + substring, and ordering operations. + +1. Introduction + + The "i;ascii-casemap" collation described in [COMPARATOR] is quite + simple to implement and provides case-independent comparisons for the + 26 Latin alphabetics. It is specified as the default and/or baseline + comparator in some application protocols, e.g., [IMAP-SORT]. + + However, the "i;ascii-casemap" collation does not produce + satisfactory results with non-ASCII characters. It is possible, with + a modest extension, to provide a more sophisticated collation with + greater multilingual applicability than "i;ascii-casemap". This + extension provides case-independent comparisons for a much greater + number of characters. It also collates characters with diacriticals + with the non-diacritical character forms. + + This collation, "i;unicode-casemap", is intended to be an alternative + to, and preferred over, "i;ascii-casemap". It does not replace the + "i;basic" collation described in [BASIC]. + +2. Unicode Casemap Collation Description + + The "i;unicode-casemap" collation is a simple collation which is + case-insensitive in its treatment of characters. It provides + equality, substring, and ordering operations. The validity test + operation returns "valid" for any input. + + + + + +Crispin Standards Track [Page 1] + +RFC 5051 i;unicode-casemap October 2007 + + + This collation allows strings in arbitrary (and mixed) character + sets, as long as the character set for each string is identified and + it is possible to convert the string to Unicode. Strings which have + an unidentified character set and/or cannot be converted to Unicode + are not rejected, but are treated as binary. + + Each input string is prepared by converting it to a "titlecased + canonicalized UTF-8" string according to the following steps, using + UnicodeData.txt ([UNICODE-DATA]): + + (1) A Unicode codepoint is obtained from the input string. + + (a) If the input string is in a known charset that can be + converted to Unicode, a sequence in the string's charset + is read and checked for validity according to the rules of + that charset. If the sequence is valid, it is converted + to a Unicode codepoint. Note that for input strings in + UTF-8, the UTF-8 sequence must be valid according to the + rules of [UTF-8]; e.g., overlong UTF-8 sequences are + invalid. + + (b) If the input string is in an unknown charset, or an + invalid sequence occurs in step (1)(a), conversion ceases. + No further preparation is performed, and any partial + preparation results are discarded. The original string is + used unchanged with the i;octet comparator. + + (2) The following steps, using UnicodeData.txt ([UNICODE-DATA]), + are performed on the resulting codepoint from step (1)(a). + + (a) If the codepoint has a titlecase property in + UnicodeData.txt (this is normally the same as the + uppercase property), the codepoint is converted to the + codepoints in the titlecase property. + + (b) If the resulting codepoint from (2)(a) has a decomposition + property of any type in UnicodeData.txt, the codepoint is + converted to the codepoints in the decomposition property. + This step is recursively applied to each of the resulting + codepoints until no more decomposition is possible + (effectively Normalization Form KD). + + Example: codepoint U+01C4 (LATIN CAPITAL LETTER DZ WITH CARON) + has a titlecase property of U+01C5 (LATIN CAPITAL LETTER D + WITH SMALL LETTER Z WITH CARON). Codepoint U+01C5 has a + decomposition property of U+0044 (LATIN CAPITAL LETTER D) + U+017E (LATIN SMALL LETTER Z WITH CARON). U+017E has a + decomposition property of U+007A (LATIN SMALL LETTER Z) U+030c + + + +Crispin Standards Track [Page 2] + +RFC 5051 i;unicode-casemap October 2007 + + + (COMBINING CARON). Neither U+0044, U+007A, nor U+030C have + any decomposition properties. Therefore, U+01C4 is converted + to U+0044 U+007A U+030C by this step. + + (3) The resulting codepoint(s) from step (2) is/are appended, in + UTF-8 format, to the "titlecased canonicalized UTF-8" string. + + (4) Repeat from step (1) until there is no more data in the input + string. + + Following the above preparation process on each string, the equality, + ordering, and substring operations are as for i;octet. + + It is permitted to use an alternative implementation of the above + preparation process if it produces the same results. For example, it + may be more convenient for an implementation to convert all input + strings to a sequence of UTF-16 or UTF-32 values prior to performing + any of the step (2) actions. Similarly, if all input strings are (or + are convertible to) Unicode, it may be possible to use UTF-32 as an + alternative to UTF-8 in step (3). + + Note: UTF-16 is unsuitable as an alternative to UTF-8 in step (3), + because UTF-16 surrogates will cause i;octet to collate codepoints + U+E0000 through U+FFFF after non-BMP codepoints. + + This collation is not locale sensitive. Consequently, care should be + taken when using OS-supplied functions to implement this collation. + Functions such as strcasecmp and toupper are sometimes locale + sensitive and may inconsistently casemap letters. + + The i;unicode-casemap collation is well suited to use with many + Internet protocols and computer languages. Use with natural language + is often inappropriate; even though the collation apparently supports + languages such as Swahili and English, in real-world use it tends to + mis-sort a number of types of string: + + o people and place names containing scripts that are not collated + according to "alphabetical order". + o words with characters that have diacriticals. However, + i;unicode-casemap generally does a better job than i;ascii-casemap + for most (but not all) languages. For example, German umlaut + letters will sort correctly, but some Scandinavian letters will + not. + o names such as "Lloyd" (which in Welsh sorts after "Lyon", unlike + in English), + o strings containing other non-letter symbols; e.g., euro and pound + sterling symbols, quotation marks other than '"', dashes/hyphens, + etc. + + + +Crispin Standards Track [Page 3] + +RFC 5051 i;unicode-casemap October 2007 + + +3. Unicode Casemap Collation Registration + + + + + i;unicode-casemap + Unicode Casemap + equality order substring + RFC 5051 + IETF + mrc@cac.washington.edu + + +4. Security Considerations + + The security considerations for [UTF-8], [STRINGPREP], and [UNICODE- + SECURITY] apply and are normative to this specification. + + The results from this comparator will vary depending upon the + implementation for several reasons. Implementations MUST consider + whether these possibilities are a problem for their use case: + + 1) New characters added in Unicode may have decomposition or + titlecase properties that will not be known to an implementation + based upon an older revision of Unicode. This impacts step (2). + + 2) Step (2)(b) defines a subset of Normalization Form KD (NFKD) that + does not require normalization of out-of-order diacriticals. + However, an implementation MAY use an NFKD library routine that + does such normalization. This impacts step (2)(b) and possibly + also step (1)(a), and is an issue only with ill-formed UTF-8 + input. + + 3) The set of charsets handled in step (1)(a) is open-ended. UTF-8 + (and, by extension, US-ASCII) are the only mandatory-to-implement + charsets. This impacts step (1)(a). + + Implementations SHOULD, as far as feasible, support all the + charsets they are likely to encounter in the input data, in order + to avoid poor collation caused by the fall through to the (1)(b) + rule. + + 4) Other charsets may have revisions which add new characters that + are not known to an implementation based upon an older revision. + This impacts step (1)(a) and possibly also step (1)(b). + + + + + + +Crispin Standards Track [Page 4] + +RFC 5051 i;unicode-casemap October 2007 + + + An attacker may create input that is ill-formed or in an unknown + charset, with the intention of impacting the results of this + comparator or exploiting other parts of the system which process this + input in different ways. Note, however, that even well-formed data + in a known charset can impact the result of this comparator in + unexpected ways. For example, an attacker can substitute U+0041 + (LATIN CAPITAL LETTER A) with U+0391 (GREEK CAPITAL LETTER ALPHA) or + U+0410 (CYRILLIC CAPITAL LETTER A) in the intention of causing a + non-match of strings which visually appear the same and/or causing + the string to appear elsewhere in a sort. + +5. IANA Considerations + + The i;unicode-casemap collation defined in section 2 has been added + to the registry of collations defined in [COMPARATOR]. + +6. Normative References + + [COMPARATOR] Newman, C., Duerst, M., and A. Gulbrandsen, + "Internet Application Protocol Collation + Registry", RFC 4790, February 2007. + + [STRINGPREP] Hoffman, P. and M. Blanchet, "Preparation of + Internationalized Strings ("stringprep")", RFC + 3454, December 2002. + + [UTF-8] Yergeau, F., "UTF-8, a transformation format of + ISO 10646", STD 63, RFC 3629, November 2003. + + [UNICODE-DATA] + + Although the UnicodeData.txt file referenced + here is part of the Unicode standard, it is + subject to change as new characters are added + to Unicode and errors are corrected in Unicode + revisions. As a result, it may be less stable + than might otherwise be implied by the + standards status of this specification. + + [UNICODE-SECURITY] Davis, M. and M. Suignard, "Unicode Security + Considerations", February 2006, + . + + + + + + + + +Crispin Standards Track [Page 5] + +RFC 5051 i;unicode-casemap October 2007 + + +7. Informative References + + [BASIC] Newman, C., Duerst, M., and A. Gulbrandsen, + "i;basic - the Unicode Collation Algorithm", + Work in Progress, March 2007. + + [IMAP-SORT] Crispin, M. and K. Murchison, "Internet Message + Access Protocol - SORT and THREAD Extensions", + Work in Progress, September 2007. + +Author's Address + + Mark R. Crispin + Networks and Distributed Computing + University of Washington + 4545 15th Avenue NE + Seattle, WA 98105-4527 + + Phone: +1 (206) 543-5762 + EMail: MRC@CAC.Washington.EDU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Crispin Standards Track [Page 6] + +RFC 5051 i;unicode-casemap October 2007 + + +Full Copyright Statement + + Copyright (C) The IETF Trust (2007). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + + + + + + + + + + + + +Crispin Standards Track [Page 7] + diff --git a/dav/SabreDAV/docs/rfc5397.txt b/dav/SabreDAV/docs/rfc5397.txt new file mode 100644 index 000000000..616055e70 --- /dev/null +++ b/dav/SabreDAV/docs/rfc5397.txt @@ -0,0 +1,281 @@ + + + +Network Working Group W. Sanchez +Request for Comments: 5397 C. Daboo +Category: Standards Track Apple Inc. + December 2008 + + + WebDAV Current Principal Extension + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (c) 2008 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. + +Abstract + + This specification defines a new WebDAV property that allows clients + to quickly determine the principal corresponding to the current + authenticated user. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 2. Conventions Used in This Document . . . . . . . . . . . . . . . 2 + 3. DAV:current-user-principal . . . . . . . . . . . . . . . . . . 3 + 4. Security Considerations . . . . . . . . . . . . . . . . . . . . 4 + 5. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . 4 + 6. Normative References . . . . . . . . . . . . . . . . . . . . . 4 + + + + + + + + + +Sanchez & Daboo Standards Track [Page 1] + +RFC 5397 WebDAV Current Principal December 2008 + + +1. Introduction + + WebDAV [RFC4918] is an extension to HTTP [RFC2616] to support + improved document authoring capabilities. The WebDAV Access Control + Protocol ("WebDAV ACL") [RFC3744] extension adds access control + capabilities to WebDAV. It introduces the concept of a "principal" + resource, which is used to represent information about authenticated + entities on the system. + + Some clients have a need to determine which [RFC3744] principal a + server is associating with the currently authenticated HTTP user. + While [RFC3744] defines a DAV:current-user-privilege-set property for + retrieving the privileges granted to that principal, there is no + recommended way to identify the principal in question, which is + necessary to perform other useful operations. For example, a client + may wish to determine which groups the current user is a member of, + or modify a property of the principal resource associated with the + current user. + + The DAV:principal-match REPORT provides some useful functionality, + but there are common situations where the results from that query can + be ambiguous. For example, not only is an individual user principal + returned, but also every group principal that the user is a member + of, and there is no clear way to distinguish which is which. + + This specification proposes an extension to WebDAV ACL that adds a + DAV:current-user-principal property to resources under access control + on the server. This property provides a URL to a principal resource + corresponding to the currently authenticated user. This allows a + client to "bootstrap" itself by performing additional queries on the + principal resource to obtain additional information from that + resource, which is the purpose of this extension. Note that while it + is possible for multiple URLs to refer to the same principal + resource, or for multiple principal resources to correspond to a + single principal, this specification only allows for a single http(s) + URL in the DAV:current-user-principal property. If a client wishes + to obtain alternate URLs for the principal, it can query the + principal resource for this information; it is not the purpose of + this extension to provide a complete list of such URLs, but simply to + provide a means to locate a resource which contains that (and other) + information. + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + + + +Sanchez & Daboo Standards Track [Page 2] + +RFC 5397 WebDAV Current Principal December 2008 + + + When XML element types in the namespace "DAV:" are referenced in this + document outside of the context of an XML fragment, the string "DAV:" + will be prefixed to the element type names. + + Processing of XML by clients and servers MUST follow the rules + defined in Section 17 of WebDAV [RFC4918]. + + Some of the declarations refer to XML elements defined by WebDAV + [RFC4918]. + +3. DAV:current-user-principal + + Name: current-user-principal + + Namespace: DAV: + + Purpose: Indicates a URL for the currently authenticated user's + principal resource on the server. + + Value: A single DAV:href or DAV:unauthenticated element. + + Protected: This property is computed on a per-request basis, and + therefore is protected. + + Description: The DAV:current-user-principal property contains either + a DAV:href or DAV:unauthenticated XML element. The DAV:href + element contains a URL to a principal resource corresponding to + the currently authenticated user. That URL MUST be one of the + URLs in the DAV:principal-URL or DAV:alternate-URI-set properties + defined on the principal resource and MUST be an http(s) scheme + URL. When authentication has not been done or has failed, this + property MUST contain the DAV:unauthenticated pseudo-principal. + + In some cases, there may be multiple principal resources + corresponding to the same authenticated principal. In that case, + the server is free to choose any one of the principal resource + URIs for the value of the DAV:current-user-principal property. + However, servers SHOULD be consistent and use the same principal + resource URI for each authenticated principal. + + COPY/MOVE behavior: This property is computed on a per-request + basis, and is thus never copied or moved. + + Definition: + + + + + + + +Sanchez & Daboo Standards Track [Page 3] + +RFC 5397 WebDAV Current Principal December 2008 + + + Example: + + + /principals/users/cdaboo + + +4. Security Considerations + + This specification does not introduce any additional security issues + beyond those defined for HTTP [RFC2616], WebDAV [RFC4918], and WebDAV + ACL [RFC3744]. + +5. Acknowledgments + + This specification is based on discussions that took place within the + Calendaring and Scheduling Consortium's CalDAV Technical Committee. + The authors thank the participants of that group for their input. + + The authors thank Julian Reschke for his valuable input via the + WebDAV working group mailing list. + +6. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web + Distributed Authoring and Versioning (WebDAV) + Access Control Protocol", RFC 3744, May 2004. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + +Authors' Addresses + + Wilfredo Sanchez + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: wsanchez@wsanchez.net + URI: http://www.apple.com/ + + + + +Sanchez & Daboo Standards Track [Page 4] + +RFC 5397 WebDAV Current Principal December 2008 + + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Sanchez & Daboo Standards Track [Page 5] + + diff --git a/dav/SabreDAV/docs/rfc5545.txt b/dav/SabreDAV/docs/rfc5545.txt new file mode 100644 index 000000000..47ea31d60 --- /dev/null +++ b/dav/SabreDAV/docs/rfc5545.txt @@ -0,0 +1,9411 @@ + + + + + + +Network Working Group B. Desruisseaux, Ed. +Request for Comments: 5545 Oracle +Obsoletes: 2445 September 2009 +Category: Standards Track + + + Internet Calendaring and Scheduling Core Object Specification + (iCalendar) + +Abstract + +This document defines the iCalendar data format for representing and +exchanging calendaring and scheduling information such as events, +to-dos, journal entries, and free/busy information, independent of any +particular calendar service or protocol. + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright and License Notice + + Copyright (c) 2009 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + + + +Desruisseaux Standards Track [Page 1] + +RFC 5545 iCalendar September 2009 + + + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 5 + 2. Basic Grammar and Conventions . . . . . . . . . . . . . . . . 6 + 2.1. Formatting Conventions . . . . . . . . . . . . . . . . . 6 + 2.2. Related Memos . . . . . . . . . . . . . . . . . . . . . . 7 + 3. iCalendar Object Specification . . . . . . . . . . . . . . . 8 + 3.1. Content Lines . . . . . . . . . . . . . . . . . . . . . . 8 + 3.1.1. List and Field Separators . . . . . . . . . . . . . . 11 + 3.1.2. Multiple Values . . . . . . . . . . . . . . . . . . . 11 + 3.1.3. Binary Content . . . . . . . . . . . . . . . . . . . 11 + 3.1.4. Character Set . . . . . . . . . . . . . . . . . . . . 12 + 3.2. Property Parameters . . . . . . . . . . . . . . . . . . . 12 + 3.2.1. Alternate Text Representation . . . . . . . . . . . . 13 + 3.2.2. Common Name . . . . . . . . . . . . . . . . . . . . . 15 + 3.2.3. Calendar User Type . . . . . . . . . . . . . . . . . 15 + 3.2.4. Delegators . . . . . . . . . . . . . . . . . . . . . 16 + 3.2.5. Delegatees . . . . . . . . . . . . . . . . . . . . . 16 + 3.2.6. Directory Entry Reference . . . . . . . . . . . . . . 17 + 3.2.7. Inline Encoding . . . . . . . . . . . . . . . . . . . 17 + 3.2.8. Format Type . . . . . . . . . . . . . . . . . . . . . 18 + 3.2.9. Free/Busy Time Type . . . . . . . . . . . . . . . . . 19 + 3.2.10. Language . . . . . . . . . . . . . . . . . . . . . . 20 + 3.2.11. Group or List Membership . . . . . . . . . . . . . . 20 + 3.2.12. Participation Status . . . . . . . . . . . . . . . . 21 + 3.2.13. Recurrence Identifier Range . . . . . . . . . . . . . 22 + 3.2.14. Alarm Trigger Relationship . . . . . . . . . . . . . 23 + 3.2.15. Relationship Type . . . . . . . . . . . . . . . . . . 24 + 3.2.16. Participation Role . . . . . . . . . . . . . . . . . 25 + 3.2.17. RSVP Expectation . . . . . . . . . . . . . . . . . . 25 + 3.2.18. Sent By . . . . . . . . . . . . . . . . . . . . . . . 26 + 3.2.19. Time Zone Identifier . . . . . . . . . . . . . . . . 26 + 3.2.20. Value Data Types . . . . . . . . . . . . . . . . . . 28 + 3.3. Property Value Data Types . . . . . . . . . . . . . . . . 29 + 3.3.1. Binary . . . . . . . . . . . . . . . . . . . . . . . 29 + 3.3.2. Boolean . . . . . . . . . . . . . . . . . . . . . . . 30 + 3.3.3. Calendar User Address . . . . . . . . . . . . . . . . 30 + 3.3.4. Date . . . . . . . . . . . . . . . . . . . . . . . . 31 + 3.3.5. Date-Time . . . . . . . . . . . . . . . . . . . . . . 31 + 3.3.6. Duration . . . . . . . . . . . . . . . . . . . . . . 34 + 3.3.7. Float . . . . . . . . . . . . . . . . . . . . . . . . 35 + 3.3.8. Integer . . . . . . . . . . . . . . . . . . . . . . . 35 + 3.3.9. Period of Time . . . . . . . . . . . . . . . . . . . 36 + 3.3.10. Recurrence Rule . . . . . . . . . . . . . . . . . . . 37 + 3.3.11. Text . . . . . . . . . . . . . . . . . . . . . . . . 45 + + + +Desruisseaux Standards Track [Page 2] + +RFC 5545 iCalendar September 2009 + + + 3.3.12. Time . . . . . . . . . . . . . . . . . . . . . . . . 46 + 3.3.13. URI . . . . . . . . . . . . . . . . . . . . . . . . . 48 + 3.3.14. UTC Offset . . . . . . . . . . . . . . . . . . . . . 49 + 3.4. iCalendar Object . . . . . . . . . . . . . . . . . . . . 49 + 3.5. Property . . . . . . . . . . . . . . . . . . . . . . . . 50 + 3.6. Calendar Components . . . . . . . . . . . . . . . . . . . 50 + 3.6.1. Event Component . . . . . . . . . . . . . . . . . . . 52 + 3.6.2. To-Do Component . . . . . . . . . . . . . . . . . . . 56 + 3.6.3. Journal Component . . . . . . . . . . . . . . . . . . 58 + 3.6.4. Free/Busy Component . . . . . . . . . . . . . . . . . 60 + 3.6.5. Time Zone Component . . . . . . . . . . . . . . . . . 63 + 3.6.6. Alarm Component . . . . . . . . . . . . . . . . . . . 72 + 3.7. Calendar Properties . . . . . . . . . . . . . . . . . . . 77 + 3.7.1. Calendar Scale . . . . . . . . . . . . . . . . . . . 77 + 3.7.2. Method . . . . . . . . . . . . . . . . . . . . . . . 78 + 3.7.3. Product Identifier . . . . . . . . . . . . . . . . . 79 + 3.7.4. Version . . . . . . . . . . . . . . . . . . . . . . . 80 + 3.8. Component Properties . . . . . . . . . . . . . . . . . . 81 + 3.8.1. Descriptive Component Properties . . . . . . . . . . 81 + 3.8.1.1. Attachment . . . . . . . . . . . . . . . . . . . 81 + 3.8.1.2. Categories . . . . . . . . . . . . . . . . . . . 82 + 3.8.1.3. Classification . . . . . . . . . . . . . . . . . 83 + 3.8.1.4. Comment . . . . . . . . . . . . . . . . . . . . . 84 + 3.8.1.5. Description . . . . . . . . . . . . . . . . . . . 85 + 3.8.1.6. Geographic Position . . . . . . . . . . . . . . . 87 + 3.8.1.7. Location . . . . . . . . . . . . . . . . . . . . 88 + 3.8.1.8. Percent Complete . . . . . . . . . . . . . . . . 89 + 3.8.1.9. Priority . . . . . . . . . . . . . . . . . . . . 90 + 3.8.1.10. Resources . . . . . . . . . . . . . . . . . . . . 92 + 3.8.1.11. Status . . . . . . . . . . . . . . . . . . . . . 93 + 3.8.1.12. Summary . . . . . . . . . . . . . . . . . . . . . 94 + 3.8.2. Date and Time Component Properties . . . . . . . . . 95 + 3.8.2.1. Date-Time Completed . . . . . . . . . . . . . . . 95 + 3.8.2.2. Date-Time End . . . . . . . . . . . . . . . . . . 96 + 3.8.2.3. Date-Time Due . . . . . . . . . . . . . . . . . . 97 + 3.8.2.4. Date-Time Start . . . . . . . . . . . . . . . . . 99 + 3.8.2.5. Duration . . . . . . . . . . . . . . . . . . . . 100 + 3.8.2.6. Free/Busy Time . . . . . . . . . . . . . . . . . 101 + 3.8.2.7. Time Transparency . . . . . . . . . . . . . . . . 102 + 3.8.3. Time Zone Component Properties . . . . . . . . . . . 103 + 3.8.3.1. Time Zone Identifier . . . . . . . . . . . . . . 103 + 3.8.3.2. Time Zone Name . . . . . . . . . . . . . . . . . 105 + 3.8.3.3. Time Zone Offset From . . . . . . . . . . . . . . 106 + 3.8.3.4. Time Zone Offset To . . . . . . . . . . . . . . . 106 + 3.8.3.5. Time Zone URL . . . . . . . . . . . . . . . . . . 107 + 3.8.4. Relationship Component Properties . . . . . . . . . . 108 + 3.8.4.1. Attendee . . . . . . . . . . . . . . . . . . . . 108 + 3.8.4.2. Contact . . . . . . . . . . . . . . . . . . . . . 111 + + + +Desruisseaux Standards Track [Page 3] + +RFC 5545 iCalendar September 2009 + + + 3.8.4.3. Organizer . . . . . . . . . . . . . . . . . . . . 113 + 3.8.4.4. Recurrence ID . . . . . . . . . . . . . . . . . . 114 + 3.8.4.5. Related To . . . . . . . . . . . . . . . . . . . 117 + 3.8.4.6. Uniform Resource Locator . . . . . . . . . . . . 118 + 3.8.4.7. Unique Identifier . . . . . . . . . . . . . . . . 119 + 3.8.5. Recurrence Component Properties . . . . . . . . . . . 120 + 3.8.5.1. Exception Date-Times . . . . . . . . . . . . . . 120 + 3.8.5.2. Recurrence Date-Times . . . . . . . . . . . . . . 122 + 3.8.5.3. Recurrence Rule . . . . . . . . . . . . . . . . . 124 + 3.8.6. Alarm Component Properties . . . . . . . . . . . . . 134 + 3.8.6.1. Action . . . . . . . . . . . . . . . . . . . . . 134 + 3.8.6.2. Repeat Count . . . . . . . . . . . . . . . . . . 135 + 3.8.6.3. Trigger . . . . . . . . . . . . . . . . . . . . . 135 + 3.8.7. Change Management Component Properties . . . . . . . 138 + 3.8.7.1. Date-Time Created . . . . . . . . . . . . . . . . 138 + 3.8.7.2. Date-Time Stamp . . . . . . . . . . . . . . . . . 139 + 3.8.7.3. Last Modified . . . . . . . . . . . . . . . . . . 140 + 3.8.7.4. Sequence Number . . . . . . . . . . . . . . . . . 141 + 3.8.8. Miscellaneous Component Properties . . . . . . . . . 142 + 3.8.8.1. IANA Properties . . . . . . . . . . . . . . . . . 142 + 3.8.8.2. Non-Standard Properties . . . . . . . . . . . . . 142 + 3.8.8.3. Request Status . . . . . . . . . . . . . . . . . 144 + 4. iCalendar Object Examples . . . . . . . . . . . . . . . . . . 146 + 5. Recommended Practices . . . . . . . . . . . . . . . . . . . . 150 + 6. Internationalization Considerations . . . . . . . . . . . . . 151 + 7. Security Considerations . . . . . . . . . . . . . . . . . . . 151 + 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 151 + 8.1. iCalendar Media Type Registration . . . . . . . . . . . . 151 + 8.2. New iCalendar Elements Registration . . . . . . . . . . . 155 + 8.2.1. iCalendar Elements Registration Procedure . . . . . . 155 + 8.2.2. Registration Template for Components . . . . . . . . 155 + 8.2.3. Registration Template for Properties . . . . . . . . 156 + 8.2.4. Registration Template for Parameters . . . . . . . . 156 + 8.2.5. Registration Template for Value Data Types . . . . . 157 + 8.2.6. Registration Template for Values . . . . . . . . . . 157 + 8.3. Initial iCalendar Elements Registries . . . . . . . . . . 158 + 8.3.1. Components Registry . . . . . . . . . . . . . . . . . 158 + 8.3.2. Properties Registry . . . . . . . . . . . . . . . . . 158 + 8.3.3. Parameters Registry . . . . . . . . . . . . . . . . . 161 + 8.3.4. Value Data Types Registry . . . . . . . . . . . . . . 162 + 8.3.5. Calendar User Types Registry . . . . . . . . . . . . 162 + 8.3.6. Free/Busy Time Types Registry . . . . . . . . . . . . 163 + 8.3.7. Participation Statuses Registry . . . . . . . . . . . 163 + 8.3.8. Relationship Types Registry . . . . . . . . . . . . . 164 + 8.3.9. Participation Roles Registry . . . . . . . . . . . . 164 + 8.3.10. Actions Registry . . . . . . . . . . . . . . . . . . 165 + 8.3.11. Classifications Registry . . . . . . . . . . . . . . 165 + 8.3.12. Methods Registry . . . . . . . . . . . . . . . . . . 165 + + + +Desruisseaux Standards Track [Page 4] + +RFC 5545 iCalendar September 2009 + + + 9. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 165 + 10. References . . . . . . . . . . . . . . . . . . . . . . . . . 166 + 10.1. Normative References . . . . . . . . . . . . . . . . . . 166 + 10.2. Informative References . . . . . . . . . . . . . . . . . 167 + Appendix A. Differences from RFC 2445 . . . . . . . . . . . . . 169 + A.1. New Restrictions . . . . . . . . . . . . . . . . . . . . 169 + A.2. Restrictions Removed . . . . . . . . . . . . . . . . . . 169 + A.3. Deprecated Features . . . . . . . . . . . . . . . . . . . 169 + +1. Introduction + + The use of calendaring and scheduling has grown considerably in the + last decade. Enterprise and inter-enterprise business has become + dependent on rapid scheduling of events and actions using this + information technology. This memo is intended to progress the level + of interoperability possible between dissimilar calendaring and + scheduling applications. This memo defines a MIME content type for + exchanging electronic calendaring and scheduling information. The + Internet Calendaring and Scheduling Core Object Specification, or + iCalendar, allows for the capture and exchange of information + normally stored within a calendaring and scheduling application; such + as a Personal Information Manager (PIM) or a Group-Scheduling + product. + + The iCalendar format is suitable as an exchange format between + applications or systems. The format is defined in terms of a MIME + content type. This will enable the object to be exchanged using + several transports, including but not limited to SMTP, HTTP, a file + system, desktop interactive protocols such as the use of a memory- + based clipboard or drag/drop interactions, point-to-point + asynchronous communication, wired-network transport, or some form of + unwired transport such as infrared. + + The memo also provides for the definition of iCalendar object methods + that will map this content type to a set of messages for supporting + calendaring and scheduling operations such as requesting, replying + to, modifying, and canceling meetings or appointments, to-dos, and + journal entries. The iCalendar object methods can be used to define + other calendaring and scheduling operations such as requesting for + and replying with free/busy time data. Such a scheduling protocol is + defined in the iCalendar Transport-independent Interoperability + Protocol (iTIP) defined in [2446bis]. + + The memo also includes a formal grammar for the content type based on + the Internet ABNF defined in [RFC5234]. This ABNF is required for + the implementation of parsers and to serve as the definitive + reference when ambiguities or questions arise in interpreting the + descriptive prose definition of the memo. Additional restrictions + + + +Desruisseaux Standards Track [Page 5] + +RFC 5545 iCalendar September 2009 + + + that could not easily be expressed with the ABNF syntax are specified + as comments in the ABNF. Comments with normative statements should + be treated as such. + +2. Basic Grammar and Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + This memo makes use of both a descriptive prose and a more formal + notation for defining the calendaring and scheduling format. + + The notation used in this memo is the ABNF notation of [RFC5234]. + Readers intending on implementing the format defined in this memo + should be familiar with this notation in order to properly interpret + the specifications of this memo. + + All numeric values used in this memo are given in decimal notation. + + All names of properties, property parameters, enumerated property + values, and property parameter values are case-insensitive. However, + all other property values are case-sensitive, unless otherwise + stated. + + Note: All indented editorial notes, such as this one, are intended + to provide the reader with additional information. The + information is not essential to the building of an implementation + conformant with this memo. The information is provided to + highlight a particular feature or characteristic of the memo. + + The format for the iCalendar object is based on the syntax of the + text/directory media type [RFC2425]. While the iCalendar object is + not a profile of the text/directory media type [RFC2425], it does + reuse a number of the elements from the [RFC2425] specification. + +2.1. Formatting Conventions + + The elements defined in this memo are defined in prose. Many of the + terms used to describe these have common usage that is different than + the standards usage of this memo. In order to reference, within this + memo, elements of the calendaring and scheduling model, core object + (this memo), or interoperability protocol [2446bis] some formatting + conventions have been used. Calendaring and scheduling roles are + referred to in quoted-strings of text with the first character of + each word in uppercase. For example, "Organizer" refers to a role of + a "Calendar User" within the scheduling protocol defined by + [2446bis]. Calendar components defined by this memo are referred to + + + +Desruisseaux Standards Track [Page 6] + +RFC 5545 iCalendar September 2009 + + + with capitalized, quoted-strings of text. All calendar components + start with the letter "V". For example, "VEVENT" refers to the event + calendar component, "VTODO" refers to the to-do calendar component, + and "VJOURNAL" refers to the daily journal calendar component. + Scheduling methods defined by iTIP [2446bis] are referred to with + capitalized, quoted-strings of text. For example, "REQUEST" refers + to the method for requesting a scheduling calendar component be + created or modified, and "REPLY" refers to the method a recipient of + a request uses to update their status with the "Organizer" of the + calendar component. + + The properties defined by this memo are referred to with capitalized, + quoted-strings of text, followed by the word "property". For + example, "ATTENDEE" property refers to the iCalendar property used to + convey the calendar address of a calendar user. Property parameters + defined by this memo are referred to with lowercase, quoted-strings + of text, followed by the word "parameter". For example, "value" + parameter refers to the iCalendar property parameter used to override + the default value type for a property value. Enumerated values + defined by this memo are referred to with capitalized text, either + alone or followed by the word "value". For example, the "MINUTELY" + value can be used with the "FREQ" component of the "RECUR" value type + to specify repeating components based on an interval of one minute or + more. + + The following table lists the different characters from the + [US-ASCII] character set that is referenced in this document. For + each character, the table specifies the character name used + throughout this document, along with its US-ASCII decimal codepoint. + + + + + + + + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 7] + +RFC 5545 iCalendar September 2009 + + + +------------------------+-------------------+ + | Character name | Decimal codepoint | + +------------------------+-------------------+ + | HTAB | 9 | + | LF | 10 | + | CR | 13 | + | DQUOTE | 22 | + | SPACE | 32 | + | PLUS SIGN | 43 | + | COMMA | 44 | + | HYPHEN-MINUS | 45 | + | PERIOD | 46 | + | SOLIDUS | 47 | + | COLON | 58 | + | SEMICOLON | 59 | + | LATIN CAPITAL LETTER N | 78 | + | LATIN CAPITAL LETTER T | 84 | + | LATIN CAPITAL LETTER X | 88 | + | LATIN CAPITAL LETTER Z | 90 | + | BACKSLASH | 92 | + | LATIN SMALL LETTER N | 110 | + +------------------------+-------------------+ + +2.2. Related Memos + + Implementers will need to be familiar with several other memos that, + along with this memo, form a framework for Internet calendaring and + scheduling standards. This memo specifies a core specification of + objects, value types, properties, and property parameters. + + o iTIP [2446bis] specifies an interoperability protocol for + scheduling between different implementations; + + o iCalendar Message-Based Interoperability Protocol (iMIP) [2447bis] + specifies an Internet email binding for [2446bis]. + + This memo does not attempt to repeat the specification of concepts or + definitions from these other memos. Where possible, references are + made to the memo that provides for the specification of these + concepts or definitions. + +3. iCalendar Object Specification + + The following sections define the details of a Calendaring and + Scheduling Core Object Specification. The Calendaring and Scheduling + Core Object is a collection of calendaring and scheduling + information. Typically, this information will consist of an + iCalendar stream with one or more iCalendar objects. The body of the + + + +Desruisseaux Standards Track [Page 8] + +RFC 5545 iCalendar September 2009 + + + iCalendar object consists of a sequence of calendar properties and + one or more calendar components. + + Section 3.1 defines the content line format; Section 3.2 defines the + property parameter format; Section 3.3 defines the data types for + property values; Section 3.4 defines the iCalendar object format; + Section 3.5 defines the iCalendar property format; Section 3.6 + defines the calendar component format; Section 3.7 defines calendar + properties; and Section 3.8 defines calendar component properties. + + This information is intended to be an integral part of the MIME + content type registration. In addition, this information can be used + independent of such content registration. In particular, this memo + has direct applicability for use as a calendaring and scheduling + exchange format in file-, memory-, or network-based transport + mechanisms. + +3.1. Content Lines + + The iCalendar object is organized into individual lines of text, + called content lines. Content lines are delimited by a line break, + which is a CRLF sequence (CR character followed by LF character). + + Lines of text SHOULD NOT be longer than 75 octets, excluding the line + break. Long content lines SHOULD be split into a multiple line + representations using a line "folding" technique. That is, a long + line can be split between any two characters by inserting a CRLF + immediately followed by a single linear white-space character (i.e., + SPACE or HTAB). Any sequence of CRLF followed immediately by a + single linear white-space character is ignored (i.e., removed) when + processing the content type. + + For example, the line: + + DESCRIPTION:This is a long description that exists on a long line. + + Can be represented as: + + DESCRIPTION:This is a lo + ng description + that exists on a long line. + + The process of moving from this folded multiple-line representation + to its single-line representation is called "unfolding". Unfolding + is accomplished by removing the CRLF and the linear white-space + character that immediately follows. + + + + + +Desruisseaux Standards Track [Page 9] + +RFC 5545 iCalendar September 2009 + + + When parsing a content line, folded lines MUST first be unfolded + according to the unfolding procedure described above. + + Note: It is possible for very simple implementations to generate + improperly folded lines in the middle of a UTF-8 multi-octet + sequence. For this reason, implementations need to unfold lines + in such a way to properly restore the original sequence. + + The content information associated with an iCalendar object is + formatted using a syntax similar to that defined by [RFC2425]. That + is, the content information consists of CRLF-separated content lines. + + The following notation defines the lines of content in an iCalendar + object: + + contentline = name *(";" param ) ":" value CRLF + ; This ABNF is just a general definition for an initial parsing + ; of the content line into its property name, parameter list, + ; and value string + + ; When parsing a content line, folded lines MUST first + ; be unfolded according to the unfolding procedure + ; described above. When generating a content line, lines + ; longer than 75 octets SHOULD be folded according to + ; the folding procedure described above. + + name = iana-token / x-name + + iana-token = 1*(ALPHA / DIGIT / "-") + ; iCalendar identifier registered with IANA + + x-name = "X-" [vendorid "-"] 1*(ALPHA / DIGIT / "-") + ; Reserved for experimental use. + + vendorid = 3*(ALPHA / DIGIT) + ; Vendor identification + + param = param-name "=" param-value *("," param-value) + ; Each property defines the specific ABNF for the parameters + ; allowed on the property. Refer to specific properties for + ; precise parameter ABNF. + + param-name = iana-token / x-name + + param-value = paramtext / quoted-string + + paramtext = *SAFE-CHAR + + + + +Desruisseaux Standards Track [Page 10] + +RFC 5545 iCalendar September 2009 + + + value = *VALUE-CHAR + + quoted-string = DQUOTE *QSAFE-CHAR DQUOTE + + QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII + ; Any character except CONTROL and DQUOTE + + SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E + / NON-US-ASCII + ; Any character except CONTROL, DQUOTE, ";", ":", "," + + VALUE-CHAR = WSP / %x21-7E / NON-US-ASCII + ; Any textual character + + NON-US-ASCII = UTF8-2 / UTF8-3 / UTF8-4 + ; UTF8-2, UTF8-3, and UTF8-4 are defined in [RFC3629] + + CONTROL = %x00-08 / %x0A-1F / %x7F + ; All the controls except HTAB + + The property value component of a content line has a format that is + property specific. Refer to the section describing each property for + a definition of this format. + + All names of properties, property parameters, enumerated property + values and property parameter values are case-insensitive. However, + all other property values are case-sensitive, unless otherwise + stated. + +3.1.1. List and Field Separators + + Some properties and parameters allow a list of values. Values in a + list of values MUST be separated by a COMMA character. There is no + significance to the order of values in a list. For those parameter + values (such as those that specify URI values) that are specified in + quoted-strings, the individual quoted-strings are separated by a + COMMA character. + + Some property values are defined in terms of multiple parts. These + structured property values MUST have their value parts separated by a + SEMICOLON character. + + Some properties allow a list of parameters. Each property parameter + in a list of property parameters MUST be separated by a SEMICOLON + character. + + + + + + +Desruisseaux Standards Track [Page 11] + +RFC 5545 iCalendar September 2009 + + + Property parameters with values containing a COLON character, a + SEMICOLON character or a COMMA character MUST be placed in quoted + text. + + For example, in the following properties, a SEMICOLON is used to + separate property parameters from each other and a COMMA character is + used to separate property values in a value list. + + ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT:mailto: + jsmith@example.com + + RDATE;VALUE=DATE:19970304,19970504,19970704,19970904 + +3.1.2. Multiple Values + + Some properties defined in the iCalendar object can have multiple + values. The general rule for encoding multi-valued items is to + simply create a new content line for each value, including the + property name. However, it should be noted that some properties + support encoding multiple values in a single property by separating + the values with a COMMA character. Individual property definitions + should be consulted for determining whether a specific property + allows multiple values and in which of these two forms. Multi-valued + properties MUST NOT be used to specify multiple language variants of + the same value. Calendar applications SHOULD display all values. + +3.1.3. Binary Content + + Binary content information in an iCalendar object SHOULD be + referenced using a URI within a property value. That is, the binary + content information SHOULD be placed in an external MIME entity that + can be referenced by a URI from within the iCalendar object. In + applications where this is not feasible, binary content information + can be included within an iCalendar object, but only after first + encoding it into text using the "BASE64" encoding method defined in + [RFC4648]. Inline binary content SHOULD only be used in applications + whose special circumstances demand that an iCalendar object be + expressed as a single entity. A property containing inline binary + content information MUST specify the "ENCODING" property parameter. + Binary content information placed external to the iCalendar object + MUST be referenced by a uniform resource identifier (URI). + + The following example specifies an "ATTACH" property that references + an attachment external to the iCalendar object with a URI reference: + + ATTACH:http://example.com/public/quarterly-report.doc + + + + + +Desruisseaux Standards Track [Page 12] + +RFC 5545 iCalendar September 2009 + + + The following example specifies an "ATTACH" property with inline + binary encoded content information: + + ATTACH;FMTTYPE=text/plain;ENCODING=BASE64;VALUE=BINARY:VGhlIH + F1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4 + +3.1.4. Character Set + + There is not a property parameter to declare the charset used in a + property value. The default charset for an iCalendar stream is UTF-8 + as defined in [RFC3629]. + + The "charset" Content-Type parameter MUST be used in MIME transports + to specify the charset being used. + +3.2. Property Parameters + + A property can have attributes with which it is associated. These + "property parameters" contain meta-information about the property or + the property value. Property parameters are provided to specify such + information as the location of an alternate text representation for a + property value, the language of a text property value, the value type + of the property value, and other attributes. + + Property parameter values that contain the COLON, SEMICOLON, or COMMA + character separators MUST be specified as quoted-string text values. + Property parameter values MUST NOT contain the DQUOTE character. The + DQUOTE character is used as a delimiter for parameter values that + contain restricted characters or URI text. For example: + + DESCRIPTION;ALTREP="cid:part1.0001@example.org":The Fall'98 Wild + Wizards Conference - - Las Vegas\, NV\, USA + + Property parameter values that are not in quoted-strings are case- + insensitive. + + The general property parameters defined by this memo are defined by + the following notation: + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 13] + +RFC 5545 iCalendar September 2009 + + + icalparameter = altrepparam ; Alternate text representation + / cnparam ; Common name + / cutypeparam ; Calendar user type + / delfromparam ; Delegator + / deltoparam ; Delegatee + / dirparam ; Directory entry + / encodingparam ; Inline encoding + / fmttypeparam ; Format type + / fbtypeparam ; Free/busy time type + / languageparam ; Language for text + / memberparam ; Group or list membership + / partstatparam ; Participation status + / rangeparam ; Recurrence identifier range + / trigrelparam ; Alarm trigger relationship + / reltypeparam ; Relationship type + / roleparam ; Participation role + / rsvpparam ; RSVP expectation + / sentbyparam ; Sent by + / tzidparam ; Reference to time zone object + / valuetypeparam ; Property value data type + / other-param + + other-param = (iana-param / x-param) + + iana-param = iana-token "=" param-value *("," param-value) + ; Some other IANA-registered iCalendar parameter. + + x-param = x-name "=" param-value *("," param-value) + ; A non-standard, experimental parameter. + + Applications MUST ignore x-param and iana-param values they don't + recognize. + +3.2.1. Alternate Text Representation + + Parameter Name: ALTREP + + Purpose: To specify an alternate text representation for the + property value. + + Format Definition: This property parameter is defined by the + following notation: + + altrepparam = "ALTREP" "=" DQUOTE uri DQUOTE + + Description: This parameter specifies a URI that points to an + alternate representation for a textual property value. A property + specifying this parameter MUST also include a value that reflects + + + +Desruisseaux Standards Track [Page 14] + +RFC 5545 iCalendar September 2009 + + + the default representation of the text value. The URI parameter + value MUST be specified in a quoted-string. + + Note: While there is no restriction imposed on the URI schemes + allowed for this parameter, Content Identifier (CID) [RFC2392], + HTTP [RFC2616], and HTTPS [RFC2818] are the URI schemes most + commonly used by current implementations. + + Example: + + DESCRIPTION;ALTREP="CID:part3.msg.970415T083000@example.com": + Project XYZ Review Meeting will include the following agenda + items: (a) Market Overview\, (b) Finances\, (c) Project Man + agement + + The "ALTREP" property parameter value might point to a "text/html" + content portion. + + Content-Type:text/html + Content-Id: + + + + + + +

+ Project XYZ Review Meeting will include + the following agenda items: +

    +
  1. Market Overview
  2. +
  3. Finances
  4. +
  5. Project Management
  6. +
+

+ + + +3.2.2. Common Name + + Parameter Name: CN + + Purpose: To specify the common name to be associated with the + calendar user specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + + + +Desruisseaux Standards Track [Page 15] + +RFC 5545 iCalendar September 2009 + + + cnparam = "CN" "=" param-value + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies the common name + to be associated with the calendar user specified by the property. + The parameter value is text. The parameter value can be used for + display text to be associated with the calendar address specified + by the property. + + Example: + + ORGANIZER;CN="John Smith":mailto:jsmith@example.com + +3.2.3. Calendar User Type + + Parameter Name: CUTYPE + + Purpose: To identify the type of calendar user specified by the + property. + + Format Definition: This property parameter is defined by the + following notation: + + cutypeparam = "CUTYPE" "=" + ("INDIVIDUAL" ; An individual + / "GROUP" ; A group of individuals + / "RESOURCE" ; A physical resource + / "ROOM" ; A room resource + / "UNKNOWN" ; Otherwise not known + / x-name ; Experimental type + / iana-token) ; Other IANA-registered + ; type + ; Default is INDIVIDUAL + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the type of + calendar user specified by the property. If not specified on a + property that allows this parameter, the default is INDIVIDUAL. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the UNKNOWN value. + + Example: + + ATTENDEE;CUTYPE=GROUP:mailto:ietf-calsch@example.org + + + + + + + +Desruisseaux Standards Track [Page 16] + +RFC 5545 iCalendar September 2009 + + +3.2.4. Delegators + + Parameter Name: DELEGATED-FROM + + Purpose: To specify the calendar users that have delegated their + participation to the calendar user specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + delfromparam = "DELEGATED-FROM" "=" DQUOTE cal-address + DQUOTE *("," DQUOTE cal-address DQUOTE) + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. This parameter specifies those calendar + users that have delegated their participation in a group-scheduled + event or to-do to the calendar user specified by the property. + The individual calendar address parameter values MUST each be + specified in a quoted-string. + + Example: + + ATTENDEE;DELEGATED-FROM="mailto:jsmith@example.com":mailto: + jdoe@example.com + +3.2.5. Delegatees + + Parameter Name: DELEGATED-TO + + Purpose: To specify the calendar users to whom the calendar user + specified by the property has delegated participation. + + Format Definition: This property parameter is defined by the + following notation: + + deltoparam = "DELEGATED-TO" "=" DQUOTE cal-address DQUOTE + *("," DQUOTE cal-address DQUOTE) + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. This parameter specifies those calendar + users whom have been delegated participation in a group-scheduled + event or to-do by the calendar user specified by the property. + The individual calendar address parameter values MUST each be + specified in a quoted-string. + + + + + + + +Desruisseaux Standards Track [Page 17] + +RFC 5545 iCalendar September 2009 + + + Example: + + ATTENDEE;DELEGATED-TO="mailto:jdoe@example.com","mailto:jqpublic + @example.com":mailto:jsmith@example.com + +3.2.6. Directory Entry Reference + + Parameter Name: DIR + + Purpose: To specify reference to a directory entry associated with + the calendar user specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + dirparam = "DIR" "=" DQUOTE uri DQUOTE + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies a reference to + the directory entry associated with the calendar user specified by + the property. The parameter value is a URI. The URI parameter + value MUST be specified in a quoted-string. + + Note: While there is no restriction imposed on the URI schemes + allowed for this parameter, CID [RFC2392], DATA [RFC2397], FILE + [RFC1738], FTP [RFC1738], HTTP [RFC2616], HTTPS [RFC2818], LDAP + [RFC4516], and MID [RFC2392] are the URI schemes most commonly + used by current implementations. + + Example: + + ORGANIZER;DIR="ldap://example.com:6666/o=ABC%20Industries, + c=US???(cn=Jim%20Dolittle)":mailto:jimdo@example.com + +3.2.7. Inline Encoding + + Parameter Name: ENCODING + + Purpose: To specify an alternate inline encoding for the property + value. + + Format Definition: This property parameter is defined by the + following notation: + + + + + + + + +Desruisseaux Standards Track [Page 18] + +RFC 5545 iCalendar September 2009 + + + encodingparam = "ENCODING" "=" + ( "8BIT" + ; "8bit" text encoding is defined in [RFC2045] + / "BASE64" + ; "BASE64" binary encoding format is defined in [RFC4648] + ) + + Description: This property parameter identifies the inline encoding + used in a property value. The default encoding is "8BIT", + corresponding to a property value consisting of text. The + "BASE64" encoding type corresponds to a property value encoded + using the "BASE64" encoding defined in [RFC2045]. + + If the value type parameter is ";VALUE=BINARY", then the inline + encoding parameter MUST be specified with the value + ";ENCODING=BASE64". + + Example: + + ATTACH;FMTTYPE=text/plain;ENCODING=BASE64;VALUE=BINARY:TG9yZW + 0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2ljaW + 5nIGVsaXQsIHNlZCBkbyBlaXVzbW9kIHRlbXBvciBpbmNpZGlkdW50IHV0IG + xhYm9yZSBldCBkb2xvcmUgbWFnbmEgYWxpcXVhLiBVdCBlbmltIGFkIG1pbm + ltIHZlbmlhbSwgcXVpcyBub3N0cnVkIGV4ZXJjaXRhdGlvbiB1bGxhbWNvIG + xhYm9yaXMgbmlzaSB1dCBhbGlxdWlwIGV4IGVhIGNvbW1vZG8gY29uc2VxdW + F0LiBEdWlzIGF1dGUgaXJ1cmUgZG9sb3IgaW4gcmVwcmVoZW5kZXJpdCBpbi + B2b2x1cHRhdGUgdmVsaXQgZXNzZSBjaWxsdW0gZG9sb3JlIGV1IGZ1Z2lhdC + BudWxsYSBwYXJpYXR1ci4gRXhjZXB0ZXVyIHNpbnQgb2NjYWVjYXQgY3VwaW + RhdGF0IG5vbiBwcm9pZGVudCwgc3VudCBpbiBjdWxwYSBxdWkgb2ZmaWNpYS + BkZXNlcnVudCBtb2xsaXQgYW5pbSBpZCBlc3QgbGFib3J1bS4= + +3.2.8. Format Type + + Parameter Name: FMTTYPE + + Purpose: To specify the content type of a referenced object. + + Format Definition: This property parameter is defined by the + following notation: + + fmttypeparam = "FMTTYPE" "=" type-name "/" subtype-name + ; Where "type-name" and "subtype-name" are + ; defined in Section 4.2 of [RFC4288]. + + Description: This parameter can be specified on properties that are + used to reference an object. The parameter specifies the media + type [RFC4288] of the referenced object. For example, on the + "ATTACH" property, an FTP type URI value does not, by itself, + + + +Desruisseaux Standards Track [Page 19] + +RFC 5545 iCalendar September 2009 + + + necessarily convey the type of content associated with the + resource. The parameter value MUST be the text for either an + IANA-registered media type or a non-standard media type. + + Example: + + ATTACH;FMTTYPE=application/msword:ftp://example.com/pub/docs/ + agenda.doc + +3.2.9. Free/Busy Time Type + + Parameter Name: FBTYPE + + Purpose: To specify the free or busy time type. + + Format Definition: This property parameter is defined by the + following notation: + + fbtypeparam = "FBTYPE" "=" ("FREE" / "BUSY" + / "BUSY-UNAVAILABLE" / "BUSY-TENTATIVE" + / x-name + ; Some experimental iCalendar free/busy type. + / iana-token) + ; Some other IANA-registered iCalendar free/busy type. + + Description: This parameter specifies the free or busy time type. + The value FREE indicates that the time interval is free for + scheduling. The value BUSY indicates that the time interval is + busy because one or more events have been scheduled for that + interval. The value BUSY-UNAVAILABLE indicates that the time + interval is busy and that the interval can not be scheduled. The + value BUSY-TENTATIVE indicates that the time interval is busy + because one or more events have been tentatively scheduled for + that interval. If not specified on a property that allows this + parameter, the default is BUSY. Applications MUST treat x-name + and iana-token values they don't recognize the same way as they + would the BUSY value. + + Example: The following is an example of this parameter on a + "FREEBUSY" property. + + FREEBUSY;FBTYPE=BUSY:19980415T133000Z/19980415T170000Z + + + + + + + + + +Desruisseaux Standards Track [Page 20] + +RFC 5545 iCalendar September 2009 + + +3.2.10. Language + + Parameter Name: LANGUAGE + + Purpose: To specify the language for text values in a property or + property parameter. + + Format Definition: This property parameter is defined by the + following notation: + + languageparam = "LANGUAGE" "=" language + + language = Language-Tag + ; As defined in [RFC5646]. + + Description: This parameter identifies the language of the text in + the property value and of all property parameter values of the + property. The value of the "LANGUAGE" property parameter is that + defined in [RFC5646]. + + For transport in a MIME entity, the Content-Language header field + can be used to set the default language for the entire body part. + Otherwise, no default language is assumed. + + Example: The following are examples of this parameter on the + "SUMMARY" and "LOCATION" properties: + + SUMMARY;LANGUAGE=en-US:Company Holiday Party + + LOCATION;LANGUAGE=en:Germany + + LOCATION;LANGUAGE=no:Tyskland + +3.2.11. Group or List Membership + + Parameter Name: MEMBER + + Purpose: To specify the group or list membership of the calendar + user specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + memberparam = "MEMBER" "=" DQUOTE cal-address DQUOTE + *("," DQUOTE cal-address DQUOTE) + + + + + + +Desruisseaux Standards Track [Page 21] + +RFC 5545 iCalendar September 2009 + + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the groups or + list membership for the calendar user specified by the property. + The parameter value is either a single calendar address in a + quoted-string or a COMMA-separated list of calendar addresses, + each in a quoted-string. The individual calendar address + parameter values MUST each be specified in a quoted-string. + + Example: + + ATTENDEE;MEMBER="mailto:ietf-calsch@example.org":mailto: + jsmith@example.com + + ATTENDEE;MEMBER="mailto:projectA@example.com","mailto:pr + ojectB@example.com":mailto:janedoe@example.com + +3.2.12. Participation Status + + Parameter Name: PARTSTAT + + Purpose: To specify the participation status for the calendar user + specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + partstatparam = "PARTSTAT" "=" + (partstat-event + / partstat-todo + / partstat-jour) + + partstat-event = ("NEEDS-ACTION" ; Event needs action + / "ACCEPTED" ; Event accepted + / "DECLINED" ; Event declined + / "TENTATIVE" ; Event tentatively + ; accepted + / "DELEGATED" ; Event delegated + / x-name ; Experimental status + / iana-token) ; Other IANA-registered + ; status + ; These are the participation statuses for a "VEVENT". + ; Default is NEEDS-ACTION. + + partstat-todo = ("NEEDS-ACTION" ; To-do needs action + / "ACCEPTED" ; To-do accepted + / "DECLINED" ; To-do declined + / "TENTATIVE" ; To-do tentatively + ; accepted + + + +Desruisseaux Standards Track [Page 22] + +RFC 5545 iCalendar September 2009 + + + / "DELEGATED" ; To-do delegated + / "COMPLETED" ; To-do completed + ; COMPLETED property has + ; DATE-TIME completed + / "IN-PROCESS" ; To-do in process of + ; being completed + / x-name ; Experimental status + / iana-token) ; Other IANA-registered + ; status + ; These are the participation statuses for a "VTODO". + ; Default is NEEDS-ACTION. + + + + partstat-jour = ("NEEDS-ACTION" ; Journal needs action + / "ACCEPTED" ; Journal accepted + / "DECLINED" ; Journal declined + / x-name ; Experimental status + / iana-token) ; Other IANA-registered + ; status + ; These are the participation statuses for a "VJOURNAL". + ; Default is NEEDS-ACTION. + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the + participation status for the calendar user specified by the + property value. The parameter values differ depending on whether + they are associated with a group-scheduled "VEVENT", "VTODO", or + "VJOURNAL". The values MUST match one of the values allowed for + the given calendar component. If not specified on a property that + allows this parameter, the default value is NEEDS-ACTION. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the NEEDS-ACTION value. + + Example: + + ATTENDEE;PARTSTAT=DECLINED:mailto:jsmith@example.com + +3.2.13. Recurrence Identifier Range + + Parameter Name: RANGE + + Purpose: To specify the effective range of recurrence instances from + the instance specified by the recurrence identifier specified by + the property. + + Format Definition: This property parameter is defined by the + following notation: + + + +Desruisseaux Standards Track [Page 23] + +RFC 5545 iCalendar September 2009 + + + rangeparam = "RANGE" "=" "THISANDFUTURE" + ; To specify the instance specified by the recurrence identifier + ; and all subsequent recurrence instances. + + Description: This parameter can be specified on a property that + specifies a recurrence identifier. The parameter specifies the + effective range of recurrence instances that is specified by the + property. The effective range is from the recurrence identifier + specified by the property. If this parameter is not specified on + an allowed property, then the default range is the single instance + specified by the recurrence identifier value of the property. The + parameter value can only be "THISANDFUTURE" to indicate a range + defined by the recurrence identifier and all subsequent instances. + The value "THISANDPRIOR" is deprecated by this revision of + iCalendar and MUST NOT be generated by applications. + + Example: + + RECURRENCE-ID;RANGE=THISANDFUTURE:19980401T133000Z + +3.2.14. Alarm Trigger Relationship + + Parameter Name: RELATED + + Purpose: To specify the relationship of the alarm trigger with + respect to the start or end of the calendar component. + + Format Definition: This property parameter is defined by the + following notation: + + trigrelparam = "RELATED" "=" + ("START" ; Trigger off of start + / "END") ; Trigger off of end + + Description: This parameter can be specified on properties that + specify an alarm trigger with a "DURATION" value type. The + parameter specifies whether the alarm will trigger relative to the + start or end of the calendar component. The parameter value START + will set the alarm to trigger off the start of the calendar + component; the parameter value END will set the alarm to trigger + off the end of the calendar component. If the parameter is not + specified on an allowable property, then the default is START. + + Example: + + TRIGGER;RELATED=END:PT5M + + + + + +Desruisseaux Standards Track [Page 24] + +RFC 5545 iCalendar September 2009 + + +3.2.15. Relationship Type + + Parameter Name: RELTYPE + + Purpose: To specify the type of hierarchical relationship associated + with the calendar component specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + reltypeparam = "RELTYPE" "=" + ("PARENT" ; Parent relationship - Default + / "CHILD" ; Child relationship + / "SIBLING" ; Sibling relationship + / iana-token ; Some other IANA-registered + ; iCalendar relationship type + / x-name) ; A non-standard, experimental + ; relationship type + + Description: This parameter can be specified on a property that + references another related calendar. The parameter specifies the + hierarchical relationship type of the calendar component + referenced by the property. The parameter value can be PARENT, to + indicate that the referenced calendar component is a superior of + calendar component; CHILD to indicate that the referenced calendar + component is a subordinate of the calendar component; or SIBLING + to indicate that the referenced calendar component is a peer of + the calendar component. If this parameter is not specified on an + allowable property, the default relationship type is PARENT. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the PARENT value. + + Example: + + RELATED-TO;RELTYPE=SIBLING:19960401-080045-4000F192713@ + example.com + +3.2.16. Participation Role + + Parameter Name: ROLE + + Purpose: To specify the participation role for the calendar user + specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + + + + +Desruisseaux Standards Track [Page 25] + +RFC 5545 iCalendar September 2009 + + + roleparam = "ROLE" "=" + ("CHAIR" ; Indicates chair of the + ; calendar entity + / "REQ-PARTICIPANT" ; Indicates a participant whose + ; participation is required + / "OPT-PARTICIPANT" ; Indicates a participant whose + ; participation is optional + / "NON-PARTICIPANT" ; Indicates a participant who + ; is copied for information + ; purposes only + / x-name ; Experimental role + / iana-token) ; Other IANA role + ; Default is REQ-PARTICIPANT + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies the participation + role for the calendar user specified by the property in the group + schedule calendar component. If not specified on a property that + allows this parameter, the default value is REQ-PARTICIPANT. + Applications MUST treat x-name and iana-token values they don't + recognize the same way as they would the REQ-PARTICIPANT value. + + Example: + + ATTENDEE;ROLE=CHAIR:mailto:mrbig@example.com + +3.2.17. RSVP Expectation + + Parameter Name: RSVP + + Purpose: To specify whether there is an expectation of a favor of a + reply from the calendar user specified by the property value. + + Format Definition: This property parameter is defined by the + following notation: + + rsvpparam = "RSVP" "=" ("TRUE" / "FALSE") + ; Default is FALSE + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter identifies the expectation + of a reply from the calendar user specified by the property value. + This parameter is used by the "Organizer" to request a + participation status reply from an "Attendee" of a group-scheduled + event or to-do. If not specified on a property that allows this + parameter, the default value is FALSE. + + + + + +Desruisseaux Standards Track [Page 26] + +RFC 5545 iCalendar September 2009 + + + Example: + + ATTENDEE;RSVP=TRUE:mailto:jsmith@example.com + +3.2.18. Sent By + + Parameter Name: SENT-BY + + Purpose: To specify the calendar user that is acting on behalf of + the calendar user specified by the property. + + Format Definition: This property parameter is defined by the + following notation: + + sentbyparam = "SENT-BY" "=" DQUOTE cal-address DQUOTE + + Description: This parameter can be specified on properties with a + CAL-ADDRESS value type. The parameter specifies the calendar user + that is acting on behalf of the calendar user specified by the + property. The parameter value MUST be a mailto URI as defined in + [RFC2368]. The individual calendar address parameter values MUST + each be specified in a quoted-string. + + Example: + + ORGANIZER;SENT-BY="mailto:sray@example.com":mailto: + jsmith@example.com + +3.2.19. Time Zone Identifier + + Parameter Name: TZID + + Purpose: To specify the identifier for the time zone definition for + a time component in the property value. + + Format Definition: This property parameter is defined by the + following notation: + + tzidparam = "TZID" "=" [tzidprefix] paramtext + + tzidprefix = "/" + + Description: This parameter MUST be specified on the "DTSTART", + "DTEND", "DUE", "EXDATE", and "RDATE" properties when either a + DATE-TIME or TIME value type is specified and when the value is + neither a UTC or a "floating" time. Refer to the DATE-TIME or + TIME value type definition for a description of UTC and "floating + time" formats. This property parameter specifies a text value + + + +Desruisseaux Standards Track [Page 27] + +RFC 5545 iCalendar September 2009 + + + that uniquely identifies the "VTIMEZONE" calendar component to be + used when evaluating the time portion of the property. The value + of the "TZID" property parameter will be equal to the value of the + "TZID" property for the matching time zone definition. An + individual "VTIMEZONE" calendar component MUST be specified for + each unique "TZID" parameter value specified in the iCalendar + object. + + The parameter MUST be specified on properties with a DATE-TIME + value if the DATE-TIME is not either a UTC or a "floating" time. + Failure to include and follow VTIMEZONE definitions in iCalendar + objects may lead to inconsistent understanding of the local time + at any given location. + + The presence of the SOLIDUS character as a prefix, indicates that + this "TZID" represents a unique ID in a globally defined time zone + registry (when such registry is defined). + + Note: This document does not define a naming convention for + time zone identifiers. Implementers may want to use the naming + conventions defined in existing time zone specifications such + as the public-domain TZ database [TZDB]. The specification of + globally unique time zone identifiers is not addressed by this + document and is left for future study. + + The following are examples of this property parameter: + + DTSTART;TZID=America/New_York:19980119T020000 + + DTEND;TZID=America/New_York:19980119T030000 + + The "TZID" property parameter MUST NOT be applied to DATE + properties and DATE-TIME or TIME properties whose time values are + specified in UTC. + + The use of local time in a DATE-TIME or TIME value without the + "TZID" property parameter is to be interpreted as floating time, + regardless of the existence of "VTIMEZONE" calendar components in + the iCalendar object. + + For more information, see the sections on the value types DATE- + TIME and TIME. + + + + + + + + + +Desruisseaux Standards Track [Page 28] + +RFC 5545 iCalendar September 2009 + + +3.2.20. Value Data Types + + Parameter Name: VALUE + + Purpose: To explicitly specify the value type format for a property + value. + + Format Definition: This property parameter is defined by the + following notation: + + valuetypeparam = "VALUE" "=" valuetype + + valuetype = ("BINARY" + / "BOOLEAN" + / "CAL-ADDRESS" + / "DATE" + / "DATE-TIME" + / "DURATION" + / "FLOAT" + / "INTEGER" + / "PERIOD" + / "RECUR" + / "TEXT" + / "TIME" + / "URI" + / "UTC-OFFSET" + / x-name + ; Some experimental iCalendar value type. + / iana-token) + ; Some other IANA-registered iCalendar value type. + + Description: This parameter specifies the value type and format of + the property value. The property values MUST be of a single value + type. For example, a "RDATE" property cannot have a combination + of DATE-TIME and TIME value types. + + If the property's value is the default value type, then this + parameter need not be specified. However, if the property's + default value type is overridden by some other allowable value + type, then this parameter MUST be specified. + + Applications MUST preserve the value data for x-name and iana- + token values that they don't recognize without attempting to + interpret or parse the value data. + + + + + + + +Desruisseaux Standards Track [Page 29] + +RFC 5545 iCalendar September 2009 + + +3.3. Property Value Data Types + + The properties in an iCalendar object are strongly typed. The + definition of each property restricts the value to be one of the + value data types, or simply value types, defined in this section. + The value type for a property will either be specified implicitly as + the default value type or will be explicitly specified with the + "VALUE" parameter. If the value type of a property is one of the + alternate valid types, then it MUST be explicitly specified with the + "VALUE" parameter. + +3.3.1. Binary + + Value Name: BINARY + + Purpose: This value type is used to identify properties that contain + a character encoding of inline binary data. For example, an + inline attachment of a document might be included in an iCalendar + object. + + Format Definition: This value type is defined by the following + notation: + + binary = *(4b-char) [b-end] + ; A "BASE64" encoded character string, as defined by [RFC4648]. + + b-end = (2b-char "==") / (3b-char "=") + + b-char = ALPHA / DIGIT / "+" / "/" + + Description: Property values with this value type MUST also include + the inline encoding parameter sequence of ";ENCODING=BASE64". + That is, all inline binary data MUST first be character encoded + using the "BASE64" encoding method defined in [RFC2045]. No + additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 30] + +RFC 5545 iCalendar September 2009 + + + Example: The following is an example of a "BASE64" encoded binary + value data: + + ATTACH;FMTTYPE=image/vnd.microsoft.icon;ENCODING=BASE64;VALUE + =BINARY:AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgIAAAICAgADAwMAA////AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAMwAAAAAAABNEMQAAAAAAAkQgAAAAAAJEREQgAA + ACECQ0QgEgAAQxQzM0E0AABERCRCREQAADRDJEJEQwAAAhA0QwEQAAAAAERE + AAAAAAAAREQAAAAAAAAkQgAAAAAAAAMgAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA + +3.3.2. Boolean + + Value Name: BOOLEAN + + Purpose: This value type is used to identify properties that contain + either a "TRUE" or "FALSE" Boolean value. + + Format Definition: This value type is defined by the following + notation: + + boolean = "TRUE" / "FALSE" + + Description: These values are case-insensitive text. No additional + content value encoding (i.e., BACKSLASH character encoding, see + Section 3.3.11) is defined for this value type. + + Example: The following is an example of a hypothetical property that + has a BOOLEAN value type: + + TRUE + +3.3.3. Calendar User Address + + Value Name: CAL-ADDRESS + + Purpose: This value type is used to identify properties that contain + a calendar user address. + + Format Definition: This value type is defined by the following + notation: + + cal-address = uri + + Description: The value is a URI as defined by [RFC3986] or any other + IANA-registered form for a URI. When used to address an Internet + + + +Desruisseaux Standards Track [Page 31] + +RFC 5545 iCalendar September 2009 + + + email transport address for a calendar user, the value MUST be a + mailto URI, as defined by [RFC2368]. No additional content value + encoding (i.e., BACKSLASH character encoding, see Section 3.3.11) + is defined for this value type. + + Example: + + mailto:jane_doe@example.com + +3.3.4. Date + + Value Name: DATE + + Purpose: This value type is used to identify values that contain a + calendar date. + + Format Definition: This value type is defined by the following + notation: + + date = date-value + + date-value = date-fullyear date-month date-mday + date-fullyear = 4DIGIT + date-month = 2DIGIT ;01-12 + date-mday = 2DIGIT ;01-28, 01-29, 01-30, 01-31 + ;based on month/year + + Description: If the property permits, multiple "date" values are + specified as a COMMA-separated list of values. The format for the + value type is based on the [ISO.8601.2004] complete + representation, basic format for a calendar date. The textual + format specifies a four-digit year, two-digit month, and two-digit + day of the month. There are no separator characters between the + year, month, and day component text. + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + + Example: The following represents July 14, 1997: + + 19970714 + +3.3.5. Date-Time + + Value Name: DATE-TIME + + Purpose: This value type is used to identify values that specify a + precise calendar date and time of day. + + + +Desruisseaux Standards Track [Page 32] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This value type is defined by the following + notation: + + date-time = date "T" time ;As specified in the DATE and TIME + ;value definitions + + Description: If the property permits, multiple "DATE-TIME" values + are specified as a COMMA-separated list of values. No additional + content value encoding (i.e., BACKSLASH character encoding, see + Section 3.3.11) is defined for this value type. + + The "DATE-TIME" value type is used to identify values that contain + a precise calendar date and time of day. The format is based on + the [ISO.8601.2004] complete representation, basic format for a + calendar date and time of day. The text format is a concatenation + of the "date", followed by the LATIN CAPITAL LETTER T character, + the time designator, followed by the "time" format. + + The "DATE-TIME" value type expresses time values in three forms: + + The form of date and time with UTC offset MUST NOT be used. For + example, the following is not valid for a DATE-TIME value: + + 19980119T230000-0800 ;Invalid time format + + FORM #1: DATE WITH LOCAL TIME + + The date with local time form is simply a DATE-TIME value that + does not contain the UTC designator nor does it reference a time + zone. For example, the following represents January 18, 1998, at + 11 PM: + + 19980118T230000 + + DATE-TIME values of this type are said to be "floating" and are + not bound to any time zone in particular. They are used to + represent the same hour, minute, and second value regardless of + which time zone is currently being observed. For example, an + event can be defined that indicates that an individual will be + busy from 11:00 AM to 1:00 PM every day, no matter which time zone + the person is in. In these cases, a local time can be specified. + The recipient of an iCalendar object with a property value + consisting of a local time, without any relative time zone + information, SHOULD interpret the value as being fixed to whatever + time zone the "ATTENDEE" is in at any given moment. This means + that two "Attendees", in different time zones, receiving the same + event definition as a floating time, may be participating in the + + + + +Desruisseaux Standards Track [Page 33] + +RFC 5545 iCalendar September 2009 + + + event at different actual times. Floating time SHOULD only be + used where that is the reasonable behavior. + + In most cases, a fixed time is desired. To properly communicate a + fixed time in a property value, either UTC time or local time with + time zone reference MUST be specified. + + The use of local time in a DATE-TIME value without the "TZID" + property parameter is to be interpreted as floating time, + regardless of the existence of "VTIMEZONE" calendar components in + the iCalendar object. + + FORM #2: DATE WITH UTC TIME + + The date with UTC time, or absolute time, is identified by a LATIN + CAPITAL LETTER Z suffix character, the UTC designator, appended to + the time value. For example, the following represents January 19, + 1998, at 0700 UTC: + + 19980119T070000Z + + The "TZID" property parameter MUST NOT be applied to DATE-TIME + properties whose time values are specified in UTC. + + FORM #3: DATE WITH LOCAL TIME AND TIME ZONE REFERENCE + + The date and local time with reference to time zone information is + identified by the use the "TZID" property parameter to reference + the appropriate time zone definition. "TZID" is discussed in + detail in Section 3.2.19. For example, the following represents + 2:00 A.M. in New York on January 19, 1998: + + TZID=America/New_York:19980119T020000 + + If, based on the definition of the referenced time zone, the local + time described occurs more than once (when changing from daylight + to standard time), the DATE-TIME value refers to the first + occurrence of the referenced time. Thus, TZID=America/ + New_York:20071104T013000 indicates November 4, 2007 at 1:30 A.M. + EDT (UTC-04:00). If the local time described does not occur (when + changing from standard to daylight time), the DATE-TIME value is + interpreted using the UTC offset before the gap in local times. + Thus, TZID=America/New_York:20070311T023000 indicates March 11, + 2007 at 3:30 A.M. EDT (UTC-04:00), one hour after 1:30 A.M. EST + (UTC-05:00). + + + + + + +Desruisseaux Standards Track [Page 34] + +RFC 5545 iCalendar September 2009 + + + A time value MUST only specify the second 60 when specifying a + positive leap second. For example: + + 19970630T235960Z + + Implementations that do not support leap seconds SHOULD interpret + the second 60 as equivalent to the second 59. + + Example: The following represents July 14, 1997, at 1:30 PM in New + York City in each of the three time formats, using the "DTSTART" + property. + + DTSTART:19970714T133000 ; Local time + DTSTART:19970714T173000Z ; UTC time + DTSTART;TZID=America/New_York:19970714T133000 + ; Local time and time + ; zone reference + +3.3.6. Duration + + Value Name: DURATION + + Purpose: This value type is used to identify properties that contain + a duration of time. + + Format Definition: This value type is defined by the following + notation: + + dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week) + + dur-date = dur-day [dur-time] + dur-time = "T" (dur-hour / dur-minute / dur-second) + dur-week = 1*DIGIT "W" + dur-hour = 1*DIGIT "H" [dur-minute] + dur-minute = 1*DIGIT "M" [dur-second] + dur-second = 1*DIGIT "S" + dur-day = 1*DIGIT "D" + + Description: If the property permits, multiple "duration" values are + specified by a COMMA-separated list of values. The format is + based on the [ISO.8601.2004] complete representation basic format + with designators for the duration of time. The format can + represent nominal durations (weeks and days) and accurate + durations (hours, minutes, and seconds). Note that unlike + [ISO.8601.2004], this value type doesn't support the "Y" and "M" + designators to specify durations in terms of years and months. + + + + + +Desruisseaux Standards Track [Page 35] + +RFC 5545 iCalendar September 2009 + + + The duration of a week or a day depends on its position in the + calendar. In the case of discontinuities in the time scale, such + as the change from standard time to daylight time and back, the + computation of the exact duration requires the subtraction or + addition of the change of duration of the discontinuity. Leap + seconds MUST NOT be considered when computing an exact duration. + When computing an exact duration, the greatest order time + components MUST be added first, that is, the number of days MUST + be added first, followed by the number of hours, number of + minutes, and number of seconds. + + Negative durations are typically used to schedule an alarm to + trigger before an associated time (see Section 3.8.6.3). + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) are defined for this value type. + + Example: A duration of 15 days, 5 hours, and 20 seconds would be: + + P15DT5H0M20S + + A duration of 7 weeks would be: + + P7W + +3.3.7. Float + + Value Name: FLOAT + + Purpose: This value type is used to identify properties that contain + a real-number value. + + Format Definition: This value type is defined by the following + notation: + + float = (["+"] / "-") 1*DIGIT ["." 1*DIGIT] + + Description: If the property permits, multiple "float" values are + specified by a COMMA-separated list of values. + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + + Example: + + 1000000.0000001 + 1.333 + -3.14 + + + +Desruisseaux Standards Track [Page 36] + +RFC 5545 iCalendar September 2009 + + +3.3.8. Integer + + Value Name: INTEGER + + Purpose: This value type is used to identify properties that contain + a signed integer value. + + Format Definition: This value type is defined by the following + notation: + + integer = (["+"] / "-") 1*DIGIT + + Description: If the property permits, multiple "integer" values are + specified by a COMMA-separated list of values. The valid range + for "integer" is -2147483648 to 2147483647. If the sign is not + specified, then the value is assumed to be positive. + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + + Example: + + 1234567890 + -1234567890 + +1234567890 + 432109876 + +3.3.9. Period of Time + + Value Name: PERIOD + + Purpose: This value type is used to identify values that contain a + precise period of time. + + Format Definition: This value type is defined by the following + notation: + + period = period-explicit / period-start + + period-explicit = date-time "/" date-time + ; [ISO.8601.2004] complete representation basic format for a + ; period of time consisting of a start and end. The start MUST + ; be before the end. + + period-start = date-time "/" dur-value + ; [ISO.8601.2004] complete representation basic format for a + ; period of time consisting of a start and positive duration + ; of time. + + + +Desruisseaux Standards Track [Page 37] + +RFC 5545 iCalendar September 2009 + + + Description: If the property permits, multiple "period" values are + specified by a COMMA-separated list of values. There are two + forms of a period of time. First, a period of time is identified + by its start and its end. This format is based on the + [ISO.8601.2004] complete representation, basic format for "DATE- + TIME" start of the period, followed by a SOLIDUS character + followed by the "DATE-TIME" of the end of the period. The start + of the period MUST be before the end of the period. Second, a + period of time can also be defined by a start and a positive + duration of time. The format is based on the [ISO.8601.2004] + complete representation, basic format for the "DATE-TIME" start of + the period, followed by a SOLIDUS character, followed by the + [ISO.8601.2004] basic format for "DURATION" of the period. + + Example: The period starting at 18:00:00 UTC, on January 1, 1997 and + ending at 07:00:00 UTC on January 2, 1997 would be: + + 19970101T180000Z/19970102T070000Z + + The period start at 18:00:00 on January 1, 1997 and lasting 5 + hours and 30 minutes would be: + + 19970101T180000Z/PT5H30M + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + +3.3.10. Recurrence Rule + + Value Name: RECUR + + Purpose: This value type is used to identify properties that contain + a recurrence rule specification. + + Format Definition: This value type is defined by the following + notation: + + recur = recur-rule-part *( ";" recur-rule-part ) + ; + ; The rule parts are not ordered in any + ; particular sequence. + ; + ; The FREQ rule part is REQUIRED, + ; but MUST NOT occur more than once. + ; + ; The UNTIL or COUNT rule parts are OPTIONAL, + ; but they MUST NOT occur in the same 'recur'. + ; + + + +Desruisseaux Standards Track [Page 38] + +RFC 5545 iCalendar September 2009 + + + ; The other rule parts are OPTIONAL, + ; but MUST NOT occur more than once. + + recur-rule-part = ( "FREQ" "=" freq ) + / ( "UNTIL" "=" enddate ) + / ( "COUNT" "=" 1*DIGIT ) + / ( "INTERVAL" "=" 1*DIGIT ) + / ( "BYSECOND" "=" byseclist ) + / ( "BYMINUTE" "=" byminlist ) + / ( "BYHOUR" "=" byhrlist ) + / ( "BYDAY" "=" bywdaylist ) + / ( "BYMONTHDAY" "=" bymodaylist ) + / ( "BYYEARDAY" "=" byyrdaylist ) + / ( "BYWEEKNO" "=" bywknolist ) + / ( "BYMONTH" "=" bymolist ) + / ( "BYSETPOS" "=" bysplist ) + / ( "WKST" "=" weekday ) + + freq = "SECONDLY" / "MINUTELY" / "HOURLY" / "DAILY" + / "WEEKLY" / "MONTHLY" / "YEARLY" + + enddate = date / date-time + + byseclist = ( seconds *("," seconds) ) + + seconds = 1*2DIGIT ;0 to 60 + + byminlist = ( minutes *("," minutes) ) + + minutes = 1*2DIGIT ;0 to 59 + + byhrlist = ( hour *("," hour) ) + + hour = 1*2DIGIT ;0 to 23 + + bywdaylist = ( weekdaynum *("," weekdaynum) ) + + weekdaynum = [[plus / minus] ordwk] weekday + + plus = "+" + + minus = "-" + + ordwk = 1*2DIGIT ;1 to 53 + + weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA" + ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, + ;FRIDAY, and SATURDAY days of the week. + + + +Desruisseaux Standards Track [Page 39] + +RFC 5545 iCalendar September 2009 + + + bymodaylist = ( monthdaynum *("," monthdaynum) ) + + monthdaynum = [plus / minus] ordmoday + + ordmoday = 1*2DIGIT ;1 to 31 + + byyrdaylist = ( yeardaynum *("," yeardaynum) ) + + yeardaynum = [plus / minus] ordyrday + + ordyrday = 1*3DIGIT ;1 to 366 + + bywknolist = ( weeknum *("," weeknum) ) + + weeknum = [plus / minus] ordwk + + bymolist = ( monthnum *("," monthnum) ) + + monthnum = 1*2DIGIT ;1 to 12 + + bysplist = ( setposday *("," setposday) ) + + setposday = yeardaynum + + Description: This value type is a structured value consisting of a + list of one or more recurrence grammar parts. Each rule part is + defined by a NAME=VALUE pair. The rule parts are separated from + each other by the SEMICOLON character. The rule parts are not + ordered in any particular sequence. Individual rule parts MUST + only be specified once. Compliant applications MUST accept rule + parts ordered in any sequence, but to ensure backward + compatibility with applications that pre-date this revision of + iCalendar the FREQ rule part MUST be the first rule part specified + in a RECUR value. + + The FREQ rule part identifies the type of recurrence rule. This + rule part MUST be specified in the recurrence rule. Valid values + include SECONDLY, to specify repeating events based on an interval + of a second or more; MINUTELY, to specify repeating events based + on an interval of a minute or more; HOURLY, to specify repeating + events based on an interval of an hour or more; DAILY, to specify + repeating events based on an interval of a day or more; WEEKLY, to + specify repeating events based on an interval of a week or more; + MONTHLY, to specify repeating events based on an interval of a + month or more; and YEARLY, to specify repeating events based on an + interval of a year or more. + + + + + +Desruisseaux Standards Track [Page 40] + +RFC 5545 iCalendar September 2009 + + + The INTERVAL rule part contains a positive integer representing at + which intervals the recurrence rule repeats. The default value is + "1", meaning every second for a SECONDLY rule, every minute for a + MINUTELY rule, every hour for an HOURLY rule, every day for a + DAILY rule, every week for a WEEKLY rule, every month for a + MONTHLY rule, and every year for a YEARLY rule. For example, + within a DAILY rule, a value of "8" means every eight days. + + The UNTIL rule part defines a DATE or DATE-TIME value that bounds + the recurrence rule in an inclusive manner. If the value + specified by UNTIL is synchronized with the specified recurrence, + this DATE or DATE-TIME becomes the last instance of the + recurrence. The value of the UNTIL rule part MUST have the same + value type as the "DTSTART" property. Furthermore, if the + "DTSTART" property is specified as a date with local time, then + the UNTIL rule part MUST also be specified as a date with local + time. If the "DTSTART" property is specified as a date with UTC + time or a date with local time and time zone reference, then the + UNTIL rule part MUST be specified as a date with UTC time. In the + case of the "STANDARD" and "DAYLIGHT" sub-components the UNTIL + rule part MUST always be specified as a date with UTC time. If + specified as a DATE-TIME value, then it MUST be specified in a UTC + time format. If not present, and the COUNT rule part is also not + present, the "RRULE" is considered to repeat forever. + + The COUNT rule part defines the number of occurrences at which to + range-bound the recurrence. The "DTSTART" property value always + counts as the first occurrence. + + The BYSECOND rule part specifies a COMMA-separated list of seconds + within a minute. Valid values are 0 to 60. The BYMINUTE rule + part specifies a COMMA-separated list of minutes within an hour. + Valid values are 0 to 59. The BYHOUR rule part specifies a COMMA- + separated list of hours of the day. Valid values are 0 to 23. + The BYSECOND, BYMINUTE and BYHOUR rule parts MUST NOT be specified + when the associated "DTSTART" property has a DATE value type. + These rule parts MUST be ignored in RECUR value that violate the + above requirement (e.g., generated by applications that pre-date + this revision of iCalendar). + + The BYDAY rule part specifies a COMMA-separated list of days of + the week; SU indicates Sunday; MO indicates Monday; TU indicates + Tuesday; WE indicates Wednesday; TH indicates Thursday; FR + indicates Friday; and SA indicates Saturday. + + Each BYDAY value can also be preceded by a positive (+n) or + negative (-n) integer. If present, this indicates the nth + occurrence of a specific day within the MONTHLY or YEARLY "RRULE". + + + +Desruisseaux Standards Track [Page 41] + +RFC 5545 iCalendar September 2009 + + + For example, within a MONTHLY rule, +1MO (or simply 1MO) + represents the first Monday within the month, whereas -1MO + represents the last Monday of the month. The numeric value in a + BYDAY rule part with the FREQ rule part set to YEARLY corresponds + to an offset within the month when the BYMONTH rule part is + present, and corresponds to an offset within the year when the + BYWEEKNO or BYMONTH rule parts are present. If an integer + modifier is not present, it means all days of this type within the + specified frequency. For example, within a MONTHLY rule, MO + represents all Mondays within the month. The BYDAY rule part MUST + NOT be specified with a numeric value when the FREQ rule part is + not set to MONTHLY or YEARLY. Furthermore, the BYDAY rule part + MUST NOT be specified with a numeric value with the FREQ rule part + set to YEARLY when the BYWEEKNO rule part is specified. + + The BYMONTHDAY rule part specifies a COMMA-separated list of days + of the month. Valid values are 1 to 31 or -31 to -1. For + example, -10 represents the tenth to the last day of the month. + The BYMONTHDAY rule part MUST NOT be specified when the FREQ rule + part is set to WEEKLY. + + The BYYEARDAY rule part specifies a COMMA-separated list of days + of the year. Valid values are 1 to 366 or -366 to -1. For + example, -1 represents the last day of the year (December 31st) + and -306 represents the 306th to the last day of the year (March + 1st). The BYYEARDAY rule part MUST NOT be specified when the FREQ + rule part is set to DAILY, WEEKLY, or MONTHLY. + + The BYWEEKNO rule part specifies a COMMA-separated list of + ordinals specifying weeks of the year. Valid values are 1 to 53 + or -53 to -1. This corresponds to weeks according to week + numbering as defined in [ISO.8601.2004]. A week is defined as a + seven day period, starting on the day of the week defined to be + the week start (see WKST). Week number one of the calendar year + is the first week that contains at least four (4) days in that + calendar year. This rule part MUST NOT be used when the FREQ rule + part is set to anything other than YEARLY. For example, 3 + represents the third week of the year. + + Note: Assuming a Monday week start, week 53 can only occur when + Thursday is January 1 or if it is a leap year and Wednesday is + January 1. + + The BYMONTH rule part specifies a COMMA-separated list of months + of the year. Valid values are 1 to 12. + + The WKST rule part specifies the day on which the workweek starts. + Valid values are MO, TU, WE, TH, FR, SA, and SU. This is + + + +Desruisseaux Standards Track [Page 42] + +RFC 5545 iCalendar September 2009 + + + significant when a WEEKLY "RRULE" has an interval greater than 1, + and a BYDAY rule part is specified. This is also significant when + in a YEARLY "RRULE" when a BYWEEKNO rule part is specified. The + default value is MO. + + The BYSETPOS rule part specifies a COMMA-separated list of values + that corresponds to the nth occurrence within the set of + recurrence instances specified by the rule. BYSETPOS operates on + a set of recurrence instances in one interval of the recurrence + rule. For example, in a WEEKLY rule, the interval would be one + week A set of recurrence instances starts at the beginning of the + interval defined by the FREQ rule part. Valid values are 1 to 366 + or -366 to -1. It MUST only be used in conjunction with another + BYxxx rule part. For example "the last work day of the month" + could be represented as: + + FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1 + + Each BYSETPOS value can include a positive (+n) or negative (-n) + integer. If present, this indicates the nth occurrence of the + specific occurrence within the set of occurrences specified by the + rule. + + Recurrence rules may generate recurrence instances with an invalid + date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM + on a day where the local time is moved forward by an hour at 1:00 + AM). Such recurrence instances MUST be ignored and MUST NOT be + counted as part of the recurrence set. + + Information, not contained in the rule, necessary to determine the + various recurrence instance start time and dates are derived from + the Start Time ("DTSTART") component attribute. For example, + "FREQ=YEARLY;BYMONTH=1" doesn't specify a specific day within the + month or a time. This information would be the same as what is + specified for "DTSTART". + + BYxxx rule parts modify the recurrence in some manner. BYxxx rule + parts for a period of time that is the same or greater than the + frequency generally reduce or limit the number of occurrences of + the recurrence generated. For example, "FREQ=DAILY;BYMONTH=1" + reduces the number of recurrence instances from all days (if + BYMONTH rule part is not present) to all days in January. BYxxx + rule parts for a period of time less than the frequency generally + increase or expand the number of occurrences of the recurrence. + For example, "FREQ=YEARLY;BYMONTH=1,2" increases the number of + days within the yearly recurrence set from 1 (if BYMONTH rule part + is not present) to 2. + + + + +Desruisseaux Standards Track [Page 43] + +RFC 5545 iCalendar September 2009 + + + If multiple BYxxx rule parts are specified, then after evaluating + the specified FREQ and INTERVAL rule parts, the BYxxx rule parts + are applied to the current set of evaluated occurrences in the + following order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, + BYHOUR, BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are + evaluated. + + The table below summarizes the dependency of BYxxx rule part + expand or limit behavior on the FREQ rule part value. + + The term "N/A" means that the corresponding BYxxx rule part MUST + NOT be used with the corresponding FREQ value. + + BYDAY has some special behavior depending on the FREQ value and + this is described in separate notes below the table. + + +----------+--------+--------+-------+-------+------+-------+------+ + | |SECONDLY|MINUTELY|HOURLY |DAILY |WEEKLY|MONTHLY|YEARLY| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYMONTH |Limit |Limit |Limit |Limit |Limit |Limit |Expand| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYWEEKNO |N/A |N/A |N/A |N/A |N/A |N/A |Expand| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYYEARDAY |Limit |Limit |Limit |N/A |N/A |N/A |Expand| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYMONTHDAY|Limit |Limit |Limit |Limit |N/A |Expand |Expand| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYDAY |Limit |Limit |Limit |Limit |Expand|Note 1 |Note 2| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYHOUR |Limit |Limit |Limit |Expand |Expand|Expand |Expand| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYMINUTE |Limit |Limit |Expand |Expand |Expand|Expand |Expand| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYSECOND |Limit |Expand |Expand |Expand |Expand|Expand |Expand| + +----------+--------+--------+-------+-------+------+-------+------+ + |BYSETPOS |Limit |Limit |Limit |Limit |Limit |Limit |Limit | + +----------+--------+--------+-------+-------+------+-------+------+ + + Note 1: Limit if BYMONTHDAY is present; otherwise, special expand + for MONTHLY. + + Note 2: Limit if BYYEARDAY or BYMONTHDAY is present; otherwise, + special expand for WEEKLY if BYWEEKNO present; otherwise, + special expand for MONTHLY if BYMONTH present; otherwise, + special expand for YEARLY. + + + + + + +Desruisseaux Standards Track [Page 44] + +RFC 5545 iCalendar September 2009 + + + Here is an example of evaluating multiple BYxxx rule parts. + + DTSTART;TZID=America/New_York:19970105T083000 + RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9; + BYMINUTE=30 + + First, the "INTERVAL=2" would be applied to "FREQ=YEARLY" to + arrive at "every other year". Then, "BYMONTH=1" would be applied + to arrive at "every January, every other year". Then, "BYDAY=SU" + would be applied to arrive at "every Sunday in January, every + other year". Then, "BYHOUR=8,9" would be applied to arrive at + "every Sunday in January at 8 AM and 9 AM, every other year". + Then, "BYMINUTE=30" would be applied to arrive at "every Sunday in + January at 8:30 AM and 9:30 AM, every other year". Then, lacking + information from "RRULE", the second is derived from "DTSTART", to + end up in "every Sunday in January at 8:30:00 AM and 9:30:00 AM, + every other year". Similarly, if the BYMINUTE, BYHOUR, BYDAY, + BYMONTHDAY, or BYMONTH rule part were missing, the appropriate + minute, hour, day, or month would have been retrieved from the + "DTSTART" property. + + If the computed local start time of a recurrence instance does not + exist, or occurs more than once, for the specified time zone, the + time of the recurrence instance is interpreted in the same manner + as an explicit DATE-TIME value describing that date and time, as + specified in Section 3.3.5. + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + + Example: The following is a rule that specifies 10 occurrences that + occur every other day: + + FREQ=DAILY;COUNT=10;INTERVAL=2 + + There are other examples specified in Section 3.8.5.3. + +3.3.11. Text + + Value Name: TEXT + + Purpose: This value type is used to identify values that contain + human-readable text. + + Format Definition: This value type is defined by the following + notation: + + + + + +Desruisseaux Standards Track [Page 45] + +RFC 5545 iCalendar September 2009 + + + text = *(TSAFE-CHAR / ":" / DQUOTE / ESCAPED-CHAR) + ; Folded according to description above + + ESCAPED-CHAR = ("\\" / "\;" / "\," / "\N" / "\n") + ; \\ encodes \, \N or \n encodes newline + ; \; encodes ;, \, encodes , + + TSAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-5B / + %x5D-7E / NON-US-ASCII + ; Any character except CONTROLs not needed by the current + ; character set, DQUOTE, ";", ":", "\", "," + + Description: If the property permits, multiple TEXT values are + specified by a COMMA-separated list of values. + + The language in which the text is represented can be controlled by + the "LANGUAGE" property parameter. + + An intentional formatted text line break MUST only be included in + a "TEXT" property value by representing the line break with the + character sequence of BACKSLASH, followed by a LATIN SMALL LETTER + N or a LATIN CAPITAL LETTER N, that is "\n" or "\N". + + The "TEXT" property values may also contain special characters + that are used to signify delimiters, such as a COMMA character for + lists of values or a SEMICOLON character for structured values. + In order to support the inclusion of these special characters in + "TEXT" property values, they MUST be escaped with a BACKSLASH + character. A BACKSLASH character in a "TEXT" property value MUST + be escaped with another BACKSLASH character. A COMMA character in + a "TEXT" property value MUST be escaped with a BACKSLASH + character. A SEMICOLON character in a "TEXT" property value MUST + be escaped with a BACKSLASH character. However, a COLON character + in a "TEXT" property value SHALL NOT be escaped with a BACKSLASH + character. + + Example: A multiple line value of: + + Project XYZ Final Review + Conference Room - 3B + Come Prepared. + + would be represented as: + + Project XYZ Final Review\nConference Room - 3B\nCome Prepared. + + + + + + +Desruisseaux Standards Track [Page 46] + +RFC 5545 iCalendar September 2009 + + +3.3.12. Time + + Value Name: TIME + + Purpose: This value type is used to identify values that contain a + time of day. + + Format Definition: This value type is defined by the following + notation: + + time = time-hour time-minute time-second [time-utc] + + time-hour = 2DIGIT ;00-23 + time-minute = 2DIGIT ;00-59 + time-second = 2DIGIT ;00-60 + ;The "60" value is used to account for positive "leap" seconds. + + time-utc = "Z" + + Description: If the property permits, multiple "time" values are + specified by a COMMA-separated list of values. No additional + content value encoding (i.e., BACKSLASH character encoding, see + Section 3.3.11) is defined for this value type. + + The "TIME" value type is used to identify values that contain a + time of day. The format is based on the [ISO.8601.2004] complete + representation, basic format for a time of day. The text format + consists of a two-digit, 24-hour of the day (i.e., values 00-23), + two-digit minute in the hour (i.e., values 00-59), and two-digit + seconds in the minute (i.e., values 00-60). The seconds value of + 60 MUST only be used to account for positive "leap" seconds. + Fractions of a second are not supported by this format. + + In parallel to the "DATE-TIME" definition above, the "TIME" value + type expresses time values in three forms: + + The form of time with UTC offset MUST NOT be used. For example, + the following is not valid for a time value: + + 230000-0800 ;Invalid time format + + FORM #1 LOCAL TIME + + The local time form is simply a time value that does not contain + the UTC designator nor does it reference a time zone. For + example, 11:00 PM: + + 230000 + + + +Desruisseaux Standards Track [Page 47] + +RFC 5545 iCalendar September 2009 + + + Time values of this type are said to be "floating" and are not + bound to any time zone in particular. They are used to represent + the same hour, minute, and second value regardless of which time + zone is currently being observed. For example, an event can be + defined that indicates that an individual will be busy from 11:00 + AM to 1:00 PM every day, no matter which time zone the person is + in. In these cases, a local time can be specified. The recipient + of an iCalendar object with a property value consisting of a local + time, without any relative time zone information, SHOULD interpret + the value as being fixed to whatever time zone the "ATTENDEE" is + in at any given moment. This means that two "Attendees", may + participate in the same event at different UTC times; floating + time SHOULD only be used where that is reasonable behavior. + + In most cases, a fixed time is desired. To properly communicate a + fixed time in a property value, either UTC time or local time with + time zone reference MUST be specified. + + The use of local time in a TIME value without the "TZID" property + parameter is to be interpreted as floating time, regardless of the + existence of "VTIMEZONE" calendar components in the iCalendar + object. + + FORM #2: UTC TIME + + UTC time, or absolute time, is identified by a LATIN CAPITAL + LETTER Z suffix character, the UTC designator, appended to the + time value. For example, the following represents 07:00 AM UTC: + + 070000Z + + The "TZID" property parameter MUST NOT be applied to TIME + properties whose time values are specified in UTC. + + FORM #3: LOCAL TIME AND TIME ZONE REFERENCE + + The local time with reference to time zone information form is + identified by the use the "TZID" property parameter to reference + the appropriate time zone definition. "TZID" is discussed in + detail in Section 3.2.19. + + Example: The following represents 8:30 AM in New York in winter, + five hours behind UTC, in each of the three formats: + + 083000 + 133000Z + TZID=America/New_York:083000 + + + + +Desruisseaux Standards Track [Page 48] + +RFC 5545 iCalendar September 2009 + + +3.3.13. URI + + Value Name: URI + + Purpose: This value type is used to identify values that contain a + uniform resource identifier (URI) type of reference to the + property value. + + Format Definition: This value type is defined by the following + notation: + + uri = + + Description: This value type might be used to reference binary + information, for values that are large, or otherwise undesirable + to include directly in the iCalendar object. + + Property values with this value type MUST follow the generic URI + syntax defined in [RFC3986]. + + When a property parameter value is a URI value type, the URI MUST + be specified as a quoted-string value. + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + + Example: The following is a URI for a network file: + + http://example.com/my-report.txt + +3.3.14. UTC Offset + + Value Name: UTC-OFFSET + + Purpose: This value type is used to identify properties that contain + an offset from UTC to local time. + + Format Definition: This value type is defined by the following + notation: + + utc-offset = time-numzone + + time-numzone = ("+" / "-") time-hour time-minute [time-second] + + Description: The PLUS SIGN character MUST be specified for positive + UTC offsets (i.e., ahead of UTC). The HYPHEN-MINUS character MUST + be specified for negative UTC offsets (i.e., behind of UTC). The + + + + +Desruisseaux Standards Track [Page 49] + +RFC 5545 iCalendar September 2009 + + + value of "-0000" and "-000000" are not allowed. The time-second, + if present, MUST NOT be 60; if absent, it defaults to zero. + + No additional content value encoding (i.e., BACKSLASH character + encoding, see Section 3.3.11) is defined for this value type. + + Example: The following UTC offsets are given for standard time for + New York (five hours behind UTC) and Geneva (one hour ahead of + UTC): + + -0500 + + +0100 + +3.4. iCalendar Object + + The Calendaring and Scheduling Core Object is a collection of + calendaring and scheduling information. Typically, this information + will consist of an iCalendar stream with a single iCalendar object. + However, multiple iCalendar objects can be sequentially grouped + together in an iCalendar stream. The first line and last line of the + iCalendar object MUST contain a pair of iCalendar object delimiter + strings. The syntax for an iCalendar stream is as follows: + + icalstream = 1*icalobject + + icalobject = "BEGIN" ":" "VCALENDAR" CRLF + icalbody + "END" ":" "VCALENDAR" CRLF + + The following is a simple example of an iCalendar object: + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//hacksw/handcal//NONSGML v1.0//EN + BEGIN:VEVENT + UID:19970610T172345Z-AF23B2@example.com + DTSTAMP:19970610T172345Z + DTSTART:19970714T170000Z + DTEND:19970715T040000Z + SUMMARY:Bastille Day Party + END:VEVENT + END:VCALENDAR + + + + + + + + +Desruisseaux Standards Track [Page 50] + +RFC 5545 iCalendar September 2009 + + +3.5. Property + + A property is the definition of an individual attribute describing a + calendar object or a calendar component. A property takes the form + defined by the "contentline" notation defined in Section 3.1. + + The following is an example of a property: + + DTSTART:19960415T133000Z + + This memo imposes no ordering of properties within an iCalendar + object. + + Property names, parameter names, and enumerated parameter values are + case-insensitive. For example, the property name "DUE" is the same + as "due" and "Due", DTSTART;TZID=America/New_York:19980714T120000 is + the same as DtStart;TzID=America/New_York:19980714T120000. + +3.6. Calendar Components + + The body of the iCalendar object consists of a sequence of calendar + properties and one or more calendar components. The calendar + properties are attributes that apply to the calendar object as a + whole. The calendar components are collections of properties that + express a particular calendar semantic. For example, the calendar + component can specify an event, a to-do, a journal entry, time zone + information, free/busy time information, or an alarm. + + The body of the iCalendar object is defined by the following + notation: + + icalbody = calprops component + + calprops = *( + ; + ; The following are REQUIRED, + ; but MUST NOT occur more than once. + ; + prodid / version / + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + calscale / method / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + + + +Desruisseaux Standards Track [Page 51] + +RFC 5545 iCalendar September 2009 + + + x-prop / iana-prop + ; + ) + + component = 1*(eventc / todoc / journalc / freebusyc / + timezonec / iana-comp / x-comp) + + iana-comp = "BEGIN" ":" iana-token CRLF + 1*contentline + "END" ":" iana-token CRLF + + x-comp = "BEGIN" ":" x-name CRLF + 1*contentline + "END" ":" x-name CRLF + + An iCalendar object MUST include the "PRODID" and "VERSION" calendar + properties. In addition, it MUST include at least one calendar + component. Special forms of iCalendar objects are possible to + publish just busy time (i.e., only a "VFREEBUSY" calendar component) + or time zone (i.e., only a "VTIMEZONE" calendar component) + information. In addition, a complex iCalendar object that is used to + capture a complete snapshot of the contents of a calendar is possible + (e.g., composite of many different calendar components). More + commonly, an iCalendar object will consist of just a single "VEVENT", + "VTODO", or "VJOURNAL" calendar component. Applications MUST ignore + x-comp and iana-comp values they don't recognize. Applications that + support importing iCalendar objects SHOULD support all of the + component types defined in this document, and SHOULD NOT silently + drop any components as that can lead to user data loss. + +3.6.1. Event Component + + Component Name: VEVENT + + Purpose: Provide a grouping of component properties that describe an + event. + + Format Definition: A "VEVENT" calendar component is defined by the + following notation: + + eventc = "BEGIN" ":" "VEVENT" CRLF + eventprop *alarmc + "END" ":" "VEVENT" CRLF + + eventprop = *( + ; + ; The following are REQUIRED, + ; but MUST NOT occur more than once. + + + +Desruisseaux Standards Track [Page 52] + +RFC 5545 iCalendar September 2009 + + + ; + dtstamp / uid / + ; + ; The following is REQUIRED if the component + ; appears in an iCalendar object that doesn't + ; specify the "METHOD" property; otherwise, it + ; is OPTIONAL; in any case, it MUST NOT occur + ; more than once. + ; + dtstart / + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + class / created / description / geo / + last-mod / location / organizer / priority / + seq / status / summary / transp / + url / recurid / + ; + ; The following is OPTIONAL, + ; but SHOULD NOT occur more than once. + ; + rrule / + ; + ; Either 'dtend' or 'duration' MAY appear in + ; a 'eventprop', but 'dtend' and 'duration' + ; MUST NOT occur in the same 'eventprop'. + ; + dtend / duration / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + attach / attendee / categories / comment / + contact / exdate / rstatus / related / + resources / rdate / x-prop / iana-prop + ; + ) + + Description: A "VEVENT" calendar component is a grouping of + component properties, possibly including "VALARM" calendar + components, that represents a scheduled amount of time on a + calendar. For example, it can be an activity; such as a one-hour + long, department meeting from 8:00 AM to 9:00 AM, tomorrow. + Generally, an event will take up time on an individual calendar. + Hence, the event will appear as an opaque interval in a search for + busy time. Alternately, the event can have its Time Transparency + + + + +Desruisseaux Standards Track [Page 53] + +RFC 5545 iCalendar September 2009 + + + set to "TRANSPARENT" in order to prevent blocking of the event in + searches for busy time. + + The "VEVENT" is also the calendar component used to specify an + anniversary or daily reminder within a calendar. These events + have a DATE value type for the "DTSTART" property instead of the + default value type of DATE-TIME. If such a "VEVENT" has a "DTEND" + property, it MUST be specified as a DATE value also. The + anniversary type of "VEVENT" can span more than one date (i.e., + "DTEND" property value is set to a calendar date after the + "DTSTART" property value). If such a "VEVENT" has a "DURATION" + property, it MUST be specified as a "dur-day" or "dur-week" value. + + The "DTSTART" property for a "VEVENT" specifies the inclusive + start of the event. For recurring events, it also specifies the + very first instance in the recurrence set. The "DTEND" property + for a "VEVENT" calendar component specifies the non-inclusive end + of the event. For cases where a "VEVENT" calendar component + specifies a "DTSTART" property with a DATE value type but no + "DTEND" nor "DURATION" property, the event's duration is taken to + be one day. For cases where a "VEVENT" calendar component + specifies a "DTSTART" property with a DATE-TIME value type but no + "DTEND" property, the event ends on the same calendar date and + time of day specified by the "DTSTART" property. + + The "VEVENT" calendar component cannot be nested within another + calendar component. However, "VEVENT" calendar components can be + related to each other or to a "VTODO" or to a "VJOURNAL" calendar + component with the "RELATED-TO" property. + + Example: The following is an example of the "VEVENT" calendar + component used to represent a meeting that will also be opaque to + searches for busy time: + + BEGIN:VEVENT + UID:19970901T130000Z-123401@example.com + DTSTAMP:19970901T130000Z + DTSTART:19970903T163000Z + DTEND:19970903T190000Z + SUMMARY:Annual Employee Review + CLASS:PRIVATE + CATEGORIES:BUSINESS,HUMAN RESOURCES + END:VEVENT + + The following is an example of the "VEVENT" calendar component + used to represent a reminder that will not be opaque, but rather + transparent, to searches for busy time: + + + + +Desruisseaux Standards Track [Page 54] + +RFC 5545 iCalendar September 2009 + + + BEGIN:VEVENT + UID:19970901T130000Z-123402@example.com + DTSTAMP:19970901T130000Z + DTSTART:19970401T163000Z + DTEND:19970402T010000Z + SUMMARY:Laurel is in sensitivity awareness class. + CLASS:PUBLIC + CATEGORIES:BUSINESS,HUMAN RESOURCES + TRANSP:TRANSPARENT + END:VEVENT + + The following is an example of the "VEVENT" calendar component + used to represent an anniversary that will occur annually: + + BEGIN:VEVENT + UID:19970901T130000Z-123403@example.com + DTSTAMP:19970901T130000Z + DTSTART;VALUE=DATE:19971102 + SUMMARY:Our Blissful Anniversary + TRANSP:TRANSPARENT + CLASS:CONFIDENTIAL + CATEGORIES:ANNIVERSARY,PERSONAL,SPECIAL OCCASION + RRULE:FREQ=YEARLY + END:VEVENT + + The following is an example of the "VEVENT" calendar component + used to represent a multi-day event scheduled from June 28th, 2007 + to July 8th, 2007 inclusively. Note that the "DTEND" property is + set to July 9th, 2007, since the "DTEND" property specifies the + non-inclusive end of the event. + + BEGIN:VEVENT + UID:20070423T123432Z-541111@example.com + DTSTAMP:20070423T123432Z + DTSTART;VALUE=DATE:20070628 + DTEND;VALUE=DATE:20070709 + SUMMARY:Festival International de Jazz de Montreal + TRANSP:TRANSPARENT + END:VEVENT + +3.6.2. To-Do Component + + Component Name: VTODO + + Purpose: Provide a grouping of calendar properties that describe a + to-do. + + + + + +Desruisseaux Standards Track [Page 55] + +RFC 5545 iCalendar September 2009 + + + Format Definition: A "VTODO" calendar component is defined by the + following notation: + + todoc = "BEGIN" ":" "VTODO" CRLF + todoprop *alarmc + "END" ":" "VTODO" CRLF + + todoprop = *( + ; + ; The following are REQUIRED, + ; but MUST NOT occur more than once. + ; + dtstamp / uid / + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + class / completed / created / description / + dtstart / geo / last-mod / location / organizer / + percent / priority / recurid / seq / status / + summary / url / + ; + ; The following is OPTIONAL, + ; but SHOULD NOT occur more than once. + ; + rrule / + ; + ; Either 'due' or 'duration' MAY appear in + ; a 'todoprop', but 'due' and 'duration' + ; MUST NOT occur in the same 'todoprop'. + ; If 'duration' appear in a 'todoprop', + ; then 'dtstart' MUST also appear in + ; the same 'todoprop'. + ; + due / duration / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + attach / attendee / categories / comment / contact / + exdate / rstatus / related / resources / + rdate / x-prop / iana-prop + ; + ) + + Description: A "VTODO" calendar component is a grouping of component + properties and possibly "VALARM" calendar components that + represent an action-item or assignment. For example, it can be + + + +Desruisseaux Standards Track [Page 56] + +RFC 5545 iCalendar September 2009 + + + used to represent an item of work assigned to an individual; such + as "turn in travel expense today". + + The "VTODO" calendar component cannot be nested within another + calendar component. However, "VTODO" calendar components can be + related to each other or to a "VEVENT" or to a "VJOURNAL" calendar + component with the "RELATED-TO" property. + + A "VTODO" calendar component without the "DTSTART" and "DUE" (or + "DURATION") properties specifies a to-do that will be associated + with each successive calendar date, until it is completed. + + Examples: The following is an example of a "VTODO" calendar + component that needs to be completed before May 1st, 2007. On + midnight May 1st, 2007 this to-do would be considered overdue. + + BEGIN:VTODO + UID:20070313T123432Z-456553@example.com + DTSTAMP:20070313T123432Z + DUE;VALUE=DATE:20070501 + SUMMARY:Submit Quebec Income Tax Return for 2006 + CLASS:CONFIDENTIAL + CATEGORIES:FAMILY,FINANCE + STATUS:NEEDS-ACTION + END:VTODO + + The following is an example of a "VTODO" calendar component that + was due before 1:00 P.M. UTC on July 9th, 2007 and was completed + on July 7th, 2007 at 10:00 A.M. UTC. + + BEGIN:VTODO + UID:20070514T103211Z-123404@example.com + DTSTAMP:20070514T103211Z + DTSTART:20070514T110000Z + DUE:20070709T130000Z + COMPLETED:20070707T100000Z + SUMMARY:Submit Revised Internet-Draft + PRIORITY:1 + STATUS:NEEDS-ACTION + END:VTODO + +3.6.3. Journal Component + + Component Name: VJOURNAL + + Purpose: Provide a grouping of component properties that describe a + journal entry. + + + + +Desruisseaux Standards Track [Page 57] + +RFC 5545 iCalendar September 2009 + + + Format Definition: A "VJOURNAL" calendar component is defined by the + following notation: + + journalc = "BEGIN" ":" "VJOURNAL" CRLF + jourprop + "END" ":" "VJOURNAL" CRLF + + jourprop = *( + ; + ; The following are REQUIRED, + ; but MUST NOT occur more than once. + ; + dtstamp / uid / + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + class / created / dtstart / + last-mod / organizer / recurid / seq / + status / summary / url / + ; + ; The following is OPTIONAL, + ; but SHOULD NOT occur more than once. + ; + rrule / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + attach / attendee / categories / comment / + contact / description / exdate / related / rdate / + rstatus / x-prop / iana-prop + ; + ) + + Description: A "VJOURNAL" calendar component is a grouping of + component properties that represent one or more descriptive text + notes associated with a particular calendar date. The "DTSTART" + property is used to specify the calendar date with which the + journal entry is associated. Generally, it will have a DATE value + data type, but it can also be used to specify a DATE-TIME value + data type. Examples of a journal entry include a daily record of + a legislative body or a journal entry of individual telephone + contacts for the day or an ordered list of accomplishments for the + day. The "VJOURNAL" calendar component can also be used to + associate a document with a calendar date. + + + + + +Desruisseaux Standards Track [Page 58] + +RFC 5545 iCalendar September 2009 + + + The "VJOURNAL" calendar component does not take up time on a + calendar. Hence, it does not play a role in free or busy time + searches -- it is as though it has a time transparency value of + TRANSPARENT. It is transparent to any such searches. + + The "VJOURNAL" calendar component cannot be nested within another + calendar component. However, "VJOURNAL" calendar components can + be related to each other or to a "VEVENT" or to a "VTODO" calendar + component, with the "RELATED-TO" property. + + Example: The following is an example of the "VJOURNAL" calendar + component: + + BEGIN:VJOURNAL + UID:19970901T130000Z-123405@example.com + DTSTAMP:19970901T130000Z + DTSTART;VALUE=DATE:19970317 + SUMMARY:Staff meeting minutes + DESCRIPTION:1. Staff meeting: Participants include Joe\, + Lisa\, and Bob. Aurora project plans were reviewed. + There is currently no budget reserves for this project. + Lisa will escalate to management. Next meeting on Tuesday.\n + 2. Telephone Conference: ABC Corp. sales representative + called to discuss new printer. Promised to get us a demo by + Friday.\n3. Henry Miller (Handsoff Insurance): Car was + totaled by tree. Is looking into a loaner car. 555-2323 + (tel). + END:VJOURNAL + +3.6.4. Free/Busy Component + + Component Name: VFREEBUSY + + Purpose: Provide a grouping of component properties that describe + either a request for free/busy time, describe a response to a + request for free/busy time, or describe a published set of busy + time. + + Format Definition: A "VFREEBUSY" calendar component is defined by + the following notation: + + freebusyc = "BEGIN" ":" "VFREEBUSY" CRLF + fbprop + "END" ":" "VFREEBUSY" CRLF + + fbprop = *( + ; + ; The following are REQUIRED, + + + +Desruisseaux Standards Track [Page 59] + +RFC 5545 iCalendar September 2009 + + + ; but MUST NOT occur more than once. + ; + dtstamp / uid / + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + contact / dtstart / dtend / + organizer / url / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + attendee / comment / freebusy / rstatus / x-prop / + iana-prop + ; + ) + + Description: A "VFREEBUSY" calendar component is a grouping of + component properties that represents either a request for free or + busy time information, a reply to a request for free or busy time + information, or a published set of busy time information. + + When used to request free/busy time information, the "ATTENDEE" + property specifies the calendar users whose free/busy time is + being requested; the "ORGANIZER" property specifies the calendar + user who is requesting the free/busy time; the "DTSTART" and + "DTEND" properties specify the window of time for which the free/ + busy time is being requested; the "UID" and "DTSTAMP" properties + are specified to assist in proper sequencing of multiple free/busy + time requests. + + When used to reply to a request for free/busy time, the "ATTENDEE" + property specifies the calendar user responding to the free/busy + time request; the "ORGANIZER" property specifies the calendar user + that originally requested the free/busy time; the "FREEBUSY" + property specifies the free/busy time information (if it exists); + and the "UID" and "DTSTAMP" properties are specified to assist in + proper sequencing of multiple free/busy time replies. + + When used to publish busy time, the "ORGANIZER" property specifies + the calendar user associated with the published busy time; the + "DTSTART" and "DTEND" properties specify an inclusive time window + that surrounds the busy time information; the "FREEBUSY" property + specifies the published busy time information; and the "DTSTAMP" + property specifies the DATE-TIME that iCalendar object was + created. + + + + +Desruisseaux Standards Track [Page 60] + +RFC 5545 iCalendar September 2009 + + + The "VFREEBUSY" calendar component cannot be nested within another + calendar component. Multiple "VFREEBUSY" calendar components can + be specified within an iCalendar object. This permits the + grouping of free/busy information into logical collections, such + as monthly groups of busy time information. + + The "VFREEBUSY" calendar component is intended for use in + iCalendar object methods involving requests for free time, + requests for busy time, requests for both free and busy, and the + associated replies. + + Free/Busy information is represented with the "FREEBUSY" property. + This property provides a terse representation of time periods. + One or more "FREEBUSY" properties can be specified in the + "VFREEBUSY" calendar component. + + When present in a "VFREEBUSY" calendar component, the "DTSTART" + and "DTEND" properties SHOULD be specified prior to any "FREEBUSY" + properties. + + The recurrence properties ("RRULE", "RDATE", "EXDATE") are not + permitted within a "VFREEBUSY" calendar component. Any recurring + events are resolved into their individual busy time periods using + the "FREEBUSY" property. + + Example: The following is an example of a "VFREEBUSY" calendar + component used to request free or busy time information: + + BEGIN:VFREEBUSY + UID:19970901T082949Z-FA43EF@example.com + ORGANIZER:mailto:jane_doe@example.com + ATTENDEE:mailto:john_public@example.com + DTSTART:19971015T050000Z + DTEND:19971016T050000Z + DTSTAMP:19970901T083000Z + END:VFREEBUSY + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 61] + +RFC 5545 iCalendar September 2009 + + + The following is an example of a "VFREEBUSY" calendar component + used to reply to the request with busy time information: + + BEGIN:VFREEBUSY + UID:19970901T095957Z-76A912@example.com + ORGANIZER:mailto:jane_doe@example.com + ATTENDEE:mailto:john_public@example.com + DTSTAMP:19970901T100000Z + FREEBUSY:19971015T050000Z/PT8H30M, + 19971015T160000Z/PT5H30M,19971015T223000Z/PT6H30M + URL:http://example.com/pub/busy/jpublic-01.ifb + COMMENT:This iCalendar file contains busy time information for + the next three months. + END:VFREEBUSY + + The following is an example of a "VFREEBUSY" calendar component + used to publish busy time information: + + BEGIN:VFREEBUSY + UID:19970901T115957Z-76A912@example.com + DTSTAMP:19970901T120000Z + ORGANIZER:jsmith@example.com + DTSTART:19980313T141711Z + DTEND:19980410T141711Z + FREEBUSY:19980314T233000Z/19980315T003000Z + FREEBUSY:19980316T153000Z/19980316T163000Z + FREEBUSY:19980318T030000Z/19980318T040000Z + URL:http://www.example.com/calendar/busytime/jsmith.ifb + END:VFREEBUSY + +3.6.5. Time Zone Component + + Component Name: VTIMEZONE + + Purpose: Provide a grouping of component properties that defines a + time zone. + + Format Definition: A "VTIMEZONE" calendar component is defined by + the following notation: + + timezonec = "BEGIN" ":" "VTIMEZONE" CRLF + *( + ; + ; 'tzid' is REQUIRED, but MUST NOT occur more + ; than once. + ; + tzid / + ; + + + +Desruisseaux Standards Track [Page 62] + +RFC 5545 iCalendar September 2009 + + + ; 'last-mod' and 'tzurl' are OPTIONAL, + ; but MUST NOT occur more than once. + ; + last-mod / tzurl / + ; + ; One of 'standardc' or 'daylightc' MUST occur + ; and each MAY occur more than once. + ; + standardc / daylightc / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + x-prop / iana-prop + ; + ) + "END" ":" "VTIMEZONE" CRLF + + standardc = "BEGIN" ":" "STANDARD" CRLF + tzprop + "END" ":" "STANDARD" CRLF + + daylightc = "BEGIN" ":" "DAYLIGHT" CRLF + tzprop + "END" ":" "DAYLIGHT" CRLF + + tzprop = *( + ; + ; The following are REQUIRED, + ; but MUST NOT occur more than once. + ; + dtstart / tzoffsetto / tzoffsetfrom / + ; + ; The following is OPTIONAL, + ; but SHOULD NOT occur more than once. + ; + rrule / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + comment / rdate / tzname / x-prop / iana-prop + ; + ) + + Description: A time zone is unambiguously defined by the set of time + measurement rules determined by the governing body for a given + geographic area. These rules describe, at a minimum, the base + + + +Desruisseaux Standards Track [Page 63] + +RFC 5545 iCalendar September 2009 + + + offset from UTC for the time zone, often referred to as the + Standard Time offset. Many locations adjust their Standard Time + forward or backward by one hour, in order to accommodate seasonal + changes in number of daylight hours, often referred to as Daylight + Saving Time. Some locations adjust their time by a fraction of an + hour. Standard Time is also known as Winter Time. Daylight + Saving Time is also known as Advanced Time, Summer Time, or Legal + Time in certain countries. The following table shows the changes + in time zone rules in effect for New York City starting from 1967. + Each line represents a description or rule for a particular + observance. + + Effective Observance Rule + + +-----------+--------------------------+--------+--------------+ + | Date | (Date-Time) | Offset | Abbreviation | + +-----------+--------------------------+--------+--------------+ + | 1967-1973 | last Sun in Apr, 02:00 | -0400 | EDT | + | | | | | + | 1967-2006 | last Sun in Oct, 02:00 | -0500 | EST | + | | | | | + | 1974-1974 | Jan 6, 02:00 | -0400 | EDT | + | | | | | + | 1975-1975 | Feb 23, 02:00 | -0400 | EDT | + | | | | | + | 1976-1986 | last Sun in Apr, 02:00 | -0400 | EDT | + | | | | | + | 1987-2006 | first Sun in Apr, 02:00 | -0400 | EDT | + | | | | | + | 2007-* | second Sun in Mar, 02:00 | -0400 | EDT | + | | | | | + | 2007-* | first Sun in Nov, 02:00 | -0500 | EST | + +-----------+--------------------------+--------+--------------+ + + Note: The specification of a global time zone registry is not + addressed by this document and is left for future study. + However, implementers may find the TZ database [TZDB] a useful + reference. It is an informal, public-domain collection of time + zone information, which is currently being maintained by + volunteer Internet participants, and is used in several + operating systems. This database contains current and + historical time zone information for a wide variety of + locations around the globe; it provides a time zone identifier + for every unique time zone rule set in actual use since 1970, + with historical data going back to the introduction of standard + time. + + + + + +Desruisseaux Standards Track [Page 64] + +RFC 5545 iCalendar September 2009 + + + Interoperability between two calendaring and scheduling + applications, especially for recurring events, to-dos or journal + entries, is dependent on the ability to capture and convey date + and time information in an unambiguous format. The specification + of current time zone information is integral to this behavior. + + If present, the "VTIMEZONE" calendar component defines the set of + Standard Time and Daylight Saving Time observances (or rules) for + a particular time zone for a given interval of time. The + "VTIMEZONE" calendar component cannot be nested within other + calendar components. Multiple "VTIMEZONE" calendar components can + exist in an iCalendar object. In this situation, each "VTIMEZONE" + MUST represent a unique time zone definition. This is necessary + for some classes of events, such as airline flights, that start in + one time zone and end in another. + + The "VTIMEZONE" calendar component MUST include the "TZID" + property and at least one definition of a "STANDARD" or "DAYLIGHT" + sub-component. The "STANDARD" or "DAYLIGHT" sub-component MUST + include the "DTSTART", "TZOFFSETFROM", and "TZOFFSETTO" + properties. + + An individual "VTIMEZONE" calendar component MUST be specified for + each unique "TZID" parameter value specified in the iCalendar + object. In addition, a "VTIMEZONE" calendar component, referred + to by a recurring calendar component, MUST provide valid time zone + information for all recurrence instances. + + Each "VTIMEZONE" calendar component consists of a collection of + one or more sub-components that describe the rule for a particular + observance (either a Standard Time or a Daylight Saving Time + observance). The "STANDARD" sub-component consists of a + collection of properties that describe Standard Time. The + "DAYLIGHT" sub-component consists of a collection of properties + that describe Daylight Saving Time. In general, this collection + of properties consists of: + + * the first onset DATE-TIME for the observance; + + * the last onset DATE-TIME for the observance, if a last onset is + known; + + * the offset to be applied for the observance; + + * a rule that describes the day and time when the observance + takes effect; + + * an optional name for the observance. + + + +Desruisseaux Standards Track [Page 65] + +RFC 5545 iCalendar September 2009 + + + For a given time zone, there may be multiple unique definitions of + the observances over a period of time. Each observance is + described using either a "STANDARD" or "DAYLIGHT" sub-component. + The collection of these sub-components is used to describe the + time zone for a given period of time. The offset to apply at any + given time is found by locating the observance that has the last + onset date and time before the time in question, and using the + offset value from that observance. + + The top-level properties in a "VTIMEZONE" calendar component are: + + The mandatory "TZID" property is a text value that uniquely + identifies the "VTIMEZONE" calendar component within the scope of + an iCalendar object. + + The optional "LAST-MODIFIED" property is a UTC value that + specifies the date and time that this time zone definition was + last updated. + + The optional "TZURL" property is a url value that points to a + published "VTIMEZONE" definition. "TZURL" SHOULD refer to a + resource that is accessible by anyone who might need to interpret + the object. This SHOULD NOT normally be a "file" URL or other URL + that is not widely accessible. + + The collection of properties that are used to define the + "STANDARD" and "DAYLIGHT" sub-components include: + + The mandatory "DTSTART" property gives the effective onset date + and local time for the time zone sub-component definition. + "DTSTART" in this usage MUST be specified as a date with a local + time value. + + The mandatory "TZOFFSETFROM" property gives the UTC offset that is + in use when the onset of this time zone observance begins. + "TZOFFSETFROM" is combined with "DTSTART" to define the effective + onset for the time zone sub-component definition. For example, + the following represents the time at which the observance of + Standard Time took effect in Fall 1967 for New York City: + + DTSTART:19671029T020000 + + TZOFFSETFROM:-0400 + + The mandatory "TZOFFSETTO" property gives the UTC offset for the + time zone sub-component (Standard Time or Daylight Saving Time) + when this observance is in use. + + + + +Desruisseaux Standards Track [Page 66] + +RFC 5545 iCalendar September 2009 + + + The optional "TZNAME" property is the customary name for the time + zone. This could be used for displaying dates. + + The onset DATE-TIME values for the observance defined by the time + zone sub-component is defined by the "DTSTART", "RRULE", and + "RDATE" properties. + + The "RRULE" property defines the recurrence rule for the onset of + the observance defined by this time zone sub-component. Some + specific requirements for the usage of "RRULE" for this purpose + include: + + * If observance is known to have an effective end date, the + "UNTIL" recurrence rule parameter MUST be used to specify the + last valid onset of this observance (i.e., the UNTIL DATE-TIME + will be equal to the last instance generated by the recurrence + pattern). It MUST be specified in UTC time. + + * The "DTSTART" and the "TZOFFSETFROM" properties MUST be used + when generating the onset DATE-TIME values (instances) from the + "RRULE". + + The "RDATE" property can also be used to define the onset of the + observance by giving the individual onset date and times. "RDATE" + in this usage MUST be specified as a date with local time value, + relative to the UTC offset specified in the "TZOFFSETFROM" + property. + + The optional "COMMENT" property is also allowed for descriptive + explanatory text. + + Example: The following are examples of the "VTIMEZONE" calendar + component: + + This is an example showing all the time zone rules for New York + City since April 30, 1967 at 03:00:00 EDT. + + BEGIN:VTIMEZONE + TZID:America/New_York + LAST-MODIFIED:20050809T050000Z + BEGIN:DAYLIGHT + DTSTART:19670430T020000 + RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19730429T070000Z + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + BEGIN:STANDARD + + + +Desruisseaux Standards Track [Page 67] + +RFC 5545 iCalendar September 2009 + + + DTSTART:19671029T020000 + RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=20061029T060000Z + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19740106T020000 + RDATE:19750223T020000 + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + BEGIN:DAYLIGHT + DTSTART:19760425T020000 + RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19860427T070000Z + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + BEGIN:DAYLIGHT + DTSTART:19870405T020000 + RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=20060402T070000Z + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + BEGIN:DAYLIGHT + DTSTART:20070311T020000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20071104T020000 + RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + END:STANDARD + END:VTIMEZONE + + This is an example showing time zone information for New York City + using only the "DTSTART" property. Note that this is only + suitable for a recurring event that starts on or later than March + 11, 2007 at 03:00:00 EDT (i.e., the earliest effective transition + date and time) and ends no later than March 9, 2008 at 01:59:59 + + + +Desruisseaux Standards Track [Page 68] + +RFC 5545 iCalendar September 2009 + + + EST (i.e., latest valid date and time for EST in this scenario). + For example, this can be used for a recurring event that occurs + every Friday, 8:00 A.M.-9:00 A.M., starting June 1, 2007, ending + December 31, 2007, + + BEGIN:VTIMEZONE + TZID:America/New_York + LAST-MODIFIED:20050809T050000Z + BEGIN:STANDARD + DTSTART:20071104T020000 + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:20070311T020000 + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + END:VTIMEZONE + + This is a simple example showing the current time zone rules for + New York City using a "RRULE" recurrence pattern. Note that there + is no effective end date to either of the Standard Time or + Daylight Time rules. This information would be valid for a + recurring event starting today and continuing indefinitely. + + BEGIN:VTIMEZONE + TZID:America/New_York + LAST-MODIFIED:20050809T050000Z + TZURL:http://zones.example.com/tz/America-New_York.ics + BEGIN:STANDARD + DTSTART:20071104T020000 + RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:20070311T020000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + END:VTIMEZONE + + + + +Desruisseaux Standards Track [Page 69] + +RFC 5545 iCalendar September 2009 + + + This is an example showing a set of rules for a fictitious time + zone where the Daylight Time rule has an effective end date (i.e., + after that date, Daylight Time is no longer observed). + + BEGIN:VTIMEZONE + TZID:Fictitious + LAST-MODIFIED:19870101T000000Z + BEGIN:STANDARD + DTSTART:19671029T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19870405T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=19980404T070000Z + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + END:VTIMEZONE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 70] + +RFC 5545 iCalendar September 2009 + + + This is an example showing a set of rules for a fictitious time + zone where the first Daylight Time rule has an effective end date. + There is a second Daylight Time rule that picks up where the other + left off. + + BEGIN:VTIMEZONE + TZID:Fictitious + LAST-MODIFIED:19870101T000000Z + BEGIN:STANDARD + DTSTART:19671029T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19870405T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=19980404T070000Z + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + BEGIN:DAYLIGHT + DTSTART:19990424T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=4 + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:EDT + END:DAYLIGHT + END:VTIMEZONE + +3.6.6. Alarm Component + + Component Name: VALARM + + Purpose: Provide a grouping of component properties that define an + alarm. + + Format Definition: A "VALARM" calendar component is defined by the + following notation: + + alarmc = "BEGIN" ":" "VALARM" CRLF + (audioprop / dispprop / emailprop) + "END" ":" "VALARM" CRLF + + audioprop = *( + ; + ; 'action' and 'trigger' are both REQUIRED, + + + +Desruisseaux Standards Track [Page 71] + +RFC 5545 iCalendar September 2009 + + + ; but MUST NOT occur more than once. + ; + action / trigger / + ; + ; 'duration' and 'repeat' are both OPTIONAL, + ; and MUST NOT occur more than once each; + ; but if one occurs, so MUST the other. + ; + duration / repeat / + ; + ; The following is OPTIONAL, + ; but MUST NOT occur more than once. + ; + attach / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + x-prop / iana-prop + ; + ) + + dispprop = *( + ; + ; The following are REQUIRED, + ; but MUST NOT occur more than once. + ; + action / description / trigger / + ; + ; 'duration' and 'repeat' are both OPTIONAL, + ; and MUST NOT occur more than once each; + ; but if one occurs, so MUST the other. + ; + duration / repeat / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + x-prop / iana-prop + ; + ) + + emailprop = *( + ; + ; The following are all REQUIRED, + ; but MUST NOT occur more than once. + ; + action / description / trigger / summary / + + + +Desruisseaux Standards Track [Page 72] + +RFC 5545 iCalendar September 2009 + + + ; + ; The following is REQUIRED, + ; and MAY occur more than once. + ; + attendee / + ; + ; 'duration' and 'repeat' are both OPTIONAL, + ; and MUST NOT occur more than once each; + ; but if one occurs, so MUST the other. + ; + duration / repeat / + ; + ; The following are OPTIONAL, + ; and MAY occur more than once. + ; + attach / x-prop / iana-prop + ; + ) + + Description: A "VALARM" calendar component is a grouping of + component properties that is a reminder or alarm for an event or a + to-do. For example, it may be used to define a reminder for a + pending event or an overdue to-do. + + The "VALARM" calendar component MUST include the "ACTION" and + "TRIGGER" properties. The "ACTION" property further constrains + the "VALARM" calendar component in the following ways: + + When the action is "AUDIO", the alarm can also include one and + only one "ATTACH" property, which MUST point to a sound resource, + which is rendered when the alarm is triggered. + + When the action is "DISPLAY", the alarm MUST also include a + "DESCRIPTION" property, which contains the text to be displayed + when the alarm is triggered. + + When the action is "EMAIL", the alarm MUST include a "DESCRIPTION" + property, which contains the text to be used as the message body, + a "SUMMARY" property, which contains the text to be used as the + message subject, and one or more "ATTENDEE" properties, which + contain the email address of attendees to receive the message. It + can also include one or more "ATTACH" properties, which are + intended to be sent as message attachments. When the alarm is + triggered, the email message is sent. + + The "VALARM" calendar component MUST only appear within either a + "VEVENT" or "VTODO" calendar component. "VALARM" calendar + components cannot be nested. Multiple mutually independent + + + +Desruisseaux Standards Track [Page 73] + +RFC 5545 iCalendar September 2009 + + + "VALARM" calendar components can be specified for a single + "VEVENT" or "VTODO" calendar component. + + The "TRIGGER" property specifies when the alarm will be triggered. + The "TRIGGER" property specifies a duration prior to the start of + an event or a to-do. The "TRIGGER" edge may be explicitly set to + be relative to the "START" or "END" of the event or to-do with the + "RELATED" parameter of the "TRIGGER" property. The "TRIGGER" + property value type can alternatively be set to an absolute + calendar date with UTC time. + + In an alarm set to trigger on the "START" of an event or to-do, + the "DTSTART" property MUST be present in the associated event or + to-do. In an alarm in a "VEVENT" calendar component set to + trigger on the "END" of the event, either the "DTEND" property + MUST be present, or the "DTSTART" and "DURATION" properties MUST + both be present. In an alarm in a "VTODO" calendar component set + to trigger on the "END" of the to-do, either the "DUE" property + MUST be present, or the "DTSTART" and "DURATION" properties MUST + both be present. + + The alarm can be defined such that it triggers repeatedly. A + definition of an alarm with a repeating trigger MUST include both + the "DURATION" and "REPEAT" properties. The "DURATION" property + specifies the delay period, after which the alarm will repeat. + The "REPEAT" property specifies the number of additional + repetitions that the alarm will be triggered. This repetition + count is in addition to the initial triggering of the alarm. Both + of these properties MUST be present in order to specify a + repeating alarm. If one of these two properties is absent, then + the alarm will not repeat beyond the initial trigger. + + The "ACTION" property is used within the "VALARM" calendar + component to specify the type of action invoked when the alarm is + triggered. The "VALARM" properties provide enough information for + a specific action to be invoked. It is typically the + responsibility of a "Calendar User Agent" (CUA) to deliver the + alarm in the specified fashion. An "ACTION" property value of + AUDIO specifies an alarm that causes a sound to be played to alert + the user; DISPLAY specifies an alarm that causes a text message to + be displayed to the user; and EMAIL specifies an alarm that causes + an electronic email message to be delivered to one or more email + addresses. + + In an AUDIO alarm, if the optional "ATTACH" property is included, + it MUST specify an audio sound resource. The intention is that + the sound will be played as the alarm effect. If an "ATTACH" + property is specified that does not refer to a sound resource, or + + + +Desruisseaux Standards Track [Page 74] + +RFC 5545 iCalendar September 2009 + + + if the specified sound resource cannot be rendered (because its + format is unsupported, or because it cannot be retrieved), then + the CUA or other entity responsible for playing the sound may + choose a fallback action, such as playing a built-in default + sound, or playing no sound at all. + + In a DISPLAY alarm, the intended alarm effect is for the text + value of the "DESCRIPTION" property to be displayed to the user. + + In an EMAIL alarm, the intended alarm effect is for an email + message to be composed and delivered to all the addresses + specified by the "ATTENDEE" properties in the "VALARM" calendar + component. The "DESCRIPTION" property of the "VALARM" calendar + component MUST be used as the body text of the message, and the + "SUMMARY" property MUST be used as the subject text. Any "ATTACH" + properties in the "VALARM" calendar component SHOULD be sent as + attachments to the message. + + Note: Implementations should carefully consider whether they + accept alarm components from untrusted sources, e.g., when + importing calendar objects from external sources. One + reasonable policy is to always ignore alarm components that the + calendar user has not set herself, or at least ask for + confirmation in such a case. + + Example: The following example is for a "VALARM" calendar component + that specifies an audio alarm that will sound at a precise time + and repeat 4 more times at 15-minute intervals: + + BEGIN:VALARM + TRIGGER;VALUE=DATE-TIME:19970317T133000Z + REPEAT:4 + DURATION:PT15M + ACTION:AUDIO + ATTACH;FMTTYPE=audio/basic:ftp://example.com/pub/ + sounds/bell-01.aud + END:VALARM + + The following example is for a "VALARM" calendar component that + specifies a display alarm that will trigger 30 minutes before the + scheduled start of the event or of the to-do it is associated with + and will repeat 2 more times at 15-minute intervals: + + + + + + + + + +Desruisseaux Standards Track [Page 75] + +RFC 5545 iCalendar September 2009 + + + BEGIN:VALARM + TRIGGER:-PT30M + REPEAT:2 + DURATION:PT15M + ACTION:DISPLAY + DESCRIPTION:Breakfast meeting with executive\n + team at 8:30 AM EST. + END:VALARM + + The following example is for a "VALARM" calendar component that + specifies an email alarm that will trigger 2 days before the + scheduled due DATE-TIME of a to-do with which it is associated. + It does not repeat. The email has a subject, body, and attachment + link. + + BEGIN:VALARM + TRIGGER;RELATED=END:-P2D + ACTION:EMAIL + ATTENDEE:mailto:john_doe@example.com + SUMMARY:*** REMINDER: SEND AGENDA FOR WEEKLY STAFF MEETING *** + DESCRIPTION:A draft agenda needs to be sent out to the attendees + to the weekly managers meeting (MGR-LIST). Attached is a + pointer the document template for the agenda file. + ATTACH;FMTTYPE=application/msword:http://example.com/ + templates/agenda.doc + END:VALARM + +3.7. Calendar Properties + + The Calendar Properties are attributes that apply to the iCalendar + object, as a whole. These properties do not appear within a calendar + component. They SHOULD be specified after the "BEGIN:VCALENDAR" + delimiter string and prior to any calendar component. + +3.7.1. Calendar Scale + + Property Name: CALSCALE + + Purpose: This property defines the calendar scale used for the + calendar information specified in the iCalendar object. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified once in an iCalendar + object. The default value is "GREGORIAN". + + + +Desruisseaux Standards Track [Page 76] + +RFC 5545 iCalendar September 2009 + + + Description: This memo is based on the Gregorian calendar scale. + The Gregorian calendar scale is assumed if this property is not + specified in the iCalendar object. It is expected that other + calendar scales will be defined in other specifications or by + future versions of this memo. + + Format Definition: This property is defined by the following + notation: + + calscale = "CALSCALE" calparam ":" calvalue CRLF + + calparam = *(";" other-param) + + calvalue = "GREGORIAN" + + Example: The following is an example of this property: + + CALSCALE:GREGORIAN + +3.7.2. Method + + Property Name: METHOD + + Purpose: This property defines the iCalendar object method + associated with the calendar object. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified once in an iCalendar + object. + + Description: When used in a MIME message entity, the value of this + property MUST be the same as the Content-Type "method" parameter + value. If either the "METHOD" property or the Content-Type + "method" parameter is specified, then the other MUST also be + specified. + + No methods are defined by this specification. This is the subject + of other specifications, such as the iCalendar Transport- + independent Interoperability Protocol (iTIP) defined by [2446bis]. + + If this property is not present in the iCalendar object, then a + scheduling transaction MUST NOT be assumed. In such cases, the + iCalendar object is merely being used to transport a snapshot of + + + + +Desruisseaux Standards Track [Page 77] + +RFC 5545 iCalendar September 2009 + + + some calendar information; without the intention of conveying a + scheduling semantic. + + Format Definition: This property is defined by the following + notation: + + method = "METHOD" metparam ":" metvalue CRLF + + metparam = *(";" other-param) + + metvalue = iana-token + + Example: The following is a hypothetical example of this property to + convey that the iCalendar object is a scheduling request: + + METHOD:REQUEST + +3.7.3. Product Identifier + + Property Name: PRODID + + Purpose: This property specifies the identifier for the product that + created the iCalendar object. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: The property MUST be specified once in an iCalendar + object. + + Description: The vendor of the implementation SHOULD assure that + this is a globally unique identifier; using some technique such as + an FPI value, as defined in [ISO.9070.1991]. + + This property SHOULD NOT be used to alter the interpretation of an + iCalendar object beyond the semantics specified in this memo. For + example, it is not to be used to further the understanding of non- + standard properties. + + Format Definition: This property is defined by the following + notation: + + prodid = "PRODID" pidparam ":" pidvalue CRLF + + pidparam = *(";" other-param) + + + + +Desruisseaux Standards Track [Page 78] + +RFC 5545 iCalendar September 2009 + + + pidvalue = text + ;Any text that describes the product and version + ;and that is generally assured of being unique. + + Example: The following is an example of this property. It does not + imply that English is the default language. + + PRODID:-//ABC Corporation//NONSGML My Product//EN + +3.7.4. Version + + Property Name: VERSION + + Purpose: This property specifies the identifier corresponding to the + highest version number or the minimum and maximum range of the + iCalendar specification that is required in order to interpret the + iCalendar object. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property MUST be specified once in an iCalendar + object. + + Description: A value of "2.0" corresponds to this memo. + + Format Definition: This property is defined by the following + notation: + + version = "VERSION" verparam ":" vervalue CRLF + + verparam = *(";" other-param) + + vervalue = "2.0" ;This memo + / maxver + / (minver ";" maxver) + + minver =
+ ;Minimum iCalendar version needed to parse the iCalendar object. + + maxver = + ;Maximum iCalendar version needed to parse the iCalendar object. + + + + + + + +Desruisseaux Standards Track [Page 79] + +RFC 5545 iCalendar September 2009 + + + Example: The following is an example of this property: + + VERSION:2.0 + +3.8. Component Properties + + The following properties can appear within calendar components, as + specified by each component property definition. + +3.8.1. Descriptive Component Properties + + The following properties specify descriptive information about + calendar components. + +3.8.1.1. Attachment + + Property Name: ATTACH + + Purpose: This property provides the capability to associate a + document object with a calendar component. + + Value Type: The default value type for this property is URI. The + value type can also be set to BINARY to indicate inline binary + encoded content information. + + Property Parameters: IANA, non-standard, inline encoding, and value + data type property parameters can be specified on this property. + The format type parameter can be specified on this property and is + RECOMMENDED for inline binary encoded content information. + + Conformance: This property can be specified multiple times in a + "VEVENT", "VTODO", "VJOURNAL", or "VALARM" calendar component with + the exception of AUDIO alarm that only allows this property to + occur once. + + Description: This property is used in "VEVENT", "VTODO", and + "VJOURNAL" calendar components to associate a resource (e.g., + document) with the calendar component. This property is used in + "VALARM" calendar components to specify an audio sound resource or + an email message attachment. This property can be specified as a + URI pointing to a resource or as inline binary encoded content. + + When this property is specified as inline binary encoded content, + calendar applications MAY attempt to guess the media type of the + resource via inspection of its content if and only if the media + type of the resource is not given by the "FMTTYPE" parameter. If + the media type remains unknown, calendar applications SHOULD treat + it as type "application/octet-stream". + + + +Desruisseaux Standards Track [Page 80] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + attach = "ATTACH" attachparam ( ":" uri ) / + ( + ";" "ENCODING" "=" "BASE64" + ";" "VALUE" "=" "BINARY" + ":" binary + ) + CRLF + + attachparam = *( + ; + ; The following is OPTIONAL for a URI value, + ; RECOMMENDED for a BINARY value, + ; and MUST NOT occur more than once. + ; + (";" fmttypeparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following are examples of this property: + + ATTACH:CID:jsmith.part3.960817T083000.xyzMail@example.com + + ATTACH;FMTTYPE=application/postscript:ftp://example.com/pub/ + reports/r-960812.ps + +3.8.1.2. Categories + + Property Name: CATEGORIES + + Purpose: This property defines the categories for a calendar + component. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, and language property + parameters can be specified on this property. + + Conformance: The property can be specified within "VEVENT", "VTODO", + or "VJOURNAL" calendar components. + + + + +Desruisseaux Standards Track [Page 81] + +RFC 5545 iCalendar September 2009 + + + Description: This property is used to specify categories or subtypes + of the calendar component. The categories are useful in searching + for a calendar component of a particular type and category. + Within the "VEVENT", "VTODO", or "VJOURNAL" calendar components, + more than one category can be specified as a COMMA-separated list + of categories. + + Format Definition: This property is defined by the following + notation: + + categories = "CATEGORIES" catparam ":" text *("," text) + CRLF + + catparam = *( + ; + ; The following is OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" languageparam ) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following are examples of this property: + + CATEGORIES:APPOINTMENT,EDUCATION + + CATEGORIES:MEETING + +3.8.1.3. Classification + + Property Name: CLASS + + Purpose: This property defines the access classification for a + calendar component. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: The property can be specified once in a "VEVENT", + "VTODO", or "VJOURNAL" calendar components. + + + + +Desruisseaux Standards Track [Page 82] + +RFC 5545 iCalendar September 2009 + + + Description: An access classification is only one component of the + general security system within a calendar application. It + provides a method of capturing the scope of the access the + calendar owner intends for information within an individual + calendar entry. The access classification of an individual + iCalendar component is useful when measured along with the other + security components of a calendar system (e.g., calendar user + authentication, authorization, access rights, access role, etc.). + Hence, the semantics of the individual access classifications + cannot be completely defined by this memo alone. Additionally, + due to the "blind" nature of most exchange processes using this + memo, these access classifications cannot serve as an enforcement + statement for a system receiving an iCalendar object. Rather, + they provide a method for capturing the intention of the calendar + owner for the access to the calendar component. If not specified + in a component that allows this property, the default value is + PUBLIC. Applications MUST treat x-name and iana-token values they + don't recognize the same way as they would the PRIVATE value. + + Format Definition: This property is defined by the following + notation: + + class = "CLASS" classparam ":" classvalue CRLF + + classparam = *(";" other-param) + + classvalue = "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token + / x-name + ;Default is PUBLIC + + Example: The following is an example of this property: + + CLASS:PUBLIC + +3.8.1.4. Comment + + Property Name: COMMENT + + Purpose: This property specifies non-processing information intended + to provide a comment to the calendar user. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, alternate text + representation, and language property parameters can be specified + on this property. + + + + + +Desruisseaux Standards Track [Page 83] + +RFC 5545 iCalendar September 2009 + + + Conformance: This property can be specified multiple times in + "VEVENT", "VTODO", "VJOURNAL", and "VFREEBUSY" calendar components + as well as in the "STANDARD" and "DAYLIGHT" sub-components. + + Description: This property is used to specify a comment to the + calendar user. + + Format Definition: This property is defined by the following + notation: + + comment = "COMMENT" commparam ":" text CRLF + + commparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" altrepparam) / (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following is an example of this property: + + COMMENT:The meeting really needs to include both ourselves + and the customer. We can't hold this meeting without them. + As a matter of fact\, the venue for the meeting ought to be at + their site. - - John + +3.8.1.5. Description + + Property Name: DESCRIPTION + + Purpose: This property provides a more complete description of the + calendar component than that provided by the "SUMMARY" property. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, alternate text + representation, and language property parameters can be specified + on this property. + + + + + + +Desruisseaux Standards Track [Page 84] + +RFC 5545 iCalendar September 2009 + + + Conformance: The property can be specified in the "VEVENT", "VTODO", + "VJOURNAL", or "VALARM" calendar components. The property can be + specified multiple times only within a "VJOURNAL" calendar + component. + + Description: This property is used in the "VEVENT" and "VTODO" to + capture lengthy textual descriptions associated with the activity. + + This property is used in the "VJOURNAL" calendar component to + capture one or more textual journal entries. + + This property is used in the "VALARM" calendar component to + capture the display text for a DISPLAY category of alarm, and to + capture the body text for an EMAIL category of alarm. + + Format Definition: This property is defined by the following + notation: + + description = "DESCRIPTION" descparam ":" text CRLF + + descparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" altrepparam) / (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following is an example of this property with formatted + line breaks in the property value: + + DESCRIPTION:Meeting to provide technical review for "Phoenix" + design.\nHappy Face Conference Room. Phoenix design team + MUST attend this meeting.\nRSVP to team leader. + +3.8.1.6. Geographic Position + + Property Name: GEO + + Purpose: This property specifies information related to the global + position for the activity specified by a calendar component. + + + + +Desruisseaux Standards Track [Page 85] + +RFC 5545 iCalendar September 2009 + + + Value Type: FLOAT. The value MUST be two SEMICOLON-separated FLOAT + values. + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified in "VEVENT" or "VTODO" + calendar components. + + Description: This property value specifies latitude and longitude, + in that order (i.e., "LAT LON" ordering). The longitude + represents the location east or west of the prime meridian as a + positive or negative real number, respectively. The longitude and + latitude values MAY be specified up to six decimal places, which + will allow for accuracy to within one meter of geographical + position. Receiving applications MUST accept values of this + precision and MAY truncate values of greater precision. + + Values for latitude and longitude shall be expressed as decimal + fractions of degrees. Whole degrees of latitude shall be + represented by a two-digit decimal number ranging from 0 through + 90. Whole degrees of longitude shall be represented by a decimal + number ranging from 0 through 180. When a decimal fraction of a + degree is specified, it shall be separated from the whole number + of degrees by a decimal point. + + Latitudes north of the equator shall be specified by a plus sign + (+), or by the absence of a minus sign (-), preceding the digits + designating degrees. Latitudes south of the Equator shall be + designated by a minus sign (-) preceding the digits designating + degrees. A point on the Equator shall be assigned to the Northern + Hemisphere. + + Longitudes east of the prime meridian shall be specified by a plus + sign (+), or by the absence of a minus sign (-), preceding the + digits designating degrees. Longitudes west of the meridian shall + be designated by minus sign (-) preceding the digits designating + degrees. A point on the prime meridian shall be assigned to the + Eastern Hemisphere. A point on the 180th meridian shall be + assigned to the Western Hemisphere. One exception to this last + convention is permitted. For the special condition of describing + a band of latitude around the earth, the East Bounding Coordinate + data element shall be assigned the value +180 (180) degrees. + + Any spatial address with a latitude of +90 (90) or -90 degrees + will specify the position at the North or South Pole, + respectively. The component for longitude may have any legal + value. + + + +Desruisseaux Standards Track [Page 86] + +RFC 5545 iCalendar September 2009 + + + With the exception of the special condition described above, this + form is specified in [ANSI INCITS 61-1986]. + + The simple formula for converting degrees-minutes-seconds into + decimal degrees is: + + decimal = degrees + minutes/60 + seconds/3600. + + Format Definition: This property is defined by the following + notation: + + geo = "GEO" geoparam ":" geovalue CRLF + + geoparam = *(";" other-param) + + geovalue = float ";" float + ;Latitude and Longitude components + + Example: The following is an example of this property: + + GEO:37.386013;-122.082932 + +3.8.1.7. Location + + Property Name: LOCATION + + Purpose: This property defines the intended venue for the activity + defined by a calendar component. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, alternate text + representation, and language property parameters can be specified + on this property. + + Conformance: This property can be specified in "VEVENT" or "VTODO" + calendar component. + + Description: Specific venues such as conference or meeting rooms may + be explicitly specified using this property. An alternate + representation may be specified that is a URI that points to + directory information with more structured specification of the + location. For example, the alternate representation may specify + either an LDAP URL [RFC4516] pointing to an LDAP server entry or a + CID URL [RFC2392] pointing to a MIME body part containing a + Virtual-Information Card (vCard) [RFC2426] for the location. + + + + + +Desruisseaux Standards Track [Page 87] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + location = "LOCATION" locparam ":" text CRLF + + locparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" altrepparam) / (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following are some examples of this property: + + LOCATION:Conference Room - F123\, Bldg. 002 + + LOCATION;ALTREP="http://xyzcorp.com/conf-rooms/f123.vcf": + Conference Room - F123\, Bldg. 002 + +3.8.1.8. Percent Complete + + Property Name: PERCENT-COMPLETE + + Purpose: This property is used by an assignee or delegatee of a + to-do to convey the percent completion of a to-do to the + "Organizer". + + Value Type: INTEGER + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified once in a "VTODO" + calendar component. + + Description: The property value is a positive integer between 0 and + 100. A value of "0" indicates the to-do has not yet been started. + A value of "100" indicates that the to-do has been completed. + Integer values in between indicate the percent partially complete. + + + + + +Desruisseaux Standards Track [Page 88] + +RFC 5545 iCalendar September 2009 + + + When a to-do is assigned to multiple individuals, the property + value indicates the percent complete for that portion of the to-do + assigned to the assignee or delegatee. For example, if a to-do is + assigned to both individuals "A" and "B". A reply from "A" with a + percent complete of "70" indicates that "A" has completed 70% of + the to-do assigned to them. A reply from "B" with a percent + complete of "50" indicates "B" has completed 50% of the to-do + assigned to them. + + Format Definition: This property is defined by the following + notation: + + percent = "PERCENT-COMPLETE" pctparam ":" integer CRLF + + pctparam = *(";" other-param) + + Example: The following is an example of this property to show 39% + completion: + + PERCENT-COMPLETE:39 + +3.8.1.9. Priority + + Property Name: PRIORITY + + Purpose: This property defines the relative priority for a calendar + component. + + Value Type: INTEGER + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified in "VEVENT" and "VTODO" + calendar components. + + Description: This priority is specified as an integer in the range 0 + to 9. A value of 0 specifies an undefined priority. A value of 1 + is the highest priority. A value of 2 is the second highest + priority. Subsequent numbers specify a decreasing ordinal + priority. A value of 9 is the lowest priority. + + A CUA with a three-level priority scheme of "HIGH", "MEDIUM", and + "LOW" is mapped into this property such that a property value in + the range of 1 to 4 specifies "HIGH" priority. A value of 5 is + the normal or "MEDIUM" priority. A value in the range of 6 to 9 + is "LOW" priority. + + + + +Desruisseaux Standards Track [Page 89] + +RFC 5545 iCalendar September 2009 + + + A CUA with a priority schema of "A1", "A2", "A3", "B1", "B2", ..., + "C3" is mapped into this property such that a property value of 1 + specifies "A1", a property value of 2 specifies "A2", a property + value of 3 specifies "A3", and so forth up to a property value of + 9 specifies "C3". + + Other integer values are reserved for future use. + + Within a "VEVENT" calendar component, this property specifies a + priority for the event. This property may be useful when more + than one event is scheduled for a given time period. + + Within a "VTODO" calendar component, this property specified a + priority for the to-do. This property is useful in prioritizing + multiple action items for a given time period. + + Format Definition: This property is defined by the following + notation: + + priority = "PRIORITY" prioparam ":" priovalue CRLF + ;Default is zero (i.e., undefined). + + prioparam = *(";" other-param) + + priovalue = integer ;Must be in the range [0..9] + ; All other values are reserved for future use. + + Example: The following is an example of a property with the highest + priority: + + PRIORITY:1 + + The following is an example of a property with a next highest + priority: + + PRIORITY:2 + + The following is an example of a property with no priority. This + is equivalent to not specifying the "PRIORITY" property: + + PRIORITY:0 + + + + + + + + + + +Desruisseaux Standards Track [Page 90] + +RFC 5545 iCalendar September 2009 + + +3.8.1.10. Resources + + Property Name: RESOURCES + + Purpose: This property defines the equipment or resources + anticipated for an activity specified by a calendar component. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, alternate text + representation, and language property parameters can be specified + on this property. + + Conformance: This property can be specified once in "VEVENT" or + "VTODO" calendar component. + + Description: The property value is an arbitrary text. More than one + resource can be specified as a COMMA-separated list of resources. + + Format Definition: This property is defined by the following + notation: + + resources = "RESOURCES" resrcparam ":" text *("," text) CRLF + + resrcparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" altrepparam) / (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following is an example of this property: + + RESOURCES:EASEL,PROJECTOR,VCR + + RESOURCES;LANGUAGE=fr:Nettoyeur haute pression + + + + + + + + +Desruisseaux Standards Track [Page 91] + +RFC 5545 iCalendar September 2009 + + +3.8.1.11. Status + + Property Name: STATUS + + Purpose: This property defines the overall status or confirmation + for the calendar component. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified once in "VEVENT", + "VTODO", or "VJOURNAL" calendar components. + + Description: In a group-scheduled calendar component, the property + is used by the "Organizer" to provide a confirmation of the event + to the "Attendees". For example in a "VEVENT" calendar component, + the "Organizer" can indicate that a meeting is tentative, + confirmed, or cancelled. In a "VTODO" calendar component, the + "Organizer" can indicate that an action item needs action, is + completed, is in process or being worked on, or has been + cancelled. In a "VJOURNAL" calendar component, the "Organizer" + can indicate that a journal entry is draft, final, or has been + cancelled or removed. + + Format Definition: This property is defined by the following + notation: + + status = "STATUS" statparam ":" statvalue CRLF + + statparam = *(";" other-param) + + statvalue = (statvalue-event + / statvalue-todo + / statvalue-jour) + + statvalue-event = "TENTATIVE" ;Indicates event is tentative. + / "CONFIRMED" ;Indicates event is definite. + / "CANCELLED" ;Indicates event was cancelled. + ;Status values for a "VEVENT" + + statvalue-todo = "NEEDS-ACTION" ;Indicates to-do needs action. + / "COMPLETED" ;Indicates to-do completed. + / "IN-PROCESS" ;Indicates to-do in process of. + / "CANCELLED" ;Indicates to-do was cancelled. + ;Status values for "VTODO". + + + + +Desruisseaux Standards Track [Page 92] + +RFC 5545 iCalendar September 2009 + + + statvalue-jour = "DRAFT" ;Indicates journal is draft. + / "FINAL" ;Indicates journal is final. + / "CANCELLED" ;Indicates journal is removed. + ;Status values for "VJOURNAL". + + Example: The following is an example of this property for a "VEVENT" + calendar component: + + STATUS:TENTATIVE + + The following is an example of this property for a "VTODO" + calendar component: + + STATUS:NEEDS-ACTION + + The following is an example of this property for a "VJOURNAL" + calendar component: + + STATUS:DRAFT + +3.8.1.12. Summary + + Property Name: SUMMARY + + Purpose: This property defines a short summary or subject for the + calendar component. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, alternate text + representation, and language property parameters can be specified + on this property. + + Conformance: The property can be specified in "VEVENT", "VTODO", + "VJOURNAL", or "VALARM" calendar components. + + Description: This property is used in the "VEVENT", "VTODO", and + "VJOURNAL" calendar components to capture a short, one-line + summary about the activity or journal entry. + + This property is used in the "VALARM" calendar component to + capture the subject of an EMAIL category of alarm. + + Format Definition: This property is defined by the following + notation: + + + + + + +Desruisseaux Standards Track [Page 93] + +RFC 5545 iCalendar September 2009 + + + summary = "SUMMARY" summparam ":" text CRLF + + summparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" altrepparam) / (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following is an example of this property: + + SUMMARY:Department Party + +3.8.2. Date and Time Component Properties + + The following properties specify date and time related information in + calendar components. + +3.8.2.1. Date-Time Completed + + Property Name: COMPLETED + + Purpose: This property defines the date and time that a to-do was + actually completed. + + Value Type: DATE-TIME + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: The property can be specified in a "VTODO" calendar + component. The value MUST be specified as a date with UTC time. + + Description: This property defines the date and time that a to-do + was actually completed. + + Format Definition: This property is defined by the following + notation: + + + + + + +Desruisseaux Standards Track [Page 94] + +RFC 5545 iCalendar September 2009 + + + completed = "COMPLETED" compparam ":" date-time CRLF + + compparam = *(";" other-param) + + Example: The following is an example of this property: + + COMPLETED:19960401T150000Z + +3.8.2.2. Date-Time End + + Property Name: DTEND + + Purpose: This property specifies the date and time that a calendar + component ends. + + Value Type: The default value type is DATE-TIME. The value type can + be set to a DATE value type. + + Property Parameters: IANA, non-standard, value data type, and time + zone identifier property parameters can be specified on this + property. + + Conformance: This property can be specified in "VEVENT" or + "VFREEBUSY" calendar components. + + Description: Within the "VEVENT" calendar component, this property + defines the date and time by which the event ends. The value type + of this property MUST be the same as the "DTSTART" property, and + its value MUST be later in time than the value of the "DTSTART" + property. Furthermore, this property MUST be specified as a date + with local time if and only if the "DTSTART" property is also + specified as a date with local time. + + Within the "VFREEBUSY" calendar component, this property defines + the end date and time for the free or busy time information. The + time MUST be specified in the UTC time format. The value MUST be + later in time than the value of the "DTSTART" property. + + Format Definition: This property is defined by the following + notation: + + + + + + + + + + + +Desruisseaux Standards Track [Page 95] + +RFC 5545 iCalendar September 2009 + + + dtend = "DTEND" dtendparam ":" dtendval CRLF + + dtendparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" ("DATE-TIME" / "DATE")) / + (";" tzidparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + dtendval = date-time / date + ;Value MUST match value type + + Example: The following is an example of this property: + + DTEND:19960401T150000Z + + DTEND;VALUE=DATE:19980704 + +3.8.2.3. Date-Time Due + + Property Name: DUE + + Purpose: This property defines the date and time that a to-do is + expected to be completed. + + Value Type: The default value type is DATE-TIME. The value type can + be set to a DATE value type. + + Property Parameters: IANA, non-standard, value data type, and time + zone identifier property parameters can be specified on this + property. + + Conformance: The property can be specified once in a "VTODO" + calendar component. + + Description: This property defines the date and time before which a + to-do is expected to be completed. For cases where this property + is specified in a "VTODO" calendar component that also specifies a + "DTSTART" property, the value type of this property MUST be the + same as the "DTSTART" property, and the value of this property + + + +Desruisseaux Standards Track [Page 96] + +RFC 5545 iCalendar September 2009 + + + MUST be later in time than the value of the "DTSTART" property. + Furthermore, this property MUST be specified as a date with local + time if and only if the "DTSTART" property is also specified as a + date with local time. + + Format Definition: This property is defined by the following + notation: + + due = "DUE" dueparam ":" dueval CRLF + + dueparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" ("DATE-TIME" / "DATE")) / + (";" tzidparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + dueval = date-time / date + ;Value MUST match value type + + Example: The following is an example of this property: + + DUE:19980430T000000Z + +3.8.2.4. Date-Time Start + + Property Name: DTSTART + + Purpose: This property specifies when the calendar component begins. + + Value Type: The default value type is DATE-TIME. The time value + MUST be one of the forms defined for the DATE-TIME value type. + The value type can be set to a DATE value type. + + Property Parameters: IANA, non-standard, value data type, and time + zone identifier property parameters can be specified on this + property. + + Conformance: This property can be specified once in the "VEVENT", + "VTODO", or "VFREEBUSY" calendar components as well as in the + + + +Desruisseaux Standards Track [Page 97] + +RFC 5545 iCalendar September 2009 + + + "STANDARD" and "DAYLIGHT" sub-components. This property is + REQUIRED in all types of recurring calendar components that + specify the "RRULE" property. This property is also REQUIRED in + "VEVENT" calendar components contained in iCalendar objects that + don't specify the "METHOD" property. + + Description: Within the "VEVENT" calendar component, this property + defines the start date and time for the event. + + Within the "VFREEBUSY" calendar component, this property defines + the start date and time for the free or busy time information. + The time MUST be specified in UTC time. + + Within the "STANDARD" and "DAYLIGHT" sub-components, this property + defines the effective start date and time for a time zone + specification. This property is REQUIRED within each "STANDARD" + and "DAYLIGHT" sub-components included in "VTIMEZONE" calendar + components and MUST be specified as a date with local time without + the "TZID" property parameter. + + Format Definition: This property is defined by the following + notation: + + dtstart = "DTSTART" dtstparam ":" dtstval CRLF + + dtstparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" ("DATE-TIME" / "DATE")) / + (";" tzidparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + dtstval = date-time / date + ;Value MUST match value type + + Example: The following is an example of this property: + + DTSTART:19980118T073000Z + + + + + +Desruisseaux Standards Track [Page 98] + +RFC 5545 iCalendar September 2009 + + +3.8.2.5. Duration + + Property Name: DURATION + + Purpose: This property specifies a positive duration of time. + + Value Type: DURATION + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified in "VEVENT", "VTODO", or + "VALARM" calendar components. + + Description: In a "VEVENT" calendar component the property may be + used to specify a duration of the event, instead of an explicit + end DATE-TIME. In a "VTODO" calendar component the property may + be used to specify a duration for the to-do, instead of an + explicit due DATE-TIME. In a "VALARM" calendar component the + property may be used to specify the delay period prior to + repeating an alarm. When the "DURATION" property relates to a + "DTSTART" property that is specified as a DATE value, then the + "DURATION" property MUST be specified as a "dur-day" or "dur-week" + value. + + Format Definition: This property is defined by the following + notation: + + duration = "DURATION" durparam ":" dur-value CRLF + ;consisting of a positive duration of time. + + durparam = *(";" other-param) + + Example: The following is an example of this property that specifies + an interval of time of one hour and zero minutes and zero seconds: + + DURATION:PT1H0M0S + + The following is an example of this property that specifies an + interval of time of 15 minutes. + + DURATION:PT15M + + + + + + + + + +Desruisseaux Standards Track [Page 99] + +RFC 5545 iCalendar September 2009 + + +3.8.2.6. Free/Busy Time + + Property Name: FREEBUSY + + Purpose: This property defines one or more free or busy time + intervals. + + Value Type: PERIOD + + Property Parameters: IANA, non-standard, and free/busy time type + property parameters can be specified on this property. + + Conformance: The property can be specified in a "VFREEBUSY" calendar + component. + + Description: These time periods can be specified as either a start + and end DATE-TIME or a start DATE-TIME and DURATION. The date and + time MUST be a UTC time format. + + "FREEBUSY" properties within the "VFREEBUSY" calendar component + SHOULD be sorted in ascending order, based on start time and then + end time, with the earliest periods first. + + The "FREEBUSY" property can specify more than one value, separated + by the COMMA character. In such cases, the "FREEBUSY" property + values MUST all be of the same "FBTYPE" property parameter type + (e.g., all values of a particular "FBTYPE" listed together in a + single property). + + Format Definition: This property is defined by the following + notation: + + freebusy = "FREEBUSY" fbparam ":" fbvalue CRLF + + fbparam = *( + ; + ; The following is OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" fbtypeparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + + + +Desruisseaux Standards Track [Page 100] + +RFC 5545 iCalendar September 2009 + + + fbvalue = period *("," period) + ;Time value MUST be in the UTC time format. + + Example: The following are some examples of this property: + + FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:19970308T160000Z/PT8H30M + + FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H + + FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H + ,19970308T230000Z/19970309T000000Z + +3.8.2.7. Time Transparency + + Property Name: TRANSP + + Purpose: This property defines whether or not an event is + transparent to busy time searches. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified once in a "VEVENT" + calendar component. + + Description: Time Transparency is the characteristic of an event + that determines whether it appears to consume time on a calendar. + Events that consume actual time for the individual or resource + associated with the calendar SHOULD be recorded as OPAQUE, + allowing them to be detected by free/busy time searches. Other + events, which do not take up the individual's (or resource's) time + SHOULD be recorded as TRANSPARENT, making them invisible to free/ + busy time searches. + + Format Definition: This property is defined by the following + notation: + + transp = "TRANSP" transparam ":" transvalue CRLF + + transparam = *(";" other-param) + + transvalue = "OPAQUE" + ;Blocks or opaque on busy time searches. + / "TRANSPARENT" + ;Transparent on busy time searches. + ;Default value is OPAQUE + + + +Desruisseaux Standards Track [Page 101] + +RFC 5545 iCalendar September 2009 + + + Example: The following is an example of this property for an event + that is transparent or does not block on free/busy time searches: + + TRANSP:TRANSPARENT + + The following is an example of this property for an event that is + opaque or blocks on free/busy time searches: + + TRANSP:OPAQUE + +3.8.3. Time Zone Component Properties + + The following properties specify time zone information in calendar + components. + +3.8.3.1. Time Zone Identifier + + Property Name: TZID + + Purpose: This property specifies the text value that uniquely + identifies the "VTIMEZONE" calendar component in the scope of an + iCalendar object. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property MUST be specified in a "VTIMEZONE" + calendar component. + + Description: This is the label by which a time zone calendar + component is referenced by any iCalendar properties whose value + type is either DATE-TIME or TIME and not intended to specify a UTC + or a "floating" time. The presence of the SOLIDUS character as a + prefix, indicates that this "TZID" represents an unique ID in a + globally defined time zone registry (when such registry is + defined). + + Note: This document does not define a naming convention for + time zone identifiers. Implementers may want to use the naming + conventions defined in existing time zone specifications such + as the public-domain TZ database [TZDB]. The specification of + globally unique time zone identifiers is not addressed by this + document and is left for future study. + + + + + + +Desruisseaux Standards Track [Page 102] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + tzid = "TZID" tzidpropparam ":" [tzidprefix] text CRLF + + tzidpropparam = *(";" other-param) + + ;tzidprefix = "/" + ; Defined previously. Just listed here for reader convenience. + + Example: The following are examples of non-globally unique time zone + identifiers: + + TZID:America/New_York + + TZID:America/Los_Angeles + + The following is an example of a fictitious globally unique time + zone identifier: + + TZID:/example.org/America/New_York + +3.8.3.2. Time Zone Name + + Property Name: TZNAME + + Purpose: This property specifies the customary designation for a + time zone description. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, and language property + parameters can be specified on this property. + + Conformance: This property can be specified in "STANDARD" and + "DAYLIGHT" sub-components. + + Description: This property specifies a customary name that can be + used when displaying dates that occur during the observance + defined by the time zone sub-component. + + Format Definition: This property is defined by the following + notation: + + + + + + + + +Desruisseaux Standards Track [Page 103] + +RFC 5545 iCalendar September 2009 + + + tzname = "TZNAME" tznparam ":" text CRLF + + tznparam = *( + ; + ; The following is OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following are examples of this property: + + TZNAME:EST + + TZNAME;LANGUAGE=fr-CA:HNE + +3.8.3.3. Time Zone Offset From + + Property Name: TZOFFSETFROM + + Purpose: This property specifies the offset that is in use prior to + this time zone observance. + + Value Type: UTC-OFFSET + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property MUST be specified in "STANDARD" and + "DAYLIGHT" sub-components. + + Description: This property specifies the offset that is in use prior + to this time observance. It is used to calculate the absolute + time at which the transition to a given observance takes place. + This property MUST only be specified in a "VTIMEZONE" calendar + component. A "VTIMEZONE" calendar component MUST include this + property. The property value is a signed numeric indicating the + number of hours and possibly minutes from UTC. Positive numbers + represent time zones east of the prime meridian, or ahead of UTC. + Negative numbers represent time zones west of the prime meridian, + or behind UTC. + + + + +Desruisseaux Standards Track [Page 104] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + tzoffsetfrom = "TZOFFSETFROM" frmparam ":" utc-offset + CRLF + + frmparam = *(";" other-param) + + Example: The following are examples of this property: + + TZOFFSETFROM:-0500 + + TZOFFSETFROM:+1345 + +3.8.3.4. Time Zone Offset To + + Property Name: TZOFFSETTO + + Purpose: This property specifies the offset that is in use in this + time zone observance. + + Value Type: UTC-OFFSET + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property MUST be specified in "STANDARD" and + "DAYLIGHT" sub-components. + + Description: This property specifies the offset that is in use in + this time zone observance. It is used to calculate the absolute + time for the new observance. The property value is a signed + numeric indicating the number of hours and possibly minutes from + UTC. Positive numbers represent time zones east of the prime + meridian, or ahead of UTC. Negative numbers represent time zones + west of the prime meridian, or behind UTC. + + Format Definition: This property is defined by the following + notation: + + tzoffsetto = "TZOFFSETTO" toparam ":" utc-offset CRLF + + toparam = *(";" other-param) + + + + + + + + +Desruisseaux Standards Track [Page 105] + +RFC 5545 iCalendar September 2009 + + + Example: The following are examples of this property: + + TZOFFSETTO:-0400 + + TZOFFSETTO:+1245 + +3.8.3.5. Time Zone URL + + Property Name: TZURL + + Purpose: This property provides a means for a "VTIMEZONE" component + to point to a network location that can be used to retrieve an up- + to-date version of itself. + + Value Type: URI + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified in a "VTIMEZONE" + calendar component. + + Description: This property provides a means for a "VTIMEZONE" + component to point to a network location that can be used to + retrieve an up-to-date version of itself. This provides a hook to + handle changes government bodies impose upon time zone + definitions. Retrieval of this resource results in an iCalendar + object containing a single "VTIMEZONE" component and a "METHOD" + property set to PUBLISH. + + Format Definition: This property is defined by the following + notation: + + tzurl = "TZURL" tzurlparam ":" uri CRLF + + tzurlparam = *(";" other-param) + + Example: The following is an example of this property: + + TZURL:http://timezones.example.org/tz/America-Los_Angeles.ics + +3.8.4. Relationship Component Properties + + The following properties specify relationship information in calendar + components. + + + + + + +Desruisseaux Standards Track [Page 106] + +RFC 5545 iCalendar September 2009 + + +3.8.4.1. Attendee + + Property Name: ATTENDEE + + Purpose: This property defines an "Attendee" within a calendar + component. + + Value Type: CAL-ADDRESS + + Property Parameters: IANA, non-standard, language, calendar user + type, group or list membership, participation role, participation + status, RSVP expectation, delegatee, delegator, sent by, common + name, or directory entry reference property parameters can be + specified on this property. + + Conformance: This property MUST be specified in an iCalendar object + that specifies a group-scheduled calendar entity. This property + MUST NOT be specified in an iCalendar object when publishing the + calendar information (e.g., NOT in an iCalendar object that + specifies the publication of a calendar user's busy time, event, + to-do, or journal). This property is not specified in an + iCalendar object that specifies only a time zone definition or + that defines calendar components that are not group-scheduled + components, but are components only on a single user's calendar. + + Description: This property MUST only be specified within calendar + components to specify participants, non-participants, and the + chair of a group-scheduled calendar entity. The property is + specified within an "EMAIL" category of the "VALARM" calendar + component to specify an email address that is to receive the email + type of iCalendar alarm. + + The property parameter "CN" is for the common or displayable name + associated with the calendar address; "ROLE", for the intended + role that the attendee will have in the calendar component; + "PARTSTAT", for the status of the attendee's participation; + "RSVP", for indicating whether the favor of a reply is requested; + "CUTYPE", to indicate the type of calendar user; "MEMBER", to + indicate the groups that the attendee belongs to; "DELEGATED-TO", + to indicate the calendar users that the original request was + delegated to; and "DELEGATED-FROM", to indicate whom the request + was delegated from; "SENT-BY", to indicate whom is acting on + behalf of the "ATTENDEE"; and "DIR", to indicate the URI that + points to the directory information corresponding to the attendee. + These property parameters can be specified on an "ATTENDEE" + property in either a "VEVENT", "VTODO", or "VJOURNAL" calendar + component. They MUST NOT be specified in an "ATTENDEE" property + in a "VFREEBUSY" or "VALARM" calendar component. If the + + + +Desruisseaux Standards Track [Page 107] + +RFC 5545 iCalendar September 2009 + + + "LANGUAGE" property parameter is specified, the identified + language applies to the "CN" parameter. + + A recipient delegated a request MUST inherit the "RSVP" and "ROLE" + values from the attendee that delegated the request to them. + + Multiple attendees can be specified by including multiple + "ATTENDEE" properties within the calendar component. + + Format Definition: This property is defined by the following + notation: + + attendee = "ATTENDEE" attparam ":" cal-address CRLF + + attparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" cutypeparam) / (";" memberparam) / + (";" roleparam) / (";" partstatparam) / + (";" rsvpparam) / (";" deltoparam) / + (";" delfromparam) / (";" sentbyparam) / + (";" cnparam) / (";" dirparam) / + (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following are examples of this property's use for a + to-do: + + ATTENDEE;MEMBER="mailto:DEV-GROUP@example.com": + mailto:joecool@example.com + ATTENDEE;DELEGATED-FROM="mailto:immud@example.com": + mailto:ildoit@example.com + + + + + + + + + + + +Desruisseaux Standards Track [Page 108] + +RFC 5545 iCalendar September 2009 + + + The following is an example of this property used for specifying + multiple attendees to an event: + + ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;CN=Henry + Cabot:mailto:hcabot@example.com + ATTENDEE;ROLE=REQ-PARTICIPANT;DELEGATED-FROM="mailto:bob@ + example.com";PARTSTAT=ACCEPTED;CN=Jane Doe:mailto:jdoe@ + example.com + + The following is an example of this property with a URI to the + directory information associated with the attendee: + + ATTENDEE;CN=John Smith;DIR="ldap://example.com:6666/o=ABC% + 20Industries,c=US???(cn=Jim%20Dolittle)":mailto:jimdo@ + example.com + + The following is an example of this property with "delegatee" and + "delegator" information for an event: + + ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;DELEGATED-FROM= + "mailto:iamboss@example.com";CN=Henry Cabot:mailto:hcabot@ + example.com + ATTENDEE;ROLE=NON-PARTICIPANT;PARTSTAT=DELEGATED;DELEGATED-TO= + "mailto:hcabot@example.com";CN=The Big Cheese:mailto:iamboss + @example.com + ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Jane Doe + :mailto:jdoe@example.com + + Example: The following is an example of this property's use when + another calendar user is acting on behalf of the "Attendee": + + ATTENDEE;SENT-BY=mailto:jan_doe@example.com;CN=John Smith: + mailto:jsmith@example.com + +3.8.4.2. Contact + + Property Name: CONTACT + + Purpose: This property is used to represent contact information or + alternately a reference to contact information associated with the + calendar component. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, alternate text + representation, and language property parameters can be specified + on this property. + + + + +Desruisseaux Standards Track [Page 109] + +RFC 5545 iCalendar September 2009 + + + Conformance: This property can be specified in a "VEVENT", "VTODO", + "VJOURNAL", or "VFREEBUSY" calendar component. + + Description: The property value consists of textual contact + information. An alternative representation for the property value + can also be specified that refers to a URI pointing to an + alternate form, such as a vCard [RFC2426], for the contact + information. + + Format Definition: This property is defined by the following + notation: + + contact = "CONTACT" contparam ":" text CRLF + + contparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" altrepparam) / (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following is an example of this property referencing + textual contact information: + + CONTACT:Jim Dolittle\, ABC Industries\, +1-919-555-1234 + + The following is an example of this property with an alternate + representation of an LDAP URI to a directory entry containing the + contact information: + + CONTACT;ALTREP="ldap://example.com:6666/o=ABC%20Industries\, + c=US???(cn=Jim%20Dolittle)":Jim Dolittle\, ABC Industries\, + +1-919-555-1234 + + The following is an example of this property with an alternate + representation of a MIME body part containing the contact + information, such as a vCard [RFC2426] embedded in a text/ + directory media type [RFC2425]: + + CONTACT;ALTREP="CID:part3.msg970930T083000SILVER@example.com": + Jim Dolittle\, ABC Industries\, +1-919-555-1234 + + + +Desruisseaux Standards Track [Page 110] + +RFC 5545 iCalendar September 2009 + + + The following is an example of this property referencing a network + resource, such as a vCard [RFC2426] object containing the contact + information: + + CONTACT;ALTREP="http://example.com/pdi/jdoe.vcf":Jim + Dolittle\, ABC Industries\, +1-919-555-1234 + +3.8.4.3. Organizer + + Property Name: ORGANIZER + + Purpose: This property defines the organizer for a calendar + component. + + Value Type: CAL-ADDRESS + + Property Parameters: IANA, non-standard, language, common name, + directory entry reference, and sent-by property parameters can be + specified on this property. + + Conformance: This property MUST be specified in an iCalendar object + that specifies a group-scheduled calendar entity. This property + MUST be specified in an iCalendar object that specifies the + publication of a calendar user's busy time. This property MUST + NOT be specified in an iCalendar object that specifies only a time + zone definition or that defines calendar components that are not + group-scheduled components, but are components only on a single + user's calendar. + + Description: This property is specified within the "VEVENT", + "VTODO", and "VJOURNAL" calendar components to specify the + organizer of a group-scheduled calendar entity. The property is + specified within the "VFREEBUSY" calendar component to specify the + calendar user requesting the free or busy time. When publishing a + "VFREEBUSY" calendar component, the property is used to specify + the calendar that the published busy time came from. + + The property has the property parameters "CN", for specifying the + common or display name associated with the "Organizer", "DIR", for + specifying a pointer to the directory information associated with + the "Organizer", "SENT-BY", for specifying another calendar user + that is acting on behalf of the "Organizer". The non-standard + parameters may also be specified on this property. If the + "LANGUAGE" property parameter is specified, the identified + language applies to the "CN" parameter value. + + + + + + +Desruisseaux Standards Track [Page 111] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + organizer = "ORGANIZER" orgparam ":" + cal-address CRLF + + orgparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" cnparam) / (";" dirparam) / (";" sentbyparam) / + (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + Example: The following is an example of this property: + + ORGANIZER;CN=John Smith:mailto:jsmith@example.com + + The following is an example of this property with a pointer to the + directory information associated with the organizer: + + ORGANIZER;CN=JohnSmith;DIR="ldap://example.com:6666/o=DC%20Ass + ociates,c=US???(cn=John%20Smith)":mailto:jsmith@example.com + + The following is an example of this property used by another + calendar user who is acting on behalf of the organizer, with + responses intended to be sent back to the organizer, not the other + calendar user: + + ORGANIZER;SENT-BY="mailto:jane_doe@example.com": + mailto:jsmith@example.com + +3.8.4.4. Recurrence ID + + Property Name: RECURRENCE-ID + + Purpose: This property is used in conjunction with the "UID" and + "SEQUENCE" properties to identify a specific instance of a + recurring "VEVENT", "VTODO", or "VJOURNAL" calendar component. + The property value is the original value of the "DTSTART" property + of the recurrence instance. + + + +Desruisseaux Standards Track [Page 112] + +RFC 5545 iCalendar September 2009 + + + Value Type: The default value type is DATE-TIME. The value type can + be set to a DATE value type. This property MUST have the same + value type as the "DTSTART" property contained within the + recurring component. Furthermore, this property MUST be specified + as a date with local time if and only if the "DTSTART" property + contained within the recurring component is specified as a date + with local time. + + Property Parameters: IANA, non-standard, value data type, time zone + identifier, and recurrence identifier range parameters can be + specified on this property. + + Conformance: This property can be specified in an iCalendar object + containing a recurring calendar component. + + Description: The full range of calendar components specified by a + recurrence set is referenced by referring to just the "UID" + property value corresponding to the calendar component. The + "RECURRENCE-ID" property allows the reference to an individual + instance within the recurrence set. + + If the value of the "DTSTART" property is a DATE type value, then + the value MUST be the calendar date for the recurrence instance. + + The DATE-TIME value is set to the time when the original + recurrence instance would occur; meaning that if the intent is to + change a Friday meeting to Thursday, the DATE-TIME is still set to + the original Friday meeting. + + The "RECURRENCE-ID" property is used in conjunction with the "UID" + and "SEQUENCE" properties to identify a particular instance of a + recurring event, to-do, or journal. For a given pair of "UID" and + "SEQUENCE" property values, the "RECURRENCE-ID" value for a + recurrence instance is fixed. + + The "RANGE" parameter is used to specify the effective range of + recurrence instances from the instance specified by the + "RECURRENCE-ID" property value. The value for the range parameter + can only be "THISANDFUTURE" to indicate a range defined by the + given recurrence instance and all subsequent instances. + Subsequent instances are determined by their "RECURRENCE-ID" value + and not their current scheduled start time. Subsequent instances + defined in separate components are not impacted by the given + recurrence instance. When the given recurrence instance is + rescheduled, all subsequent instances are also rescheduled by the + same time difference. For instance, if the given recurrence + instance is rescheduled to start 2 hours later, then all + subsequent instances are also rescheduled 2 hours later. + + + +Desruisseaux Standards Track [Page 113] + +RFC 5545 iCalendar September 2009 + + + Similarly, if the duration of the given recurrence instance is + modified, then all subsequence instances are also modified to have + this same duration. + + Note: The "RANGE" parameter may not be appropriate to + reschedule specific subsequent instances of complex recurring + calendar component. Assuming an unbounded recurring calendar + component scheduled to occur on Mondays and Wednesdays, the + "RANGE" parameter could not be used to reschedule only the + future Monday instances to occur on Tuesday instead. In such + cases, the calendar application could simply truncate the + unbounded recurring calendar component (i.e., with the "COUNT" + or "UNTIL" rule parts), and create two new unbounded recurring + calendar components for the future instances. + + Format Definition: This property is defined by the following + notation: + + recurid = "RECURRENCE-ID" ridparam ":" ridval CRLF + + ridparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" ("DATE-TIME" / "DATE")) / + (";" tzidparam) / (";" rangeparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + ridval = date-time / date + ;Value MUST match value type + + Example: The following are examples of this property: + + RECURRENCE-ID;VALUE=DATE:19960401 + + RECURRENCE-ID;RANGE=THISANDFUTURE:19960120T120000Z + + + + + + + + +Desruisseaux Standards Track [Page 114] + +RFC 5545 iCalendar September 2009 + + +3.8.4.5. Related To + + Property Name: RELATED-TO + + Purpose: This property is used to represent a relationship or + reference between one calendar component and another. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, and relationship type + property parameters can be specified on this property. + + Conformance: This property can be specified in the "VEVENT", + "VTODO", and "VJOURNAL" calendar components. + + Description: The property value consists of the persistent, globally + unique identifier of another calendar component. This value would + be represented in a calendar component by the "UID" property. + + By default, the property value points to another calendar + component that has a PARENT relationship to the referencing + object. The "RELTYPE" property parameter is used to either + explicitly state the default PARENT relationship type to the + referenced calendar component or to override the default PARENT + relationship type and specify either a CHILD or SIBLING + relationship. The PARENT relationship indicates that the calendar + component is a subordinate of the referenced calendar component. + The CHILD relationship indicates that the calendar component is a + superior of the referenced calendar component. The SIBLING + relationship indicates that the calendar component is a peer of + the referenced calendar component. + + Changes to a calendar component referenced by this property can + have an implicit impact on the related calendar component. For + example, if a group event changes its start or end date or time, + then the related, dependent events will need to have their start + and end dates changed in a corresponding way. Similarly, if a + PARENT calendar component is cancelled or deleted, then there is + an implied impact to the related CHILD calendar components. This + property is intended only to provide information on the + relationship of calendar components. It is up to the target + calendar system to maintain any property implications of this + relationship. + + + + + + + + +Desruisseaux Standards Track [Page 115] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + related = "RELATED-TO" relparam ":" text CRLF + + relparam = *( + ; + ; The following is OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" reltypeparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + The following is an example of this property: + + RELATED-TO:jsmith.part7.19960817T083000.xyzMail@example.com + + RELATED-TO:19960401-080045-4000F192713-0052@example.com + +3.8.4.6. Uniform Resource Locator + + Property Name: URL + + Purpose: This property defines a Uniform Resource Locator (URL) + associated with the iCalendar object. + + Value Type: URI + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified once in the "VEVENT", + "VTODO", "VJOURNAL", or "VFREEBUSY" calendar components. + + Description: This property may be used in a calendar component to + convey a location where a more dynamic rendition of the calendar + information associated with the calendar component can be found. + This memo does not attempt to standardize the form of the URI, nor + the format of the resource pointed to by the property value. If + the URL property and Content-Location MIME header are both + specified, they MUST point to the same resource. + + + + +Desruisseaux Standards Track [Page 116] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + url = "URL" urlparam ":" uri CRLF + + urlparam = *(";" other-param) + + Example: The following is an example of this property: + + URL:http://example.com/pub/calendars/jsmith/mytime.ics + +3.8.4.7. Unique Identifier + + Property Name: UID + + Purpose: This property defines the persistent, globally unique + identifier for the calendar component. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: The property MUST be specified in the "VEVENT", + "VTODO", "VJOURNAL", or "VFREEBUSY" calendar components. + + Description: The "UID" itself MUST be a globally unique identifier. + The generator of the identifier MUST guarantee that the identifier + is unique. There are several algorithms that can be used to + accomplish this. A good method to assure uniqueness is to put the + domain name or a domain literal IP address of the host on which + the identifier was created on the right-hand side of an "@", and + on the left-hand side, put a combination of the current calendar + date and time of day (i.e., formatted in as a DATE-TIME value) + along with some other currently unique (perhaps sequential) + identifier available on the system (for example, a process id + number). Using a DATE-TIME value on the left-hand side and a + domain name or domain literal on the right-hand side makes it + possible to guarantee uniqueness since no two hosts should be + using the same domain name or IP address at the same time. Though + other algorithms will work, it is RECOMMENDED that the right-hand + side contain some domain identifier (either of the host itself or + otherwise) such that the generator of the message identifier can + guarantee the uniqueness of the left-hand side within the scope of + that domain. + + This is the method for correlating scheduling messages with the + referenced "VEVENT", "VTODO", or "VJOURNAL" calendar component. + + + +Desruisseaux Standards Track [Page 117] + +RFC 5545 iCalendar September 2009 + + + The full range of calendar components specified by a recurrence + set is referenced by referring to just the "UID" property value + corresponding to the calendar component. The "RECURRENCE-ID" + property allows the reference to an individual instance within the + recurrence set. + + This property is an important method for group-scheduling + applications to match requests with later replies, modifications, + or deletion requests. Calendaring and scheduling applications + MUST generate this property in "VEVENT", "VTODO", and "VJOURNAL" + calendar components to assure interoperability with other group- + scheduling applications. This identifier is created by the + calendar system that generates an iCalendar object. + + Implementations MUST be able to receive and persist values of at + least 255 octets for this property, but they MUST NOT truncate + values in the middle of a UTF-8 multi-octet sequence. + + Format Definition: This property is defined by the following + notation: + + uid = "UID" uidparam ":" text CRLF + + uidparam = *(";" other-param) + + Example: The following is an example of this property: + + UID:19960401T080045Z-4000F192713-0052@example.com + +3.8.5. Recurrence Component Properties + + The following properties specify recurrence information in calendar + components. + +3.8.5.1. Exception Date-Times + + Property Name: EXDATE + + Purpose: This property defines the list of DATE-TIME exceptions for + recurring events, to-dos, journal entries, or time zone + definitions. + + Value Type: The default value type for this property is DATE-TIME. + The value type can be set to DATE. + + Property Parameters: IANA, non-standard, value data type, and time + zone identifier property parameters can be specified on this + property. + + + +Desruisseaux Standards Track [Page 118] + +RFC 5545 iCalendar September 2009 + + + Conformance: This property can be specified in recurring "VEVENT", + "VTODO", and "VJOURNAL" calendar components as well as in the + "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" + calendar component. + + Description: The exception dates, if specified, are used in + computing the recurrence set. The recurrence set is the complete + set of recurrence instances for a calendar component. The + recurrence set is generated by considering the initial "DTSTART" + property along with the "RRULE", "RDATE", and "EXDATE" properties + contained within the recurring component. The "DTSTART" property + defines the first instance in the recurrence set. The "DTSTART" + property value SHOULD match the pattern of the recurrence rule, if + specified. The recurrence set generated with a "DTSTART" property + value that doesn't match the pattern of the rule is undefined. + The final recurrence set is generated by gathering all of the + start DATE-TIME values generated by any of the specified "RRULE" + and "RDATE" properties, and then excluding any start DATE-TIME + values specified by "EXDATE" properties. This implies that start + DATE-TIME values specified by "EXDATE" properties take precedence + over those specified by inclusion properties (i.e., "RDATE" and + "RRULE"). When duplicate instances are generated by the "RRULE" + and "RDATE" properties, only one recurrence is considered. + Duplicate instances are ignored. + + The "EXDATE" property can be used to exclude the value specified + in "DTSTART". However, in such cases, the original "DTSTART" date + MUST still be maintained by the calendaring and scheduling system + because the original "DTSTART" value has inherent usage + dependencies by other properties such as the "RECURRENCE-ID". + + Format Definition: This property is defined by the following + notation: + + + + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 119] + +RFC 5545 iCalendar September 2009 + + + exdate = "EXDATE" exdtparam ":" exdtval *("," exdtval) CRLF + + exdtparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" ("DATE-TIME" / "DATE")) / + ; + (";" tzidparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + exdtval = date-time / date + ;Value MUST match value type + + Example: The following is an example of this property: + + EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z + +3.8.5.2. Recurrence Date-Times + + Property Name: RDATE + + Purpose: This property defines the list of DATE-TIME values for + recurring events, to-dos, journal entries, or time zone + definitions. + + Value Type: The default value type for this property is DATE-TIME. + The value type can be set to DATE or PERIOD. + + Property Parameters: IANA, non-standard, value data type, and time + zone identifier property parameters can be specified on this + property. + + Conformance: This property can be specified in recurring "VEVENT", + "VTODO", and "VJOURNAL" calendar components as well as in the + "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" + calendar component. + + Description: This property can appear along with the "RRULE" + property to define an aggregate set of repeating occurrences. + When they both appear in a recurring component, the recurrence + + + +Desruisseaux Standards Track [Page 120] + +RFC 5545 iCalendar September 2009 + + + instances are defined by the union of occurrences defined by both + the "RDATE" and "RRULE". + + The recurrence dates, if specified, are used in computing the + recurrence set. The recurrence set is the complete set of + recurrence instances for a calendar component. The recurrence set + is generated by considering the initial "DTSTART" property along + with the "RRULE", "RDATE", and "EXDATE" properties contained + within the recurring component. The "DTSTART" property defines + the first instance in the recurrence set. The "DTSTART" property + value SHOULD match the pattern of the recurrence rule, if + specified. The recurrence set generated with a "DTSTART" property + value that doesn't match the pattern of the rule is undefined. + The final recurrence set is generated by gathering all of the + start DATE-TIME values generated by any of the specified "RRULE" + and "RDATE" properties, and then excluding any start DATE-TIME + values specified by "EXDATE" properties. This implies that start + DATE-TIME values specified by "EXDATE" properties take precedence + over those specified by inclusion properties (i.e., "RDATE" and + "RRULE"). Where duplicate instances are generated by the "RRULE" + and "RDATE" properties, only one recurrence is considered. + Duplicate instances are ignored. + + Format Definition: This property is defined by the following + notation: + + rdate = "RDATE" rdtparam ":" rdtval *("," rdtval) CRLF + + rdtparam = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" ("DATE-TIME" / "DATE" / "PERIOD")) / + (";" tzidparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + rdtval = date-time / date / period + ;Value MUST match value type + + + + + + +Desruisseaux Standards Track [Page 121] + +RFC 5545 iCalendar September 2009 + + + Example: The following are examples of this property: + + RDATE:19970714T123000Z + RDATE;TZID=America/New_York:19970714T083000 + + RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z, + 19960404T010000Z/PT3H + + RDATE;VALUE=DATE:19970101,19970120,19970217,19970421 + 19970526,19970704,19970901,19971014,19971128,19971129,19971225 + +3.8.5.3. Recurrence Rule + + Property Name: RRULE + + Purpose: This property defines a rule or repeating pattern for + recurring events, to-dos, journal entries, or time zone + definitions. + + Value Type: RECUR + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified in recurring "VEVENT", + "VTODO", and "VJOURNAL" calendar components as well as in the + "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" + calendar component, but it SHOULD NOT be specified more than once. + The recurrence set generated with multiple "RRULE" properties is + undefined. + + Description: The recurrence rule, if specified, is used in computing + the recurrence set. The recurrence set is the complete set of + recurrence instances for a calendar component. The recurrence set + is generated by considering the initial "DTSTART" property along + with the "RRULE", "RDATE", and "EXDATE" properties contained + within the recurring component. The "DTSTART" property defines + the first instance in the recurrence set. The "DTSTART" property + value SHOULD be synchronized with the recurrence rule, if + specified. The recurrence set generated with a "DTSTART" property + value not synchronized with the recurrence rule is undefined. The + final recurrence set is generated by gathering all of the start + DATE-TIME values generated by any of the specified "RRULE" and + "RDATE" properties, and then excluding any start DATE-TIME values + specified by "EXDATE" properties. This implies that start DATE- + TIME values specified by "EXDATE" properties take precedence over + those specified by inclusion properties (i.e., "RDATE" and + "RRULE"). Where duplicate instances are generated by the "RRULE" + + + +Desruisseaux Standards Track [Page 122] + +RFC 5545 iCalendar September 2009 + + + and "RDATE" properties, only one recurrence is considered. + Duplicate instances are ignored. + + The "DTSTART" property specified within the iCalendar object + defines the first instance of the recurrence. In most cases, a + "DTSTART" property of DATE-TIME value type used with a recurrence + rule, should be specified as a date with local time and time zone + reference to make sure all the recurrence instances start at the + same local time regardless of time zone changes. + + If the duration of the recurring component is specified with the + "DTEND" or "DUE" property, then the same exact duration will apply + to all the members of the generated recurrence set. Else, if the + duration of the recurring component is specified with the + "DURATION" property, then the same nominal duration will apply to + all the members of the generated recurrence set and the exact + duration of each recurrence instance will depend on its specific + start time. For example, recurrence instances of a nominal + duration of one day will have an exact duration of more or less + than 24 hours on a day where a time zone shift occurs. The + duration of a specific recurrence may be modified in an exception + component or simply by using an "RDATE" property of PERIOD value + type. + + Format Definition: This property is defined by the following + notation: + + rrule = "RRULE" rrulparam ":" recur CRLF + + rrulparam = *(";" other-param) + + Example: All examples assume the Eastern United States time zone. + + Daily for 10 occurrences: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=DAILY;COUNT=10 + + ==> (1997 9:00 AM EDT) September 2-11 + + Daily until December 24, 1997: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=DAILY;UNTIL=19971224T000000Z + + ==> (1997 9:00 AM EDT) September 2-30;October 1-25 + (1997 9:00 AM EST) October 26-31;November 1-30;December 1-23 + + + + +Desruisseaux Standards Track [Page 123] + +RFC 5545 iCalendar September 2009 + + + Every other day - forever: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=DAILY;INTERVAL=2 + + ==> (1997 9:00 AM EDT) September 2,4,6,8...24,26,28,30; + October 2,4,6...20,22,24 + (1997 9:00 AM EST) October 26,28,30; + November 1,3,5,7...25,27,29; + December 1,3,... + + Every 10 days, 5 occurrences: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5 + + ==> (1997 9:00 AM EDT) September 2,12,22; + October 2,12 + + Every day in January, for 3 years: + + DTSTART;TZID=America/New_York:19980101T090000 + + RRULE:FREQ=YEARLY;UNTIL=20000131T140000Z; + BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA + or + RRULE:FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1 + + ==> (1998 9:00 AM EST)January 1-31 + (1999 9:00 AM EST)January 1-31 + (2000 9:00 AM EST)January 1-31 + + Weekly for 10 occurrences: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=WEEKLY;COUNT=10 + + ==> (1997 9:00 AM EDT) September 2,9,16,23,30;October 7,14,21 + (1997 9:00 AM EST) October 28;November 4 + + + + + + + + + + + + +Desruisseaux Standards Track [Page 124] + +RFC 5545 iCalendar September 2009 + + + Weekly until December 24, 1997: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z + + ==> (1997 9:00 AM EDT) September 2,9,16,23,30; + October 7,14,21 + (1997 9:00 AM EST) October 28; + November 4,11,18,25; + December 2,9,16,23 + + Every other week - forever: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU + + ==> (1997 9:00 AM EDT) September 2,16,30; + October 14 + (1997 9:00 AM EST) October 28; + November 11,25; + December 9,23 + (1998 9:00 AM EST) January 6,20; + February 3, 17 + ... + + Weekly on Tuesday and Thursday for five weeks: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH + + or + + RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH + + ==> (1997 9:00 AM EDT) September 2,4,9,11,16,18,23,25,30; + October 2 + + Every other week on Monday, Wednesday, and Friday until December + 24, 1997, starting on Monday, September 1, 1997: + + DTSTART;TZID=America/New_York:19970901T090000 + RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU; + BYDAY=MO,WE,FR + + ==> (1997 9:00 AM EDT) September 1,3,5,15,17,19,29; + October 1,3,13,15,17 + (1997 9:00 AM EST) October 27,29,31; + November 10,12,14,24,26,28; + + + +Desruisseaux Standards Track [Page 125] + +RFC 5545 iCalendar September 2009 + + + December 8,10,12,22 + + Every other week on Tuesday and Thursday, for 8 occurrences: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH + + ==> (1997 9:00 AM EDT) September 2,4,16,18,30; + October 2,14,16 + + Monthly on the first Friday for 10 occurrences: + + DTSTART;TZID=America/New_York:19970905T090000 + RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR + + ==> (1997 9:00 AM EDT) September 5;October 3 + (1997 9:00 AM EST) November 7;December 5 + (1998 9:00 AM EST) January 2;February 6;March 6;April 3 + (1998 9:00 AM EDT) May 1;June 5 + + Monthly on the first Friday until December 24, 1997: + + DTSTART;TZID=America/New_York:19970905T090000 + RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR + + ==> (1997 9:00 AM EDT) September 5; October 3 + (1997 9:00 AM EST) November 7; December 5 + + Every other month on the first and last Sunday of the month for 10 + occurrences: + + DTSTART;TZID=America/New_York:19970907T090000 + RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU + + ==> (1997 9:00 AM EDT) September 7,28 + (1997 9:00 AM EST) November 2,30 + (1998 9:00 AM EST) January 4,25;March 1,29 + (1998 9:00 AM EDT) May 3,31 + + Monthly on the second-to-last Monday of the month for 6 months: + + DTSTART;TZID=America/New_York:19970922T090000 + RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO + + ==> (1997 9:00 AM EDT) September 22;October 20 + (1997 9:00 AM EST) November 17;December 22 + (1998 9:00 AM EST) January 19;February 16 + + + + +Desruisseaux Standards Track [Page 126] + +RFC 5545 iCalendar September 2009 + + + Monthly on the third-to-the-last day of the month, forever: + + DTSTART;TZID=America/New_York:19970928T090000 + RRULE:FREQ=MONTHLY;BYMONTHDAY=-3 + + ==> (1997 9:00 AM EDT) September 28 + (1997 9:00 AM EST) October 29;November 28;December 29 + (1998 9:00 AM EST) January 29;February 26 + ... + + Monthly on the 2nd and 15th of the month for 10 occurrences: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15 + + ==> (1997 9:00 AM EDT) September 2,15;October 2,15 + (1997 9:00 AM EST) November 2,15;December 2,15 + (1998 9:00 AM EST) January 2,15 + + Monthly on the first and last day of the month for 10 occurrences: + + DTSTART;TZID=America/New_York:19970930T090000 + RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1 + + ==> (1997 9:00 AM EDT) September 30;October 1 + (1997 9:00 AM EST) October 31;November 1,30;December 1,31 + (1998 9:00 AM EST) January 1,31;February 1 + + Every 18 months on the 10th thru 15th of the month for 10 + occurrences: + + DTSTART;TZID=America/New_York:19970910T090000 + RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12, + 13,14,15 + + ==> (1997 9:00 AM EDT) September 10,11,12,13,14,15 + (1999 9:00 AM EST) March 10,11,12,13 + + Every Tuesday, every other month: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU + + ==> (1997 9:00 AM EDT) September 2,9,16,23,30 + (1997 9:00 AM EST) November 4,11,18,25 + (1998 9:00 AM EST) January 6,13,20,27;March 3,10,17,24,31 + ... + + + + +Desruisseaux Standards Track [Page 127] + +RFC 5545 iCalendar September 2009 + + + Yearly in June and July for 10 occurrences: + + DTSTART;TZID=America/New_York:19970610T090000 + RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7 + + ==> (1997 9:00 AM EDT) June 10;July 10 + (1998 9:00 AM EDT) June 10;July 10 + (1999 9:00 AM EDT) June 10;July 10 + (2000 9:00 AM EDT) June 10;July 10 + (2001 9:00 AM EDT) June 10;July 10 + + Note: Since none of the BYDAY, BYMONTHDAY, or BYYEARDAY + components are specified, the day is gotten from "DTSTART". + + Every other year on January, February, and March for 10 + occurrences: + + DTSTART;TZID=America/New_York:19970310T090000 + RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3 + + ==> (1997 9:00 AM EST) March 10 + (1999 9:00 AM EST) January 10;February 10;March 10 + (2001 9:00 AM EST) January 10;February 10;March 10 + (2003 9:00 AM EST) January 10;February 10;March 10 + + Every third year on the 1st, 100th, and 200th day for 10 + occurrences: + + DTSTART;TZID=America/New_York:19970101T090000 + RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200 + + ==> (1997 9:00 AM EST) January 1 + (1997 9:00 AM EDT) April 10;July 19 + (2000 9:00 AM EST) January 1 + (2000 9:00 AM EDT) April 9;July 18 + (2003 9:00 AM EST) January 1 + (2003 9:00 AM EDT) April 10;July 19 + (2006 9:00 AM EST) January 1 + + Every 20th Monday of the year, forever: + + DTSTART;TZID=America/New_York:19970519T090000 + RRULE:FREQ=YEARLY;BYDAY=20MO + + ==> (1997 9:00 AM EDT) May 19 + (1998 9:00 AM EDT) May 18 + (1999 9:00 AM EDT) May 17 + ... + + + +Desruisseaux Standards Track [Page 128] + +RFC 5545 iCalendar September 2009 + + + Monday of week number 20 (where the default start of the week is + Monday), forever: + + DTSTART;TZID=America/New_York:19970512T090000 + RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO + + ==> (1997 9:00 AM EDT) May 12 + (1998 9:00 AM EDT) May 11 + (1999 9:00 AM EDT) May 17 + ... + + Every Thursday in March, forever: + + DTSTART;TZID=America/New_York:19970313T090000 + RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH + + ==> (1997 9:00 AM EST) March 13,20,27 + (1998 9:00 AM EST) March 5,12,19,26 + (1999 9:00 AM EST) March 4,11,18,25 + ... + + Every Thursday, but only during June, July, and August, forever: + + DTSTART;TZID=America/New_York:19970605T090000 + RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8 + + ==> (1997 9:00 AM EDT) June 5,12,19,26;July 3,10,17,24,31; + August 7,14,21,28 + (1998 9:00 AM EDT) June 4,11,18,25;July 2,9,16,23,30; + August 6,13,20,27 + (1999 9:00 AM EDT) June 3,10,17,24;July 1,8,15,22,29; + August 5,12,19,26 + ... + + Every Friday the 13th, forever: + + DTSTART;TZID=America/New_York:19970902T090000 + EXDATE;TZID=America/New_York:19970902T090000 + RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 + + ==> (1998 9:00 AM EST) February 13;March 13;November 13 + (1999 9:00 AM EDT) August 13 + (2000 9:00 AM EDT) October 13 + ... + + + + + + + +Desruisseaux Standards Track [Page 129] + +RFC 5545 iCalendar September 2009 + + + The first Saturday that follows the first Sunday of the month, + forever: + + DTSTART;TZID=America/New_York:19970913T090000 + RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13 + + ==> (1997 9:00 AM EDT) September 13;October 11 + (1997 9:00 AM EST) November 8;December 13 + (1998 9:00 AM EST) January 10;February 7;March 7 + (1998 9:00 AM EDT) April 11;May 9;June 13... + ... + + Every 4 years, the first Tuesday after a Monday in November, + forever (U.S. Presidential Election day): + + DTSTART;TZID=America/New_York:19961105T090000 + RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU; + BYMONTHDAY=2,3,4,5,6,7,8 + + ==> (1996 9:00 AM EST) November 5 + (2000 9:00 AM EST) November 7 + (2004 9:00 AM EST) November 2 + ... + + The third instance into the month of one of Tuesday, Wednesday, or + Thursday, for the next 3 months: + + DTSTART;TZID=America/New_York:19970904T090000 + RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3 + + ==> (1997 9:00 AM EDT) September 4;October 7 + (1997 9:00 AM EST) November 6 + + The second-to-last weekday of the month: + + DTSTART;TZID=America/New_York:19970929T090000 + RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2 + + ==> (1997 9:00 AM EDT) September 29 + (1997 9:00 AM EST) October 30;November 27;December 30 + (1998 9:00 AM EST) January 29;February 26;March 30 + ... + + + + + + + + + +Desruisseaux Standards Track [Page 130] + +RFC 5545 iCalendar September 2009 + + + Every 3 hours from 9:00 AM to 5:00 PM on a specific day: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z + + ==> (September 2, 1997 EDT) 09:00,12:00,15:00 + + Every 15 minutes for 6 occurrences: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6 + + ==> (September 2, 1997 EDT) 09:00,09:15,09:30,09:45,10:00,10:15 + + Every hour and a half for 4 occurrences: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4 + + ==> (September 2, 1997 EDT) 09:00,10:30;12:00;13:30 + + Every 20 minutes from 9:00 AM to 4:40 PM every day: + + DTSTART;TZID=America/New_York:19970902T090000 + RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40 + or + RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16 + + ==> (September 2, 1997 EDT) 9:00,9:20,9:40,10:00,10:20, + ... 16:00,16:20,16:40 + (September 3, 1997 EDT) 9:00,9:20,9:40,10:00,10:20, + ...16:00,16:20,16:40 + ... + + An example where the days generated makes a difference because of + WKST: + + DTSTART;TZID=America/New_York:19970805T090000 + RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO + + ==> (1997 EDT) August 5,10,19,24 + + changing only WKST from MO to SU, yields different results... + + DTSTART;TZID=America/New_York:19970805T090000 + RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU + + ==> (1997 EDT) August 5,17,19,31 + + + +Desruisseaux Standards Track [Page 131] + +RFC 5545 iCalendar September 2009 + + + An example where an invalid date (i.e., February 30) is ignored. + + DTSTART;TZID=America/New_York:20070115T090000 + RRULE:FREQ=MONTHLY;BYMONTHDAY=15,30;COUNT=5 + + ==> (2007 EST) January 15,30 + (2007 EST) February 15 + (2007 EDT) March 15,30 + +3.8.6. Alarm Component Properties + + The following properties specify alarm information in calendar + components. + +3.8.6.1. Action + + Property Name: ACTION + + Purpose: This property defines the action to be invoked when an + alarm is triggered. + + Value Type: TEXT + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property MUST be specified once in a "VALARM" + calendar component. + + Description: Each "VALARM" calendar component has a particular type + of action with which it is associated. This property specifies + the type of action. Applications MUST ignore alarms with x-name + and iana-token values they don't recognize. + + Format Definition: This property is defined by the following + notation: + + action = "ACTION" actionparam ":" actionvalue CRLF + + actionparam = *(";" other-param) + + + actionvalue = "AUDIO" / "DISPLAY" / "EMAIL" + / iana-token / x-name + + Example: The following are examples of this property in a "VALARM" + calendar component: + + + + +Desruisseaux Standards Track [Page 132] + +RFC 5545 iCalendar September 2009 + + + ACTION:AUDIO + + ACTION:DISPLAY + +3.8.6.2. Repeat Count + + Property Name: REPEAT + + Purpose: This property defines the number of times the alarm should + be repeated, after the initial trigger. + + Value Type: INTEGER + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified in a "VALARM" calendar + component. + + Description: This property defines the number of times an alarm + should be repeated after its initial trigger. If the alarm + triggers more than once, then this property MUST be specified + along with the "DURATION" property. + + Format Definition: This property is defined by the following + notation: + + repeat = "REPEAT" repparam ":" integer CRLF + ;Default is "0", zero. + + repparam = *(";" other-param) + + Example: The following is an example of this property for an alarm + that repeats 4 additional times with a 5-minute delay after the + initial triggering of the alarm: + + REPEAT:4 + DURATION:PT5M + +3.8.6.3. Trigger + + Property Name: TRIGGER + + Purpose: This property specifies when an alarm will trigger. + + Value Type: The default value type is DURATION. The value type can + be set to a DATE-TIME value type, in which case the value MUST + specify a UTC-formatted DATE-TIME value. + + + +Desruisseaux Standards Track [Page 133] + +RFC 5545 iCalendar September 2009 + + + Property Parameters: IANA, non-standard, value data type, time zone + identifier, or trigger relationship property parameters can be + specified on this property. The trigger relationship property + parameter MUST only be specified when the value type is + "DURATION". + + Conformance: This property MUST be specified in the "VALARM" + calendar component. + + Description: This property defines when an alarm will trigger. The + default value type is DURATION, specifying a relative time for the + trigger of the alarm. The default duration is relative to the + start of an event or to-do with which the alarm is associated. + The duration can be explicitly set to trigger from either the end + or the start of the associated event or to-do with the "RELATED" + parameter. A value of START will set the alarm to trigger off the + start of the associated event or to-do. A value of END will set + the alarm to trigger off the end of the associated event or to-do. + + Either a positive or negative duration may be specified for the + "TRIGGER" property. An alarm with a positive duration is + triggered after the associated start or end of the event or to-do. + An alarm with a negative duration is triggered before the + associated start or end of the event or to-do. + + The "RELATED" property parameter is not valid if the value type of + the property is set to DATE-TIME (i.e., for an absolute date and + time alarm trigger). If a value type of DATE-TIME is specified, + then the property value MUST be specified in the UTC time format. + If an absolute trigger is specified on an alarm for a recurring + event or to-do, then the alarm will only trigger for the specified + absolute DATE-TIME, along with any specified repeating instances. + + If the trigger is set relative to START, then the "DTSTART" + property MUST be present in the associated "VEVENT" or "VTODO" + calendar component. If an alarm is specified for an event with + the trigger set relative to the END, then the "DTEND" property or + the "DTSTART" and "DURATION " properties MUST be present in the + associated "VEVENT" calendar component. If the alarm is specified + for a to-do with a trigger set relative to the END, then either + the "DUE" property or the "DTSTART" and "DURATION " properties + MUST be present in the associated "VTODO" calendar component. + + Alarms specified in an event or to-do that is defined in terms of + a DATE value type will be triggered relative to 00:00:00 of the + user's configured time zone on the specified date, or relative to + 00:00:00 UTC on the specified date if no configured time zone can + be found for the user. For example, if "DTSTART" is a DATE value + + + +Desruisseaux Standards Track [Page 134] + +RFC 5545 iCalendar September 2009 + + + set to 19980205 then the duration trigger will be relative to + 19980205T000000 America/New_York for a user configured with the + America/New_York time zone. + + Format Definition: This property is defined by the following + notation: + + trigger = "TRIGGER" (trigrel / trigabs) CRLF + + trigrel = *( + ; + ; The following are OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" "DURATION") / + (";" trigrelparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) ":" dur-value + + trigabs = *( + ; + ; The following is REQUIRED, + ; but MUST NOT occur more than once. + ; + (";" "VALUE" "=" "DATE-TIME") / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) ":" date-time + + Example: A trigger set 15 minutes prior to the start of the event or + to-do. + + TRIGGER:-PT15M + + A trigger set five minutes after the end of an event or the due + date of a to-do. + + TRIGGER;RELATED=END:PT5M + + + + +Desruisseaux Standards Track [Page 135] + +RFC 5545 iCalendar September 2009 + + + A trigger set to an absolute DATE-TIME. + + TRIGGER;VALUE=DATE-TIME:19980101T050000Z + +3.8.7. Change Management Component Properties + + The following properties specify change management information in + calendar components. + +3.8.7.1. Date-Time Created + + Property Name: CREATED + + Purpose: This property specifies the date and time that the calendar + information was created by the calendar user agent in the calendar + store. + + Note: This is analogous to the creation date and time for a + file in the file system. + + Value Type: DATE-TIME + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: The property can be specified once in "VEVENT", + "VTODO", or "VJOURNAL" calendar components. The value MUST be + specified as a date with UTC time. + + Description: This property specifies the date and time that the + calendar information was created by the calendar user agent in the + calendar store. + + Format Definition: This property is defined by the following + notation: + + created = "CREATED" creaparam ":" date-time CRLF + + creaparam = *(";" other-param) + + Example: The following is an example of this property: + + CREATED:19960329T133000Z + + + + + + + + +Desruisseaux Standards Track [Page 136] + +RFC 5545 iCalendar September 2009 + + +3.8.7.2. Date-Time Stamp + + Property Name: DTSTAMP + + Purpose: In the case of an iCalendar object that specifies a + "METHOD" property, this property specifies the date and time that + the instance of the iCalendar object was created. In the case of + an iCalendar object that doesn't specify a "METHOD" property, this + property specifies the date and time that the information + associated with the calendar component was last revised in the + calendar store. + + Value Type: DATE-TIME + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property MUST be included in the "VEVENT", + "VTODO", "VJOURNAL", or "VFREEBUSY" calendar components. + + Description: The value MUST be specified in the UTC time format. + + This property is also useful to protocols such as [2447bis] that + have inherent latency issues with the delivery of content. This + property will assist in the proper sequencing of messages + containing iCalendar objects. + + In the case of an iCalendar object that specifies a "METHOD" + property, this property differs from the "CREATED" and "LAST- + MODIFIED" properties. These two properties are used to specify + when the particular calendar data in the calendar store was + created and last modified. This is different than when the + iCalendar object representation of the calendar service + information was created or last modified. + + In the case of an iCalendar object that doesn't specify a "METHOD" + property, this property is equivalent to the "LAST-MODIFIED" + property. + + Format Definition: This property is defined by the following + notation: + + dtstamp = "DTSTAMP" stmparam ":" date-time CRLF + + stmparam = *(";" other-param) + + + + + + +Desruisseaux Standards Track [Page 137] + +RFC 5545 iCalendar September 2009 + + + Example: + + DTSTAMP:19971210T080000Z + +3.8.7.3. Last Modified + + Property Name: LAST-MODIFIED + + Purpose: This property specifies the date and time that the + information associated with the calendar component was last + revised in the calendar store. + + Note: This is analogous to the modification date and time for a + file in the file system. + + Value Type: DATE-TIME + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + Conformance: This property can be specified in the "VEVENT", + "VTODO", "VJOURNAL", or "VTIMEZONE" calendar components. + + Description: The property value MUST be specified in the UTC time + format. + + Format Definition: This property is defined by the following + notation: + + last-mod = "LAST-MODIFIED" lstparam ":" date-time CRLF + + lstparam = *(";" other-param) + + Example: The following is an example of this property: + + LAST-MODIFIED:19960817T133000Z + +3.8.7.4. Sequence Number + + Property Name: SEQUENCE + + Purpose: This property defines the revision sequence number of the + calendar component within a sequence of revisions. + + Value Type: INTEGER + + Property Parameters: IANA and non-standard property parameters can + be specified on this property. + + + +Desruisseaux Standards Track [Page 138] + +RFC 5545 iCalendar September 2009 + + + Conformance: The property can be specified in "VEVENT", "VTODO", or + "VJOURNAL" calendar component. + + Description: When a calendar component is created, its sequence + number is 0. It is monotonically incremented by the "Organizer's" + CUA each time the "Organizer" makes a significant revision to the + calendar component. + + The "Organizer" includes this property in an iCalendar object that + it sends to an "Attendee" to specify the current version of the + calendar component. + + The "Attendee" includes this property in an iCalendar object that + it sends to the "Organizer" to specify the version of the calendar + component to which the "Attendee" is referring. + + A change to the sequence number is not the mechanism that an + "Organizer" uses to request a response from the "Attendees". The + "RSVP" parameter on the "ATTENDEE" property is used by the + "Organizer" to indicate that a response from the "Attendees" is + requested. + + Recurrence instances of a recurring component MAY have different + sequence numbers. + + Format Definition: This property is defined by the following + notation: + + seq = "SEQUENCE" seqparam ":" integer CRLF + ; Default is "0" + + seqparam = *(";" other-param) + + Example: The following is an example of this property for a calendar + component that was just created by the "Organizer": + + SEQUENCE:0 + + The following is an example of this property for a calendar + component that has been revised two different times by the + "Organizer": + + SEQUENCE:2 + +3.8.8. Miscellaneous Component Properties + + The following properties specify information about a number of + miscellaneous features of calendar components. + + + +Desruisseaux Standards Track [Page 139] + +RFC 5545 iCalendar September 2009 + + +3.8.8.1. IANA Properties + + Property Name: An IANA-registered property name + + Value Type: The default value type is TEXT. The value type can be + set to any value type. + + Property Parameters: Any parameter can be specified on this + property. + + Description: This specification allows other properties registered + with IANA to be specified in any calendar components. Compliant + applications are expected to be able to parse these other IANA- + registered properties but can ignore them. + + Format Definition: This property is defined by the following + notation: + + iana-prop = iana-token *(";" icalparameter) ":" value CRLF + + Example: The following are examples of properties that might be + registered to IANA: + + DRESSCODE:CASUAL + + NON-SMOKING;VALUE=BOOLEAN:TRUE + +3.8.8.2. Non-Standard Properties + + Property Name: Any property name with a "X-" prefix + + Purpose: This class of property provides a framework for defining + non-standard properties. + + Value Type: The default value type is TEXT. The value type can be + set to any value type. + + Property Parameters: IANA, non-standard, and language property + parameters can be specified on this property. + + Conformance: This property can be specified in any calendar + component. + + Description: The MIME Calendaring and Scheduling Content Type + provides a "standard mechanism for doing non-standard things". + This extension support is provided for implementers to "push the + envelope" on the existing version of the memo. Extension + properties are specified by property and/or property parameter + + + +Desruisseaux Standards Track [Page 140] + +RFC 5545 iCalendar September 2009 + + + names that have the prefix text of "X-" (the two-character + sequence: LATIN CAPITAL LETTER X character followed by the HYPHEN- + MINUS character). It is recommended that vendors concatenate onto + this sentinel another short prefix text to identify the vendor. + This will facilitate readability of the extensions and minimize + possible collision of names between different vendors. User + agents that support this content type are expected to be able to + parse the extension properties and property parameters but can + ignore them. + + At present, there is no registration authority for names of + extension properties and property parameters. The value type for + this property is TEXT. Optionally, the value type can be any of + the other valid value types. + + Format Definition: This property is defined by the following + notation: + + x-prop = x-name *(";" icalparameter) ":" value CRLF + + Example: The following might be the ABC vendor's extension for an + audio-clip form of subject property: + + X-ABC-MMSUBJ;VALUE=URI;FMTTYPE=audio/basic:http://www.example. + org/mysubj.au + +3.8.8.3. Request Status + + Property Name: REQUEST-STATUS + + Purpose: This property defines the status code returned for a + scheduling request. + + Value Type: TEXT + + Property Parameters: IANA, non-standard, and language property + parameters can be specified on this property. + + Conformance: The property can be specified in the "VEVENT", "VTODO", + "VJOURNAL", or "VFREEBUSY" calendar component. + + Description: This property is used to return status code information + related to the processing of an associated iCalendar object. The + value type for this property is TEXT. + + + + + + + +Desruisseaux Standards Track [Page 141] + +RFC 5545 iCalendar September 2009 + + + The value consists of a short return status component, a longer + return status description component, and optionally a status- + specific data component. The components of the value are + separated by the SEMICOLON character. + + The short return status is a PERIOD character separated pair or + 3-tuple of integers. For example, "3.1" or "3.1.1". The + successive levels of integers provide for a successive level of + status code granularity. + + The following are initial classes for the return status code. + Individual iCalendar object methods will define specific return + status codes for these classes. In addition, other classes for + the return status code may be defined using the registration + process defined later in this memo. + + +--------+----------------------------------------------------------+ + | Short | Longer Return Status Description | + | Return | | + | Status | | + | Code | | + +--------+----------------------------------------------------------+ + | 1.xx | Preliminary success. This class of status code | + | | indicates that the request has been initially processed | + | | but that completion is pending. | + | | | + | 2.xx | Successful. This class of status code indicates that | + | | the request was completed successfully. However, the | + | | exact status code can indicate that a fallback has been | + | | taken. | + | | | + | 3.xx | Client Error. This class of status code indicates that | + | | the request was not successful. The error is the result | + | | of either a syntax or a semantic error in the client- | + | | formatted request. Request should not be retried until | + | | the condition in the request is corrected. | + | | | + | 4.xx | Scheduling Error. This class of status code indicates | + | | that the request was not successful. Some sort of error | + | | occurred within the calendaring and scheduling service, | + | | not directly related to the request itself. | + +--------+----------------------------------------------------------+ + + + + + + + + + +Desruisseaux Standards Track [Page 142] + +RFC 5545 iCalendar September 2009 + + + Format Definition: This property is defined by the following + notation: + + rstatus = "REQUEST-STATUS" rstatparam ":" + statcode ";" statdesc [";" extdata] + + rstatparam = *( + ; + ; The following is OPTIONAL, + ; but MUST NOT occur more than once. + ; + (";" languageparam) / + ; + ; The following is OPTIONAL, + ; and MAY occur more than once. + ; + (";" other-param) + ; + ) + + statcode = 1*DIGIT 1*2("." 1*DIGIT) + ;Hierarchical, numeric return status code + + statdesc = text + ;Textual status description + + extdata = text + ;Textual exception data. For example, the offending property + ;name and value or complete property line. + + Example: The following are some possible examples of this property. + + The COMMA and SEMICOLON separator characters in the property value + are BACKSLASH character escaped because they appear in a text + value. + + REQUEST-STATUS:2.0;Success + + REQUEST-STATUS:3.1;Invalid property value;DTSTART:96-Apr-01 + + REQUEST-STATUS:2.8; Success\, repeating event ignored. Scheduled + as a single event.;RRULE:FREQ=WEEKLY\;INTERVAL=2 + + REQUEST-STATUS:4.1;Event conflict. Date-time is busy. + + REQUEST-STATUS:3.7;Invalid calendar user;ATTENDEE: + mailto:jsmith@example.com + + + + +Desruisseaux Standards Track [Page 143] + +RFC 5545 iCalendar September 2009 + + +4. iCalendar Object Examples + + The following examples are provided as an informational source of + illustrative iCalendar objects consistent with this content type. + + The following example specifies a three-day conference that begins at + 2:30 P.M. UTC, September 18, 1996 and ends at 10:00 P.M. UTC, + September 20, 1996. + + BEGIN:VCALENDAR + PRODID:-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN + VERSION:2.0 + BEGIN:VEVENT + DTSTAMP:19960704T120000Z + UID:uid1@example.com + ORGANIZER:mailto:jsmith@example.com + DTSTART:19960918T143000Z + DTEND:19960920T220000Z + STATUS:CONFIRMED + CATEGORIES:CONFERENCE + SUMMARY:Networld+Interop Conference + DESCRIPTION:Networld+Interop Conference + and Exhibit\nAtlanta World Congress Center\n + Atlanta\, Georgia + END:VEVENT + END:VCALENDAR + + The following example specifies a group-scheduled meeting that begins + at 8:30 AM EST on March 12, 1998 and ends at 9:30 AM EST on March 12, + 1998. The "Organizer" has scheduled the meeting with one or more + calendar users in a group. A time zone specification for Eastern + United States has been specified. + + BEGIN:VCALENDAR + PRODID:-//RDU Software//NONSGML HandCal//EN + VERSION:2.0 + BEGIN:VTIMEZONE + TZID:America/New_York + BEGIN:STANDARD + DTSTART:19981025T020000 + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:EST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19990404T020000 + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + + + +Desruisseaux Standards Track [Page 144] + +RFC 5545 iCalendar September 2009 + + + TZNAME:EDT + END:DAYLIGHT + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:19980309T231000Z + UID:guid-1.example.com + ORGANIZER:mailto:mrbig@example.com + ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP: + mailto:employee-A@example.com + DESCRIPTION:Project XYZ Review Meeting + CATEGORIES:MEETING + CLASS:PUBLIC + CREATED:19980309T130000Z + SUMMARY:XYZ Project Review + DTSTART;TZID=America/New_York:19980312T083000 + DTEND;TZID=America/New_York:19980312T093000 + LOCATION:1CP Conference Room 4350 + END:VEVENT + END:VCALENDAR + + The following is an example of an iCalendar object passed in a MIME + message with a single body part consisting of a "text/calendar" + Content Type. + + TO:jsmith@example.com + FROM:jdoe@example.com + MIME-VERSION:1.0 + MESSAGE-ID: + CONTENT-TYPE:text/calendar; method="xyz"; component="VEVENT" + + BEGIN:VCALENDAR + METHOD:xyz + VERSION:2.0 + PRODID:-//ABC Corporation//NONSGML My Product//EN + BEGIN:VEVENT + DTSTAMP:19970324T120000Z + SEQUENCE:0 + UID:uid3@example.com + ORGANIZER:mailto:jdoe@example.com + ATTENDEE;RSVP=TRUE:mailto:jsmith@example.com + DTSTART:19970324T123000Z + DTEND:19970324T210000Z + CATEGORIES:MEETING,PROJECT + CLASS:PUBLIC + SUMMARY:Calendaring Interoperability Planning Meeting + DESCRIPTION:Discuss how we can test c&s interoperability\n + using iCalendar and other IETF standards. + LOCATION:LDB Lobby + + + +Desruisseaux Standards Track [Page 145] + +RFC 5545 iCalendar September 2009 + + + ATTACH;FMTTYPE=application/postscript:ftp://example.com/pub/ + conf/bkgrnd.ps + END:VEVENT + END:VCALENDAR + + The following is an example of a to-do due on April 15, 1998. An + audio alarm has been specified to remind the calendar user at noon, + the day before the to-do is expected to be completed and repeat + hourly, four additional times. The to-do definition has been + modified twice since it was initially created. + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//ABC Corporation//NONSGML My Product//EN + BEGIN:VTODO + DTSTAMP:19980130T134500Z + SEQUENCE:2 + UID:uid4@example.com + ORGANIZER:mailto:unclesam@example.com + ATTENDEE;PARTSTAT=ACCEPTED:mailto:jqpublic@example.com + DUE:19980415T000000 + STATUS:NEEDS-ACTION + SUMMARY:Submit Income Taxes + BEGIN:VALARM + ACTION:AUDIO + TRIGGER:19980403T120000Z + ATTACH;FMTTYPE=audio/basic:http://example.com/pub/audio- + files/ssbanner.aud + REPEAT:4 + DURATION:PT1H + END:VALARM + END:VTODO + END:VCALENDAR + + The following is an example of a journal entry: + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//ABC Corporation//NONSGML My Product//EN + BEGIN:VJOURNAL + DTSTAMP:19970324T120000Z + UID:uid5@example.com + ORGANIZER:mailto:jsmith@example.com + STATUS:DRAFT + CLASS:PUBLIC + CATEGORIES:Project Report,XYZ,Weekly Meeting + DESCRIPTION:Project xyz Review Meeting Minutes\n + Agenda\n1. Review of project version 1.0 requirements.\n2. + + + +Desruisseaux Standards Track [Page 146] + +RFC 5545 iCalendar September 2009 + + + Definition + of project processes.\n3. Review of project schedule.\n + Participants: John Smith\, Jane Doe\, Jim Dandy\n-It was + decided that the requirements need to be signed off by + product marketing.\n-Project processes were accepted.\n + -Project schedule needs to account for scheduled holidays + and employee vacation time. Check with HR for specific + dates.\n-New schedule will be distributed by Friday.\n- + Next weeks meeting is cancelled. No meeting until 3/23. + END:VJOURNAL + END:VCALENDAR + + The following is an example of published busy time information. The + iCalendar object might be placed in the network resource + http://www.example.com/calendar/busytime/jsmith.ifb. + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//RDU Software//NONSGML HandCal//EN + BEGIN:VFREEBUSY + ORGANIZER:mailto:jsmith@example.com + DTSTART:19980313T141711Z + DTEND:19980410T141711Z + FREEBUSY:19980314T233000Z/19980315T003000Z + FREEBUSY:19980316T153000Z/19980316T163000Z + FREEBUSY:19980318T030000Z/19980318T040000Z + URL:http://www.example.com/calendar/busytime/jsmith.ifb + END:VFREEBUSY + END:VCALENDAR + +5. Recommended Practices + + These recommended practices should be followed in order to assure + consistent handling of the following cases for an iCalendar object. + + 1. Content lines longer than 75 octets SHOULD be folded. + + 2. When the combination of the "RRULE" and "RDATE" properties in a + recurring component produces multiple instances having the same + start DATE-TIME value, they should be collapsed to, and + considered as, a single instance. If the "RDATE" property is + specified as a PERIOD value the duration of the recurrence + instance will be the one specified by the "RDATE" property, and + not the duration of the recurrence instance defined by the + "DTSTART" property. + + 3. When a calendar user receives multiple requests for the same + calendar component (e.g., REQUEST for a "VEVENT" calendar + + + +Desruisseaux Standards Track [Page 147] + +RFC 5545 iCalendar September 2009 + + + component) as a result of being on multiple mailing lists + specified by "ATTENDEE" properties in the request, they SHOULD + respond to only one of the requests. The calendar user SHOULD + also specify (using the "MEMBER" parameter of the "ATTENDEE" + property) of which mailing list they are a member. + + 4. An implementation can truncate a "SUMMARY" property value to 255 + octets, but it MUST NOT truncate the value in the middle of a + UTF-8 multi-octet sequence. + + 5. If seconds of the minute are not supported by an implementation, + then a value of "00" SHOULD be specified for the seconds + component in a time value. + + 6. "TZURL" values SHOULD NOT be specified as a file URI type. This + URI form can be useful within an organization, but is problematic + in the Internet. + + 7. Some possible English values for "CATEGORIES" property include: + "ANNIVERSARY", "APPOINTMENT", "BUSINESS", "EDUCATION", "HOLIDAY", + "MEETING", "MISCELLANEOUS", "NON-WORKING HOURS", "NOT IN OFFICE", + "PERSONAL", "PHONE CALL", "SICK DAY", "SPECIAL OCCASION", + "TRAVEL", "VACATION". Categories can be specified in any + registered language. + + 8. Some possible English values for the "RESOURCES" property + include: "CATERING", "CHAIRS", "COMPUTER PROJECTOR", "EASEL", + "OVERHEAD PROJECTOR", "SPEAKER PHONE", "TABLE", "TV", "VCR", + "VIDEO PHONE", "VEHICLE". Resources can be specified in any + registered language. + +6. Internationalization Considerations + + Applications MUST generate iCalendar streams in the UTF-8 charset and + MUST accept an iCalendar stream in the UTF-8 or US-ASCII charset. + +7. Security Considerations + + Because calendaring and scheduling information is very privacy- + sensitive, the protocol used for the transmission of calendaring and + scheduling information should have capabilities to protect the + information from possible threats, such as eavesdropping, replay, + message insertion, deletion, modification, and man-in-the-middle + attacks. + + As this document only defines the data format and media type of text/ + calendar that is independent of any calendar service or protocol, it + is up to the actual protocol specifications such as iTIP [2446bis], + + + +Desruisseaux Standards Track [Page 148] + +RFC 5545 iCalendar September 2009 + + + iMIP [2447bis], and "Calendaring Extensions to WebDAV (CalDAV)" + [RFC4791] to describe the threats that the above attacks present, as + well as ways in which to mitigate them. + +8. IANA Considerations + +8.1. iCalendar Media Type Registration + + The Calendaring and Scheduling Core Object Specification is intended + for use as a MIME content type. + + To: ietf-types@iana.org + + Subject: Registration of media type text/calendar + + Type name: text + + Subtype name: calendar + + Required parameters: none + + Optional parameters: charset, method, component, and optinfo + + The "charset" parameter is defined in [RFC2046] for subtypes of + the "text" media type. It is used to indicate the charset used in + the body part. The charset supported by this revision of + iCalendar is UTF-8. The use of any other charset is deprecated by + this revision of iCalendar; however, note that this revision + requires that compliant applications MUST accept iCalendar streams + using either the UTF-8 or US-ASCII charset. + + The "method" parameter is used to convey the iCalendar object + method or transaction semantics for the calendaring and scheduling + information. It also is an identifier for the restricted set of + properties and values of which the iCalendar object consists. The + parameter is to be used as a guide for applications interpreting + the information contained within the body part. It SHOULD NOT be + used to exclude or require particular pieces of information unless + the identified method definition specifically calls for this + behavior. Unless specifically forbidden by a particular method + definition, a text/calendar content type can contain any set of + properties permitted by the Calendaring and Scheduling Core Object + Specification. The "method" parameter MUST be specified and MUST + be set to the same value as the "METHOD" component property of the + iCalendar objects of the iCalendar stream if and only if the + iCalendar objects in the iCalendar stream all have a "METHOD" + component property set to the same value. + + + + +Desruisseaux Standards Track [Page 149] + +RFC 5545 iCalendar September 2009 + + + The value for the "method" parameter is defined as follows: + + method = 1*(ALPHA / DIGIT / "-") + ; IANA-registered iCalendar object method + + The "component" parameter conveys the type of iCalendar calendar + component within the body part. If the iCalendar object contains + more than one calendar component type, then multiple component + parameters MUST be specified. + + The value for the "component" parameter is defined as follows: + + component = "VEVENT" + / "VTODO" + / "VJOURNAL" + / "VFREEBUSY" + / "VTIMEZONE" + / iana-token + / x-name + + The "optinfo" parameter conveys optional information about the + iCalendar object within the body part. This parameter can only + specify semantics already specified by the iCalendar object and + that can be otherwise determined by parsing the body part. In + addition, the optional information specified by this parameter + MUST be consistent with that information specified by the + iCalendar object. For example, it can be used to convey the + "Attendee" response status to a meeting request. The parameter + value consists of a string value. + + The parameter can be specified multiple times. + + The value for the "optinfo" parameter is defined as follows: + + optinfo = infovalue / qinfovalue + + infovalue = iana-token / x-name + + qinfovalue = DQUOTE (infovalue) DQUOTE + + Encoding considerations: This media type can contain 8bit + characters, so the use of quoted-printable or base64 MIME Content- + Transfer-Encodings might be necessary when iCalendar objects are + transferred across protocols restricted to the 7bit repertoire. + Note that a text valued property in the content entity can also + have content encoding of special characters using a BACKSLASH + character escapement technique. This means that content values + can end up being encoded twice. + + + +Desruisseaux Standards Track [Page 150] + +RFC 5545 iCalendar September 2009 + + + Security considerations: See Section 7. + + Interoperability considerations: This media type is intended to + define a common format for conveying calendaring and scheduling + information between different systems. It is heavily based on the + earlier [VCAL] industry specification. + + Published specification: This specification. + + Applications that use this media type: This media type is designed + for widespread use by Internet calendaring and scheduling + applications. In addition, applications in the workflow and + document management area might find this content-type applicable. + The iTIP [2446bis], iMIP [2447bis], and CalDAV [RFC4791] Internet + protocols directly use this media type also. + + Additional information: + + Magic number(s): None. + + File extension(s): The file extension of "ics" is to be used to + designate a file containing (an arbitrary set of) calendaring + and scheduling information consistent with this MIME content + type. + + The file extension of "ifb" is to be used to designate a file + containing free or busy time information consistent with this + MIME content type. + + Macintosh file type code(s): The file type code of "iCal" is to + be used in Apple MacIntosh operating system environments to + designate a file containing calendaring and scheduling + information consistent with this MIME media type. + + The file type code of "iFBf" is to be used in Apple MacIntosh + operating system environments to designate a file containing + free or busy time information consistent with this MIME media + type. + + Person & email address to contact for further information: See the + "Author's Address" section of this document. + + Intended usage: COMMON + + Restrictions on usage: There are no restrictions on where this media + type can be used. + + Author: See the "Author's Address" section of this document. + + + +Desruisseaux Standards Track [Page 151] + +RFC 5545 iCalendar September 2009 + + + Change controller: IETF + +8.2. New iCalendar Elements Registration + + This section defines the process to register new or modified + iCalendar elements, that is, components, properties, parameters, + value data types, and values, with IANA. + +8.2.1. iCalendar Elements Registration Procedure + + The IETF will create a mailing list, icalendar@ietf.org, which can be + used for public discussion of iCalendar elements proposals prior to + registration. Use of the mailing list is strongly encouraged. The + IESG will appoint a designated expert who will monitor the + icalendar@ietf.org mailing list and review registrations. + + Registration of new iCalendar elements MUST be reviewed by the + designated expert and published in an RFC. A Standards Track RFC is + REQUIRED for the registration of new value data types that modify + existing properties, as well as for the registration of participation + status values to be used in "VEVENT" calendar components. A + Standards Track RFC is also REQUIRED for registration of iCalendar + elements that modify iCalendar elements previously documented in a + Standards Track RFC. + + The registration procedure begins when a completed registration + template, defined in the sections below, is sent to + icalendar@ietf.org and iana@iana.org. The designated expert is + expected to tell IANA and the submitter of the registration within + two weeks whether the registration is approved, approved with minor + changes, or rejected with cause. When a registration is rejected + with cause, it can be re-submitted if the concerns listed in the + cause are addressed. Decisions made by the designated expert can be + appealed to the IESG Applications Area Director, then to the IESG. + They follow the normal appeals procedure for IESG decisions. + +8.2.2. Registration Template for Components + + A component is defined by completing the following template. + + Component name: The name of the component. + + Purpose: The purpose of the component. Give a short but clear + description. + + Format definition: The ABNF for the component definition needs to be + specified. + + + + +Desruisseaux Standards Track [Page 152] + +RFC 5545 iCalendar September 2009 + + + Description: Any special notes about the component, how it is to be + used, etc. + + Example(s): One or more examples of instances of the component need + to be specified. + +8.2.3. Registration Template for Properties + + A property is defined by completing the following template. + + Property name: The name of the property. + + Purpose: The purpose of the property. Give a short but clear + description. + + Value type: Any of the valid value types for the property value need + to be specified. The default value type also needs to be + specified. + + Property parameters: Any of the valid property parameters for the + property MUST be specified. + + Conformance: The calendar components in which the property can + appear MUST be specified. + + Description: Any special notes about the property, how it is to be + used, etc. + + Format definition: The ABNF for the property definition needs to be + specified. + + Example(s): One or more examples of instances of the property need + to be specified. + +8.2.4. Registration Template for Parameters + + A parameter is defined by completing the following template. + + Parameter name: The name of the parameter. + + Purpose: The purpose of the parameter. Give a short but clear + description. + + Format definition: The ABNF for the parameter definition needs to be + specified. + + Description: Any special notes about the parameter, how it is to be + used, etc. + + + +Desruisseaux Standards Track [Page 153] + +RFC 5545 iCalendar September 2009 + + + Example(s): One or more examples of instances of the parameter need + to be specified. + +8.2.5. Registration Template for Value Data Types + + A value data type is defined by completing the following template. + + Value name: The name of the value type. + + Purpose: The purpose of the value type. Give a short but clear + description. + + Format definition: The ABNF for the value type definition needs to + be specified. + + Description: Any special notes about the value type, how it is to be + used, etc. + + Example(s): One or more examples of instances of the value type need + to be specified. + +8.2.6. Registration Template for Values + + A value is defined by completing the following template. + + Value: The value literal. + + Purpose: The purpose of the value. Give a short but clear + description. + + Conformance: The calendar properties and/or parameters that can take + this value need to be specified. + + Example(s): One or more examples of instances of the value need to + be specified. + + The following is a fictitious example of a registration of an + iCalendar value: + + Value: TOP-SECRET + + Purpose: This value is used to specify the access classification of + top-secret calendar components. + + Conformance: This value can be used with the "CLASS" property. + + + + + + +Desruisseaux Standards Track [Page 154] + +RFC 5545 iCalendar September 2009 + + + Example(s): The following is an example of this value used with the + "CLASS" property: + + CLASS:TOP-SECRET + +8.3. Initial iCalendar Elements Registries + + The IANA created and maintains the following registries for iCalendar + elements with pointers to appropriate reference documents. + +8.3.1. Components Registry + + The following table has been used to initialize the components + registry. + + +-----------+---------+-------------------------+ + | Component | Status | Reference | + +-----------+---------+-------------------------+ + | VCALENDAR | Current | RFC 5545, Section 3.4 | + | | | | + | VEVENT | Current | RFC 5545, Section 3.6.1 | + | | | | + | VTODO | Current | RFC 5545, Section 3.6.2 | + | | | | + | VJOURNAL | Current | RFC 5545, Section 3.6.3 | + | | | | + | VFREEBUSY | Current | RFC 5545, Section 3.6.4 | + | | | | + | VTIMEZONE | Current | RFC 5545, Section 3.6.5 | + | | | | + | VALARM | Current | RFC 5545, Section 3.6.6 | + | | | | + | STANDARD | Current | RFC 5545, Section 3.6.5 | + | | | | + | DAYLIGHT | Current | RFC 5545, Section 3.6.5 | + +-----------+---------+-------------------------+ + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 155] + +RFC 5545 iCalendar September 2009 + + +8.3.2. Properties Registry + + The following table is has been used to initialize the properties + registry. + + +------------------+------------+----------------------------+ + | Property | Status | Reference | + +------------------+------------+----------------------------+ + | CALSCALE | Current | RFC 5545, Section 3.7.1 | + | METHOD | Current | RFC 5545, Section 3.7.2 | + | | | | + | PRODID | Current | RFC 5545, Section 3.7.3 | + | | | | + | VERSION | Current | RFC 5545, Section 3.7.4 | + | | | | + | ATTACH | Current | RFC 5545, Section 3.8.1.1 | + | | | | + | CATEGORIES | Current | RFC 5545, Section 3.8.1.2 | + | | | | + | CLASS | Current | RFC 5545, Section 3.8.1.3 | + | | | | + | COMMENT | Current | RFC 5545, Section 3.8.1.4 | + | | | | + | DESCRIPTION | Current | RFC 5545, Section 3.8.1.5 | + | | | | + | GEO | Current | RFC 5545, Section 3.8.1.6 | + | | | | + | LOCATION | Current | RFC 5545, Section 3.8.1.7 | + | | | | + | PERCENT-COMPLETE | Current | RFC 5545, Section 3.8.1.8 | + | | | | + | PRIORITY | Current | RFC 5545, Section 3.8.1.9 | + | | | | + | RESOURCES | Current | RFC 5545, Section 3.8.1.10 | + | | | | + | STATUS | Current | RFC 5545, Section 3.8.1.11 | + | | | | + | SUMMARY | Current | RFC 5545, Section 3.8.1.12 | + | | | | + | COMPLETED | Current | RFC 5545, Section 3.8.2.1 | + | | | | + | DTEND | Current | RFC 5545, Section 3.8.2.2 | + | | | | + | DUE | Current | RFC 5545, Section 3.8.2.3 | + | | | | + | DTSTART | Current | RFC 5545, Section 3.8.2.4 | + | | | | + | DURATION | Current | RFC 5545, Section 3.8.2.5 | + + + +Desruisseaux Standards Track [Page 156] + +RFC 5545 iCalendar September 2009 + + + | | | | + | FREEBUSY | Current | RFC 5545, Section 3.8.2.6 | + | | | | + | TRANSP | Current | RFC 5545, Section 3.8.2.7 | + | | | | + | TZID | Current | RFC 5545, Section 3.8.3.1 | + | | | | + | TZNAME | Current | RFC 5545, Section 3.8.3.2 | + | | | | + | TZOFFSETFROM | Current | RFC 5545, Section 3.8.3.3 | + | | | | + | TZOFFSETTO | Current | RFC 5545, Section 3.8.3.4 | + | | | | + | TZURL | Current | RFC 5545, Section 3.8.3.5 | + | | | | + | ATTENDEE | Current | RFC 5545, Section 3.8.4.1 | + | | | | + | CONTACT | Current | RFC 5545, Section 3.8.4.2 | + | | | | + | ORGANIZER | Current | RFC 5545, Section 3.8.4.3 | + | | | | + | RECURRENCE-ID | Current | RFC 5545, Section 3.8.4.4 | + | | | | + | RELATED-TO | Current | RFC 5545, Section 3.8.4.5 | + | | | | + | URL | Current | RFC 5545, Section 3.8.4.6 | + | | | | + | UID | Current | RFC 5545, Section 3.8.4.7 | + | | | | + | EXDATE | Current | RFC 5545, Section 3.8.5.1 | + | | | | + | EXRULE | Deprecated | [RFC2445], Section 4.8.5.2 | + | | | | + | RDATE | Current | RFC 5545, Section 3.8.5.2 | + | | | | + | RRULE | Current | RFC 5545, Section 3.8.5.3 | + | | | | + | ACTION | Current | RFC 5545, Section 3.8.6.1 | + | | | | + | REPEAT | Current | RFC 5545, Section 3.8.6.2 | + | | | | + | TRIGGER | Current | RFC 5545, Section 3.8.6.3 | + | | | | + | CREATED | Current | RFC 5545, Section 3.8.7.1 | + | | | | + | DTSTAMP | Current | RFC 5545, Section 3.8.7.2 | + | | | | + | LAST-MODIFIED | Current | RFC 5545, Section 3.8.7.3 | + + + +Desruisseaux Standards Track [Page 157] + +RFC 5545 iCalendar September 2009 + + + | | | | + | SEQUENCE | Current | RFC 5545, Section 3.8.7.4 | + | | | | + | REQUEST-STATUS | Current | RFC 5545, Section 3.8.8.3 | + +------------------+------------+----------------------------+ + +8.3.3. Parameters Registry + + The following table has been used to initialize the parameters + registry. + + +----------------+---------+--------------------------+ + | Parameter | Status | Reference | + +----------------+---------+--------------------------+ + | ALTREP | Current | RFC 5545, Section 3.2.1 | + | | | | + | CN | Current | RFC 5545, Section 3.2.2 | + | | | | + | CUTYPE | Current | RFC 5545, Section 3.2.3 | + | | | | + | DELEGATED-FROM | Current | RFC 5545, Section 3.2.4 | + | | | | + | DELEGATED-TO | Current | RFC 5545, Section 3.2.5 | + | | | | + | DIR | Current | RFC 5545, Section 3.2.6 | + | | | | + | ENCODING | Current | RFC 5545, Section 3.2.7 | + | | | | + | FMTTYPE | Current | RFC 5545, Section 3.2.8 | + | | | | + | FBTYPE | Current | RFC 5545, Section 3.2.9 | + | | | | + | LANGUAGE | Current | RFC 5545, Section 3.2.10 | + | | | | + | MEMBER | Current | RFC 5545, Section 3.2.11 | + | | | | + | PARTSTAT | Current | RFC 5545, Section 3.2.12 | + | | | | + | RANGE | Current | RFC 5545, Section 3.2.13 | + | | | | + | RELATED | Current | RFC 5545, Section 3.2.14 | + | | | | + | RELTYPE | Current | RFC 5545, Section 3.2.15 | + | | | | + | ROLE | Current | RFC 5545, Section 3.2.16 | + | | | | + | RSVP | Current | RFC 5545, Section 3.2.17 | + | | | | + + + +Desruisseaux Standards Track [Page 158] + +RFC 5545 iCalendar September 2009 + + + | SENT-BY | Current | RFC 5545, Section 3.2.18 | + | | | | + | TZID | Current | RFC 5545, Section 3.2.19 | + | | | | + | VALUE | Current | RFC 5545, Section 3.2.20 | + +----------------+---------+--------------------------+ + +8.3.4. Value Data Types Registry + + The following table has been used to initialize the value data types + registry. + + +-----------------+---------+--------------------------+ + | Value Data Type | Status | Reference | + +-----------------+---------+--------------------------+ + | BINARY | Current | RFC 5545, Section 3.3.1 | + | | | | + | BOOLEAN | Current | RFC 5545, Section 3.3.2 | + | | | | + | CAL-ADDRESS | Current | RFC 5545, Section 3.3.3 | + | | | | + | DATE | Current | RFC 5545, Section 3.3.4 | + | | | | + | DATE-TIME | Current | RFC 5545, Section 3.3.5 | + | | | | + | DURATION | Current | RFC 5545, Section 3.3.6 | + | | | | + | FLOAT | Current | RFC 5545, Section 3.3.7 | + | | | | + | INTEGER | Current | RFC 5545, Section 3.3.8 | + | | | | + | PERIOD | Current | RFC 5545, Section 3.3.9 | + | | | | + | RECUR | Current | RFC 5545, Section 3.3.10 | + | | | | + | TEXT | Current | RFC 5545, Section 3.3.11 | + | | | | + | TIME | Current | RFC 5545, Section 3.3.12 | + | | | | + | URI | Current | RFC 5545, Section 3.3.13 | + | | | | + | UTC-OFFSET | Current | RFC 5545, Section 3.3.14 | + +-----------------+---------+--------------------------+ + + + + + + + + +Desruisseaux Standards Track [Page 159] + +RFC 5545 iCalendar September 2009 + + +8.3.5. Calendar User Types Registry + + The following table has been used to initialize the calendar user + types registry. + + +--------------------+---------+-------------------------+ + | Calendar User Type | Status | Reference | + +--------------------+---------+-------------------------+ + | INDIVIDUAL | Current | RFC 5545, Section 3.2.3 | + | | | | + | GROUP | Current | RFC 5545, Section 3.2.3 | + | | | | + | RESOURCE | Current | RFC 5545, Section 3.2.3 | + | | | | + | ROOM | Current | RFC 5545, Section 3.2.3 | + | | | | + | UNKNOWN | Current | RFC 5545, Section 3.2.3 | + +--------------------+---------+-------------------------+ + +8.3.6. Free/Busy Time Types Registry + + The following table has been used to initialize the free/busy time + types registry. + + +---------------------+---------+-------------------------+ + | Free/Busy Time Type | Status | Reference | + +---------------------+---------+-------------------------+ + | FREE | Current | RFC 5545, Section 3.2.9 | + | | | | + | BUSY | Current | RFC 5545, Section 3.2.9 | + | | | | + | BUSY-UNAVAILABLE | Current | RFC 5545, Section 3.2.9 | + | | | | + | BUSY-TENTATIVE | Current | RFC 5545, Section 3.2.9 | + +---------------------+---------+-------------------------+ + + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 160] + +RFC 5545 iCalendar September 2009 + + +8.3.7. Participation Statuses Registry + + The following table has been used to initialize the participation + statuses registry. + + +--------------------+---------+--------------------------+ + | Participant Status | Status | Reference | + +--------------------+---------+--------------------------+ + | NEEDS-ACTION | Current | RFC 5545, Section 3.2.12 | + | | | | + | ACCEPTED | Current | RFC 5545, Section 3.2.12 | + | | | | + | DECLINED | Current | RFC 5545, Section 3.2.12 | + | | | | + | TENTATIVE | Current | RFC 5545, Section 3.2.12 | + | | | | + | DELEGATED | Current | RFC 5545, Section 3.2.12 | + | | | | + | COMPLETED | Current | RFC 5545, Section 3.2.12 | + | | | | + | IN-PROCESS | Current | RFC 5545, Section 3.2.12 | + +--------------------+---------+--------------------------+ + +8.3.8. Relationship Types Registry + + The following table has been used to initialize the relationship + types registry. + + +-------------------+---------+--------------------------+ + | Relationship Type | Status | Reference | + +-------------------+---------+--------------------------+ + | CHILD | Current | RFC 5545, Section 3.2.15 | + | | | | + | PARENT | Current | RFC 5545, Section 3.2.15 | + | | | | + | SIBLING | Current | RFC 5545, Section 3.2.15 | + +-------------------+---------+--------------------------+ + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 161] + +RFC 5545 iCalendar September 2009 + + +8.3.9. Participation Roles Registry + + The following table has been used to initialize the participation + roles registry. + + +-----------------+---------+--------------------------+ + | Role Type | Status | Reference | + +-----------------+---------+--------------------------+ + | CHAIR | Current | RFC 5545, Section 3.2.16 | + | | | | + | REQ-PARTICIPANT | Current | RFC 5545, Section 3.2.16 | + | | | | + | OPT-PARTICIPANT | Current | RFC 5545, Section 3.2.16 | + | | | | + | NON-PARTICIPANT | Current | RFC 5545, Section 3.2.16 | + +-----------------+---------+--------------------------+ + +8.3.10. Actions Registry + + The following table has been used to initialize the actions registry. + + +-----------+------------+----------------------------+ + | Action | Status | Reference | + +-----------+------------+----------------------------+ + | AUDIO | Current | RFC 5545, Section 3.8.6.1 | + | | | | + | DISPLAY | Current | RFC 5545, Section 3.8.6.1 | + | | | | + | EMAIL | Current | RFC 5545, Section 3.8.6.1 | + | | | | + | PROCEDURE | Deprecated | [RFC2445], Section 4.8.6.1 | + +-----------+------------+----------------------------+ + +8.3.11. Classifications Registry + + The following table has been used to initialize the classifications + registry. + + +----------------+---------+---------------------------+ + | Classification | Status | Reference | + +----------------+---------+---------------------------+ + | PUBLIC | Current | RFC 5545, Section 3.8.1.3 | + | | | | + | PRIVATE | Current | RFC 5545, Section 3.8.1.3 | + | | | | + | CONFIDENTIAL | Current | RFC 5545, Section 3.8.1.3 | + +----------------+---------+---------------------------+ + + + + +Desruisseaux Standards Track [Page 162] + +RFC 5545 iCalendar September 2009 + + +8.3.12. Methods Registry + + No values are defined in this document for the "METHOD" property. + +9. Acknowledgments + + The editor of this document wishes to thank Frank Dawson and Derik + Stenerson, the original authors of RFC 2445, as well as the following + individuals who have participated in the drafting, review, and + discussion of this memo: + + Joe Abley, Hervey Allen, Steve Allen, Jay Batson, Oliver Block, + Stephane Bortzmeyer, Chris Bryant, Tantek Celik, Mark Crispin, Cyrus + Daboo, Mike Douglass, Andrew N. Dowden, Lisa Dusseault, Lars Eggert, + Gren Eliot, Pasi Eronen, Ben Fortuna, Ned Freed, Neal Gafter, Ted + Hardie, Tim Hare, Jeffrey Harris, Helge Hess, Paul B. Hill, Thomas + Hnetila, Russ Housley, Leif Johansson, Ciny Joy, Bruce Kahn, Reinhold + Kainhofer, Martin Kiff, Patrice Lapierre, Michiel van Leeuwen, + Jonathan Lennox, Jeff McCullough, Bill McQuillan, Alexey Melnikov, + John W. Noerenberg II, Chuck Norris, Mark Paterson, Simon Pilette, + Arnaud Quillaud, Robert Ransdell, Julian F. Reschke, Caleb + Richardson, Sam Roberts, Dan Romascanu, Mike Samuel, George Sexton, + Nigel Swinson, Clint Talbert, Simon Vaillancourt, Magnus Westerlund, + and Sandy Wills. + + A special thanks to the working group chairs Aki Niemi and Eliot Lear + for their support and guidance. + + The editor would also like to thank the Calendaring and Scheduling + Consortium for advice with this specification, and for organizing + interoperability testing events to help refine it. + + + + + + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 163] + +RFC 5545 iCalendar September 2009 + + +10. References + +10.1. Normative References + + [ISO.8601.2004] International Organization for + Standardization, "Data elements and + interchange formats -- Information interchange + -- Representation of dates and times", 2004. + + [ISO.9070.1991] International Organization for + Standardization, "Information Technology_SGML + Support Facilities -- Registration Procedures + for Public Text Owner Identifiers, Second + Edition", April 1991. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose + Internet Mail Extensions (MIME) Part One: + Format of Internet Message Bodies", RFC 2045, + November 1996. + + [RFC2046] Freed, N. and N. Borenstein, "Multipurpose + Internet Mail Extensions (MIME) Part Two: + Media Types", RFC 2046, November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, + RFC 2119, March 1997. + + [RFC2368] Hoffman, P., Masinter, L., and J. Zawinski, + "The mailto URL scheme", RFC 2368, July 1998. + + [RFC3629] Yergeau, F., "UTF-8, a transformation format + of ISO 10646", STD 63, RFC 3629, + November 2003. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. + Masinter, "Uniform Resource Identifier (URI): + Generic Syntax", STD 66, RFC 3986, + January 2005. + + [RFC4288] Freed, N. and J. Klensin, "Media Type + Specifications and Registration Procedures", + BCP 13, RFC 4288, December 2005. + + [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 + Data Encodings", RFC 4648, October 2006. + + + + + +Desruisseaux Standards Track [Page 164] + +RFC 5545 iCalendar September 2009 + + + [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for + Syntax Specifications: ABNF", STD 68, + RFC 5234, January 2008. + + [RFC5646] Phillips, A., Ed., and M. Davis, Ed., "Tags + for Identifying Languages", BCP 47, RFC 5646, + September 2009. + + [US-ASCII] American National Standards Institute, "Coded + Character Set - 7-bit American Standard Code + for Information Interchange", ANSI X3.4, 1986. + +10.2. Informative References + + [2446bis] Daboo, C., "iCalendar Transport-Independent + Interoperability Protocol (iTIP)", Work + in Progress, April 2009. + + [2447bis] Melnikov, A., "iCalendar Message-Based + Interoperability Protocol (iMIP)", Work + in Progress, June 2008. + + [ANSI INCITS 61-1986] International Committee for Information + Technology, "Representation of Geographic + Point Locations for Information Interchange + (formerly ANSI X3.61-1986 (R1997))", ANSI + INCITS 61-1986 (R2007), 2007. + + [RFC1738] Berners-Lee, T., Masinter, L., and M. + McCahill, "Uniform Resource Locators (URL)", + RFC 1738, December 1994. + + [RFC2392] Levinson, E., "Content-ID and Message-ID + Uniform Resource Locators", RFC 2392, + August 1998. + + [RFC2397] Masinter, L., "The "data" URL scheme", + RFC 2397, August 1998. + + [RFC2425] Howes, T., Smith, M., and F. Dawson, "A MIME + Content-Type for Directory Information", + RFC 2425, September 1998. + + [RFC2426] Dawson, F. and T. Howes, "vCard MIME Directory + Profile", RFC 2426, September 1998. + + + + + + +Desruisseaux Standards Track [Page 165] + +RFC 5545 iCalendar September 2009 + + + [RFC2445] Dawson, F. and Stenerson, D., "Internet + Calendaring and Scheduling Core Object + Specification (iCalendar)", RFC 2445, + November 1998. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, + H., Masinter, L., Leach, P., and T. Berners- + Lee, "Hypertext Transfer Protocol -- + HTTP/1.1", RFC 2616, June 1999. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, + May 2000. + + [RFC4516] Smith, M. and T. Howes, "Lightweight Directory + Access Protocol (LDAP): Uniform Resource + Locator", RFC 4516, June 2006. + + [RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault, + "Calendaring Extensions to WebDAV (CalDAV)", + RFC 4791, March 2007. + + [TZDB] Eggert, P. and A.D. Olson, "Sources for Time + Zone and Daylight Saving Time Data", + July 2009, + . + + [VCAL] Internet Mail Consortium, "vCalendar: The + Electronic Calendaring and Scheduling Exchange + Format", September 1996, + . + + + + + + + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 166] + +RFC 5545 iCalendar September 2009 + + +Appendix A. Differences from RFC 2445 + + This appendix contains a list of changes that have been made in the + Internet Calendaring and Scheduling Core Object Specification from + RFC 2445. + +A.1. New Restrictions + + 1. The "DTSTART" property SHOULD be synchronized with the recurrence + rule, if specified. + + 2. The "RRULE" property SHOULD NOT occur more than once in a + component. + + 3. The BYHOUR, BYMINUTE, and BYSECOND rule parts MUST NOT be + specified in the "RRULE" property when the "DTSTART" property is + specified as a DATE value. + + 4. The value type of the "DTEND" or "DUE" properties MUST match the + value type of "DTSTART" property. + + 5. The "DURATION" property can no longer appear in "VFREEBUSY" + components. + +A.2. Restrictions Removed + + 1. The "DTSTART" and "DTEND" properties are no longer required to be + specified as date with local time and time zone reference when + used with a recurrence rule. + +A.3. Deprecated Features + + 1. The "EXRULE" property can no longer be specified in a component. + + 2. The "THISANDPRIOR" value can no longer be used with the "RANGE" + parameter. + + 3. The "PROCEDURE" value can no longer be used with the "ACTION" + property. + + 4. The value type RECUR no longer allows multiple values to be + specified by a COMMA-separated list of values. + + 5. x-name rule parts can no longer be specified in properties of + RECUR value type (e.g., "RRULE"). x-param can be used on RECUR + value type properties instead. + + + + + +Desruisseaux Standards Track [Page 167] + +RFC 5545 iCalendar September 2009 + + +Author's Address + + Bernard Desruisseaux (editor) + Oracle Corporation + 600 blvd. de Maisonneuve West + Suite 1900 + Montreal, QC H3A 3J2 + CANADA + + EMail: bernard.desruisseaux@oracle.com + URI: http://www.oracle.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Desruisseaux Standards Track [Page 168] + diff --git a/dav/SabreDAV/docs/rfc5546.txt b/dav/SabreDAV/docs/rfc5546.txt new file mode 100644 index 000000000..80361d81a --- /dev/null +++ b/dav/SabreDAV/docs/rfc5546.txt @@ -0,0 +1,7451 @@ + + + + + + +Network Working Group C. Daboo, Ed. +Request for Comments: 5546 Apple Inc. +Obsoletes: 2446 December 2009 +Updates: 5545 +Category: Standards Track + + + iCalendar Transport-Independent Interoperability Protocol (iTIP) + +Abstract + + This document specifies a protocol that uses the iCalendar object + specification to provide scheduling interoperability between + different calendaring systems. This is done without reference to a + specific transport protocol so as to allow multiple methods of + communication between systems. Subsequent documents will define + profiles of this protocol that use specific, interoperable methods of + communication between systems. + + The iCalendar Transport-Independent Interoperability Protocol (iTIP) + complements the iCalendar object specification by adding semantics + for group scheduling methods commonly available in current + calendaring systems. These scheduling methods permit two or more + calendaring systems to perform transactions such as publishing, + scheduling, rescheduling, responding to scheduling requests, + negotiating changes, or canceling. + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (c) 2009 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + + + + + +Daboo Standards Track [Page 1] + +RFC 5546 iTIP December 2009 + + + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction and Overview .......................................5 + 1.1. Formatting Conventions .....................................5 + 1.2. Related Documents ..........................................6 + 1.3. Roles ......................................................6 + 1.4. Methods ....................................................7 + 2. Interoperability Models .........................................9 + 2.1. Application Protocol ......................................10 + 2.1.1. Scheduling State ...................................10 + 2.1.2. Delegation .........................................10 + 2.1.3. Acting on Behalf of Other Calendar Users ...........11 + 2.1.4. Component Revisions ................................11 + 2.1.5. Message Sequencing .................................12 + 3. Application Protocol Elements ..................................13 + 3.1. Common Component Restriction Tables .......................15 + 3.1.1. VCALENDAR ..........................................15 + 3.1.2. VTIMEZONE ..........................................15 + 3.1.3. VALARM .............................................17 + 3.2. Methods for VEVENT Calendar Components ....................17 + 3.2.1. PUBLISH ............................................18 + 3.2.2. REQUEST ............................................20 + 3.2.3. REPLY ..............................................25 + 3.2.4. ADD ................................................27 + 3.2.5. CANCEL .............................................29 + 3.2.6. REFRESH ............................................31 + 3.2.7. COUNTER ............................................33 + 3.2.8. DECLINECOUNTER .....................................35 + 3.3. Methods for VFREEBUSY Components ..........................37 + 3.3.1. PUBLISH ............................................37 + 3.3.2. REQUEST ............................................40 + 3.3.3. REPLY ..............................................42 + + + +Daboo Standards Track [Page 2] + +RFC 5546 iTIP December 2009 + + + 3.4. Methods for VTODO Components ..............................44 + 3.4.1. PUBLISH ............................................44 + 3.4.2. REQUEST ............................................46 + 3.4.3. REPLY ..............................................51 + 3.4.4. ADD ................................................53 + 3.4.5. CANCEL .............................................55 + 3.4.6. REFRESH ............................................57 + 3.4.7. COUNTER ............................................59 + 3.4.8. DECLINECOUNTER .....................................61 + 3.5. Methods for VJOURNAL Components ...........................62 + 3.5.1. PUBLISH ............................................63 + 3.5.2. ADD ................................................64 + 3.5.3. CANCEL .............................................66 + 3.6. Status Replies ............................................68 + 3.7. Implementation Considerations .............................77 + 3.7.1. Working With Recurrence Instances ..................77 + 3.7.2. Attendee Property Considerations ...................78 + 3.7.3. Extension Tokens ...................................79 + 4. Examples .......................................................79 + 4.1. Published Event Examples ..................................79 + 4.1.1. A Minimal Published Event ..........................80 + 4.1.2. Changing a Published Event .........................80 + 4.1.3. Canceling a Published Event ........................81 + 4.1.4. A Rich Published Event .............................81 + 4.1.5. Anniversaries or Events Attached to Entire Days ....83 + 4.2. Group Event Examples ......................................83 + 4.2.1. A Group Event Request ..............................84 + 4.2.2. Reply to a Group Event Request .....................85 + 4.2.3. Update an Event ....................................85 + 4.2.4. Countering an Event Proposal .......................86 + 4.2.5. Delegating an Event ................................88 + 4.2.6. Delegate Accepts the Meeting .......................90 + 4.2.7. Delegate Declines the Meeting ......................91 + 4.2.8. Forwarding an Event Request ........................92 + 4.2.9. Cancel a Group Event ...............................92 + 4.2.10. Removing Attendees ................................93 + 4.2.11. Replacing the Organizer ...........................95 + 4.3. Busy Time Examples ........................................96 + 4.3.1. Publish Busy Time ..................................96 + 4.3.2. Request Busy Time ..................................96 + 4.3.3. Reply to a Busy Time Request .......................97 + 4.4. Recurring Event and Time Zone Examples ....................98 + 4.4.1. A Recurring Event Spanning Time Zones ..............98 + 4.4.2. Modify a Recurring Instance ........................99 + 4.4.3. Cancel an Instance ................................101 + 4.4.4. Cancel a Recurring Event ..........................101 + 4.4.5. Change All Future Instances .......................102 + 4.4.6. Add a New Instance to a Recurring Event ...........102 + + + +Daboo Standards Track [Page 3] + +RFC 5546 iTIP December 2009 + + + 4.4.7. Add a New Series of Instances to a + Recurring Event ...................................103 + 4.4.8. Refreshing a Recurring Event ......................104 + 4.4.9. Counter an Instance of a Recurring Event ..........106 + 4.4.10. Error Reply to a Request .........................107 + 4.5. Group To-Do Examples .....................................108 + 4.5.1. A VTODO Request ...................................109 + 4.5.2. A VTODO Reply .....................................110 + 4.5.3. A VTODO Request for Updated Status ................110 + 4.5.4. A Reply: Percent-Complete .........................111 + 4.5.5. A Reply: Completed ................................111 + 4.5.6. An Updated VTODO Request ..........................112 + 4.5.7. Recurring VTODOs ..................................112 + 4.6. Journal Examples .........................................113 + 4.7. Other Examples ...........................................114 + 4.7.1. Event Refresh .....................................114 + 4.7.2. Bad RECURRENCE-ID .................................114 + 5. Application Protocol Fallbacks ................................116 + 5.1. Partial Implementation ...................................116 + 5.1.1. Event-Related Fallbacks ...........................117 + 5.1.2. Free/Busy-Related Fallbacks .......................119 + 5.1.3. To-Do-Related Fallbacks ...........................120 + 5.1.4. Journal-Related Fallbacks .........................122 + 5.2. Latency Issues ...........................................123 + 5.2.1. Cancellation of an Unknown Calendar Component .....123 + 5.2.2. Unexpected Reply from an Unknown Delegate .........124 + 5.3. Sequence Number ..........................................124 + 6. Security Considerations .......................................124 + 6.1. Security Threats .........................................124 + 6.1.1. Spoofing the Organizer ............................124 + 6.1.2. Spoofing the Attendee .............................124 + 6.1.3. Unauthorized Replacement of the Organizer .........125 + 6.1.4. Eavesdropping and Data Integrity ..................125 + 6.1.5. Flooding a Calendar ...............................125 + 6.1.6. Unauthorized REFRESH Requests .....................125 + 6.2. Recommendations ..........................................125 + 6.2.1. Securing iTIP transactions ........................125 + 6.2.2. Implementation Controls ...........................126 + 6.2.3. Access Controls and Filtering .....................126 + 6.3. Privacy Issues ...........................................126 + 7. IANA Considerations ...........................................127 + 7.1. Registration Template for REQUEST-STATUS Values ..........127 + 7.2. Additions to iCalendar METHOD Registry ...................127 + 7.3. REQUEST-STATUS Value Registry ............................129 + 8. Acknowledgments ...............................................130 + 9. References ....................................................131 + 9.1. Normative References .....................................131 + 9.2. Informative References ...................................131 + + + +Daboo Standards Track [Page 4] + +RFC 5546 iTIP December 2009 + + + Appendix A. Differences from RFC 2446 ...........................132 + A.1. Changed Restrictions .....................................132 + A.2. Deprecated Features ......................................133 + +1. Introduction and Overview + + This document specifies how calendaring systems use iCalendar + [RFC5545] objects to interoperate with other calendaring systems. In + particular, it specifies how to schedule events, to-dos, or daily + journal entries. It further specifies how to search for available + busy time information. It does so in a general way, without + specifying how communication between different systems actually takes + place. Subsequent documents will specify transport bindings between + systems that use this protocol. + + This protocol is based on messages sent from an originator to one or + more recipients. For certain types of messages, a recipient may + reply in order to update their status and may also return + transaction/request status information. The protocol supports the + ability for the message originator to modify or cancel the original + message. The protocol also supports the ability for recipients to + suggest changes to the originator of a message. The elements of the + protocol also define the user roles for its transactions. + + This specification obsoletes RFC 2446 - a list of important changes + is provided in Appendix A. + +1.1. Formatting Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + Calendaring and scheduling roles are referred to in quoted-strings of + text with the first character of each word in upper case. For + example, "Organizer" refers to a role of a "Calendar User" (CU) + within the scheduling protocol. + + Calendar components defined by [RFC5545] are referred to with + capitalized, quoted-strings of text. All calendar components start + with the letter "V". For example, "VEVENT" refers to the event + calendar component, "VTODO" refers to the to-do calendar component, + and "VJOURNAL" refers to the daily journal calendar component. + + + + + + + + +Daboo Standards Track [Page 5] + +RFC 5546 iTIP December 2009 + + + Scheduling methods are referred to with capitalized, quoted-strings + of text. For example, "REQUEST" refers to the method for requesting + a scheduling calendar component be created or modified; "REPLY" + refers to the method a recipient of a request uses to update their + status with the "Organizer" of the calendar component. + + Properties defined by [RFC5545] are referred to with capitalized, + quoted-strings of text, followed by the word "property". For + example, "ATTENDEE" property refers to the iCalendar property used to + convey the calendar address of a "Calendar User". + + Property parameters defined by this specification are referred to + with capitalized, quoted-strings of text, followed by the word + "parameter". For example, "VALUE" parameter refers to the iCalendar + property parameter used to override the default data type for a + property value. + + Enumerated values defined by this specification are referred to with + capitalized text, either alone or followed by the word "value". + + In tables, the quoted-string text is specified without quotes in + order to minimize the table length. + +1.2. Related Documents + + Implementers will need to be familiar with several other + specifications that, along with this one, describe the Internet + calendaring and scheduling standards. The related documents are: + + [RFC5545] - specifies the objects, data types, properties, and + property parameters used in the protocols, along with the methods + for representing and encoding them. + + [iMIP] - specifies an Internet email binding for iTIP. + + This specification does not attempt to repeat the concepts or + definitions from these other specifications. Where possible, + explicit references are made to the other specifications. + +1.3. Roles + + Exchanges of iCalendar objects for the purposes of group calendaring + and scheduling occur between "Calendar Users" (CUs). CUs take on + several roles in iTIP: + + + + + + + +Daboo Standards Track [Page 6] + +RFC 5546 iTIP December 2009 + + + +-----------+-------------------------------------------------------+ + | Role | Description | + +-----------+-------------------------------------------------------+ + | Organizer | The CU who initiates an exchange takes on the role of | + | | Organizer. For example, the CU who proposes a group | + | | meeting is the Organizer. | + | | | + | Attendee | CUs who are included in the scheduling message as | + | | possible recipients of that scheduling message. For | + | | example, the CUs asked to participate in a group | + | | meeting by the Organizer take on the role of | + | | Attendee. | + | | | + | Other CU | A CU that is not explicitly included in a scheduling | + | | message, i.e., not the Organizer or an Attendee. | + +-----------+-------------------------------------------------------+ + + Note that "ROLE" is also a descriptive parameter to the iCalendar + "ATTENDEE" property. Its use is to convey descriptive context about + an "Attendee" -- such as "chair", "required participant", or "non- + required participant" -- and has nothing to do with the calendaring + workflow. + +1.4. Methods + + The iTIP methods are listed below and their usage and semantics are + defined in Section 3 of this document. + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 7] + +RFC 5546 iTIP December 2009 + + + +----------------+--------------------------------------------------+ + | Method | Description | + +----------------+--------------------------------------------------+ + | PUBLISH | Used to publish an iCalendar object to one or | + | | more "Calendar Users". There is no | + | | interactivity between the publisher and any | + | | other "Calendar User". An example might include | + | | a baseball team publishing its schedule to the | + | | public. | + | | | + | REQUEST | Used to schedule an iCalendar object with other | + | | "Calendar Users". Requests are interactive in | + | | that they require the receiver to respond using | + | | the reply methods. Meeting requests, busy-time | + | | requests, and the assignment of tasks to other | + | | "Calendar Users" are all examples. Requests are | + | | also used by the Organizer to update the status | + | | of an iCalendar object. | + | | | + | REPLY | A reply is used in response to a request to | + | | convey Attendee status to the Organizer. | + | | Replies are commonly used to respond to meeting | + | | and task requests. | + | | | + | ADD | Add one or more new instances to an existing | + | | recurring iCalendar object. | + | | | + | CANCEL | Cancel one or more instances of an existing | + | | iCalendar object. | + | | | + | REFRESH | Used by an Attendee to request the latest | + | | version of an iCalendar object. | + | | | + | COUNTER | Used by an Attendee to negotiate a change in an | + | | iCalendar object. Examples include the request | + | | to change a proposed event time or change the | + | | due date for a task. | + | | | + | DECLINECOUNTER | Used by the Organizer to decline the proposed | + | | counter proposal. | + +----------------+--------------------------------------------------+ + + + + + + + + + + +Daboo Standards Track [Page 8] + +RFC 5546 iTIP December 2009 + + + Group scheduling in iTIP is accomplished using the set of "request" + and "response" methods described above. The following table shows + the methods broken down by who can send them. + + +------------+------------------------------------------------------+ + | Originator | Methods | + +------------+------------------------------------------------------+ + | Organizer | PUBLISH, REQUEST, ADD, CANCEL, DECLINECOUNTER | + | | | + | Attendee | REPLY, REFRESH, COUNTER, REQUEST (only when | + | | delegating) | + +------------+------------------------------------------------------+ + + Note that for some calendar component types, the allowable methods + are a subset of the above set. In addition, apart from "VTIMEZONE" + iCalendar components, only one component type is allowed in a single + iTIP message. + +2. Interoperability Models + + There are two distinct protocols relevant to interoperability: an + "application protocol" and a "transport protocol". The application + protocol defines the content of the iCalendar objects sent between + sender and receiver to accomplish the scheduling transactions listed + above. The transport protocol defines how the iCalendar objects are + sent between the sender and receiver. This document focuses on the + application protocol. Binding documents such as [iMIP] focus on the + transport protocol. + + The connection between sender and receiver in the diagram below + refers to the application protocol. The iCalendar objects passed + from the sender to the receiver are presented in Section 3, + "Application Protocol Elements". + + +----------+ +----------+ + | | iTIP | | + | Sender |<-------------->| Receiver | + | | | | + +----------+ +----------+ + + There are several variations of this diagram in which the sender and + receiver take on various roles of a "Calendar User Agent" (CUA) or a + "Calendar Service" (CS). + + The architecture of iTIP is depicted in the diagram below. An + application written to this specification may work with bindings for + the store-and-forward transport, the real-time transport, or both. + Also note that iTIP could be bound to other transports. + + + +Daboo Standards Track [Page 9] + +RFC 5546 iTIP December 2009 + + + +--------------------------------------------------------+ + | iTIP Protocol | + +--------------------------------------------------------+ + | Transport | + + - - - - - + - - - - - - + - - - - - + + | Real-Time | Store-and-Forward | Others | + +-----------------+--------------------+-----------------+ + +2.1. Application Protocol + + In the iTIP model, an iCalendar object is created and managed by an + "Organizer". The "Organizer" interacts with other CUs by sending one + or more of the iTIP messages listed above. "Attendees" use the + "REPLY" method to communicate their status. "Attendees" do not make + direct changes to the master iCalendar object. They can, however, + use the "COUNTER" method to suggest changes to the "Organizer". In + any case, the "Organizer" has complete control over the master + iCalendar object. + +2.1.1. Scheduling State + + There are two distinct states relevant to iCalendar objects used in + scheduling: the overall state of the iCalendar object and the state + associated with an "Attendee" in that iCalendar object. + + The state of an iCalendar object is defined by the "STATUS" property + and is controlled by the "Organizer." There is no default value for + the "STATUS" property. The "Organizer" sets the "STATUS" property to + the appropriate value for each iCalendar object. + + The state of a particular "Attendee" relative to an iCalendar object + used for scheduling is defined by the "PARTSTAT" parameter in the + "ATTENDEE" property for each "Attendee". When an "Organizer" issues + the initial iCalendar object, "Attendee" status is typically unknown. + The "Organizer" specifies this by setting the "PARTSTAT" parameter to + "NEEDS-ACTION". Each "Attendee" modifies their "ATTENDEE" property + "PARTSTAT" parameter to an appropriate value as part of a "REPLY" + message sent back to the "Organizer". + +2.1.2. Delegation + + Delegation is defined as the process by which an "Attendee" grants + another CU (or several CUs) the right to attend on their behalf. The + "Organizer" is made aware of this change because the delegating + "Attendee" informs the "Organizer". These steps are detailed in the + "REQUEST" method sections for the appropriate components. + + + + + +Daboo Standards Track [Page 10] + +RFC 5546 iTIP December 2009 + + +2.1.3. Acting on Behalf of Other Calendar Users + + In many organizations, one user will act on behalf of another to + organize and/or respond to meeting requests. iTIP provides two + mechanisms that support these activities. + + First, the "Organizer" is treated as a special entity, separate from + "Attendees". All responses from "Attendees" flow to the "Organizer", + making it easy to separate a "Calendar User" organizing a meeting + from "Calendar Users" attending the meeting. Additionally, iCalendar + provides descriptive roles for each "Attendee". For instance, a role + of "chair" may be ascribed to one or more "Attendees". The "chair" + and the "Organizer" may or may not be the same "Calendar User". This + maps well to scenarios where an assistant may manage meeting + logistics for another individual who chairs a meeting. + + Second, a "SENT-BY" parameter may be specified in either the + "Organizer" or "Attendee" properties. When specified, the "SENT-BY" + parameter indicates that the responding CU acted on behalf of the + specified "Attendee" or "Organizer". + +2.1.4. Component Revisions + + The "SEQUENCE" property is used by the "Organizer" to indicate + revisions to the calendar component. When the "Organizer" makes + changes to one of the following properties, the sequence number MUST + be incremented: + + o "DTSTART" + + o "DTEND" + + o "DURATION" + + o "DUE" + + o "RRULE" + + o "RDATE" + + o "EXDATE" + + o "STATUS" + + In addition, changes made by the "Organizer" to other properties MAY + also require the sequence number to be incremented. The "Organizer" + CUA MUST increment the sequence number whenever it makes changes to + properties in the calendar component that the "Organizer" deems will + + + +Daboo Standards Track [Page 11] + +RFC 5546 iTIP December 2009 + + + jeopardize the validity of the participation status of the + "Attendees". For example, changing the location of a meeting from + one location to another distant location could effectively impact the + participation status of the "Attendees". + + Depending on the "METHOD", the "SEQUENCE" property MUST follow these + rules in the context of iTIP: + + o For the "PUBLISH" and "REQUEST" methods, the "SEQUENCE" property + value is incremented according to the rules stated above. + + o The "SEQUENCE" property value MUST be incremented each time the + "Organizer" uses the "ADD" or "CANCEL" methods. + + o The "SEQUENCE" property value MUST NOT be incremented when using + "REPLY", "REFRESH", "COUNTER", "DECLINECOUNTER", or when sending a + delegation "REQUEST". + + In some circumstances, the "Organizer" may not have received + responses to the final revision sent out. In this situation, the + "Organizer" may wish to send an update "REQUEST" and set "RSVP=TRUE" + for all "Attendees" so that current responses can be collected. + + The value of the "SEQUENCE" property contained in a response from an + "Attendee" may not always match the "Organizer's" revision. + Implementations may choose to have the CUA indicate to the CU that + the response is to an iCalendar object that has been revised, and + allow the CU to decide whether or not to accept the response. + + Whilst a change in sequence number is indicative of a significant + change to a previously scheduled item, "Attendee" CUAs SHOULD NOT + rely solely on a change in sequence number as a means of detecting a + significant change. Instead, CUAs SHOULD compare the old and new + versions of the calendar components, determine the exact nature of + the changes, and make decisions -- possibly based on "Calendar User" + preferences -- as to whether the user needs to be explicitly informed + of the change. + +2.1.5. Message Sequencing + + CUAs that handle the iTIP application protocol must often correlate a + component in a calendar store with a component received in the iTIP + message. For example, an event may be updated with a later revision + of the same event. To accomplish this, a CUA must correlate the + version of the event already in its calendar store with the version + sent in the iTIP message. In addition to this correlation, there are + several factors that can cause iTIP messages to arrive in an + unexpected order. That is, an "Organizer" could receive a reply to + + + +Daboo Standards Track [Page 12] + +RFC 5546 iTIP December 2009 + + + an earlier revision of a component after receiving a reply to a later + revision. + + To maximize interoperability and to handle messages that arrive in an + unexpected order, use the following rules: + + 1. The primary key for referencing a particular iCalendar component + is the "UID" property value. To reference an instance of a + recurring component, the primary key is composed of the "UID" and + the "RECURRENCE-ID" properties. + + 2. The secondary key for referencing a component is the "SEQUENCE" + property value. For components where the "UID" and + "RECURRENCE-ID" property values are the same, the component with + the highest numeric value for the "SEQUENCE" property obsoletes + all other revisions of the component with lower values. + + 3. "Attendees" send "REPLY" messages to the "Organizer". For + replies where the "UID" and "RECURRENCE-ID" property values are + the same, the value of the "SEQUENCE" property indicates the + revision of the component to which the "Attendee" is replying. + The reply with the highest numeric value for the "SEQUENCE" + property obsoletes all other replies with lower values. + + 4. In situations where the "UID", "RECURRENCE-ID", and "SEQUENCE" + property values match, the "DTSTAMP" property is used as the tie- + breaker. The component with the latest "DTSTAMP" overrides all + others. Similarly, for "Attendee" responses where the "UID", + "RECURRENCE-ID", and "SEQUENCE" property values match, the + response with the latest "DTSTAMP" overrides all others. + + Hence, CUAs will need to persist the following component properties + in order to correctly process iTIP messages: "UID", "RECURRENCE-ID", + "SEQUENCE", and "DTSTAMP". Furthermore, for each "ATTENDEE" property + of a component, "Organizer" CUAs will need to persist the "SEQUENCE" + and "DTSTAMP" property values associated with the "Attendee's" last + response, so that any earlier responses from an "Attendee" that are + received out of order (e.g., due to a delay in the transport) can be + correctly discarded. + +3. Application Protocol Elements + + iTIP messages are "text/calendar" MIME entities that contain + calendaring and scheduling information. The particular type of + iCalendar message is referred to as the "method type". Each method + type is identified by a "METHOD" property specified as part of the + "text/calendar" content type. The table below shows various + + + + +Daboo Standards Track [Page 13] + +RFC 5546 iTIP December 2009 + + + combinations of calendar components and the method types that this + specification supports. + + +----------------+--------+-------+----------+-----------+ + | | VEVENT | VTODO | VJOURNAL | VFREEBUSY | + +----------------+--------+-------+----------+-----------+ + | PUBLISH | Yes | Yes | Yes | Yes | + | REQUEST | Yes | Yes | No | Yes | + | REFRESH | Yes | Yes | No | No | + | CANCEL | Yes | Yes | Yes | No | + | ADD | Yes | Yes | Yes | No | + | REPLY | Yes | Yes | No | Yes | + | COUNTER | Yes | Yes | No | No | + | DECLINECOUNTER | Yes | Yes | No | No | + +----------------+--------+-------+----------+-----------+ + + Each method type is defined in terms of its associated components and + properties. Some components and properties are required, some are + optional, and others are excluded. The restrictions are expressed in + this document using a simple "restriction table". The first column + indicates the name of a component or property. Properties of the + iCalendar object are not indented. Properties of a component are + indented. The second column (the "Presence" column) indicates + whether or not a component or property should be present and, if + present, how many times it can occur. The third column contains + comments for further clarification. + + The presence column uses the following values to assert whether a + property is required or optional, and the number of times it may + appear in the iCalendar object. + + +----------------+--------------------------------------------------+ + | Presence Value | Description | + +----------------+--------------------------------------------------+ + | 1 | One instance MUST be present. | + | 1+ | At least one instance MUST be present. | + | 0 | Instances of this property MUST NOT be present. | + | 0+ | Multiple instances MAY be present. | + | 0 or 1 | Up to 1 instance of this property MAY be | + | | present. | + +----------------+--------------------------------------------------+ + + The tables also call out "IANA-PROPERTY", "X-PROPERTY", "IANA- + COMPONENT", and "X-COMPONENT" to show where registered and + experimental property and component extensions can appear. The + tables do not lay out the restrictions of property parameters. Those + restrictions are defined in [RFC5545]. + + + + +Daboo Standards Track [Page 14] + +RFC 5546 iTIP December 2009 + + +3.1. Common Component Restriction Tables + +3.1.1. VCALENDAR + + The restriction table below applies to properties of the iCalendar + object. That is, the properties at the outermost scope. + + +-----------------------------------------------------+ + | Constraints for Properties in a VCALENDAR Component | + +-----------------------------------------------------+ + + +--------------------+----------+--------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+--------------------+ + | CALSCALE | 0 or 1 | | + | PRODID | 1 | | + | VERSION | 1 | Value MUST be 2.0. | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + +--------------------+----------+--------------------+ + +3.1.2. VTIMEZONE + + "VTIMEZONE" components may be referred to by other components via a + "TZID" parameter on a "DATETIME" value type. The property + restrictions in the table below apply to any "VTIMEZONE" component in + an iTIP message. + + +--------------------------------------+ + | Constraints for VTIMEZONE Components | + +--------------------------------------+ + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 15] + +RFC 5546 iTIP December 2009 + + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to timezone. | + | DAYLIGHT | 0+ | MUST be one or more of either | + | | | STANDARD or DAYLIGHT. | + | COMMENT | 0+ | | + | DTSTART | 1 | MUST be local time format. | + | RDATE | 0+ | | + | RRULE | 0 or 1 | | + | TZNAME | 0+ | | + | TZOFFSETFROM | 1 | | + | TZOFFSETTO | 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | LAST-MODIFIED | 0 or 1 | | + | STANDARD | 0+ | MUST be one or more of either | + | | | STANDARD or DAYLIGHT. | + | COMMENT | 0+ | | + | DTSTART | 1 | MUST be local time format. | + | RDATE | 0+ | If present, RRULE MUST NOT be | + | | | present. | + | RRULE | 0 or 1 | If present, RDATE MUST NOT be | + | | | present. | + | TZNAME | 0+ | | + | TZOFFSETFROM | 1 | | + | TZOFFSETTO | 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | TZID | 1 | | + | TZURL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + +--------------------+----------+-----------------------------------+ + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 16] + +RFC 5546 iTIP December 2009 + + +3.1.3. VALARM + + The property restrictions in the table below apply to any "VALARM" + component in an iTIP message. + + +-----------------------------------+ + | Constraints for VALARM Components | + +-----------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | VALARM | 0+ | | + | ACTION | 1 | | + | ATTACH | 0+ | | + | ATTENDEE | 0+ | | + | DESCRIPTION | 0 or 1 | | + | DURATION | 0 or 1 | If present, REPEAT MUST be | + | | | present. | + | REPEAT | 0 or 1 | If present, DURATION MUST be | + | | | present. | + | SUMMARY | 0 or 1 | | + | TRIGGER | 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + +--------------------+----------+-----------------------------------+ + +3.2. Methods for VEVENT Calendar Components + + This section defines the property set restrictions for the method + types that are applicable to the "VEVENT" calendar component. Each + method is defined using a table that clarifies the property + constraints that define the particular method. + + The following summarizes the methods that are defined for the + "VEVENT" calendar component. + + + + + + + + + + + + + + + +Daboo Standards Track [Page 17] + +RFC 5546 iTIP December 2009 + + + +----------------+--------------------------------------------------+ + | Method | Description | + +----------------+--------------------------------------------------+ + | PUBLISH | Post notification of an event. Used primarily | + | | as a method of advertising the existence of an | + | | event. | + | | | + | REQUEST | Make a request for an event. This is an | + | | explicit invitation to one or more Attendees. | + | | Event requests are also used to update or change | + | | an existing event. Clients that cannot handle | + | | REQUEST MAY degrade the event to view it as a | + | | PUBLISH. | + | | | + | REPLY | Reply to an event request. Clients may set | + | | their status (PARTSTAT) to ACCEPTED, DECLINED, | + | | TENTATIVE, or DELEGATED. | + | | | + | ADD | Add one or more instances to an existing event. | + | | | + | CANCEL | Cancel one or more instances of an existing | + | | event. | + | | | + | REFRESH | A request is sent to an Organizer by an Attendee | + | | asking for the latest version of an event to be | + | | resent to the requester. | + | | | + | COUNTER | Counter a REQUEST with an alternative proposal. | + | | Sent by an Attendee to the Organizer. | + | | | + | DECLINECOUNTER | Decline a counter proposal. Sent to an Attendee | + | | by the Organizer. | + +----------------+--------------------------------------------------+ + +3.2.1. PUBLISH + + The "PUBLISH" method in a "VEVENT" calendar component is an + unsolicited posting of an iCalendar object. Any CU may add published + components to their calendar. The "Organizer" MUST be present in a + published iCalendar component. "Attendees" MUST NOT be present. Its + expected usage is for encapsulating an arbitrary event as an + iCalendar object. The "Organizer" may subsequently update (with + another "PUBLISH" method), add instances to (with an "ADD" method), + or cancel (with a "CANCEL" method) a previously published "VEVENT" + calendar component. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + +Daboo Standards Track [Page 18] + +RFC 5546 iTIP December 2009 + + + +----------------------------------------------+ + | Constraints for a METHOD:PUBLISH of a VEVENT | + +----------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST equal PUBLISH. | + | | | | + | VEVENT | 1+ | | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + | ORGANIZER | 1 | | + | SUMMARY | 1 | Can be null. | + | UID | 1 | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | SEQUENCE | 0 or 1 | MUST be present if value is | + | | | greater than 0; MAY be present if | + | | | 0. | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0 or 1 | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null. | + | DTEND | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DTEND MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RDATE | 0+ | | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MAY be one of | + | | | TENTATIVE/CONFIRMED/CANCELLED. | + | TRANSP | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + + + +Daboo Standards Track [Page 19] + +RFC 5546 iTIP December 2009 + + + | ATTENDEE | 0 | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0+ | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VTODO | 0 | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + +--------------------+----------+-----------------------------------+ + +3.2.2. REQUEST + + The "REQUEST" method in a "VEVENT" component provides the following + scheduling functions: + + o Invite "Attendees" to an event. + + o Reschedule an existing event. + + o Response to a "REFRESH" request. + + o Update the details of an existing event, without rescheduling it. + + o Update the status of "Attendees" of an existing event, without + rescheduling it. + + o Reconfirm an existing event, without rescheduling it. + + o Forward a "VEVENT" to another uninvited CU. + + o For an existing "VEVENT" calendar component, delegate the role of + "Attendee" to another CU. + + o For an existing "VEVENT" calendar component, change the role of + "Organizer" to another CU. + + The "Organizer" originates the "REQUEST". The recipients of the + "REQUEST" method are the CUs invited to the event, the "Attendees". + "Attendees" use the "REPLY" method to convey attendance status to the + "Organizer". + + + +Daboo Standards Track [Page 20] + +RFC 5546 iTIP December 2009 + + + The "UID" and "SEQUENCE" properties are used to distinguish the + various uses of the "REQUEST" method. If the "UID" property value in + the "REQUEST" is not found on the recipient's calendar, then the + "REQUEST" is for a new "VEVENT" calendar component. If the "UID" + property value is found on the recipient's calendar, then the + "REQUEST" is for a rescheduling, an update, or a reconfirmation of + the "VEVENT" calendar component. + + For the "REQUEST" method, multiple "VEVENT" components in a single + iCalendar object are only permitted for components with the same + "UID" property. That is, a series of recurring events may have + instance-specific information. In this case, multiple "VEVENT" + components are needed to express the entire series. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +----------------------------------------------+ + | Constraints for a METHOD:REQUEST of a VEVENT | + +----------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REQUEST. | + | | | | + | VEVENT | 1+ | All components MUST have the same | + | | | UID. | + | ATTENDEE | 1+ | | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 0 or 1 | MUST be present if value is | + | | | greater than 0; MAY be present if | + | | | 0. | + | SUMMARY | 1 | Can be null. | + | UID | 1 | | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null. | + | DTEND | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + + + + + +Daboo Standards Track [Page 21] + +RFC 5546 iTIP December 2009 + + + | DURATION | 0 or 1 | If present, DTEND MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | REQUEST-STATUS | 0 | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MAY be one of | + | | | TENTATIVE/CONFIRMED. | + | TRANSP | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VTODO | 0 | | + +--------------------+----------+-----------------------------------+ + +3.2.2.1. Rescheduling an Event + + The "REQUEST" method may be used to reschedule an event. A + rescheduled event involves a change to the existing event in terms of + its time or recurrence intervals and possibly the location or + description. If the recipient CUA of a "REQUEST" method finds that + the "UID" property value already exists on the calendar but that the + "SEQUENCE" (or "DTSTAMP") property value in the "REQUEST" method is + greater than the value for the existing event, then the "REQUEST" + method describes a rescheduling of the event. + + + +Daboo Standards Track [Page 22] + +RFC 5546 iTIP December 2009 + + +3.2.2.2. Updating or Reconfirmation of an Event + + The "REQUEST" method may be used to update or reconfirm an event. An + update to an existing event does not involve changes to the time or + recurrence intervals, and might not involve a change to the location + or description for the event. If the recipient CUA of a "REQUEST" + method finds that the "UID" property value already exists on the + calendar and that the "SEQUENCE" property value in the "REQUEST" is + the same as the value for the existing event, then the "REQUEST" + method describes an update of the event details, but not a + rescheduling of the event. + + The update "REQUEST" method is the appropriate response to a + "REFRESH" method sent from an "Attendee" to the "Organizer" of an + event. + + The "Organizer" of an event may also send unsolicited "REQUEST" + methods. The unsolicited "REQUEST" methods may be used to update the + details of the event without rescheduling it, to update the + "PARTSTAT" parameter of "Attendees", or to reconfirm the event. + +3.2.2.3. Delegating an Event to Another CU + + Some calendar and scheduling systems allow "Attendees" to delegate + their presence at an event to another "Calendar User". iTIP supports + this concept using the following workflow. Any "Attendee" may + delegate their right to participate in a calendar "VEVENT" to another + CU. The implication is that the delegate participates in lieu of the + original "Attendee", NOT in addition to the "Attendee". The + delegator MUST notify the "Organizer" of this action using the steps + outlined below. Implementations may support or restrict delegation + as they see fit. For instance, some implementations may restrict a + delegate from delegating a "REQUEST" to another CU. + + The "Delegator" of an event forwards the existing "REQUEST" to the + "Delegate". The "REQUEST" method MUST include an "ATTENDEE" property + with the calendar address of the "Delegate". The "Delegator" MUST + also send a "REPLY" method to the "Organizer" with the "Delegator's" + "ATTENDEE" property "PARTSTAT" parameter value set to "DELEGATED". + In addition, the "DELEGATED-TO" parameter MUST be included with the + calendar address of the "Delegate". Also, a new "ATTENDEE" property + for the "Delegate" MUST be included and must specify the calendar + user address set in the "DELEGATED-TO" parameter, as above. + + In response to the request, the "Delegate" MUST send a "REPLY" method + to the "Organizer", and optionally to the "Delegator". The "REPLY" + method SHOULD include the "ATTENDEE" property with the "DELEGATED- + FROM" parameter value of the "Delegator's" calendar address. + + + +Daboo Standards Track [Page 23] + +RFC 5546 iTIP December 2009 + + + The "Delegator" may continue to receive updates to the event even + though they will not be attending. This is accomplished by the + "Delegator" setting their "role" attribute to "NON-PARTICIPANT" in + the "REPLY" to the "Organizer". + +3.2.2.4. Changing the Organizer + + The situation may arise where the "Organizer" of a "VEVENT" is no + longer able to perform the "Organizer" role and abdicates without + passing on the "Organizer" role to someone else. When this occurs, + the "Attendees" of the "VEVENT" may use out-of-band mechanisms to + communicate the situation and agree upon a new "Organizer". The new + "Organizer" should then send out a new "REQUEST" with a modified + version of the "VEVENT" in which the "SEQUENCE" number has been + incremented and the "ORGANIZER" property has been changed to the new + "Organizer". + +3.2.2.5. Sending on Behalf of the Organizer + + There are a number of scenarios that support the need for a "Calendar + User" to act on behalf of the "Organizer" without explicit role + changing. This might be the case if the CU designated as "Organizer" + is sick or unable to perform duties associated with that function. + In these cases, iTIP supports the notion of one CU acting on behalf + of another. Using the "SENT-BY" parameter, a "Calendar User" could + send an updated "VEVENT" "REQUEST". In the case where one CU sends + on behalf of another CU, the "Attendee" responses are still directed + back towards the CU designated as "Organizer". + +3.2.2.6. Forwarding to an Uninvited CU + + An "Attendee" invited to a "VEVENT" calendar component may send the + "VEVENT" calendar component to another new CU not previously + associated with the "VEVENT" calendar component. The current + "Attendee" invited to the "VEVENT" calendar component does this by + forwarding the original "REQUEST" method to the new CU. The new CU + can send a "REPLY" to the "Organizer" of the "VEVENT" calendar + component. The reply contains an "ATTENDEE" property for the new CU. + + The "Organizer" ultimately decides whether or not the new CU becomes + part of the event and is not obligated to do anything with a "REPLY" + from a new (uninvited) CU. If the "Organizer" does not want the new + CU to be part of the event, the new "ATTENDEE" property is not added + to the "VEVENT" calendar component. The "Organizer" MAY send the CU + a "CANCEL" message to indicate that they will not be added to the + event. If the "Organizer" decides to add the new CU, the new + "ATTENDEE" property is added to the "VEVENT" calendar component. + Furthermore, the "Organizer" is free to change any "ATTENDEE" + + + +Daboo Standards Track [Page 24] + +RFC 5546 iTIP December 2009 + + + property parameter from the values supplied by the new CU to + something the "Organizer" considers appropriate. The "Organizer" + SHOULD send the new CU a "REQUEST" message to inform them that they + have been added. + + When forwarding a "REQUEST" to another CU, the forwarding "Attendee" + MUST NOT make changes to the original message. + +3.2.2.7. Updating Attendee Status + + The "Organizer" of an event may also request updated status from one + or more "Attendees". The "Organizer" sends a "REQUEST" method to the + "Attendee" and sets the "ATTENDEE;RSVP=TRUE" property parameter. The + "SEQUENCE" property for the event is not changed from its previous + value. A recipient will determine that the only change in the + "REQUEST" is that their "RSVP" property parameter indicates a request + for updated status. The recipient SHOULD respond with a "REPLY" + method indicating their current status with respect to the "REQUEST". + +3.2.3. REPLY + + The "REPLY" method in a "VEVENT" calendar component is used to + respond (e.g., accept or decline) to a "REQUEST" or to reply to a + delegation "REQUEST". When used to provide a delegation response, + the "Delegator" SHOULD include the calendar address of the "Delegate" + on the "DELEGATED-TO" property parameter of the "Delegator's" + "ATTENDEE" property. The "Delegate" SHOULD include the calendar + address of the "Delegator" on the "DELEGATED-FROM" property parameter + of the "Delegate's" "ATTENDEE" property. + + The "REPLY" method is also used when processing of a "REQUEST" fails. + Depending on the value of the "REQUEST-STATUS" property, no + scheduling action may have been performed. + + The "Organizer" of an event may receive the "REPLY" method from a CU + not in the original "REQUEST". For example, a "REPLY" may be + received from a "Delegate" to an event. In addition, the "REPLY" + method may be received from an unknown CU (a "Party Crasher"). This + uninvited "Attendee" may be accepted, or the "Organizer" may cancel + the event for the uninvited "Attendee" by sending a "CANCEL" method + to the uninvited "Attendee". + + An "Attendee" MAY include a message to the "Organizer" using the + "COMMENT" property. For example, if the user indicates tentative + acceptance and wants to let the "Organizer" know why, the reason can + be expressed in the "COMMENT" property value. + + + + + +Daboo Standards Track [Page 25] + +RFC 5546 iTIP December 2009 + + + The "Organizer" may also receive a "REPLY" from one CU on behalf of + another. Like the scenario enumerated above for the "Organizer", + "Attendees" may have another CU respond on their behalf. This is + done using the "SENT-BY" parameter. + + The optional properties listed in the table below (those listed as + "0+" or "0 or 1") MUST NOT be changed from those of the original + request. If property changes are desired, the "COUNTER" message must + be used. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +--------------------------------------------+ + | Constraints for a METHOD:REPLY of a VEVENT | + +--------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REPLY. | + | | | | + | VEVENT | 1+ | All components MUST have the same | + | | | UID. | + | ATTENDEE | 1 | MUST be the address of the | + | | | Attendee replying. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | UID | 1 | MUST be the UID of the original | + | | | REQUEST. | + | SEQUENCE | 0 or 1 | If non-zero, MUST be the sequence | + | | | number of the original REQUEST. | + | | | MAY be present if 0. | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | | + | DTEND | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DTSTART | 0 or 1 | | + + + + +Daboo Standards Track [Page 26] + +RFC 5546 iTIP December 2009 + + + | DURATION | 0 or 1 | If present, DTEND MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RDATE | 0+ | | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | REQUEST-STATUS | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | | + | SUMMARY | 0 or 1 | | + | TRANSP | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | | | | + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0 or 1 | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VTODO | 0 | | + +--------------------+----------+-----------------------------------+ + +3.2.4. ADD + + The "ADD" method allows the "Organizer" to add one or more new + instances to an existing "VEVENT" using a single iTIP message without + having to send the entire "VEVENT" with all the existing instance + data, as it would have to do if the "REQUEST" method were used. + + The "UID" must be that of the existing event. If the "UID" property + value in the "ADD" is not found on the recipient's calendar, then the + recipient SHOULD send a "REFRESH" to the "Organizer" in order to be + updated with the latest version of the "VEVENT". If an "Attendee" + implementation does not support the "ADD" method, it should respond + with a "REQUEST-STATUS" value of 3.14 and ask for a "REFRESH". + + + + +Daboo Standards Track [Page 27] + +RFC 5546 iTIP December 2009 + + + When handling an "ADD" message, the "Attendee" treats each component + in the "ADD" message as if it were referenced via an "RDATE" in the + main component. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +------------------------------------------+ + | Constraints for a METHOD:ADD of a VEVENT | + +------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be ADD. | + | | | | + | VEVENT | 1 | | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 1 | MUST be greater than 0. | + | SUMMARY | 1 | Can be null. | + | UID | 1 | MUST match that of the original | + | | | event. | + | ATTACH | 0+ | | + | ATTENDEE | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null. | + | DTEND | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DTEND MUST NOT be | + | | | present. | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | STATUS | 0 or 1 | MAY be one of | + | | | TENTATIVE/CONFIRMED. | + | TRANSP | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + + + +Daboo Standards Track [Page 28] + +RFC 5546 iTIP December 2009 + + + | EXDATE | 0 | | + | RECURRENCE-ID | 0 | | + | REQUEST-STATUS | 0 | | + | RDATE | 0 | | + | RRULE | 0 | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VTODO | 0 | | + | | | | + | VJOURNAL | 0 | | + +--------------------+----------+-----------------------------------+ + +3.2.5. CANCEL + + The "CANCEL" method in a "VEVENT" calendar component is used to send + a cancellation notice of an existing event request to the affected + "Attendees". The message is sent by the "Organizer" of the event. + For a recurring event, either the whole event or instances of an + event may be cancelled. To cancel the complete range of a recurring + event, the "UID" property value for the event MUST be specified and a + "RECURRENCE-ID" MUST NOT be specified in the "CANCEL" method. In + order to cancel an individual instance of the event, the + "RECURRENCE-ID" property value for the event MUST be specified in the + "CANCEL" method. + + There are two options for canceling a sequence of instances of a + recurring "VEVENT" calendar component: + + a. The "RECURRENCE-ID" property for an instance in the sequence MUST + be specified with the "RANGE" property parameter value of + "THISANDFUTURE" to indicate cancellation of the specified + "VEVENT" calendar component and all instances after. + + b. Individual recurrence instances may be cancelled by specifying + multiple "VEVENT" components each with a "RECURRENCE-ID" property + corresponding to one of the instances to be cancelled. + + + + + + +Daboo Standards Track [Page 29] + +RFC 5546 iTIP December 2009 + + + The "Organizer" MUST send a "CANCEL" message to each "Attendee" + affected by the cancellation. This can be done using a single + "CANCEL" message for all "Attendees" or by using multiple messages + with different subsets of the affected "Attendees" in each. + + When a "VEVENT" is cancelled, the "SEQUENCE" property value MUST be + incremented as described in Section 2.1.4. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +---------------------------------------------+ + | Constraints for a METHOD:CANCEL of a VEVENT | + +---------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be CANCEL. | + | | | | + | VEVENT | 1+ | All must have the same UID. | + | ATTENDEE | 0+ | MUST include some or all | + | | | Attendees being removed from the | + | | | event. MUST include some or all | + | | | Attendees if the entire event is | + | | | cancelled. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 1 | | + | UID | 1 | MUST be the UID of the original | + | | | REQUEST. | + | COMMENT | 0+ | | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | | + | DTEND | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DTSTART | 0 or 1 | | + | DURATION | 0 or 1 | If present, DTEND MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PRIORITY | 0 or 1 | | + + + +Daboo Standards Track [Page 30] + +RFC 5546 iTIP December 2009 + + + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MUST be set to CANCELLED to | + | | | cancel the entire event. If | + | | | uninviting specific Attendees, | + | | | then MUST NOT be included. | + | SUMMARY | 0 or 1 | | + | TRANSP | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VTODO | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.2.6. REFRESH + + The "REFRESH" method in a "VEVENT" calendar component is used by + "Attendees" of an existing event to request an updated description + from the event "Organizer". The "REFRESH" method must specify the + "UID" property of the event to update. A recurrence instance of an + event may be requested by specifying the "RECURRENCE-ID" property + corresponding to the associated event. The "Organizer" responds with + the latest description and version of the event. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + + + +Daboo Standards Track [Page 31] + +RFC 5546 iTIP December 2009 + + + +----------------------------------------------+ + | Constraints for a METHOD:REFRESH of a VEVENT | + +----------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REFRESH. | + | | | | + | VEVENT | 1 | | + | ATTENDEE | 1 | MUST be the address of requester. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | UID | 1 | MUST be the UID associated with | + | | | original REQUEST. | + | COMMENT | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | ATTACH | 0 | | + | CATEGORIES | 0 | | + | CLASS | 0 | | + | CONTACT | 0 | | + | CREATED | 0 | | + | DESCRIPTION | 0 | | + | DTEND | 0 | | + | DTSTART | 0 | | + | DURATION | 0 | | + | EXDATE | 0 | | + | GEO | 0 | | + | LAST-MODIFIED | 0 | | + | LOCATION | 0 | | + | PRIORITY | 0 | | + | RDATE | 0 | | + | RELATED-TO | 0 | | + | REQUEST-STATUS | 0 | | + | RESOURCES | 0 | | + | RRULE | 0 | | + | SEQUENCE | 0 | | + | STATUS | 0 | | + | SUMMARY | 0 | | + | TRANSP | 0 | | + | URL | 0 | | + | | | | + + + + +Daboo Standards Track [Page 32] + +RFC 5546 iTIP December 2009 + + + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0+ | | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VTODO | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.2.7. COUNTER + + The "COUNTER" method for a "VEVENT" calendar component is used by an + "Attendee" of an existing event to submit to the "Organizer" a + counter proposal to the event. The "Attendee" sends this message to + the "Organizer" of the event. + + The counter proposal is an iCalendar object consisting of a "VEVENT" + calendar component that provides the complete description of the + alternate event. + + The "Organizer" rejects the counter proposal by sending the + "Attendee" a "DECLINECOUNTER" method. The "Organizer" accepts the + counter proposal by rescheduling the event as described in + Section 3.2.2.1, "Rescheduling an Event". The "Organizer's" CUA + SHOULD send a "REQUEST" message to all "Attendees" affected by any + change triggered by an accepted "COUNTER". + + This method type is an iCalendar object that conforms to the + following property constraints: + + +----------------------------------------------+ + | Constraints for a METHOD:COUNTER of a VEVENT | + +----------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be COUNTER. | + | | | | + | VEVENT | 1 | | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + + + + +Daboo Standards Track [Page 33] + +RFC 5546 iTIP December 2009 + + + | ORGANIZER | 1 | MUST be the Organizer of the | + | | | original event. | + | SEQUENCE | 1 | MUST echo the original SEQUENCE | + | | | number. MUST be present if | + | | | non-zero. MAY be present if | + | | | zero. | + | SUMMARY | 1 | Can be null. | + | UID | 1 | MUST be the UID associated with | + | | | the REQUEST being countered. | + | ATTACH | 0+ | | + | ATTENDEE | 0+ | Can also be used to propose other | + | | | Attendees. | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | | + | DTEND | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DTEND MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | REQUEST-STATUS | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | Value must be one of | + | | | CONFIRMED/TENATIVE/CANCELLED. | + | TRANSP | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + + + +Daboo Standards Track [Page 34] + +RFC 5546 iTIP December 2009 + + + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VTODO | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.2.8. DECLINECOUNTER + + The "DECLINECOUNTER" method in a "VEVENT" calendar component is used + by the "Organizer" of an event to reject a counter proposal submitted + by an "Attendee". The "Organizer" must send the "DECLINECOUNTER" + message to the "Attendee" that sent the "COUNTER" method to the + "Organizer". + + This method type is an iCalendar object that conforms to the + following property constraints: + + +-----------------------------------------------------+ + | Constraints for a METHOD:DECLINECOUNTER of a VEVENT | + +-----------------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be DECLINECOUNTER. | + | | | | + | VEVENT | 1+ | All components MUST have the same | + | | | UID. | + | ATTENDEE | 1+ | MUST for all Attendees. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 1 | MUST echo the original SEQUENCE | + | | | number. | + | UID | 1 | MUST echo original UID. | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null. | + | DTSTART | 0 or 1 | | + | DTEND | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + + + +Daboo Standards Track [Page 35] + +RFC 5546 iTIP December 2009 + + + | DURATION | 0 or 1 | If present, DTEND MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | REQUEST-STATUS | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MAY be one of | + | | | TENTATIVE/CONFIRMED. | + | SUMMARY | 0 or 1 | Can be null. | + | TRANSP | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | | | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VALARM | 0 | | + | VFREEBUSY | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VTODO | 0 | | + +--------------------+----------+-----------------------------------+ + + + + + + + + + + + + + +Daboo Standards Track [Page 36] + +RFC 5546 iTIP December 2009 + + +3.3. Methods for VFREEBUSY Components + + This section defines the property set for the methods that are + applicable to the "VFREEBUSY" calendar component. Each of the + methods is defined using a restriction table. + + This document only addresses the transfer of busy time information. + Applications desiring free time information MUST infer this from + available busy time information. + + The "FREEBUSY" property value MAY include a list of values, separated + by the COMMA character (US-ASCII decimal 44). Alternately, multiple + busy time periods MAY be specified with multiple instances of the + "FREEBUSY" property. Both forms MUST be supported by implementations + conforming to this document. Duplicate busy time periods SHOULD NOT + be specified in an iCalendar object. However, two different busy + time periods MAY overlap. + + "FREEBUSY" properties SHOULD be sorted such that their values are in + ascending order, based on the start time and then the end time, with + the earliest periods first. For example, today's busy time + information should appear after yesterday's busy time information. + And the busy time for this half-hour should appear after the busy + time for earlier today. Busy time periods can also span a day + boundary. + + The following summarizes the methods that are defined for the + "VFREEBUSY" calendar component. + + +---------+-------------------------------------+ + | Method | Description | + +---------+-------------------------------------+ + | PUBLISH | Publish unsolicited busy time data. | + | | | + | REQUEST | Request busy time data. | + | | | + | REPLY | Reply to a busy time request. | + +---------+-------------------------------------+ + +3.3.1. PUBLISH + + The "PUBLISH" method in a "VFREEBUSY" calendar component is used to + publish busy time data. The method may be sent from one CU to any + other. The purpose of the method is to provide a way to send + unsolicited busy time data. That is, the busy time data is not being + sent as a "REPLY" to the receipt of a "REQUEST" method. + + + + + +Daboo Standards Track [Page 37] + +RFC 5546 iTIP December 2009 + + + The "ORGANIZER" property MUST be specified in the busy time + information. The value is the CU address of the originator of the + busy time information. + + The busy time information within the iCalendar object MAY be grouped + into more than one "VFREEBUSY" calendar component. This capability + allows busy time periods to be grouped according to some common + periodicity, such as a calendar week, month, or year. In this case, + each "VFREEBUSY" calendar component MUST include the "ORGANIZER", + "DTSTART", and "DTEND" properties in order to specify the source of + the busy time information and the date and time interval over which + the busy time information covers. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 38] + +RFC 5546 iTIP December 2009 + + + +-------------------------------------------------+ + | Constraints for a METHOD:PUBLISH of a VFREEBUSY | + +-------------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be PUBLISH. | + | | | | + | VFREEBUSY | 1+ | | + | DTSTAMP | 1 | | + | DTSTART | 1 | DateTime values must be in UTC. | + | DTEND | 1 | DateTime values must be in UTC. | + | FREEBUSY | 0+ | MUST be BUSYTIME. Multiple | + | | | instances are allowed. Multiple | + | | | instances SHOULD be sorted in | + | | | ascending order. | + | ORGANIZER | 1 | MUST contain the address of | + | | | originator of busy time data. | + | UID | 1 | | + | COMMENT | 0+ | | + | CONTACT | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | URL | 0 or 1 | Specifies busy time URL. | + | ATTENDEE | 0 | | + | DURATION | 0 | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0 | | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VTODO | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VTIMEZONE | 0 | | + +--------------------+----------+-----------------------------------+ + + + + + + + + + +Daboo Standards Track [Page 39] + +RFC 5546 iTIP December 2009 + + +3.3.2. REQUEST + + The "REQUEST" method in a "VFREEBUSY" calendar component is used to + ask a "Calendar User" for their busy time information. The request + may be for a busy time information bounded by a specific date and + time interval. + + This message only permits requests for busy time information. The + message is sent from a "Calendar User" requesting the busy time + information of one or more intended recipients. + + If the originator of the "REQUEST" method is not authorized to make a + busy time request on the recipient's calendar system, then an + exception message SHOULD be returned in a "REPLY" method, but no busy + time data need be returned. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 40] + +RFC 5546 iTIP December 2009 + + + +-------------------------------------------------+ + | Constraints for a METHOD:REQUEST of a VFREEBUSY | + +-------------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REQUEST. | + | | | | + | VFREEBUSY | 1 | | + | ATTENDEE | 1+ | Contains the calendar user | + | | | addresses of the "Calendar Users" | + | | | whose freebusy is being | + | | | requested. | + | DTEND | 1 | DateTime values must be in UTC. | + | DTSTAMP | 1 | | + | DTSTART | 1 | DateTime values must be in UTC. | + | ORGANIZER | 1 | MUST be the request originator's | + | | | address. | + | UID | 1 | | + | COMMENT | 0+ | | + | CONTACT | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | FREEBUSY | 0 | | + | DURATION | 0 | | + | REQUEST-STATUS | 0 | | + | URL | 0 | | + | | | | + | VALARM | 0 | | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VTODO | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VTIMEZONE | 0 | | + +--------------------+----------+-----------------------------------+ + + + + + + + + + +Daboo Standards Track [Page 41] + +RFC 5546 iTIP December 2009 + + +3.3.3. REPLY + + The "REPLY" method in a "VFREEBUSY" calendar component is used to + respond to a busy time request. The method is sent by the recipient + of a busy time request to the originator of the request. + + The "REPLY" method may also be used to respond to an unsuccessful + "REQUEST" method. Depending on the "REQUEST-STATUS" value, no busy + time information may be returned. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 42] + +RFC 5546 iTIP December 2009 + + + +-----------------------------------------------+ + | Constraints for a METHOD:REPLY of a VFREEBUSY | + +-----------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REPLY. | + | | | | + | VFREEBUSY | 1 | | + | ATTENDEE | 1 | MUST be the address of the | + | | | Attendee replying. | + | DTSTAMP | 1 | | + | DTEND | 1 | DateTime values must be in UTC. | + | DTSTART | 1 | DateTime values must be in UTC. | + | FREEBUSY | 0+ | MUST be BUSYTIME. Multiple | + | | | instances are allowed. Multiple | + | | | instances SHOULD be sorted in | + | | | ascending order. | + | ORGANIZER | 1 | MUST be the request originator's | + | | | address. | + | UID | 1 | MUST be the UID of the original | + | | | REQUEST. | + | COMMENT | 0+ | | + | CONTACT | 0 or 1 | | + | REQUEST-STATUS | 0+ | | + | URL | 0 or 1 | Specifies busy time URL. | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | DURATION | 0 | | + | SEQUENCE | 0 | | + | | | | + | VALARM | 0 | | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VTODO | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VTIMEZONE | 0 | | + +--------------------+----------+-----------------------------------+ + + + + + + +Daboo Standards Track [Page 43] + +RFC 5546 iTIP December 2009 + + +3.4. Methods for VTODO Components + + This section defines the property set for the methods that are + applicable to the "VTODO" calendar component. Each of the methods is + defined using a restriction table that specifies the property + constraints that define the particular method. + + The following summarizes the methods that are defined for the "VTODO" + calendar component. + + +----------------+--------------------------------------------------+ + | Method | Description | + +----------------+--------------------------------------------------+ + | PUBLISH | Post notification of a VTODO. Used primarily as | + | | a method of advertising the existence of a | + | | VTODO. | + | | | + | REQUEST | Assign a VTODO. This is an explicit assignment | + | | to one or more Calendar Users. The REQUEST | + | | method is also used to update or change an | + | | existing VTODO. Clients that cannot handle | + | | REQUEST MAY degrade the method to treat it as a | + | | PUBLISH. | + | | | + | REPLY | Reply to a VTODO request. Attendees MAY set | + | | PARTSTAT to ACCEPTED, DECLINED, TENTATIVE, | + | | DELEGATED, PARTIAL, and COMPLETED. | + | | | + | ADD | Add one or more instances to an existing to-do. | + | | | + | CANCEL | Cancel one or more instances of an existing | + | | to-do. | + | | | + | REFRESH | A request sent to a VTODO Organizer asking for | + | | the latest version of a VTODO. | + | | | + | COUNTER | Counter a REQUEST with an alternative proposal. | + | | | + | DECLINECOUNTER | Decline a counter proposal by an Attendee. | + +----------------+--------------------------------------------------+ + +3.4.1. PUBLISH + + The "PUBLISH" method in a "VTODO" calendar component has no + associated response. It is simply a posting of an iCalendar object + that may be added to a calendar. It MUST have an "Organizer". It + MUST NOT have "Attendees". Its expected usage is for encapsulating + an arbitrary "VTODO" calendar component as an iCalendar object. The + + + +Daboo Standards Track [Page 44] + +RFC 5546 iTIP December 2009 + + + "Organizer" MAY subsequently update (with another "PUBLISH" method), + add instances to (with an "ADD" method), or cancel (with a "CANCEL" + method) a previously published "VTODO" calendar component. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +---------------------------------------------+ + | Constraints for a METHOD:PUBLISH of a VTODO | + +---------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be PUBLISH. | + | | | | + | VTODO | 1+ | | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + | ORGANIZER | 1 | | + | PRIORITY | 1 | | + | SEQUENCE | 0 or 1 | MUST be present if value is | + | | | greater than 0; MAY be present if | + | | | 0. | + | SUMMARY | 1 | Can be null. | + | UID | 1 | | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | COMPLETED | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null. | + | DUE | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DUE MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PERCENT-COMPLETE | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + + + +Daboo Standards Track [Page 45] + +RFC 5546 iTIP December 2009 + + + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MAY be one of | + | | | COMPLETED/NEEDS-ACTION/ | + | | | IN-PROCESS/CANCELLED. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | ATTENDEE | 0 | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VEVENT | 0 | | + | | | | + | VJOURNAL | 0 | | + +--------------------+----------+-----------------------------------+ + +3.4.2. REQUEST + + The "REQUEST" method in a "VTODO" calendar component provides the + following scheduling functions: + + o Assign a to-do to one or more "Calendar Users". + + o Reschedule an existing to-do. + + o Update the details of an existing to-do, without rescheduling it. + + o Update the completion status of "Attendees" of an existing to-do, + without rescheduling it. + + o Reconfirm an existing to-do, without rescheduling it. + + o Delegate/reassign an existing to-do to another "Calendar User". + + The assigned "Calendar Users" are identified in the "VTODO" calendar + component by individual "ATTENDEE;ROLE=REQ-PARTICIPANT" property + value sequences. + + + +Daboo Standards Track [Page 46] + +RFC 5546 iTIP December 2009 + + + Typically, the originator of a "REQUEST" is the "Organizer" of the + to-do, and the recipient of a "REQUEST" is the "Calendar User" + assigned the to-do. The "Attendee" uses the "REPLY" method to convey + their acceptance and completion status to the "Organizer" of the + "REQUEST". + + The "UID", "SEQUENCE", and "DTSTAMP" properties are used to + distinguish the various uses of the "REQUEST" method. If the "UID" + property value in the "REQUEST" is not found on the recipient's + calendar, then the "REQUEST" is for a new to-do. If the "UID" + property value is found on the recipient's calendar, then the + "REQUEST" is a rescheduling, an update, or a reconfirmation of the + "VTODO" calendar object. + + If the "Organizer" of the "REQUEST" method is not authorized to make + a to-do request on the "Attendee's" calendar system, then an + exception is returned in the "REQUEST-STATUS" property of a + subsequent "REPLY" method, but no scheduling action is performed. + + For the "REQUEST" method, multiple "VTODO" components in a single + iCalendar object are only permitted for components with the same + "UID" property. That is, a series of recurring events may have + instance-specific information. In this case, multiple "VTODO" + components are needed to express the entire series. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +---------------------------------------------+ + | Constraints for a METHOD:REQUEST of a VTODO | + +---------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REQUEST. | + | | | | + | VTODO | 1+ | All components must have the same | + | | | UID. | + | ATTENDEE | 1+ | | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + | ORGANIZER | 1 | | + | PRIORITY | 1 | | + | SEQUENCE | 0 or 1 | MUST be present if value is | + | | | greater than 0; MAY be present if | + | | | 0. | + | SUMMARY | 1 | Can be null. | + + + +Daboo Standards Track [Page 47] + +RFC 5546 iTIP December 2009 + + + | UID | 1 | | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | COMPLETED | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null | + | DUE | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DUE MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PERCENT-COMPLETE | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MAY be one of | + | | | COMPLETED/NEEDS-ACTION/ | + | | | IN-PROCESS. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VJOURNAL | 0 | | + +--------------------+----------+-----------------------------------+ + + + +Daboo Standards Track [Page 48] + +RFC 5546 iTIP December 2009 + + +3.4.2.1. REQUEST for Rescheduling a VTODO + + The "REQUEST" method may be used to reschedule a "VTODO" calendar + component. + + Rescheduling a "VTODO" calendar component involves a change to the + existing "VTODO" calendar component in terms of its start or due + time, recurrence intervals, and possibly the description. If the + recipient CUA of a "REQUEST" method finds that the "UID" property + value already exists on the calendar but that the "SEQUENCE" property + value in the "REQUEST" is greater than the value for the existing + "VTODO", then the "REQUEST" method describes a rescheduling of the + "VTODO" calendar component. + +3.4.2.2. REQUEST for Update or Reconfirmation of a VTODO + + The "REQUEST" method may be used to update or reconfirm a "VTODO" + calendar component. Reconfirmation is merely an update of "Attendee" + completion status or overall "VTODO" calendar component status. + + An update to an existing "VTODO" calendar component does not involve + changes to the start or due time, recurrence intervals, or + (generally) the description for the "VTODO" calendar component. If + the recipient CUA of a "REQUEST" method finds that the "UID" property + value already exists on the calendar and that the "SEQUENCE" property + value in the "REQUEST" is the same as the value for the existing + event, then the "REQUEST" method describes an update of the "VTODO" + calendar component details, but not a rescheduling of the "VTODO" + calendar component. + + The update "REQUEST" is the appropriate response to a "REFRESH" + method sent from an "Attendee" to the "Organizer" of a "VTODO" + calendar component. + + Unsolicited "REQUEST" methods MAY be sent by the "Organizer" of a + "VTODO" calendar component. The unsolicited "REQUEST" methods are + used to update the details of the "VTODO" (without rescheduling it or + updating the completion status of "Attendees") or the "VTODO" + calendar component itself (i.e., reconfirm the "VTODO"). + +3.4.2.3. REQUEST for Delegating a VTODO + + The "REQUEST" method is also used to delegate or reassign ownership + of a "VTODO" calendar component to another "Calendar User". For + example, it may be used to delegate an "Attendee's" role (i.e., + "chair" or "participant") for a "VTODO" calendar component. The + "REQUEST" method is sent by one of the "Attendees" of an existing + "VTODO" calendar component to some other individual. + + + +Daboo Standards Track [Page 49] + +RFC 5546 iTIP December 2009 + + + For the purposes of this description, the "Attendee" delegating the + "VTODO" calendar component is referred to as the "Delegator". The + "Attendee" receiving the delegation request is referred to as the + "Delegate". + + The "Delegator" of a "VTODO" calendar component MUST forward the + existing "REQUEST" method for a "VTODO" calendar component to the + "Delegate". The "VTODO" calendar component description MUST include + the "Delegator's" up-to-date "VTODO" calendar component definition. + The "REQUEST" method MUST also include an "ATTENDEE" property with + the calendar address of the "Delegate". The "Delegator" MUST also + send a "REPLY" method back to the "Organizer" with the "Delegator's" + "Attendee" property "PARTSTAT" parameter value set to "DELEGATED". + In addition, the "DELEGATED-TO" parameter MUST be included with the + calendar address of the "Delegate". A response to the delegation + "REQUEST" is sent from the "Delegate" to the "Organizer", and + optionally to the "Delegator". The "REPLY" method from the + "Delegate" SHOULD include the "ATTENDEE" property with their calendar + address and the "DELEGATED-FROM" parameter with the value of the + "Delegator's" calendar address. + + The delegation "REQUEST" method MUST assign a value for the "RSVP" + property parameter associated with the "Delegator's" "Attendee" + property to that of the "Delegate's" "ATTENDEE" property. For + example, if the "Delegator's" "ATTENDEE" property specifies + "RSVP=TRUE", then the "Delegate's" "ATTENDEE" property MUST specify + "RSVP=TRUE". + +3.4.2.4. REQUEST Forwarded to an Uninvited Calendar User + + An "Attendee" assigned a "VTODO" calendar component may send the + "VTODO" calendar component to another new CU not previously + associated with the "VTODO" calendar component. The current + "Attendee" assigned the "VTODO" calendar component does this by + forwarding the original "REQUEST" method to the new CU. The new CU + can send a "REPLY" to the "Organizer" of the "VTODO" calendar + component. The reply contains an "ATTENDEE" property for the new CU. + + The "Organizer" ultimately decides whether or not the new CU becomes + part of the to-do and is not obligated to do anything with a "REPLY" + from a new (uninvited) CU. If the "Organizer" does not want the new + CU to be part of the to-do, the new "ATTENDEE" property is not added + to the "VTODO" calendar component. The "Organizer" MAY send the CU a + "CANCEL" message to indicate that they will not be added to the to- + do. If the "Organizer" decides to add the new CU, the new "ATTENDEE" + property is added to the "VTODO" calendar component. Furthermore, + the "Organizer" is free to change any "ATTENDEE" property parameter + from the values supplied by the new CU to something the "Organizer" + + + +Daboo Standards Track [Page 50] + +RFC 5546 iTIP December 2009 + + + considers appropriate. The "Organizer" SHOULD send the new + "Attendee" a "REQUEST" message to inform them that they have been + added. + + When forwarding a "REQUEST" to another CU, the forwarding "Attendee" + MUST NOT make changes to the original message. + +3.4.2.5. REQUEST Updated Attendee Status + + An "Organizer" of a "VTODO" may request an updated status from one or + more "Attendees". The "Organizer" sends a "REQUEST" method to the + "Attendee" with the "ATTENDEE;RSVP=TRUE" property sequence. The + "SEQUENCE" property for the "VTODO" is not changed from its previous + value. A recipient determines that the only change in the "REQUEST" + is that their "RSVP" property parameter indicates a request for an + updated status. The recipient SHOULD respond with a "REPLY" method + indicating their current status with respect to the "REQUEST". + +3.4.3. REPLY + + The "REPLY" method in a "VTODO" calendar component is used to respond + (e.g., accept or decline) to a request or to reply to a delegation + request. It is also used by an "Attendee" to update their completion + status. When used to provide a delegation response, the "Delegator" + MUST include the calendar address of the "Delegate" in the + "DELEGATED-TO" parameter of the "Delegator's" "ATTENDEE" property. + The "Delegate" MUST include the calendar address of the "Delegator" + on the "DELEGATED-FROM" parameter of the "Delegate's" "ATTENDEE" + property. + + The "REPLY" method MAY also be used to respond to an unsuccessful + "VTODO" calendar component "REQUEST" method. Depending on the + "REQUEST-STATUS" value, no scheduling action may have been performed. + + The "Organizer" of a "VTODO" calendar component MAY receive a "REPLY" + method from a "Calendar User" not in the original "REQUEST". For + example, a "REPLY" method MAY be received from a "Delegate" of a + "VTODO" calendar component. In addition, the "REPLY" method MAY be + received from an unknown "Calendar User" who has been forwarded the + "REQUEST" by an original "Attendee" of the "VTODO" calendar + component. This uninvited "Attendee" MAY be accepted or the + "Organizer" MAY cancel the "VTODO" calendar component for the + uninvited "Attendee" by sending them a "CANCEL" method. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + + + +Daboo Standards Track [Page 51] + +RFC 5546 iTIP December 2009 + + + +-------------------------------------------+ + | Constraints for a METHOD:REPLY of a VTODO | + +-------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REPLY. | + | | | | + | VTODO | 1+ | All components MUST have the same | + | | | UID. | + | ATTENDEE | 1 | MUST be the address of the | + | | | Attendee replying. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | REQUEST-STATUS | 0+ | | + | UID | 1 | MUST be the UID of the original | + | | | REQUEST. | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | COMPLETED | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | | + | DTSTART | 0 or 1 | | + | DUE | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DUE MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PERCENT-COMPLETE | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RDATE | 0+ | | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | SEQUENCE | 0 or 1 | MUST be the sequence number of | + | | | the original REQUEST if greater | + | | | than 0. MAY be present if 0. | + + + +Daboo Standards Track [Page 52] + +RFC 5546 iTIP December 2009 + + + | STATUS | 0 or 1 | | + | SUMMARY | 0 or 1 | Can be null. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | | | | + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0 or 1 | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.4.4. ADD + + The "ADD" method allows the "Organizer" to add one or more new + instances to an existing "VTODO" using a single iTIP message without + having to send the entire "VTODO" with all the existing instance + data, as it would have to do if the "REQUEST" method were used. + + The "UID" must be that of the existing to-do. If the "UID" property + value in the "ADD" is not found on the recipient's calendar, then the + recipient SHOULD send a "REFRESH" to the "Organizer" in order to be + updated with the latest version of the "VTODO". If an "Attendee" + implementation does not support the "ADD" method, it should respond + with a "REQUEST-STATUS" value of 3.14 and ask for a "REFRESH". + + When handling an "ADD" message, the "Attendee" treats each component + in the "ADD" message as if it were referenced via an "RDATE" in the + main component. + + The "SEQUENCE" property value is incremented since the sequence of + to-dos has changed. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + + + + + + + +Daboo Standards Track [Page 53] + +RFC 5546 iTIP December 2009 + + + +-----------------------------------------+ + | Constraints for a METHOD:ADD of a VTODO | + +-----------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be ADD. | + | | | | + | VTODO | 1 | | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | PRIORITY | 1 | | + | SEQUENCE | 1 | MUST be greater than 0. | + | SUMMARY | 1 | Can be null. | + | UID | 1 | MUST match that of the original | + | | | to-do. | + | ATTACH | 0+ | | + | ATTENDEE | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | COMPLETED | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null. | + | DTSTART | 0 or 1 | | + | DUE | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DUE MUST NOT be | + | | | present. | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PERCENT-COMPLETE | 0 or 1 | | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | STATUS | 0 or 1 | MAY be one of | + | | | COMPLETED/NEEDS-ACTION/ | + | | | IN-PROCESS. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | EXDATE | 0 | | + | RECURRENCE-ID | 0 | | + | REQUEST-STATUS | 0 | | + | RDATE | 0 | | + | RRULE | 0 | | + + + +Daboo Standards Track [Page 54] + +RFC 5546 iTIP December 2009 + + + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VJOURNAL | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.4.5. CANCEL + + The "CANCEL" method in a "VTODO" calendar component is used to send a + cancellation notice of an existing "VTODO" calendar request to the + affected "Attendees". The message is sent by the "Organizer" of a + "VTODO" calendar component to the "Attendees" of the "VTODO" calendar + component. For a recurring "VTODO" calendar component, either the + whole "VTODO" calendar component or instances of a "VTODO" calendar + component may be cancelled. To cancel the complete range of a + recurring "VTODO" calendar component, the "UID" property value for + the "VTODO" calendar component MUST be specified and a "RECURRENCE- + ID" MUST NOT be specified in the "CANCEL" method. In order to cancel + an individual instance of a recurring "VTODO" calendar component, the + "RECURRENCE-ID" property value for the "VTODO" calendar component + MUST be specified in the "CANCEL" method. + + There are two options for canceling a sequence of instances of a + recurring "VTODO" calendar component: + + a. The "RECURRENCE-ID" property for an instance in the sequence MUST + be specified with the "RANGE" property parameter value of + "THISANDFUTURE" to indicate cancellation of the specified "VTODO" + calendar component and all instances after. + + b. Individual recurrence instances may be cancelled by specifying + multiple "VTODO" components each with a "RECURRENCE-ID" property + corresponding to one of the instances to be cancelled. + + The "Organizer" MUST send a "CANCEL" message to each "Attendee" + affected by the cancellation. This can be done by using either a + single "CANCEL" message for all "Attendees" or multiple messages with + different subsets of the affected "Attendees" in each. + + + +Daboo Standards Track [Page 55] + +RFC 5546 iTIP December 2009 + + + When a "VTODO" is cancelled, the "SEQUENCE" property value MUST be + incremented as described in Section 2.1.4. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +--------------------------------------------+ + | Constraints for a METHOD:CANCEL of a VTODO | + +--------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be CANCEL. | + | | | | + | VTODO | 1+ | | + | ATTENDEE | 0+ | MUST include some or all | + | | | Attendees being removed from the | + | | | to-do. MUST include some or all | + | | | Attendees if the entire to-do is | + | | | cancelled. | + | UID | 1 | MUST echo original UID. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 1 | | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | COMPLETED | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | | + | DTSTART | 0 or 1 | | + | DUE | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DUE MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PERCENT-COMPLETE | 0 or 1 | | + | RDATE | 0+ | | + + + + + + + +Daboo Standards Track [Page 56] + +RFC 5546 iTIP December 2009 + + + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | STATUS | 0 or 1 | MUST be set to CANCELLED to | + | | | cancel the entire VTODO. If | + | | | removing specific Attendees, then | + | | | MUST NOT be included. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0 or 1 | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.4.6. REFRESH + + The "REFRESH" method in a "VTODO" calendar component is used by + "Attendees" of an existing "VTODO" calendar component to request an + updated description from the "Organizer" of the "VTODO" calendar + component. The "Organizer" of the "VTODO" calendar component MAY use + this method to request an updated status from the "Attendees". The + "REFRESH" method MUST specify the "UID" property corresponding to the + "VTODO" calendar component needing update. + + A refresh of a recurrence instance of a "VTODO" calendar component + may be requested by specifying the "RECURRENCE-ID" property + corresponding to the associated "VTODO" calendar component. The + "Organizer" responds with the latest description and rendition of the + "VTODO" calendar component. In most cases, this will be a "REQUEST" + unless the "VTODO" has been cancelled, in which case the "Organizer" + MUST send a "CANCEL". This method is intended to facilitate machine + processing of requests for updates to a "VTODO" calendar component. + + + +Daboo Standards Track [Page 57] + +RFC 5546 iTIP December 2009 + + + This method type is an iCalendar object that conforms to the + following property constraints: + + +---------------------------------------------+ + | Constraints for a METHOD:REFRESH of a VTODO | + +---------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be REFRESH. | + | | | | + | VTODO | 1 | | + | ATTENDEE | 1 | | + | DTSTAMP | 1 | | + | UID | 1 | MUST echo original UID. | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | ATTACH | 0 | | + | CATEGORIES | 0 | | + | CLASS | 0 | | + | COMMENT | 0 | | + | COMPLETED | 0 | | + | CONTACT | 0 | | + | CREATED | 0 | | + | DESCRIPTION | 0 | | + | DTSTART | 0 | | + | DUE | 0 | | + | DURATION | 0 | | + | EXDATE | 0 | | + | GEO | 0 | | + | LAST-MODIFIED | 0 | | + | LOCATION | 0 | | + | ORGANIZER | 0 | | + | PERCENT-COMPLETE | 0 | | + | PRIORITY | 0 | | + | RDATE | 0 | | + | RELATED-TO | 0 | | + | REQUEST-STATUS | 0 | | + | RESOURCES | 0 | | + | RRULE | 0 | | + | SEQUENCE | 0 | | + | STATUS | 0 | | + | URL | 0 | | + + + +Daboo Standards Track [Page 58] + +RFC 5546 iTIP December 2009 + + + | | | | + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0+ | | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.4.7. COUNTER + + The "COUNTER" method in a "VTODO" calendar component is used by an + "Attendee" of an existing "VTODO" calendar component to submit to the + "Organizer" a counter proposal for the "VTODO" calendar component. + + The counter proposal is an iCalendar object consisting of a "VTODO" + calendar component that provides the complete description of the + alternate "VTODO" calendar component. + + The "Organizer" rejects the counter proposal by sending the + "Attendee" a "DECLINECOUNTER" method. The "Organizer" accepts the + counter proposal by rescheduling the to-do as described in + Section 3.4.2.1, "REQUEST for Rescheduling a To-Do". The + "Organizer's" CUA SHOULD send a "REQUEST" message to all "Attendees" + affected by any change triggered by an accepted "COUNTER". + + This method type is an iCalendar object that conforms to the + following property constraints: + + +---------------------------------------------+ + | Constraints for a METHOD:COUNTER of a VTODO | + +---------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be COUNTER. | + | | | | + | VTODO | 1 | | + | ATTENDEE | 1+ | | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | PRIORITY | 1 | | + | SUMMARY | 1 | Can be null. | + + + +Daboo Standards Track [Page 59] + +RFC 5546 iTIP December 2009 + + + | UID | 1 | | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | COMPLETED | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | Can be null. | + | DTSTART | 0 or 1 | | + | DUE | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DUE MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | LOCATION | 0 or 1 | | + | PERCENT-COMPLETE | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | REQUEST-STATUS | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | SEQUENCE | 0 or 1 | MUST echo the original SEQUENCE | + | | | number. MUST be present if | + | | | non-zero. MAY be present if | + | | | zero. | + | STATUS | 0 or 1 | MAY be one of | + | | | COMPLETED/NEEDS-ACTION/ | + | | | IN-PROCESS/CANCELLED. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0 or 1 | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + + + + +Daboo Standards Track [Page 60] + +RFC 5546 iTIP December 2009 + + + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.4.8. DECLINECOUNTER + + The "DECLINECOUNTER" method in a "VTODO" calendar component is used + by an "Organizer" of the "VTODO" calendar component to reject a + counter proposal offered by one of the "Attendees". The "Organizer" + sends the message to the "Attendee" that sent the "COUNTER" method to + the "Organizer". + + This method type is an iCalendar object that conforms to the + following property constraints: + + +----------------------------------------------------+ + | Constraints for a METHOD:DECLINECOUNTER of a VTODO | + +----------------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be DECLINECOUNTER. | + | | | | + | VTODO | 1 | | + | ATTENDEE | 1+ | MUST for all ATTENDEEs. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 1 | MUST echo the original SEQUENCE | + | | | number. | + | UID | 1 | MUST echo original UID. | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | COMPLETED | 0 or 1 | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | | + | DTSTART | 0 or 1 | | + | DUE | 0 or 1 | If present, DURATION MUST NOT be | + | | | present. | + | DURATION | 0 or 1 | If present, DUE MUST NOT be | + | | | present. | + | EXDATE | 0+ | | + | GEO | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + + + +Daboo Standards Track [Page 61] + +RFC 5546 iTIP December 2009 + + + | LOCATION | 0 or 1 | | + | PERCENT-COMPLETE | 0 or 1 | | + | PRIORITY | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | REQUEST-STATUS | 0+ | | + | RESOURCES | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MAY be one of | + | | | COMPLETED/NEEDS-ACTION/ | + | | | IN-PROCESS. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | | | | + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + +--------------------+----------+-----------------------------------+ + +3.5. Methods for VJOURNAL Components + + This section defines the property set for the methods that are + applicable to the "VJOURNAL" calendar component. + + The following summarizes the methods that are defined for the + "VJOURNAL" calendar component. + + + + + + + + + + + + +Daboo Standards Track [Page 62] + +RFC 5546 iTIP December 2009 + + + +---------+---------------------------------------------------------+ + | Method | Description | + +---------+---------------------------------------------------------+ + | PUBLISH | Post a journal entry. Used primarily as a method of | + | | advertising the existence of a journal entry. | + | | | + | ADD | Add one or more instances to an existing journal entry. | + | | | + | CANCEL | Cancel one or more instances of an existing journal | + | | entry. | + +---------+---------------------------------------------------------+ + +3.5.1. PUBLISH + + The "PUBLISH" method in a "VJOURNAL" calendar component has no + associated response. It is simply a posting of an iCalendar object + that may be added to a calendar. It MUST have an "Organizer". It + MUST NOT have "Attendees". The expected usage is for encapsulating + an arbitrary journal entry as an iCalendar object. The "Organizer" + MAY subsequently update (with another "PUBLISH" method) or cancel + (with a "CANCEL" method) a previously published journal entry. + + This method type is an iCalendar object that conforms to the + following property constraints: + + +------------------------------------------------+ + | Constraints for a METHOD:PUBLISH of a VJOURNAL | + +------------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be PUBLISH. | + | | | | + | VJOURNAL | 1+ | | + | DESCRIPTION | 1 | Can be null. | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + | ORGANIZER | 1 | | + | UID | 1 | | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | EXDATE | 0+ | | + | LAST-MODIFIED | 0 or 1 | | + + + +Daboo Standards Track [Page 63] + +RFC 5546 iTIP December 2009 + + + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | RRULE | 0 or 1 | | + | SEQUENCE | 0 or 1 | MUST be present if non-zero. MAY | + | | | be present if zero. | + | STATUS | 0 or 1 | MAY be one of | + | | | DRAFT/FINAL/CANCELLED. | + | SUMMARY | 0 or 1 | Can be null. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | ATTENDEE | 0 | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VTODO | 0 | | + +--------------------+----------+-----------------------------------+ + +3.5.2. ADD + + The "ADD" method allows the "Organizer" to add one or more new + instances to an existing "VJOURNAL" using a single iTIP message + without having to send the entire "VJOURNAL" with all the existing + instance data, as it would have to do if the "REQUEST" method were + used. + + The "UID" must be that of the existing journal entry. If the "UID" + property value in the "ADD" is not found on the recipient's calendar, + then the recipient MAY treat the "ADD" as a "PUBLISH". + + When handling an "ADD" message, the "Attendee" treats each component + in the "ADD" message as if it were referenced via an "RDATE" in the + main component. There is no response to the "Organizer". + + + +Daboo Standards Track [Page 64] + +RFC 5546 iTIP December 2009 + + + This method type is an iCalendar object that conforms to the + following property constraints: + + +--------------------------------------------+ + | Constraints for a METHOD:ADD of a VJOURNAL | + +--------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be ADD. | + | | | | + | VJOURNAL | 1 | | + | DESCRIPTION | 1 | Can be null. | + | DTSTAMP | 1 | | + | DTSTART | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 1 | MUST be greater than 0. | + | UID | 1 | MUST match that of the original | + | | | journal. | + | ATTACH | 0+ | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | LAST-MODIFIED | 0 or 1 | | + | RELATED-TO | 0+ | | + | STATUS | 0 or 1 | MAY be one of | + | | | DRAFT/FINAL/CANCELLED. | + | SUMMARY | 0 or 1 | Can be null. | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | ATTENDEE | 0 | | + | EXDATE | 0 | | + | RECURRENCE-ID | 0 | | + | REQUEST-STATUS | 0 | | + | RDATE | 0 | | + | RRULE | 0 | | + | | | | + | VALARM | 0+ | | + | | | | + | VTIMEZONE | 0 or 1 | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + + + +Daboo Standards Track [Page 65] + +RFC 5546 iTIP December 2009 + + + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VTODO | 0 | | + +--------------------+----------+-----------------------------------+ + +3.5.3. CANCEL + + The "CANCEL" method in a "VJOURNAL" calendar component is used to + send a cancellation notice of an existing journal entry. The message + is sent by the "Organizer" of a journal entry. For a recurring + journal entry, either the whole journal entry or instances of a + journal entry may be cancelled. To cancel the complete range of a + recurring journal entry, the "UID" property value for the journal + entry MUST be specified and a "RECURRENCE-ID" property MUST NOT be + specified in the "CANCEL" method. In order to cancel an individual + instance of the journal entry, the "RECURRENCE-ID" property value for + the journal entry MUST be specified in the "CANCEL" method. + + There are two options for canceling a sequence of instances of a + recurring "VJOURNAL" calendar component: + + a. The "RECURRENCE-ID" property for an instance in the sequence MUST + be specified with the "RANGE" property parameter value of + "THISANDFUTURE" to indicate cancellation of the specified + "VJOURNAL" calendar component and all instances after. + + b. Individual recurrence instances may be cancelled by specifying + multiple "VJOURNAL" components each with a "RECURRENCE-ID" + property corresponding to one of the instances to be cancelled. + + When a "VJOURNAL" is cancelled, the "SEQUENCE" property value MUST be + incremented as described in Section 2.1.4. + + This method type is an iCalendar object that conforms to the + following property constraints: + + + + + + + + + + + + + +Daboo Standards Track [Page 66] + +RFC 5546 iTIP December 2009 + + + +-----------------------------------------------+ + | Constraints for a METHOD:CANCEL of a VJOURNAL | + +-----------------------------------------------+ + + +--------------------+----------+-----------------------------------+ + | Component/Property | Presence | Comment | + +--------------------+----------+-----------------------------------+ + | METHOD | 1 | MUST be CANCEL. | + | | | | + | VJOURNAL | 1+ | All MUST have the same UID. | + | DTSTAMP | 1 | | + | ORGANIZER | 1 | | + | SEQUENCE | 1 | | + | UID | 1 | MUST be the UID of the original | + | | | REQUEST. | + | ATTACH | 0+ | | + | ATTENDEE | 0 | | + | CATEGORIES | 0+ | | + | CLASS | 0 or 1 | | + | COMMENT | 0+ | | + | CONTACT | 0+ | | + | CREATED | 0 or 1 | | + | DESCRIPTION | 0 or 1 | | + | DTSTART | 0 or 1 | | + | EXDATE | 0+ | | + | LAST-MODIFIED | 0 or 1 | | + | RDATE | 0+ | | + | RECURRENCE-ID | 0 or 1 | Only if referring to an instance | + | | | of a recurring calendar | + | | | component. Otherwise, it MUST | + | | | NOT be present. | + | RELATED-TO | 0+ | | + | RRULE | 0 or 1 | | + | STATUS | 0 or 1 | MAY be present; MUST be CANCELLED | + | | | if present. | + | SUMMARY | 0 or 1 | | + | URL | 0 or 1 | | + | IANA-PROPERTY | 0+ | | + | X-PROPERTY | 0+ | | + | REQUEST-STATUS | 0 | | + | | | | + | VALARM | 0 | | + | | | | + | VTIMEZONE | 0+ | MUST be present if any date/time | + | | | refers to a timezone. | + | | | | + | IANA-COMPONENT | 0+ | | + | X-COMPONENT | 0+ | | + + + +Daboo Standards Track [Page 67] + +RFC 5546 iTIP December 2009 + + + | | | | + | VEVENT | 0 | | + | | | | + | VFREEBUSY | 0 | | + | | | | + | VTODO | 0 | | + +--------------------+----------+-----------------------------------+ + +3.6. Status Replies + + The "REQUEST-STATUS" property is used to convey status information + about a "REPLY", "COUNTER", or "DECLINECOUNTER" iTIP message. The + codes listed in the table below SHOULD be used. If the "REQUEST- + STATUS" property is not present in one of these iTIP messages, then a + status code of "2.0" (success) MUST be assumed. + + This specification adds a new IANA registry for "REQUEST-STATUS" + property values, as defined in Section 7, which includes a new + registration template for defining the specific components of the + "REQUEST-STATUS" property value. Additional codes MAY be used, + provided the process described in Section 8.2.1 of [RFC5545] is used + to register them. + + This specification allows for multiple "REQUEST-STATUS" properties to + be returned in iCalendar components in the appropriate iTIP messages. + When multiple "REQUEST-STATUS" properties are present, the following + restrictions apply: + + 1. Within any one component, the "top-level" numeric value of the + "short return status code" MUST be the same for all "REQUEST- + STATUS" properties, i.e., there cannot be a mixture of, e.g., + 2.xx and 5.xx codes within a single component. + + 2. Across all components in the iTIP message, the following applies: + + A. If any one component would have a 5.xx code, then either all + components MUST have a code in that range or "REQUEST-STATUS" + MUST NOT be present in the other components if a 5.xx code is + not appropriate for those components. + + B. Otherwise, if any one component would have a 3.xx code, then + either all components MUST have a code in that range or + "REQUEST-STATUS" MUST NOT be present in the other components + if a 3.xx code is not appropriate for those components. + + C. 2.xx and 4.xx codes can be used in different components, + provided that each component follows the restriction in (1) + above. + + + +Daboo Standards Track [Page 68] + +RFC 5546 iTIP December 2009 + + + The following "REQUEST-STATUS" codes are defined (any "Offending + Data" MAY be specified in the "REQUEST-STATUS" value as the extdata + field): + +3.6.1. Status Code 2.0 + + Status Code: 2.0 + + Status Description: Success. + + Status Exception Data: None. + + Description: iTIP operation succeeded. + +3.6.2. Status Code 2.1 + + Status Code: 2.1 + + Status Description: Success, but fallback taken on one or more + property values. + + Status Exception Data: Property name and value MAY be specified. + + Description: iTIP operation succeeded with fallback on one or more + property values. + +3.6.3. Status Code 2.2 + + Status Code: 2.2 + + Status Description: Success; invalid property ignored. + + Status Exception Data: Property name MAY be specified. + + Description: iTIP operation succeeded but a property was ignored. + +3.6.4. Status Code 2.3 + + Status Code: 2.3 + + Status Description: Success; invalid property parameter ignored. + + Status Exception Data: Property parameter name and value MAY be + specified. + + Description: iTIP operation succeeded but a property parameter was + ignored because it was invalid. + + + + +Daboo Standards Track [Page 69] + +RFC 5546 iTIP December 2009 + + +3.6.5. Status Code 2.4 + + Status Code: 2.4 + + Status Description: Success; unknown, non-standard property ignored. + + Status Exception Data: Non-standard property name MAY be specified. + + Description: iTIP operation succeeded but a property parameter was + ignored because it was unknown. + +3.6.6. Status Code 2.5 + + Status Code: 2.5 + + Status Description: Success; unknown, non-standard property value + ignored. + + Status Exception Data: Property and non-standard value MAY be + specified. + + Description: iTIP operation succeeded but a property was ignored + because its value was unknown. + +3.6.7. Status Code 2.6 + + Status Code: 2.6 + + Status Description: Success; invalid calendar component ignored. + + Status Exception Data: Calendar component sentinel (e.g., BEGIN: + ALARM) MAY be specified. + + Description: iTIP operation succeeded but a component was ignored + because it was invalid. + +3.6.8. Status Code 2.7 + + Status Code: 2.7 + + Status Description: Success; request forwarded to Calendar User. + + Status Exception Data: Original and forwarded calendar user + addresses MAY be specified. + + Description: iTIP operation succeeded, and the request was forwarded + to another Calendar User. + + + + +Daboo Standards Track [Page 70] + +RFC 5546 iTIP December 2009 + + +3.6.9. Status Code 2.8 + + Status Code: 2.8 + + Status Description: Success; repeating event ignored. Scheduled as + a single component. + + Status Exception Data: RRULE or RDATE property name and value MAY be + specified. + + Description: iTIP operation succeeded but a repeating event was + truncated to a single instance. + +3.6.10. Status Code 2.9 + + Status Code: 2.9 + + Status Description: Success; truncated end date time to date + boundary. + + Status Exception Data: DTEND property value MAY be specified. + + Description: iTIP operation succeeded but the end time was truncated + to a date boundary. + +3.6.11. Status Code 2.10 + + Status Code: 2.10 + + Status Description: Success; repeating VTODO ignored. Scheduled as + a single VTODO. + + Status Exception Data: RRULE or RDATE property name and value MAY be + specified. + + Description: iTIP operation succeeded but a repeating to-do was + truncated to a single instance. + + + + + + + + + + + + + + +Daboo Standards Track [Page 71] + +RFC 5546 iTIP December 2009 + + +3.6.12. Status Code 2.11 + + Status Code: 2.11 + + Status Description: Success; unbounded RRULE clipped at some finite + number of instances. + + Status Exception Data: RRULE property name and value MAY be + specified. Number of instances MAY also be specified. + + Description: iTIP operation succeeded but an unbounded repeating + object was clipped to a finite number of instances. + +3.6.13. Status Code 3.0 + + Status Code: 3.0 + + Status Description: Invalid property name. + + Status Exception Data: Property name MAY be specified. + + Description: iTIP operation failed because of an invalid property + name. + +3.6.14. Status Code 3.1 + + Status Code: 3.1 + + Status Description: Invalid property value. + + Status Exception Data: Property name and value MAY be specified. + + Description: iTIP operation failed because of an invalid property + value. + +3.6.15. Status Code 3.2 + + Status Code: 3.2 + + Status Description: Invalid property parameter. + + Status Exception Data: Property parameter name and value MAY be + specified. + + Description: iTIP operation failed because of an invalid property + parameter. + + + + + +Daboo Standards Track [Page 72] + +RFC 5546 iTIP December 2009 + + +3.6.16. Status Code 3.3 + + Status Code: 3.3 + + Status Description: Invalid property parameter value. + + Status Exception Data: Property parameter name and value MAY be + specified. + + Description: iTIP operation failed because of an invalid property + parameter value. + +3.6.17. Status Code 3.4 + + Status Code: 3.4 + + Status Description: Invalid calendar component sequence. + + Status Exception Data: Calendar component sentinel MAY be specified + (e.g., BEGIN:VTIMEZONE). + + Description: iTIP operation failed because of an invalid component. + +3.6.18. Status Code 3.5 + + Status Code: 3.5 + + Status Description: Invalid date or time. + + Status Exception Data: Date/time value(s) MAY be specified. + + Description: iTIP operation failed because of an invalid date or + time property. + +3.6.19. Status Code 3.6 + + Status Code: 3.6 + + Status Description: Invalid rule. + + Status Exception Data: RRULE property value MAY be specified. + + Description: iTIP operation failed because of an invalid rule + property. + + + + + + + +Daboo Standards Track [Page 73] + +RFC 5546 iTIP December 2009 + + +3.6.20. Status Code 3.7 + + Status Code: 3.7 + + Status Description: Invalid Calendar User. + + Status Exception Data: ATTENDEE property value MAY be specified. + + Description: iTIP operation failed because of an invalid ATTENDEE + property. + +3.6.21. Status Code 3.8 + + Status Code: 3.8 + + Status Description: No authority. + + Status Exception Data: METHOD and ATTENDEE property values MAY be + specified. + + Description: iTIP operation failed because an Attendee does not have + suitable privileges for the operation. + +3.6.22. Status Code 3.9 + + Status Code: 3.9 + + Status Description: Unsupported version. + + Status Exception Data: VERSION property name and value MAY be + specified. + + Description: iTIP operation failed because the calendar data version + is not supported. + +3.6.23. Status Code 3.10 + + Status Code: 3.10 + + Status Description: Request entity too large. + + Status Exception Data: None. + + Description: iTIP operation failed because the calendar data was too + large. + + + + + + +Daboo Standards Track [Page 74] + +RFC 5546 iTIP December 2009 + + +3.6.24. Status Code 3.11 + + Status Code: 3.11 + + Status Description: Required component or property missing. + + Status Exception Data: Component or property name MAY be specified. + + Description: iTIP operation failed because the calendar data did not + contain a required property or component. + +3.6.25. Status Code 3.12 + + Status Code: 3.12 + + Status Description: Unknown component or property found. + + Status Exception Data: Component or property name MAY be specified. + + Description: iTIP operation failed because the calendar data + contained an unknown property or component. + +3.6.26. Status Code 3.13 + + Status Code: 3.13 + + Status Description: Unsupported component or property found. + + Status Exception Data: Component or property name MAY be specified. + + Description: iTIP operation failed because the calendar data + contained an unsupported property or component. + +3.6.27. Status Code 3.14 + + Status Code: 3.14 + + Status Description: Unsupported capability. + + Status Exception Data: METHOD or action MAY be specified. + + Description: iTIP operation failed because the operation is not + supported. + + + + + + + + +Daboo Standards Track [Page 75] + +RFC 5546 iTIP December 2009 + + +3.6.28. Status Code 4.0 + + Status Code: 4.0 + + Status Description: Event conflict. Date/time is busy. + + Status Exception Data: DTSTART and DTEND property names and values + MAY be specified. + + Description: iTIP operation failed because the event overlaps the + date and time of another event. + +3.6.29. Status Code 5.0 + + Status Code: 5.0 + + Status Description: Request not supported. + + Status Exception Data: METHOD property value MAY be specified. + + Description: iTIP operation failed because the operation is not + supported. + +3.6.30. Status Code 5.1 + + Status Code: 5.1 + + Status Description: Service unavailable. + + Status Exception Data: ATTENDEE property value MAY be specified. + + Description: iTIP operation failed because scheduling is not active. + +3.6.31. Status Code 5.2 + + Status Code: 5.2 + + Status Description: Invalid calendar service. + + Status Exception Data: ATTENDEE property value MAY be specified. + + Description: iTIP operation failed because there is no scheduling + capability. + + + + + + + + +Daboo Standards Track [Page 76] + +RFC 5546 iTIP December 2009 + + +3.6.32. Status Code 5.3 + + Status Code: 5.3 + + Status Description: No scheduling support for user. + + Status Exception Data: ATTENDEE property value MAY be specified. + + Description: iTIP operation failed because scheduling is not enabled + for an Attendee. + +3.7. Implementation Considerations + +3.7.1. Working With Recurrence Instances + + iCalendar includes a recurrence grammar to represent recurring + events. The benefit of such a grammar is the ability to represent a + number of events in a single object. However, while this simplifies + creation of a recurring event, meeting instances still need to be + referenced. For instance, an "Attendee" may decline the third + instance of a recurring Friday event. Similarly, the "Organizer" may + change the time or location to a single instance of the recurring + event. + + Since implementations may elect to store recurring events as either a + single event object or a collection of discrete, related event + objects, the protocol is designed so that each recurring instance may + be both referenced and versioned. Hence, implementations that choose + to maintain per-instance properties (such as "ATTENDEE" property + "PARTSTAT" parameter) may do so. However, the protocol does not + require per-instance recognition unless the instance itself must be + renegotiated. + + The scenarios for recurrence instance referencing are listed below. + For purposes of simplification, a change to an event refers to a + "trigger property." That is, a property that has a substantive + effect on the meeting itself, such as start time, location, due date + (for "VTODO" calendar components), and possibly description. + + "Organizer"-initiated actions: + + o deletes or changes a single instance of a recurring event + + o makes changes that affect all future instances + + o makes changes that affect all previous instances + + o deletes or modifies a previously changed instance + + + +Daboo Standards Track [Page 77] + +RFC 5546 iTIP December 2009 + + + "Attendee"-initiated actions: + + o changes status for a particular recurrence instance + + o sends a "COUNTER" for a particular recurrence instance + + An instance of a recurring event is assigned a unique identification, + "RECURRENCE-ID" property, when that instance is renegotiated. + Negotiation may be necessary when a substantive change to the event + or to-do has been made (such as changing the start time, end time, + due date, or location). The "Organizer" can identify a specific + recurrence instance using the "RECURRENCE-ID" property. The property + value is equal to the date/time of the instance. If the "Organizer" + wishes to change the "DTSTART", the original, unmodified "DTSTART" + value of the instance is used as the value "RECURRENCE-ID" property, + and the new "DTSTART" and "DTEND" values reflect the change. + +3.7.2. Attendee Property Considerations + + The "ORGANIZER" property is required on published events, to-dos, and + journal entries for two reasons. First, only the "Organizer" is + allowed to update and redistribute an event or to-do component. It + follows that the "ORGANIZER" property MUST be present in the event, + to-do, or journal entry component so that the CUA has a basis for + authorizing an update. Second, it is prudent to provide a point of + contact for anyone who receives a published component, in case of + problems. + + Email addresses that correspond to groups of "Calendar Users" could + be specified as a mailto: URI [RFC2368] calendar user address. + Sending email to such an address results in email being sent to + multiple recipients. Such an address may be used as the value of an + "ATTENDEE" property. Thus, it is possible that the recipient of a + "REQUEST" does not appear explicitly in the list. + + It is recommended that the general approach to finding a "Calendar + User" in an "Attendee" list be as follows: + + 1. Search for the "Calendar User" in the "Attendee" list where + "CUTYPE=INDIVIDUAL" + + 2. Failing (1), look for "Attendees" where "CUTYPE=GROUP" or + "CUTYPE=UNKNOWN". The CUA then determines if the "Calendar User" + is a member of one of these groups. If so, the "REPLY" method + sent to the "Organizer" MUST contain a new "ATTENDEE" property in + which: + + * the "TYPE" property parameter is set to INDIVIDUAL + + + +Daboo Standards Track [Page 78] + +RFC 5546 iTIP December 2009 + + + * the "MEMBER" property parameter is set to the name of the + group + + 3. Failing (2), the CUA MAY ignore or accept the request as the + "Calendar User" wishes. + +3.7.3. Extension Tokens + + To make iCalendar objects extensible, new component, property, or + property parameters can be used. Two types of extensions are defined + by [RFC5545]: IANA-registered tokens ("iana-token") and experimental + use tokens ("x-name"). A client SHOULD save "iana-token's" and MAY + use them in replies. A client MAY save "x-name's" and MAY use them + in replies. When delegating or forwarding messages to other CUs, a + client SHOULD include "iana-token's" and "x-names's". + +4. Examples + +4.1. Published Event Examples + + In the calendaring and scheduling context, publication refers to the + one-way transfer of event information. Consumers of published events + simply incorporate the event into a calendar. No reply is expected. + Individual "A" publishes an event. Individual "B" reads the event + and incorporates it into their calendar. Events are published in + several ways, including embedding the event as an object in a web + page, emailing the event to a distribution list, or posting the event + to a newsgroup. + + The table below illustrates the sequence of events between the + publisher and the consumers of a published event. + + +----------------+-----------------------+--------------------------+ + | Action | Organizer | Receiver | + +----------------+-----------------------+--------------------------+ + | Publish an | "A" sends or posts a | "B" reads a published | + | event | PUBLISH message. | event. | + | | | | + | Publish an | "A" sends or posts a | "B" reads the updated | + | updated event | PUBLISH message. | event. | + | | | | + | Cancel a | "A" sends or posts a | "B" reads the canceled | + | published | CANCEL message. | event publication. | + | event | | | + +----------------+-----------------------+--------------------------+ + + + + + + +Daboo Standards Track [Page 79] + +RFC 5546 iTIP December 2009 + + +4.1.1. A Minimal Published Event + + The iCalendar object below describes a single event that begins on + July 1, 1997 at 20:00 UTC. This event contains the minimum set of + properties for a "PUBLISH" for a "VEVENT" calendar component. + + BEGIN:VCALENDAR + METHOD:PUBLISH + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + DTSTART:19970701T200000Z + DTSTAMP:19970611T190000Z + SUMMARY:ST. PAUL SAINTS -VS- DULUTH-SUPERIOR DUKES + UID:0981234-1234234-23@example.com + END:VEVENT + END:VCALENDAR + +4.1.2. Changing a Published Event + + The iCalendar object below describes an update to the event described + in Section 4.1.1; the time has been changed, an end time has been + added, and the sequence number has been adjusted. + + BEGIN:VCALENDAR + METHOD:PUBLISH + VERSION:2.0 + PRODID:-//Example/ExampleCalendarClient//EN + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + DTSTAMP:19970612T190000Z + DTSTART:19970701T210000Z + DTEND:19970701T230000Z + SEQUENCE:1 + UID:0981234-1234234-23@example.com + SUMMARY:ST. PAUL SAINTS -VS- DULUTH-SUPERIOR DUKES + END:VEVENT + END:VCALENDAR + + The "UID" property is used by the client to identify the event. The + "SEQUENCE" property indicates that this is a change to the event. + The event with a matching "UID" and sequence number 0 is superseded + by this event. + + The "SEQUENCE" property provides a reliable way to distinguish + different versions of the same event. Each time an event is + published, its sequence number is incremented. If a client receives + + + +Daboo Standards Track [Page 80] + +RFC 5546 iTIP December 2009 + + + an event with a sequence number 5 and finds it has the same event + with sequence number 2, the event SHOULD be updated. However, if the + client received an event with sequence number 2 and finds it already + has sequence number 5 of the same event, the event MUST NOT be + updated. + +4.1.3. Canceling a Published Event + + The iCalendar object below cancels the event described in + Section 4.1.1. This cancels the event with "SEQUENCE" property of 0, + 1, and 2. + + BEGIN:VCALENDAR + METHOD:CANCEL + VERSION:2.0 + PRODID:-//Example/ExampleCalendarClient//EN + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + COMMENT:DUKES forfeit the game + SEQUENCE:2 + UID:0981234-1234234-23@example.com + DTSTAMP:19970613T190000Z + END:VEVENT + END:VCALENDAR + +4.1.4. A Rich Published Event + + This example describes the same event as in Section 4.1.1, but in + much greater detail. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:PUBLISH + SCALE:GREGORIAN + VERSION:2.0 + BEGIN:VTIMEZONE + TZID:America-Chicago + TZURL:http://example.com/tz/America-Chicago + BEGIN:STANDARD + DTSTART:19671029T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZOFFSETFROM:-0500 + TZOFFSETTO:-0600 + TZNAME:CST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19870405T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + + + +Daboo Standards Track [Page 81] + +RFC 5546 iTIP December 2009 + + + TZOFFSETFROM:-0600 + TZOFFSETTO:-0500 + TZNAME:CDT + END:DAYLIGHT + END:VTIMEZONE + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTACH:http://www.example.com/ + CATEGORIES:SPORTS EVENT,ENTERTAINMENT + CLASS:PRIVATE + DESCRIPTION:MIDWAY STADIUM\n + Big time game. MUST see.\n + Expected duration:2 hours\n + DTEND;TZID=America-Chicago:19970701T180000 + DTSTART;TZID=America-Chicago:19970702T160000 + DTSTAMP:19970614T190000Z + STATUS:CONFIRMED + LOCATION;VALUE=URI:http://stadium.example.com/ + PRIORITY:2 + RESOURCES:SCOREBOARD + SEQUENCE:3 + SUMMARY:ST. PAUL SAINTS -VS- DULUTH-SUPERIOR DUKES + UID:0981234-1234234-23@example.com + RELATED-TO:0981234-1234234-14@example.com + BEGIN:VALARM + TRIGGER:-PT2H + ACTION:DISPLAY + DESCRIPTION:You should be leaving for the game now. + END:VALARM + BEGIN:VALARM + TRIGGER:-PT30M + ACTION:AUDIO + END:VALARM + END:VEVENT + END:VCALENDAR + + The "RELATED-TO" field contains the "UID" property of a related + calendar event. The "SEQUENCE" property 3 indicates that this event + supersedes versions 0, 1, and 2. + + + + + + + + + + + + +Daboo Standards Track [Page 82] + +RFC 5546 iTIP December 2009 + + +4.1.5. Anniversaries or Events Attached to Entire Days + + This example demonstrates the use of the "VALUE" parameter to tie a + "VEVENT" to a day rather than a specific time. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:PUBLISH + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + DTSTAMP:19970614T190000Z + UID:0981234-1234234-23@example.com + DTSTART;VALUE=DATE:19970714 + RRULE:FREQ=YEARLY;INTERVAL=1 + SUMMARY: Bastille Day + END:VEVENT + END:VCALENDAR + +4.2. Group Event Examples + + Group events are distinguished from published events in that they + have "Attendees" and there is interaction between the "Attendees" and + the "Organizer" with respect to the event. Individual "A" requests a + meeting between individuals "A", "B", "C", and "D". Individual "B" + confirms attendance to the meeting. Individual "C" declines + attendance. Individual "D" tentatively confirms attendance. The + following table illustrates the message flow between these + individuals. "A", the CU scheduling the meeting, is referenced as + the "Organizer". + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 83] + +RFC 5546 iTIP December 2009 + + + +--------------+-----------------------+----------------------------+ + | Action | "Organizer" | Attendee | + +--------------+-----------------------+----------------------------+ + | Initiate a | "A" sends a REQUEST | | + | meeting | message to "B", "C", | | + | request | and "D". | | + | | | | + | Accept the | | "B" sends a REPLY message | + | meeting | | to "A" with its ATTENDEE | + | request | | PARTSTAT parameter set to | + | | | ACCEPTED. | + | | | | + | Decline the | | "C" sends a REPLY message | + | meeting | | to "A" with its ATTENDEE | + | request | | PARTSTAT parameter set to | + | | | DECLINED. | + | | | | + | Tentatively | | "D" sends a REPLY message | + | accept the | | to "A" with its ATTENDEE | + | meeting | | PARTSTAT parameter set to | + | request | | TENTATIVE. | + | | | | + | Confirm | "A" sends a REQUEST | | + | meeting | message to "B" and | | + | status with | "D" with updated | | + | Attendees | information. | | + +--------------+-----------------------+----------------------------+ + +4.2.1. A Group Event Request + + A sample meeting request is sent from "A" to "B", "C", and "D". "E" + is also sent a copy of the request but is not expected to attend and + need not reply. "E" illustrates how CUAs might implement an "FYI"- + type feature. Note the use of the "ROLE" parameter. The default + value for the "ROLE" parameter is "REQ-PARTICIPANT" and it need not + be enumerated. In this case, we are using the value "NON- + PARTICIPANT" to indicate "E" is a non-attending CU. The parameter is + not needed on other "Attendees" since "PARTICIPANT" is the default + value. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED;CN=A:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;CN=B:mailto:b@example.com + + + +Daboo Standards Track [Page 84] + +RFC 5546 iTIP December 2009 + + + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;CN=C:mailto:c@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;CN=Hal:mailto:d@example.com + ATTENDEE;RSVP=FALSE;CUTYPE=ROOM:conf_big@example.com + ATTENDEE;ROLE=NON-PARTICIPANT;RSVP=FALSE:mailto:e@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970701T200000Z + DTEND:19970701T2100000Z + SUMMARY:Conference + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.2.2. Reply to a Group Event Request + + "Attendee" "B" accepts the meeting. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED:mailto:b@example.com + ORGANIZER:mailto:a@example.com + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + REQUEST-STATUS:2.0;Success + DTSTAMP:19970612T190000Z + END:VEVENT + END:VCALENDAR + + "B" could have declined the meeting or indicated tentative acceptance + by setting the "ATTENDEE" "PARTSTAT" parameter to "DECLINED" or + "TENTATIVE", respectively. Also, "REQUEST-STATUS" is not required in + successful transactions. + +4.2.3. Update an Event + + The event is moved to a different time. The combination of the "UID" + property (unchanged) and the "SEQUENCE" (bumped to 1) properties + indicate the update. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + + + +Daboo Standards Track [Page 85] + +RFC 5546 iTIP December 2009 + + + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:c@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;CN=Hal:mailto:d@example.com + ATTENDEE;ROLE=NON-PARTICIPANT;RSVP=FALSE; + CUTYPE=ROOM:mailto:conf@example.com + ATTENDEE;ROLE=NON-PARTICIPANT;RSVP=FALSE:mailto:e@example.com + DTSTART:19970701T180000Z + DTEND:19970701T190000Z + SUMMARY:Phone Conference + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:1 + DTSTAMP:19970613T190000Z + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.2.4. Countering an Event Proposal + + "A" sends a "REQUEST" to "B" and "C". "B" makes a counter proposal + to "A" to change the time and location. + + "A" sends the following "REQUEST": + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:c@example.com + DTSTART:19970701T190000Z + DTEND:19970701T200000Z + SUMMARY:Discuss the Merits of the election results + LOCATION:Green Conference Room + UID:calsrv.example.com-873970198738777a@example.com + SEQUENCE:0 + DTSTAMP:19970611T190000Z + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + + + + + + +Daboo Standards Track [Page 86] + +RFC 5546 iTIP December 2009 + + + "B" sends "COUNTER" to "A", requesting changes to time and place. + "B" uses the "COMMENT" property to communicate a rationale for the + change. Note that the "SEQUENCE" property is not incremented on a + "COUNTER". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:COUNTER + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:c@example.com + DTSTART:19970701T160000Z + DTEND:19970701T170000Z + DTSTAMP:19970612T190000Z + SUMMARY:Discuss the Merits of the election results + LOCATION:Blue Conference Room + COMMENT:This time works much better and I think the big conference + room is too big + UID:calsrv.example.com-873970198738777a@example.com + SEQUENCE:0 + END:VEVENT + END:VCALENDAR + + "A" accepts the changes from "B". To accept a counter proposal, the + "Organizer" sends a new event "REQUEST" with an incremented sequence + number. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:c@example.com + DTSTAMP:19970613T190000Z + DTSTART:19970701T160000Z + DTEND:19970701T170000Z + SUMMARY:Discuss the Merits of the election results - changed to + meet B's schedule + LOCATION:Blue Conference Room + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:1 + STATUS:CONFIRMED + + + +Daboo Standards Track [Page 87] + +RFC 5546 iTIP December 2009 + + + END:VEVENT + END:VCALENDAR + + Instead, "A" rejects "B's" counter proposal. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:DECLINECOUNTER + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:b@example.com + COMMENT:Sorry, I cannot change this meeting time + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + DTSTAMP:19970614T190000Z + END:VEVENT + END:VCALENDAR + +4.2.5. Delegating an Event + + When delegating an event request to another "Calendar User", the + "Delegator" must both update the "Organizer" with a "REPLY" and send + a request to the "Delegate". There is currently no protocol + limitation to delegation depth. It is possible for the original + delegate to delegate the meeting to someone else, and so on. When a + request is delegated from one CUA to another, there are a number of + responsibilities required of the "Delegator". The "Delegator" MUST: + + o Send a "REPLY" to the "Organizer" with the following updates: + + A. The "Delegator's" "ATTENDEE" property "PARTSTAT" parameter is + set to "DELEGATED" and the "DELEGATED-TO" parameter is set to + the address of the "Delegate". + + B. Add an additional "ATTENDEE" property for the "Delegate" with + the "DELEGATED-FROM" property parameter set to the + "Delegator". + + C. Indicate whether they want to continue to receive updates when + the "Organizer" sends out updated versions of the event. + Setting the "RSVP" property parameter to "TRUE" will cause the + updates to be sent; setting it to "FALSE" causes no further + updates to be sent. Note that in either case, if the + "Delegate" declines the invitation, the "Delegator" will be + notified. + + + + + +Daboo Standards Track [Page 88] + +RFC 5546 iTIP December 2009 + + + o The "Delegator" MUST also send a copy of the original "REQUEST" + method to the "Delegate", with changes (A) and (B), as detailed + above applied. + + If the "Delegate" declines the meeting, the "Organizer" MUST send an + update "REQUEST" to the "Delegator" so that the "Delegator" may elect + to delegate the "REQUEST" to another CUA. + + +----------------+-----------------+--------------------------------+ + | Action | "Organizer" | Attendee | + +----------------+-----------------+--------------------------------+ + | Initiate a | "A" sends a | | + | meeting | REQUEST message | | + | request | to "B" and "C". | | + | | | | + | Delegate: "C" | | "C" sends a REPLY to "A" with | + | delegates to | | the ATTENDEE PARTSTAT | + | "E" | | parameter set to DELEGATED and | + | | | with a new ATTENDEE property | + | | | for "E". "E's" ATTENDEE | + | | | DELEGATED-FROM parameter is | + | | | set to "C". "C's" ATTENDEE | + | | | DELEGATED-TO parameter is set | + | | | to "E". "C" sends REQUEST | + | | | message to "E" with the | + | | | original meeting request | + | | | information. The PARTSTAT | + | | | property parameter for "C" is | + | | | set to DELEGATED and the | + | | | DELEGATED-TO parameter is set | + | | | to the address of "E". An | + | | | ATTENDEE property is added for | + | | | "E" and the DELEGATED-FROM | + | | | parameter is set to the | + | | | address of "C". | + | | | | + | Confirm | | "E" sends REPLY message to | + | meeting | | "A", and optionally to "C", | + | attendance | | with its PARTSTAT property | + | | | parameter set to ACCEPTED. | + | | | | + | Optional: | "A" sends | | + | Redistribute | REQUEST message | | + | meeting to | to "B", "C", | | + | Attendees | and "E". | | + +----------------+-----------------+--------------------------------+ + + + + + +Daboo Standards Track [Page 89] + +RFC 5546 iTIP December 2009 + + + "C" responds to the "Organizer". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=DELEGATED;DELEGATED- + TO="mailto:e@example.com":mailto:c@example.com + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + REQUEST-STATUS:2.0;Success + DTSTAMP:19970611T190000Z + END:VEVENT + END:VCALENDAR + + "Attendee" "C" delegates presence at the meeting to "E". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=DELEGATED;DELEGATED- + TO="mailto:e@example.com":mailto:c@example.com + ATTENDEE;RSVP=TRUE; + DELEGATED-FROM="mailto:c@example.com":mailto:e@example.com + DTSTART:19970701T180000Z + DTEND:19970701T200000Z + SUMMARY:Phone Conference + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + STATUS:CONFIRMED + DTSTAMP:19970611T190000Z + END:VEVENT + END:VCALENDAR + +4.2.6. Delegate Accepts the Meeting + + To accept a delegated meeting, the delegate, "E", sends the following + message to "A" and "C". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + + + +Daboo Standards Track [Page 90] + +RFC 5546 iTIP December 2009 + + + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=ACCEPTED;DELEGATED- + FROM="mailto:c@example.com":mailto:e@example.com + ATTENDEE;PARTSTAT=DELEGATED; + DELEGATED-TO="mailto:e@example.com":mailto:c@example.com + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + REQUEST-STATUS:2.0;Success + DTSTAMP:19970614T190000Z + END:VEVENT + END:VCALENDAR + +4.2.7. Delegate Declines the Meeting + + In this example, the "Delegate" declines the meeting request and sets + the "ATTENDEE" property "PARTSTAT" parameter to "DECLINED". The + "Organizer" SHOULD resend the "REQUEST" to "C" with the "PARTSTAT" + parameter of the "Delegate" set to "DECLINED". This lets the + "Delegator" know that the "Delegate" has declined and provides an + opportunity to the "Delegator" to either accept the request or + delegate it to another CU. + + "E" responds to "A" and "C". Note the use of the "COMMENT" property + "E" uses to indicate why the delegation was declined. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=DELEGATED; + DELEGATED-TO="mailto:e@example.com":mailto:c@example.com + ATTENDEE;PARTSTAT=DECLINED; + DELEGATED-FROM="mailto:c@example.com":mailto:e@example.com + COMMENT:Sorry, I will be out of town at that time. + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + REQUEST-STATUS:2.0;Success + DTSTAMP:19970614T190000Z + END:VEVENT + END:VCALENDAR + + "A" resends the "REQUEST" method to "C". "A" may also wish to + express the fact that the item was delegated in the "COMMENT" + property. + + + + +Daboo Standards Track [Page 91] + +RFC 5546 iTIP December 2009 + + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=DECLINED; + DELEGATED-FROM="mailto:c@example.com":mailto:e@example.com + ATTENDEE;RSVP=TRUE:mailto:c@example.com + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:0 + SUMMARY:Phone Conference + DTSTART:19970701T180000Z + DTEND:19970701T200000Z + DTSTAMP:19970614T200000Z + COMMENT:DELEGATE (ATTENDEE mailto:e@example.com) DECLINED YOUR + INVITATION + END:VEVENT + END:VCALENDAR + +4.2.8. Forwarding an Event Request + + The protocol does not prevent an "Attendee" from "forwarding" a + "VEVENT" calendar component to other "Calendar Users". Forwarding + differs from delegation in that the forwarded "Calendar User" (often + referred to as a "Party Crasher") does not replace the forwarding + "Calendar User". Implementations are not required to add the "Party + Crasher" to the "Attendee" list, and hence there is no guarantee that + a "Party Crasher" will receive additional updates to the event. The + forwarding "Calendar User" SHOULD NOT add the "Party Crasher" to the + "Attendee" list. The "Organizer" MAY add the forwarded "Calendar + User" to the "Attendee" list. + +4.2.9. Cancel a Group Event + + Individual "A" requests a meeting between individuals "A", "B", "C", + and "D". Individual "B" declines attendance to the meeting. + Individual "A" decides to cancel the meeting. The following table + illustrates the sequence of messages that would be exchanged between + these individuals. + + Messages related to a previously canceled event ("SEQUENCE" property + value is less than the "SEQUENCE" property value of the "CANCEL" + message) MUST be ignored. + + + + + + + +Daboo Standards Track [Page 92] + +RFC 5546 iTIP December 2009 + + + +-------------+---------------------+-------------------------------+ + | Action | Organizer | Attendee | + +-------------+---------------------+-------------------------------+ + | Initiate a | "A" sends a REQUEST | | + | meeting | message to "B", | | + | request | "C", and "D". | | + | | | | + | Decline the | | "B" sends a REPLY message to | + | meeting | | "A" with its PARTSTAT | + | request | | parameter set to DECLINED. | + | | | | + | Cancel the | "A" sends a CANCEL | | + | meeting | message to "B", | | + | | "C", and "D". | | + +-------------+---------------------+-------------------------------+ + + This example shows how "A" cancels the event. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:CANCEL + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;CUTYPE=INDIVIDUAL;mailto:a@example.com + ATTENDEE;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;CUTYPE=INDIVIDUAL:mailto:c@example.com + ATTENDEE;CUTYPE=INDIVIDUAL:mailto:d@example.com + COMMENT:Mr. B cannot attend. It's raining. Lets cancel. + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:1 + STATUS:CANCELLED + DTSTAMP:19970613T190000Z + END:VEVENT + END:VCALENDAR + +4.2.10. Removing Attendees + + "A" wants to remove "B" from a meeting. This is done by sending a + "CANCEL" to "B" and removing "B" from the "Attendee" list in the + master copy of the event. + + + + + + + + + + +Daboo Standards Track [Page 93] + +RFC 5546 iTIP December 2009 + + + +--------------------+-----------------------------------+----------+ + | Action | Organizer | Attendee | + +--------------------+-----------------------------------+----------+ + | Remove "B" as an | "A" sends a CANCEL message to | | + | Attendee | "B". | | + | | | | + | Update the master | "A" optionally sends the updated | | + | copy of the event | event to the remaining Attendees. | | + +--------------------+-----------------------------------+----------+ + + The original meeting includes "A", "B", "C", and "D". The example + below shows the "CANCEL" that "A" sends to "B". Note that in the + example below, the "STATUS" property is omitted. This is used when + the meeting itself is cancelled and not when the intent is to remove + an "Attendee" from the event. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:CANCEL + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE:mailto:b@example.com + COMMENT:You're off the hook for this meeting + UID:calsrv.example.com-873970198738777@example.com + DTSTAMP:19970613T193000Z + SEQUENCE:1 + END:VEVENT + END:VCALENDAR + + The updated master copy of the event is shown below. The "Organizer" + MAY resend the updated event to the remaining "Attendees". Note that + "B" has been removed. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;CUTYPE=INDIVIDUAL:mailto:c@example.com + ATTENDEE;CUTYPE=INDIVIDUAL:mailto:d@example.com + ATTENDEE;CUTYPE=ROOM:mailto:cr_big@example.com + ATTENDEE;ROLE=NON-PARTICIPANT; + RSVP=FALSE:mailto:e@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970701T200000Z + + + +Daboo Standards Track [Page 94] + +RFC 5546 iTIP December 2009 + + + DTEND:19970701T203000Z + SUMMARY:Phone Conference + UID:calsrv.example.com-873970198738777@example.com + SEQUENCE:2 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.2.11. Replacing the Organizer + + The scenario for this example begins with "A" as the "Organizer" for + a recurring meeting with "B", "C", and "D". "A" receives a new job + offer in another country and drops out of touch. "A" left no + forwarding address or way to be reached. Using out-of-band + communication, the other "Attendees" eventually learn what has + happened and reach an agreement that "B" should become the new + "Organizer" for the meeting. To do this, "B" sends out a new version + of the event and the other "Attendees" agree to accept "B" as the new + "Organizer". "B" also removes "A" from the event. + + When the "Organizer" is replaced, the "SEQUENCE" property value MUST + be incremented. + + This is the message "B" sends to "C" and "D". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:b@example.com + ATTENDEE;ROLE=CHAIR;STATUS=ACCEPTED:mailto:b@example.com + ATTENDEE;CUTYPE=INDIVIDUAL:mailto:c@example.com + ATTENDEE;CUTYPE=INDIVIDUAL:mailto:d@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970701T200000Z + DTEND:19970701T203000Z + RRULE:FREQ=WEEKLY + SUMMARY:Phone Conference + UID:123456@example.com + SEQUENCE:1 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + + + + + + +Daboo Standards Track [Page 95] + +RFC 5546 iTIP December 2009 + + +4.3. Busy Time Examples + + Busy time objects can be used in several ways. First, a CU may + request busy time from another CU for a specific range of time. That + request can be answered with a busy time "REPLY". Additionally, a CU + may simply publish their busy time for a given interval and point + other CUs to the published location. The following examples outline + both scenarios. + +4.3.1. Publish Busy Time + + Individual "A" publishes busy time for one week. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + METHOD:PUBLISH + BEGIN:VFREEBUSY + DTSTAMP:19980101T124100Z + ORGANIZER:mailto:a@example.com + DTSTART:19980101T124200Z + DTEND:19980108T124200Z + FREEBUSY:19980101T180000Z/19980101T190000Z + FREEBUSY:19980103T020000Z/19980103T050000Z + FREEBUSY:19980107T020000Z/19980107T050000Z + FREEBUSY:19980113T000000Z/19980113T010000Z + FREEBUSY:19980115T190000Z/19980115T200000Z + FREEBUSY:19980115T220000Z/19980115T230000Z + FREEBUSY:19980116T013000Z/19980116T043000Z + END:VFREEBUSY + END:VCALENDAR + +4.3.2. Request Busy Time + + Individual "A" requests busy time from individuals "B" and "C". + Individuals "B" and "C" reply with busy time data to individual "A". + The following table illustrates the sequence of messages that would + be exchanged between these individuals. + + + + + + + + + + + + + +Daboo Standards Track [Page 96] + +RFC 5546 iTIP December 2009 + + + +---------------------+--------------------+------------------------+ + | Action | Organizer | Attendee | + +---------------------+--------------------+------------------------+ + | Initiate a busy | "A" sends REQUEST | | + | time request | message to "B" and | | + | | "C". | | + | | | | + | Reply to the BUSY | | "B" sends a REPLY | + | request with BUSY | | message to "A" with | + | time data | | busy time data. | + +---------------------+--------------------+------------------------+ + + "A" sends a "REQUEST" to "B" and "C" for busy time. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VFREEBUSY + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR:mailto:a@example.com + ATTENDEE:mailto:b@example.com + ATTENDEE:mailto:c@example.com + DTSTAMP:19970613T190000Z + DTSTART:19970701T080000Z + DTEND:19970701T200000 + UID:calsrv.example.com-873970198738777@example.com + END:VFREEBUSY + END:VCALENDAR + +4.3.3. Reply to a Busy Time Request + + "B" sends a "REPLY" method type of a "VFREEBUSY" calendar component + to "A". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VFREEBUSY + ORGANIZER:mailto:a@example.com + ATTENDEE:mailto:b@example.com + DTSTART:19970701T080000Z + DTEND:19970701T200000Z + UID:calsrv.example.com-873970198738777@example.com + FREEBUSY:19970701T090000Z/PT1H,19970701T140000Z/PT30M + DTSTAMP:19970613T190030Z + END:VFREEBUSY + + + +Daboo Standards Track [Page 97] + +RFC 5546 iTIP December 2009 + + + END:VCALENDAR + + "B" is busy from 09:00 to 10:00 and from 14:00 to 14:30. + +4.4. Recurring Event and Time Zone Examples + +4.4.1. A Recurring Event Spanning Time Zones + + This event describes a weekly phone conference. The "Attendees" are + each in a different time zone. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VTIMEZONE + TZID:America-SanJose + TZURL:http://example.com/tz/America-SanJose + BEGIN:STANDARD + DTSTART:19671029T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZOFFSETFROM:-0700 + TZOFFSETTO:-0800 + TZNAME:PST + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19870405T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZOFFSETFROM:-0800 + TZOFFSETTO:-0700 + TZNAME:PDT + END:DAYLIGHT + END:VTIMEZONE + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED; + CUTYPE=INDIVIDUAL:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:b@example.fr + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:c@example.jp + DTSTAMP:19970613T190030Z + DTSTART;TZID=America-SanJose:19970701T140000 + DTEND;TZID=America-SanJose:19970701T150000 + RRULE:FREQ=WEEKLY;COUNT=20;WKST=SU;BYDAY=TU + RDATE;TZID=America-SanJose:19970910T140000 + EXDATE;TZID=America-SanJose:19970909T140000 + EXDATE;TZID=America-SanJose:19971028T140000 + SUMMARY:Weekly Phone Conference + UID:calsrv.example.com-873970198738777@example.com + + + +Daboo Standards Track [Page 98] + +RFC 5546 iTIP December 2009 + + + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + The first component of this iCalendar object is the time zone + component. The "DTSTART" date coincides with the first instance of + the "RRULE" property. + + The recurring meeting is defined in a particular time zone, + presumably that of the originator. The client for each "Attendee" + has the responsibility of determining the recurrence time in the + "Attendee's" time zone. + + The repeating event starts on Tuesday, July 1, 1997 at 2:00pm PDT + (UTC-7). "Attendee" B@example.fr is in France, where the local time + on this date is 9 hours ahead of PDT, or 23:00 CEST (UTC+2). + "Attendee" C@example.jp is in Japan, where local time is 16 hours + ahead of PDT, or Wednesday, July 2 at 06:00 JST (UTC+9). The event + repeats weekly on Tuesdays (in PST/PDT). The "RRULE" property + results in 20 instances. The last instance falls on Tuesday, + November 11, 1997 2:00pm PST. The "RDATE" property adds another + instance: WED, 10-SEP-1997 2:00 PM PDT. + + There are also two exception dates to the recurrence rule. The first + one is: + + o TUE, 09-SEP-1997 14:00 PDT (UTC-7) + + o TUE, 09-SEP-1997 23:00 CEST (UTC+2) + + o WED, 10-SEP-1997 06:00 JST (UTC+9) + + + and the second is: + + o TUE, 28-OCT-1997 14:00 PST (UTC-8) + + o TUE, 28-OCT-1997 23:00 CET (UTC+1) + + o WED, 29-OCT-1997 07:00 JST (UTC+9) + +4.4.2. Modify a Recurring Instance + + In this example, the "Organizer" issues a recurring meeting. Later, + the "Organizer" changes an instance of the event by changing the + "DTSTART" property. Note the use of "RECURRENCE-ID" property and + "SEQUENCE" property in the second request. + + + +Daboo Standards Track [Page 99] + +RFC 5546 iTIP December 2009 + + + Original Request: + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:guid-1@example.com + SEQUENCE:0 + RRULE:FREQ=MONTHLY;BYMONTHDAY=1;UNTIL=19980901T210000Z + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE:mailto:b@example.com + ATTENDEE:mailto:c@example.com + ATTENDEE:mailto:d@example.com + DESCRIPTION:IETF-C&S Conference Call + CLASS:PUBLIC + SUMMARY:IETF Calendaring Working Group Meeting + DTSTART:19970601T210000Z + DTEND:19970601T220000Z + LOCATION:Conference Call + DTSTAMP:19970526T083000Z + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + The event request below is to change the time of a specific instance. + This changes the July 1st instance to July 3rd. + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:guid-1@example.com + RECURRENCE-ID:19970701T210000Z + SEQUENCE:1 + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE:mailto:b@example.com + ATTENDEE:mailto:c@example.com + ATTENDEE:mailto:d@example.com + DESCRIPTION:IETF-C&S Conference Call + CLASS:PUBLIC + SUMMARY:IETF Calendaring Working Group Meeting + DTSTART:19970703T210000Z + DTEND:19970703T220000Z + LOCATION:Conference Call + + + +Daboo Standards Track [Page 100] + +RFC 5546 iTIP December 2009 + + + DTSTAMP:19970626T093000Z + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.4.3. Cancel an Instance + + In this example, the "Organizer" of a recurring event deletes the + August 1st instance. + + BEGIN:VCALENDAR + METHOD:CANCEL + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:guid-1@example.com + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE:mailto:b@example.com + ATTENDEE:mailto:c@example.com + ATTENDEE:mailto:d@example.com + RECURRENCE-ID:19970801T210000Z + SEQUENCE:2 + STATUS:CANCELLED + DTSTAMP:19970721T093000Z + END:VEVENT + END:VCALENDAR + +4.4.4. Cancel a Recurring Event + + In this example, the "Organizer" wishes to cancel the entire + recurring event and any exceptions. + + BEGIN:VCALENDAR + METHOD:CANCEL + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:guid-1@example.com + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE:mailto:b@example.com + ATTENDEE:mailto:c@example.com + ATTENDEE:mailto:d@example.com + DTSTAMP:19970721T103000Z + STATUS:CANCELLED + SEQUENCE:3 + END:VEVENT + + + +Daboo Standards Track [Page 101] + +RFC 5546 iTIP December 2009 + + + END:VCALENDAR + +4.4.5. Change All Future Instances + + This example changes the meeting location from a conference call to + Seattle, starting September 1 and extending to all future instances. + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:guid-1@example.com + RECURRENCE-ID;THISANDFUTURE:19970901T210000Z + SEQUENCE:3 + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + ATTENDEE;RSVP=TRUE:mailto:c@example.com + ATTENDEE;RSVP=TRUE:mailto:d@example.com + DESCRIPTION:IETF-C&S Discussion + CLASS:PUBLIC + SUMMARY:IETF Calendaring Working Group Meeting + DTSTART:19970901T210000Z + DTEND:19970901T220000Z + LOCATION:Building 32, Microsoft, Seattle, WA + DTSTAMP:19970526T083000Z + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.4.6. Add a New Instance to a Recurring Event + + This example adds a one-time additional instance to the recurring + event. "Organizer" adds a second July meeting on the 15th. + + BEGIN:VCALENDAR + METHOD:ADD + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:123456789@example.com + SEQUENCE:4 + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + ATTENDEE;RSVP=TRUE:mailto:c@example.com + ATTENDEE;RSVP=TRUE:mailto:d@example.com + + + +Daboo Standards Track [Page 102] + +RFC 5546 iTIP December 2009 + + + DESCRIPTION:IETF-C&S Conference Call + CLASS:PUBLIC + SUMMARY:IETF Calendaring Working Group Meeting + DTSTART:19970715T210000Z + DTEND:19970715T220000Z + LOCATION:Conference Call + DTSTAMP:19970629T093000Z + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.4.7. Add a New Series of Instances to a Recurring Event + + The scenario for this example involves an ongoing meeting, originally + set up to occur every Tuesday. The "Organizer" later decides that + the meetings need to be on Tuesdays and Thursdays. + + The original event: + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:123456789@example.com + SEQUENCE:0 + RRULE:WKST=SU;BYDAY=TU;FREQ=WEEKLY + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + SUMMARY:Review Accounts + DTSTART:19980303T210000Z + DTEND:19980303T220000Z + LOCATION:The White Room + DTSTAMP:19980301T093000Z + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + The entire event can be rescheduled using a "REQUEST". This is done + by using the "UID" of the event to reschedule and including the + modified "RRULE". Note that since this is an entire rescheduling of + the event, any instance-specific information will be lost, unless + explicitly included with the update "REQUEST". + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + + + +Daboo Standards Track [Page 103] + +RFC 5546 iTIP December 2009 + + + VERSION:2.0 + BEGIN:VEVENT + UID:123456789@example.com + SEQUENCE:7 + RRULE:WKST=SU;BYDAY=TU,TH;FREQ=WEEKLY + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + SUMMARY:Review Accounts + DTSTART:19980303T210000Z + DTEND:19980303T220000Z + DTSTAMP:19980303T193000Z + LOCATION:The White Room + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.4.8. Refreshing a Recurring Event + + The next series of examples illustrate how an "Organizer" would + respond to a "REFRESH" submitted by an "Attendee" after a series of + instance-specific modifications. To convey all instance-specific + changes, the "Organizer" must provide the latest event description + and the relevant instances. The first three examples show the + history, including the initial "VEVENT" request and subsequent + instance changes, and finally the "REFRESH". + + Original Request: + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:123456789@example.com + SEQUENCE:0 + RDATE:19980304T180000Z + RDATE:19980311T180000Z + RDATE:19980318T180000Z + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + SUMMARY:Review Accounts + DTSTART:19980304T180000Z + DTEND:19980304T200000Z + DTSTAMP:19980303T193000Z + LOCATION:Conference Room A + STATUS:CONFIRMED + + + +Daboo Standards Track [Page 104] + +RFC 5546 iTIP December 2009 + + + END:VEVENT + END:VCALENDAR + + Organizer changes 2nd instance location and time: + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:123456789@example.com + SEQUENCE:1 + RECURRENCE-ID:19980311T180000Z + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + SUMMARY:Review Accounts + DTSTART:19980311T160000Z + DTEND:19980311T180000Z + DTSTAMP:19980306T193000Z + LOCATION:The Small conference room + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + Organizer adds a 4th instance of the meeting using the "ADD" method. + + BEGIN:VCALENDAR + METHOD:ADD + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:123456789@example.com + SEQUENCE:2 + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + SUMMARY:Review Accounts + DTSTART:19980315T180000Z + DTEND:19980315T200000Z + DTSTAMP:19980307T193000Z + LOCATION:Conference Room A + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + + + + + +Daboo Standards Track [Page 105] + +RFC 5546 iTIP December 2009 + + + If "B" requests a "REFRESH", "A" responds with the following to + capture all instance-specific data. In this case, both the initial + request and an additional "VEVENT" that specifies the instance- + specific data are included. Because these are both of the same type + (they are both "VEVENTS"), they can be conveyed in the same iCalendar + object. + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:123456789@example.com + SEQUENCE:2 + RDATE:19980304T180000Z + RDATE:19980311T160000Z + RDATE:19980315T180000Z + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + SUMMARY:Review Accounts + DTSTART:19980304T180000Z + DTEND:19980304T200000Z + DTSTAMP:19980303T193000Z + LOCATION:Conference Room A + STATUS:CONFIRMED + END:VEVENT + BEGIN:VEVENT + SEQUENCE:2 + UID:123456789@example.com + RECURRENCE-ID:19980311T160000Z + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + SUMMARY:Review Accounts + DTSTART:19980311T160000Z + DTEND:19980304T180000Z + DTSTAMP:19980306T193000Z + LOCATION:The Small conference room + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.4.9. Counter an Instance of a Recurring Event + + In this example, one of the "Attendees" counters the "DTSTART" + property of the proposed second July meeting. + + + + + +Daboo Standards Track [Page 106] + +RFC 5546 iTIP December 2009 + + + BEGIN:VCALENDAR + METHOD:COUNTER + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:guid-1@example.com + RECURRENCE-ID:19970715T210000Z + SEQUENCE:4 + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;RSVP=TRUE:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + ATTENDEE;RSVP=TRUE:mailto:c@example.com + ATTENDEE;RSVP=TRUE:mailto:d@example.com + DESCRIPTION:IETF-C&S Conference Call + CLASS:PUBLIC + SUMMARY:IETF Calendaring Working Group Meeting + DTSTART:19970715T220000Z + DTEND:19970715T230000Z + LOCATION:Conference Call + COMMENT:May we bump this by an hour? I have a conflict + DTSTAMP:19970629T094000Z + END:VEVENT + END:VCALENDAR + +4.4.10. Error Reply to a Request + + The following example illustrates a scenario where a meeting is + proposed containing an unsupported property and a bad property. + + Original Request: + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:guid-1@example.com + SEQUENCE:0 + RRULE:FREQ=MONTHLY;BYMONTHDAY=1 + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + ATTENDEE;RSVP=TRUE:mailto:c@example.com + ATTENDEE;RSVP=TRUE:mailto:d@example.com + DESCRIPTION:IETF-C&S Conference Call + CLASS:PUBLIC + SUMMARY:IETF Calendaring Working Group Meeting + DTSTART:19970601T210000Z + + + +Daboo Standards Track [Page 107] + +RFC 5546 iTIP December 2009 + + + DTEND:19970601T220000Z + DTSTAMP:19970602T094000Z + LOCATION:Conference Call + STATUS:CONFIRMED + FOO:BAR + END:VEVENT + END:VCALENDAR + + "B" responds to indicate that "RRULE" is not supported and that an + unrecognized property was encountered. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE:mailto:b@example.com + REQUEST-STATUS:3.0;Invalid Property Name;FOO + UID:guid-1@example.com + SEQUENCE:0 + DTSTAMP:19970603T094000Z + END:VEVENT + END:VCALENDAR + +4.5. Group To-Do Examples + + Individual "A" creates a group task in which individuals "A", "B", + "C", and "D" will participate. Individual "B" confirms acceptance of + the task. Individual "C" declines the task. Individual "D" + tentatively accepts the task. The following table illustrates the + sequence of messages that would be exchanged between these + individuals. Individual "A" then issues a "REQUEST" method to obtain + the status of the to-do from each participant. The response + indicates the individual "Attendee's" completion status. The table + below illustrates the message flow. + + + + + + + + + + + + + + + +Daboo Standards Track [Page 108] + +RFC 5546 iTIP December 2009 + + + +--------------+------------------------+---------------------------+ + | Action | Organizer | Attendee | + +--------------+------------------------+---------------------------+ + | Initiate a | "A" sends a REQUEST | | + | to-do | message to "B", "C", | | + | request | and "D". | | + | | | | + | Accept the | | "B" sends a REPLY message | + | to-do | | to "A" with its PARTSTAT | + | request | | parameter set to | + | | | ACCEPTED. | + | | | | + | Decline the | | "C" sends a REPLY message | + | to-do | | to "A" with its PARTSTAT | + | request | | parameter set to | + | | | DECLINED. | + | | | | + | Tentatively | | "D" sends a REPLY message | + | accept the | | to "A" with its PARTSTAT | + | to-do | | parameter set to | + | request | | TENTATIVE. | + | | | | + | Check | "A" sends a REQUEST | | + | Attendee | message to "B" and "D" | | + | completion | with current | | + | status | information. | | + | | | | + | Attendee | | "B" sends a REPLY message | + | indicates | | indicating percent | + | percent | | complete. | + | complete | | | + | | | | + | Attendee | | "D" sends a REPLY message | + | indicates | | indicating completion. | + | completion | | | + +--------------+------------------------+---------------------------+ + +4.5.1. A VTODO Request + + A sample "REQUEST" for a "VTODO" calendar component that "A" sends to + "B", "C", and "D". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VTODO + ORGANIZER:mailto:a@example.com + + + +Daboo Standards Track [Page 109] + +RFC 5546 iTIP December 2009 + + + ATTENDEE;ROLE=CHAIR:mailto:a@example.com + ATTENDEE;RSVP=TRUE:mailto:b@example.com + ATTENDEE;RSVP=TRUE:mailto:c@example.com + ATTENDEE;RSVP=TRUE:mailto:d@example.com + DTSTART:19970701T170000Z + DUE:19970722T170000Z + PRIORITY:1 + SUMMARY:Create the requirements document + UID:calsrv.example.com-873970198738777-00@example.com + SEQUENCE:0 + DTSTAMP:19970717T200000Z + STATUS:NEEDS-ACTION + END:VTODO + END:VCALENDAR + +4.5.2. A VTODO Reply + + "B" accepts the to-do. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VTODO + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=ACCEPTED:mailto:b@example.com + UID:calsrv.example.com-873970198738777-00@example.com + COMMENT:I'll send you my input by email + SEQUENCE:0 + DTSTAMP:19970717T203000Z + REQUEST-STATUS:2.0;Success + END:VTODO + END:VCALENDAR + + "B" could have declined the "VTODO" or indicated tentative acceptance + by setting the "PARTSTAT" property parameter sequence to "DECLINED" + or "TENTATIVE", respectively. + +4.5.3. A VTODO Request for Updated Status + + "A" requests status from all "Attendees". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VTODO + ORGANIZER:mailto:a@example.com + + + +Daboo Standards Track [Page 110] + +RFC 5546 iTIP December 2009 + + + ATTENDEE;ROLE=CHAIR:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:d@example.com + UID:calsrv.example.com-873970198738777-00@example.com + SUMMARY:Create the requirements document + PRIORITY:1 + SEQUENCE:0 + STATUS:IN-PROCESS + DTSTART:19970701T170000Z + DTSTAMP:19970717T230000Z + END:VTODO + END:VCALENDAR + +4.5.4. A Reply: Percent-Complete + + A reply indicating the task being worked on and that "B" is 75% + complete with "B's" part of the assignment. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VTODO + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=IN-PROCESS:mailto:b@example.com + PERCENT-COMPLETE:75 + UID:calsrv.example.com-873970198738777-00@example.com + DTSTAMP:19970717T233000Z + SEQUENCE:0 + END:VTODO + END:VCALENDAR + +4.5.5. A Reply: Completed + + A reply indicating that "D" completed "D's" part of the assignment. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VTODO + ORGANIZER:mailto:a@example.com + ATTENDEE;PARTSTAT=COMPLETED:mailto:d@example.com + UID:calsrv.example.com-873970198738777-00@example.com + DTSTAMP:19970717T233000Z + SEQUENCE:0 + END:VTODO + END:VCALENDAR + + + +Daboo Standards Track [Page 111] + +RFC 5546 iTIP December 2009 + + +4.5.6. An Updated VTODO Request + + "Organizer" "A" resends the "VTODO" calendar component. "A" sets the + overall completion for the to-do at 40%. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VTODO + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE;PARTSTAT=ACCEPTED;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;PARTSTAT=COMPLETED;CUTYPE=INDIVIDUAL:mailto:d@example.com + DTSTART:19970701T170000Z + DUE:19970722T170000Z + PRIORITY:1 + SUMMARY:Create the requirements document + UID:calsrv.example.com-873970198738777-00@example.com + SEQUENCE:1 + DTSTAMP:19970718T100000Z + STATUS:IN-PROCESS + PERCENT-COMPLETE:40 + END:VTODO + END:VCALENDAR + +4.5.7. Recurring VTODOs + + The following examples relate to recurring "VTODO" calendar + components. + +4.5.7.1. Request for a Recurring VTODO + + In this example, "A" sends a recurring "VTODO" calendar component to + "B" and "D". + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VTODO + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR:mailto:a@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:b@example.com + ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL:mailto:d@example.com + RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR + DTSTART:19980101T100000Z + DUE:19980103T100000Z + + + +Daboo Standards Track [Page 112] + +RFC 5546 iTIP December 2009 + + + SUMMARY:Send Status Reports to Area Managers + UID:calsrv.example.com-873970198738777-00@example.com + SEQUENCE:0 + DTSTAMP:19970717T200000Z + STATUS:NEEDS-ACTION + PRIORITY:1 + END:VTODO + END:VCALENDAR + +4.5.7.2. Replying to an Instance of a Recurring VTODO + + In this example, "B" updates "A" on a single instance of the "VTODO" + calendar component. + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REPLY + VERSION:2.0 + BEGIN:VTODO + ATTENDEE;PARTSTAT=IN-PROCESS:mailto:b@example.com + PERCENT-COMPLETE:75 + UID:calsrv.example.com-873970198738777-00@example.com + DTSTAMP:19970717T233000Z + RECURRENCE-ID:19980101T170000Z + SEQUENCE:1 + END:VTODO + END:VCALENDAR + +4.6. Journal Examples + + The iCalendar object below describes a single journal entry for + October 2, 1997. The "RELATED-TO" property references the phone + conference event for which minutes were taken. + + BEGIN:VCALENDAR + METHOD:PUBLISH + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VJOURNAL + DTSTART:19971002T200000Z + DTSTAMP:19970717T233100Z + ORGANIZER:mailto:a@example.com + SUMMARY:Phone conference minutes + DESCRIPTION:The editors meeting was held on October 1, 1997. + Details are in the attached document. + UID:0981234-1234234-2410@example.com + RELATED-TO:0981234-1234234-2402-35@example.com + ATTACH:ftp://ftp.example.com/pub/ed/minutes100197.txt + + + +Daboo Standards Track [Page 113] + +RFC 5546 iTIP December 2009 + + + END:VJOURNAL + END:VCALENDAR + +4.7. Other Examples + +4.7.1. Event Refresh + + Refresh the event with a "UID" property value of + "guid-1-12345@example.com": + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REFRESH + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE:mailto:b@example.com + ATTENDEE:mailto:c@example.com + ATTENDEE:mailto:d@example.com + UID:guid-1-12345@example.com + DTSTAMP:19970603T094000 + END:VEVENT + END:VCALENDAR + +4.7.2. Bad RECURRENCE-ID + + Component instances are identified by the combination of "UID", + "RECURRENCE-ID", and "SEQUENCE". When an "Organizer" sends an iTIP + message to an "Attendee", there are three cases in which an instance + cannot be found. They are: + + 1. The component with the referenced "UID" and "RECURRENCE-ID" has + been found but the "SEQUENCE" number in the calendar store does + not match that of the iTIP message. + + 2. The component with the referenced "UID" has been found, the + "SEQUENCE" numbers match, but the "RECURRENCE-ID" cannot be + found. + + 3. The "UID" and "SEQUENCE" numbers are found but the CUA does not + support recurrences. + + In case (1), two things can happen. If the "SEQUENCE" number of the + "Attendee's" instance is larger than that in the "Organizer's" + message, then the "Attendee" is receiving an out-of-sequence message + and MUST ignore it. If the "SEQUENCE" number of the "Attendee's" + instance is smaller, then the "Organizer" is sending out a newer + + + +Daboo Standards Track [Page 114] + +RFC 5546 iTIP December 2009 + + + version of the component and the "Attendee's" version needs to be + updated. Since one or more updates have been missed, the "Attendee" + SHOULD send a "REFRESH" message to the "Organizer" to get an updated + version of the event. + + In case (2), something has gone wrong. Both the "Organizer" and the + "Attendee" should have the same instances, but the "Attendee" does + not have the referenced instance. In this case, the "Attendee" + SHOULD send a "REFRESH" to the "Organizer" to get an updated version + of the event. + + In case (3), the limitations of the "Attendee's" CUA makes it + impossible to match an instance other than the single instance + scheduled. In this case, the "Attendee" need not send a "REFRESH" to + the "Organizer". + + The example below shows a sequence in which an "Attendee" sends a + "REFRESH" to the "Organizer". + + +-------------------------+--------------------+--------------------+ + | Action | Organizer | Attendee | + +-------------------------+--------------------+--------------------+ + | Update an instance | "A" sends REQUEST | | + | request | message to "B". | | + | | | | + | Attendee requests | | "B" sends a | + | refresh because | | REFRESH message to | + | RECURRENCE-ID was not | | "A". | + | found | | | + | | | | + | Refresh the entire | "A" sends the | | + | event | latest copy of the | | + | | event to "B" | | + | | | | + | Attendee handles the | | "B" updates to the | + | request and updates the | | latest copy of the | + | instance | | meeting. | + +-------------------------+--------------------+--------------------+ + + Request from "A": + + BEGIN:VCALENDAR + METHOD:REQUEST + PRODID:-//Example/ExampleCalendarClient//EN + VERSION:2.0 + BEGIN:VEVENT + UID:example-12345@example.com + SEQUENCE:3 + + + +Daboo Standards Track [Page 115] + +RFC 5546 iTIP December 2009 + + + RRULE:FREQ=WEEKLY + RDATE;VALUE=PERIOD:19970819T210000Z/199700819T220000Z + ORGANIZER:mailto:a@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com + ATTENDEE:mailto:b@example.com + DESCRIPTION:IETF-C&S Conference Call + SUMMARY:IETF Calendaring Working Group Meeting + DTSTART:19970801T210000Z + DTEND:19970801T220000Z + RECURRENCE-ID:19970809T210000Z + DTSTAMP:19970726T083000 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + "B" has the event with "UID" property "example-12345@example.com", + but "B's" "SEQUENCE" property value is "1" and the event does not + have an instance at the specified recurrence time. This means that + "B" has missed at least one update and needs a new copy of the event. + "B" requests the latest copy of the event with the following refresh + message: + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REFRESH + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:a@example.com + ATTENDEE:mailto:b@example.com + UID:example-12345@example.com + DTSTAMP:19970603T094000 + END:VEVENT + END:VCALENDAR + +5. Application Protocol Fallbacks + +5.1. Partial Implementation + + Applications that support this specification are not required to + support the entire protocol. The following describes how methods and + properties SHOULD "fallback" in applications that do not support the + complete protocol. If a method or property is not addressed in this + section, it may be ignored. + + + + + + + + +Daboo Standards Track [Page 116] + +RFC 5546 iTIP December 2009 + + +5.1.1. Event-Related Fallbacks + + +----------------+--------------------------------------------------+ + | Method | Fallback | + +----------------+--------------------------------------------------+ + | PUBLISH | Required | + | REQUEST | PUBLISH | + | REPLY | Required | + | ADD | Required if recurrences supported; otherwise, | + | | reply with a REQUEST-STATUS "2.8; Success, | + | | repeating event ignored. Scheduled as a single | + | | component", and schedule as a single component. | + | CANCEL | Required | + | REFRESH | Required | + | COUNTER | Reply with "Not Supported". | + | DECLINECOUNTER | Required if COUNTER is implemented for VEVENTs; | + | | otherwise, reply with "Not Supported". | + +----------------+--------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | iCalendar | Fallback | + | Property | | + +-----------------+-------------------------------------------------+ + | CALSCALE | Ignore - assume GREGORIAN. | + | PRODID | Ignore | + | METHOD | Required as described in the Method list above. | + | VERSION | Ignore | + +-----------------+-------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | Event-Related | Fallback | + | Components | | + +-----------------+-------------------------------------------------+ + | VALARM | Reply with "Not Supported". | + | VTIMEZONE | Required if any DateTime value refers to a time | + | | zone. | + +-----------------+-------------------------------------------------+ + + + + + + + + + + + + + + +Daboo Standards Track [Page 117] + +RFC 5546 iTIP December 2009 + + + +-----------------+-------------------------------------------------+ + | Component | Fallback | + | Property | | + +-----------------+-------------------------------------------------+ + | ATTACH | Ignore | + | ATTENDEE | Required if METHOD is REQUEST; otherwise, | + | | ignore. | + | CATEGORIES | Ignore | + | CLASS | Ignore | + | COMMENT | Ignore | + | COMPLETED | Ignore | + | CONTACT | Ignore | + | CREATED | Ignore | + | DESCRIPTION | Ignore | + | DURATION | Required | + | DTSTAMP | Required | + | DTSTART | Required | + | DTEND | Required | + | EXDATE | Ignore | + | GEO | Ignore | + | LAST-MODIFIED | Ignore | + | LOCATION | Required | + | ORGANIZER | Required if METHOD is REQUEST; otherwise, | + | | ignore. | + | PRIORITY | Ignore | + | RELATED-TO | Ignore | + | RDATE | Ignore | + | RRULE | Ignore - assume the first instance occurs on | + | | the DTSTART property. If implemented, | + | | VTIMEZONE MUST also be implemented. | + | RECURRENCE-ID | Required if RRULE is implemented; otherwise, | + | | ignore. | + | REQUEST-STATUS | Required | + | RESOURCES | Ignore | + | SEQUENCE | Required | + | STATUS | Ignore | + | SUMMARY | Ignore | + | TRANSP | Required if FREEBUSY is implemented; otherwise, | + | | ignore. | + | URL | Ignore | + | UID | Required | + | X- | Ignore | + +-----------------+-------------------------------------------------+ + + + + + + + + +Daboo Standards Track [Page 118] + +RFC 5546 iTIP December 2009 + + +5.1.2. Free/Busy-Related Fallbacks + + +---------+---------------------------------------------------------+ + | Method | Fallback | + +---------+---------------------------------------------------------+ + | PUBLISH | Required if freebusy lookups are supported; otherwise, | + | | reply with a REQUEST-STATUS "3.14; Unsupported | + | | capability". | + | REQUEST | Required if freebusy lookups are supported; otherwise, | + | | reply with a REQUEST-STATUS "3.14; Unsupported | + | | capability". | + | REPLY | Required if freebusy lookups are supported; otherwise, | + | | reply with a REQUEST-STATUS "3.14; Unsupported | + | | capability". | + +---------+---------------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | iCalendar | Fallback | + | Property | | + +-----------------+-------------------------------------------------+ + | CALSCALE | Ignore - assume GREGORIAN. | + | PRODID | Ignore | + | METHOD | Required as described in the Method list above. | + | VERSION | Ignore | + +-----------------+-------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | Component | Fallback | + | Property | | + +-----------------+-------------------------------------------------+ + | ATTENDEE | Required if METHOD is REQUEST; otherwise, | + | | ignore. | + | COMMENT | Ignore | + | CONTACT | Ignore | + | DTEND | Required | + | DTSTAMP | Required | + | DTSTART | Required | + | DURATION | Ignore | + | FREEBUSY | Required | + | ORGANIZER | Required if METHOD is REQUEST; otherwise, | + | | ignore. | + | REQUEST-STATUS | Ignore | + | UID | Required | + | URL | Ignore | + | X- | Ignore | + +-----------------+-------------------------------------------------+ + + + + + +Daboo Standards Track [Page 119] + +RFC 5546 iTIP December 2009 + + +5.1.3. To-Do-Related Fallbacks + + +----------------+--------------------------------------------------+ + | Method | Fallback | + +----------------+--------------------------------------------------+ + | PUBLISH | Required | + | REQUEST | PUBLISH | + | REPLY | Required | + | ADD | Required if recurrences supported; otherwise, | + | | reply with a REQUEST-STATUS "2.8; Success, | + | | repeating event ignored. Scheduled as a single | + | | component", and schedule as a single component. | + | CANCEL | Required | + | REFRESH | Required | + | COUNTER | Reply with "Not Supported". | + | DECLINECOUNTER | Required if COUNTER for VTODOs is implemented; | + | | otherwise, reply with "Not Supported". | + +----------------+--------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | iCalendar | Fallback | + | Property | | + +-----------------+-------------------------------------------------+ + | CALSCALE | Ignore - assume GREGORIAN. | + | PRODID | Ignore | + | METHOD | Required as described in the Method list above. | + | VERSION | Ignore | + +-----------------+-------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | To-Do-Related | Fallback | + | Components | | + +-----------------+-------------------------------------------------+ + | VALARM | Reply with "Not Supported". | + | VTIMEZONE | Required if any DateTime value refers to a time | + | | zone. | + +-----------------+-------------------------------------------------+ + + + + + + + + + + + + + + +Daboo Standards Track [Page 120] + +RFC 5546 iTIP December 2009 + + + +------------------+------------------------------------------------+ + | Component | Fallback | + | Property | | + +------------------+------------------------------------------------+ + | ATTACH | Ignore | + | ATTENDEE | Required if METHOD is REQUEST; otherwise, | + | | ignore. | + | CATEGORIES | Ignore | + | CLASS | Ignore | + | COMMENT | Ignore | + | COMPLETED | Required | + | CONTACT | Ignore | + | CREATED | Ignore | + | DESCRIPTION | Required if METHOD is REQUEST; otherwise, | + | | ignore. | + | DUE | Required | + | DURATION | Required | + | DTSTAMP | Required | + | DTSTART | Required | + | EXDATE | Ignore - reply with "Not Supported". | + | LAST-MODIFIED | Ignore | + | LOCATION | Ignore | + | ORGANIZER | Required if METHOD is REQUEST; otherwise, | + | | ignore. | + | PERCENT-COMPLETE | Ignore | + | PRIORITY | Required | + | RECURRENCE-ID | Required if RRULE is implemented; otherwise, | + | | ignore. | + | RELATED-TO | Ignore | + | REQUEST-STATUS | Ignore | + | RDATE | Ignore | + | RRULE | Ignore - assume the first instance occurs on | + | | the DTSTART property. If implemented, | + | | VTIMEZONE MUST also be implemented. | + | RESOURCES | Ignore | + | SEQUENCE | Required | + | STATUS | Required | + | SUMMARY | Ignore | + | URL | Ignore | + | UID | Required | + | X- | Ignore | + +------------------+------------------------------------------------+ + + + + + + + + + +Daboo Standards Track [Page 121] + +RFC 5546 iTIP December 2009 + + +5.1.4. Journal-Related Fallbacks + + +---------+---------------------------------------------------------+ + | Method | Fallback | + +---------+---------------------------------------------------------+ + | PUBLISH | Implementations MAY ignore the METHOD type. The | + | | REQUEST-STATUS "3.14; Unsupported capability" MUST be | + | | returned. | + | ADD | Implementations MAY ignore the METHOD type. The | + | | REQUEST-STATUS "3.14; Unsupported capability" MUST be | + | | returned. | + | CANCEL | Implementations MAY ignore the METHOD type. The | + | | REQUEST-STATUS "3.14; Unsupported capability" MUST be | + | | returned. | + +---------+---------------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | iCalendar | Fallback | + | Property | | + +-----------------+-------------------------------------------------+ + | CALSCALE | Ignore - assume GREGORIAN. | + | PRODID | Ignore | + | METHOD | Required as described in the Method list above. | + | VERSION | Ignore | + +-----------------+-------------------------------------------------+ + + +-----------------+-------------------------------------------------+ + | Journal-Related | Fallback | + | Components | | + +-----------------+-------------------------------------------------+ + | VTIMEZONE | Required if any DateTime value refers to a time | + | | zone. | + +-----------------+-------------------------------------------------+ + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 122] + +RFC 5546 iTIP December 2009 + + + +-----------------+-------------------------------------------------+ + | Component | Fallback | + | Property | | + +-----------------+-------------------------------------------------+ + | ATTACH | Ignore | + | ATTENDEE | Ignore | + | CATEGORIES | Ignore | + | CLASS | Ignore | + | COMMENT | Ignore | + | CONTACT | Ignore | + | CREATED | Ignore | + | DESCRIPTION | Ignore | + | DTSTAMP | Required | + | DTSTART | Required | + | EXDATE | Ignore | + | LAST-MODIFIED | Ignore | + | ORGANIZER | Ignore | + | RECURRENCE-ID | Required if RRULE is implemented; otherwise, | + | | ignore. | + | RELATED-TO | Ignore | + | RDATE | Ignore | + | RRULE | Ignore - assume the first instance occurs on | + | | the DTSTART property. If implemented, | + | | VTIMEZONE MUST also be implemented. | + | SEQUENCE | Required | + | STATUS | Ignore | + | SUMMARY | Required | + | URL | Ignore | + | UID | Required | + | X- | Ignore | + +-----------------+-------------------------------------------------+ + +5.2. Latency Issues + + With a store-and-forward transport, it is possible for events to + arrive out of sequence. That is, a "CANCEL" method may be received + prior to receiving the associated "REQUEST" for the calendar + component. This section discusses a few of these scenarios. + +5.2.1. Cancellation of an Unknown Calendar Component + + When a "CANCEL" method is received before the original "REQUEST" + method, the calendar will be unable to correlate the "UID" property + of the cancellation with an existing calendar component. It is + suggested that messages that cannot be correlated and that also + contain non-zero sequence numbers be held and not discarded. + Implementations MAY age them out if no other messages arrive with the + same "UID" property value and a lower sequence number. + + + +Daboo Standards Track [Page 123] + +RFC 5546 iTIP December 2009 + + +5.2.2. Unexpected Reply from an Unknown Delegate + + When an "Attendee" delegates an item to another CU, they MUST send a + "REPLY" method to the "Organizer" using the "ATTENDEE" properties to + indicate that the request was delegated and to whom. Hence, it is + possible for an "Organizer" to receive a "REPLY" from a CU not listed + as one of the original "Attendees". The resolution is left to the + implementation, but it is expected that the calendaring software will + either accept the reply or hold it until the related "REPLY" method + is received from the "Delegator". If the version of the "REPLY" + method is out of date, the "Organizer" SHOULD treat the message as a + "REFRESH" message and update the "Delegate" with the correct version, + provided that delegation to that delegate is acceptable. + +5.3. Sequence Number + + Under some conditions, a CUA may receive requests and replies with + the same "SEQUENCE" property value. The "DTSTAMP" property is + utilized as a tie-breaker when two items with the same "SEQUENCE" + property value are evaluated. + +6. Security Considerations + + iTIP is an abstract transport protocol that will be bound to a real- + time transport, a store-and-forward transport, and perhaps other + transports. The transport protocol will be responsible for providing + facilities for authentication and encryption using standard Internet + mechanisms that are mutually understood between the sender and + receiver. + +6.1. Security Threats + +6.1.1. Spoofing the Organizer + + In iTIP, the "Organizer" (or someone working on the "Organizer's" + behalf) is the only person authorized to make changes to an existing + "VEVENT", "VTODO", or "VJOURNAL" calendar component and republish it + or redistribute updates to the "Attendees". An iCalendar object that + maliciously changes or cancels an existing "VEVENT", "VTODO", or + "VJOURNAL" calendar component may be constructed by someone other + than the "Organizer" and republished or sent to the "Attendees". + +6.1.2. Spoofing the Attendee + + In iTIP, an "Attendee" of a "VEVENT" or "VTODO" calendar component + (or someone working on the "Attendee's" behalf) is the only person + authorized to update any parameter associated with their "ATTENDEE" + + + + +Daboo Standards Track [Page 124] + +RFC 5546 iTIP December 2009 + + + property and send it to the "Organizer". An iCalendar object that + maliciously changes the "ATTENDEE" parameters may be constructed by + someone other than the real "Attendee" and sent to the "Organizer". + +6.1.3. Unauthorized Replacement of the Organizer + + There will be circumstances when "Attendees" of an event or to-do + decide, using out-of-band mechanisms, that the "Organizer" must be + replaced. When the new "Organizer" sends out the updated "VEVENT" or + "VTODO", the "Attendee's" CUA will detect that the "Organizer" has + been changed, but it has no way of knowing whether or not the change + was mutually agreed upon. + +6.1.4. Eavesdropping and Data Integrity + + The iCalendar object is constructed with human-readable clear text. + Any information contained in an iCalendar object may be read and/or + changed by unauthorized persons while the object is in transit. + +6.1.5. Flooding a Calendar + + Implementations could provide a means to automatically incorporate + "REQUEST" methods into a calendar. This presents the opportunity for + a calendar to be flooded with requests, which effectively blocks all + the calendar's free time. + +6.1.6. Unauthorized REFRESH Requests + + It is possible for an "Organizer" to receive a "REFRESH" request from + someone who is not an "Attendee" of an event or to-do. Only + "Attendees" of an event or to-do are authorized to receive replies to + "REFRESH" requests. Replying to such requests to anyone who is not + an "Attendee" may be a security problem. + +6.2. Recommendations + + For an application where the information is sensitive or critical and + the network is subject to a high probability of attack, iTIP + transactions SHOULD be encrypted and authenticated. This helps + mitigate the threats of spoofing, eavesdropping, and malicious + changes in transit. + +6.2.1. Securing iTIP transactions + + iTIP transport bindings MUST provide a mechanism to enable + authentication of the sender's identity as well as privacy and + integrity of the data being transmitted. This allows the receiver of + a signed iCalendar object to verify the identity of the sender. This + + + +Daboo Standards Track [Page 125] + +RFC 5546 iTIP December 2009 + + + sender may then be correlated to an "ATTENDEE" property in the + iCalendar object. If the correlation is made and the sender is + authorized to make the requested change or update, then the operation + may proceed. It also allows the message to be encrypted to prevent + unauthorized reading of the message contents in transit. iTIP + transport binding documents describe this process in detail. + +6.2.2. Implementation Controls + + The threat of unauthorized replacement of the "Organizer" SHOULD be + mitigated by a calendar system that uses this protocol by providing + controls or alerts that make "Calendar Users" aware of such + "Organizer" changes and allowing them to decide whether or not the + request should be honored. + + The threat of flooding a calendar SHOULD be mitigated by a calendar + system that uses this protocol by providing controls that may be used + to limit the acceptable sources for iTIP transactions, and perhaps + the size of messages and volume of traffic, by source. + + The threat of unauthorized "REFRESH" requests SHOULD be mitigated by + a calendar system that uses this protocol by providing controls or + alerts that allow "Calendar Users" to decide whether or not the + request should be honored. An implementation MAY decide to maintain, + for audit or historical purposes, "Calendar Users" who were part of + an "Attendee" list and who were subsequently uninvited. Similar + controls or alerts should be provided when a "REFRESH" request is + received from these "Calendar Users" as well. + +6.2.3. Access Controls and Filtering + + In many environments, there could be restrictions on who is allowed + to schedule with whom and who the allowed delegates are for + particular "Calendar Users". + + iTIP transport bindings SHOULD provide mechanisms for implementing + access controls or filtering to ensure iTIP transactions only take + place between authorized "Calendar Users". That would include + preventing one "Calendar User" from scheduling with another or one + "Calendar User" delegating to another. + +6.3. Privacy Issues + + The "Organizer" might want to keep "Attendees" from knowing which + other "Attendees" are participating in an event or to-do. The + "Organizer" has the choice of sending single iTIP messages with a + full list of "Attendees" or sending iTIP messages to each "Attendee" + with only that "Attendee" listed. + + + +Daboo Standards Track [Page 126] + +RFC 5546 iTIP December 2009 + + +7. IANA Considerations + +7.1. Registration Template for REQUEST-STATUS Values + + This specification updates [RFC5545] by adding a "REQUEST-STATUS" + value registry to the iCalendar Elements registry. + + A "REQUEST-STATUS" value is defined by completing the following + template. + + Status Code: Hierarchical, numeric return status code, following + the rules defined in Section 3.8.8.3 of [RFC5545]. + + Status Description: Textual status description. A short but + clear description of the error. + + Status Exception Data: Textual exception data. A short but clear + description of what might appear in this field. + + Description: Describe the underlying cause for this status code + value. + +7.2. Additions to iCalendar METHOD Registry + + This document defines the following values for the iCalendar "METHOD" + property, using the values template from Section 8.2.6 of [RFC5545]. + These should be added to the Methods Registry defined in Section + 8.3.12 of [RFC5545]: + +7.2.1. METHOD:PUBLISH + + Value: PUBLISH + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + Examples: See this RFC. + +7.2.2. METHOD:REQUEST + + Value: REQUEST + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + Examples: See this RFC. + + + +Daboo Standards Track [Page 127] + +RFC 5546 iTIP December 2009 + + +7.2.3. METHOD:REPLY + + Value: REPLY + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + Examples: See this RFC. + +7.2.4. METHOD:ADD + + Value: ADD + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + Examples: See this RFC. + +7.2.5. METHOD:CANCEL + + Value: CANCEL + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + Examples: See this RFC. + +7.2.6. METHOD:REFRESH + + Value: REFRESH + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + Examples: See this RFC. + +7.2.7. METHOD:COUNTER + + Value: COUNTER + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + + + +Daboo Standards Track [Page 128] + +RFC 5546 iTIP December 2009 + + + Examples: See this RFC. + +7.2.8. METHOD:DECLINECOUNTER + + Value: DECLINECOUNTER + + Purpose: Standard iTIP "METHOD" value. + + Conformance: Only used with the "METHOD" property. + + Examples: See this RFC. + +7.3. REQUEST-STATUS Value Registry + + New "REQUEST-STATUS" values can be registered using the process + described in Section 8.2.1 of [RFC5545]. + + The following table is to be used to initialize the "REQUEST-STATUS" + value registry. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 129] + +RFC 5546 iTIP December 2009 + + + +-------------+---------+--------------------------+ + | Status Code | Status | Reference | + +-------------+---------+--------------------------+ + | 2.0 | Current | RFC 5546, Section 3.6.1 | + | 2.1 | Current | RFC 5546, Section 3.6.2 | + | 2.2 | Current | RFC 5546, Section 3.6.3 | + | 2.3 | Current | RFC 5546, Section 3.6.4 | + | 2.4 | Current | RFC 5546, Section 3.6.5 | + | 2.5 | Current | RFC 5546, Section 3.6.6 | + | 2.6 | Current | RFC 5546, Section 3.6.7 | + | 2.7 | Current | RFC 5546, Section 3.6.8 | + | 2.8 | Current | RFC 5546, Section 3.6.9 | + | 2.9 | Current | RFC 5546, Section 3.6.10 | + | 2.10 | Current | RFC 5546, Section 3.6.11 | + | 2.11 | Current | RFC 5546, Section 3.6.12 | + | 3.0 | Current | RFC 5546, Section 3.6.13 | + | 3.1 | Current | RFC 5546, Section 3.6.14 | + | 3.2 | Current | RFC 5546, Section 3.6.15 | + | 3.3 | Current | RFC 5546, Section 3.6.16 | + | 3.4 | Current | RFC 5546, Section 3.6.17 | + | 3.5 | Current | RFC 5546, Section 3.6.18 | + | 3.6 | Current | RFC 5546, Section 3.6.19 | + | 3.7 | Current | RFC 5546, Section 3.6.20 | + | 3.8 | Current | RFC 5546, Section 3.6.21 | + | 3.9 | Current | RFC 5546, Section 3.6.22 | + | 3.10 | Current | RFC 5546, Section 3.6.23 | + | 3.11 | Current | RFC 5546, Section 3.6.24 | + | 3.12 | Current | RFC 5546, Section 3.6.25 | + | 3.13 | Current | RFC 5546, Section 3.6.26 | + | 3.14 | Current | RFC 5546, Section 3.6.27 | + | 4.0 | Current | RFC 5546, Section 3.6.28 | + | 5.0 | Current | RFC 5546, Section 3.6.29 | + | 5.1 | Current | RFC 5546, Section 3.6.30 | + | 5.2 | Current | RFC 5546, Section 3.6.31 | + | 5.3 | Current | RFC 5546, Section 3.6.32 | + +-------------+---------+--------------------------+ + +8. Acknowledgments + + This is an update to the original iTIP document authored by S. + Silverberg, S. Mansour, F. Dawson, and R. Hopson. + + This revision is the product of the Calsify IETF Working Group, and + several participants have made important contributions to this + specification, including Oliver Block, Bernard Desruisseaux, Mike + Douglass, Tim Hare, Ciny Joy, Bruce Kahn, Reinhold Kainhofer, Eliot + Lear, Jonathan Lennox, Andy Mabbett, Aki Niemi, John W. Noerenberg + II, Robert Ransdell, and Caleb Richardson. + + + +Daboo Standards Track [Page 130] + +RFC 5546 iTIP December 2009 + + +9. References + +9.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2368] Hoffman, P., Masinter, L., and J. Zawinski, "The mailto + URL scheme", RFC 2368, July 1998. + + [RFC5545] Desruisseaux, B., "Internet Calendaring and Scheduling + Core Object Specification (iCalendar)", RFC 5545, + September 2009. + +9.2. Informative References + + [iMIP] Melnikov, A., Ed., "iCalendar Message-Based + Interoperability Protocol (iMIP)", Work in Progress, + October 2009. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 131] + +RFC 5546 iTIP December 2009 + + +Appendix A. Differences from RFC 2446 + +A.1. Changed Restrictions + + This specification now defines an allowed combination of "REQUEST- + STATUS" codes when multiple iCalendar components are included in an + iTIP message. + + This specification now restricts "RECURRENCE-ID" to only a single + occurrence in any one iCalendar component in an iTIP message, as + required by [RFC5545]. + + Changed the "RECURRENCE-ID" entry in the component restriction table + to "0 or 1" from "0+", to fall in line with [RFC5545]. + + Changed the "FREEBUSY" entry in the "VFREEBUSY", "PUBLISH", and + "REPLY" restriction tables to "0+" from "1+", to fall in line with + [RFC5545]. + + Changed the "FREEBUSY" description in the "VFREEBUSY" and "REPLY" + restriction tables to indicate that different "FBTYPE" ranges MUST + NOT overlap. + + Changed the "TZNAME" entry in the "VTIMEZONE" restriction table to + "0+" from "0 or 1", to fall in line with [RFC5545]. + + Changed the "COMMENT" entry in the component restriction tables to + "0+" from "0 or 1", to fall in line with [RFC5545]. + + Added the "ATTENDEE" entry in the "VALARM" restriction table to match + the email alarm type in [RFC5545]. + + Changed the "CATEGORIES" entry in the component restriction tables to + "0+" from "0 or 1", to fall in line with [RFC5545]. + + Changed the "RESOURCES" entry in the component restriction tables to + "0+" from "0 or 1", to fall in line with [RFC5545]. + + Changed the "CONTACT" entry in the "VFREEBUSY" restriction table to + "0 or 1" from "0+", to fall in line with [RFC5545]. + + Changed the "UID" entry in the "VFREEBUSY" and "PUBLISH" restriction + tables to "1" from "0", to fall in line with [RFC5545]. + + Added the "COMPLETED" entry in the "VTODO" restriction tables to fall + in line with [RFC5545]. + + + + + +Daboo Standards Track [Page 132] + +RFC 5546 iTIP December 2009 + + + Added the "REQUEST-STATUS" entry in the "VJOURNAL" restriction tables + to fall in line with [RFC5545]. + +A.2. Deprecated Features + + The "EXRULE" property was removed in [RFC5545] and references to that + have been removed in this document too. + + The "PROCEDURE" value for the "ACTION" property was removed in + [RFC5545] and references to that have been removed in this document + too. + + The "THISANDPRIOR" option for the "RANGE" parameter was removed in + [RFC5545] and references to that have been removed in this document + too. + +Author's Address + + Cyrus Daboo (editor) + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 133] + diff --git a/dav/SabreDAV/docs/rfc5689.txt b/dav/SabreDAV/docs/rfc5689.txt new file mode 100644 index 000000000..bce3cfc89 --- /dev/null +++ b/dav/SabreDAV/docs/rfc5689.txt @@ -0,0 +1,675 @@ + + + + + + +Network Working Group C. Daboo +Request for Comments: 5689 Apple Inc. +Updates: 4791, 4918 September 2009 +Category: Standards Track + + + Extended MKCOL for Web Distributed Authoring and Versioning (WebDAV) + +Abstract + + This specification extends the Web Distributed Authoring and + Versioning (WebDAV) MKCOL (Make Collection) method to allow + collections of arbitrary resourcetype to be created and to allow + properties to be set at the same time. + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (c) 2009 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + + + + + +Daboo Standards Track [Page 1] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 2. Conventions Used in This Document . . . . . . . . . . . . . . 3 + 3. WebDAV Extended MKCOL . . . . . . . . . . . . . . . . . . . . 4 + 3.1. Extended MKCOL Support . . . . . . . . . . . . . . . . . . 5 + 3.1.1. Example: Using OPTIONS for the Discovery of + Support for Extended MKCOL . . . . . . . . . . . . . . 5 + 3.2. Status Codes . . . . . . . . . . . . . . . . . . . . . . . 5 + 3.3. Additional Precondition for Extended MKCOL . . . . . . . . 5 + 3.4. Example: Successful Extended MKCOL Request . . . . . . . . 6 + 3.5. Example: Unsuccessful Extended MKCOL Request . . . . . . . 6 + 4. Using Extended MKCOL as an Alternative for MKxxx Methods . . . 8 + 4.1. MKCALENDAR Alternative . . . . . . . . . . . . . . . . . . 8 + 4.1.1. Example: Using MKCOL Instead of MKCALENDAR . . . . . . 8 + 5. XML Element Definitions . . . . . . . . . . . . . . . . . . . 10 + 5.1. mkcol XML Element . . . . . . . . . . . . . . . . . . . . 10 + 5.2. mkcol-response XML Element . . . . . . . . . . . . . . . . 10 + 6. Security Considerations . . . . . . . . . . . . . . . . . . . 11 + 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 11 + 8. Normative References . . . . . . . . . . . . . . . . . . . . . 11 + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 2] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + +1. Introduction + + WebDAV [RFC4918] defines the HTTP [RFC2616] method MKCOL. This + method is used to create WebDAV collections on the server. However, + several WebDAV-based specifications (e.g., CalDAV [RFC4791]) define + "special" collections -- ones that are identified by additional + values in the DAV:resourcetype property assigned to the collection + resource or by other means. These "special" collections are created + by new methods (e.g., MKCALENDAR). The addition of a new MKxxx + method for each new "special" collection adds to server complexity + and is detrimental to overall reliability due to the need to make + sure intermediaries are aware of these methods. + + This specification defines an extension to the WebDAV MKCOL method + that adds a request body allowing a client to specify WebDAV + properties to be set on the newly created collection or resource. In + particular, the DAV:resourcetype property can be used to create a + "special" collection; alternatively, other properties can be used to + create a "special" resource. This avoids the need to invent new + MKxxx methods. + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + This document uses XML DTD fragments (Section 3.2 of + [W3C.REC-xml-20081126]) as a purely notational convention. WebDAV + request and response bodies cannot be validated by a DTD due to the + specific extensibility rules defined in Section 17 of [RFC4918] and + due to the fact that all XML elements defined by this specification + use the XML namespace name "DAV:". In particular: + + 1. Element names use the "DAV:" namespace. + + 2. Element ordering is irrelevant unless explicitly stated. + + 3. Extension elements (elements not already defined as valid child + elements) may be added anywhere, except when explicitly stated + otherwise. + + 4. Extension attributes (attributes not already defined as valid for + this element) may be added anywhere, except when explicitly + stated otherwise. + + + + + + +Daboo Standards Track [Page 3] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + + When an XML element type in the "DAV:" namespace is referenced in + this document outside of the context of an XML fragment, the string + "DAV:" will be prefixed to the element type. + + This document inherits, and sometimes extends, DTD productions from + Section 14 of [RFC4918]. + +3. WebDAV Extended MKCOL + + The WebDAV MKCOL request is extended to allow the inclusion of a + request body. The request body is an XML document containing a + single DAV:mkcol XML element as the root element. The Content-Type + request header MUST be set appropriately for an XML body (e.g., set + to "text/xml" or "application/xml"). XML-typed bodies for an MKCOL + request that do not have DAV:mkcol as the root element are reserved + for future usage. + + One or more DAV:set XML elements may be included in the DAV:mkcol XML + element to allow setting properties on the collection as it is + created. In particular, to create a collection of a particular type, + the DAV:resourcetype XML element MUST be included in a DAV:set XML + element and MUST specify the expected resource type elements for the + new resource, which MUST include the DAV:collection element that + needs to be present for any WebDAV collection. + + As per the PROPPATCH method (Section 9.2 of [RFC4918]), servers MUST + process any DAV:set instructions in document order (an exception to + the normal rule that ordering is irrelevant). If any one instruction + fails to execute successfully, all instructions MUST fail (i.e., + either all succeed or all fail). Thus, if any error occurs during + processing, all executed instructions MUST be undone and a proper + error result returned. Failure to set a property value on the + collection MUST result in a failure of the overall MKCOL request -- + i.e., the collection is not created. + + The response to an extended MKCOL request MUST be an XML document + containing a single DAV:mkcol-response XML element, which MUST + contain DAV:propstat XML elements with the status of each property + when the request fails due to a failure to set one or more of the + properties specified in the request body. The server MAY return a + response body in the case where the request is successful, indicating + success for setting each property specified in the request body. + When an empty response body is returned with a success request status + code, the client can assume that all properties were set. + + In all other respects, the behavior of the extended MKCOL request + follows that of the standard MKCOL request. + + + + +Daboo Standards Track [Page 4] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + +3.1. Extended MKCOL Support + + A server supporting the features described in this document MUST + include "extended-mkcol" as a field in the DAV response header from + an OPTIONS request on any URI that supports use of the extended MKCOL + method. + +3.1.1. Example: Using OPTIONS for the Discovery of Support for Extended + MKCOL + + >> Request << + + OPTIONS /addressbooks/users/ HTTP/1.1 + Host: addressbook.example.com + + >> Response << + + HTTP/1.1 200 OK + Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE + Allow: MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL + DAV: 1, 2, 3, access-control, extended-mkcol + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Length: 0 + +3.2. Status Codes + + As per Section 9.3.1 of [RFC4918]. + +3.3. Additional Precondition for Extended MKCOL + + WebDAV ([RFC4918], Section 16) defines preconditions and + postconditions for request behavior. This specification adds the + following precondition for the extended MKCOL request. + + Name: valid-resourcetype + + Namespace: DAV: + + Use with: Typically 403 (Forbidden) + + Purpose: (precondition) -- The server MUST support the specified + resourcetype value for the specified collection. + + + + + + + + + +Daboo Standards Track [Page 5] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + +3.4. Example: Successful Extended MKCOL Request + + This example shows how the extended MKCOL request is used to create a + collection of a fictitious type "special-resource". The response + body is empty as the request completed successfully. + + >> Request << + + MKCOL /home/special/ HTTP/1.1 + Host: special.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + Special Resource + + + + + >> Response << + + HTTP/1.1 201 Created + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + +3.5. Example: Unsuccessful Extended MKCOL Request + + This example shows an attempt to use the extended MKCOL request to + create a collection of a fictitious type "special-resource", which is + not actually supported by the server. The response body shows that + an error occurred specifically with the DAV:resourcetype property. + + + + + + + + + + + + +Daboo Standards Track [Page 6] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + + >> Request << + + MKCOL /home/special/ HTTP/1.1 + Host: special.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + Special Resource + + + + + >> Response << + + HTTP/1.1 403 Forbidden + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + HTTP/1.1 403 Forbidden + + Resource type is not + supported by this server + + + + + + HTTP/1.1 424 Failed Dependency + + + + + + +Daboo Standards Track [Page 7] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + +4. Using Extended MKCOL as an Alternative for MKxxx Methods + + One of the goals of this extension is to eliminate the need for other + extensions to define their own variant of MKCOL to create the special + collections they need. This extension can be used as an alternative + to existing MKxxx methods in other extensions as detailed below. If + a server supports this extension and the other extension listed, then + the server MUST support use of the extended MKCOL method to achieve + the same result as the MKxxx method of the other extension. + +4.1. MKCALENDAR Alternative + + CalDAV defines the MKCALENDAR method to create a calendar collection + as well as to set properties during creation (Section 5.3.1 of + [RFC4791]). + + The extended MKCOL method can be used instead by specifying both DAV: + collection and CALDAV:calendar-collection XML elements in the DAV: + resourcetype property, set during the extended MKCOL request. + +4.1.1. Example: Using MKCOL Instead of MKCALENDAR + + The first example below shows an MKCALENDAR request containing a + CALDAV:mkcalendar XML element in the request body and returning a + CALDAV:mkcalendar-response XML element in the response body. + + >> MKCALENDAR Request << + + MKCALENDAR /home/lisa/calendars/events/ HTTP/1.1 + Host: calendar.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + Lisa's Events + + + + + + + + + + + + +Daboo Standards Track [Page 8] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + + >> MKCALENDAR Response << + + HTTP/1.1 201 Created + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + HTTP/1.1 200 OK + + + + The second example shows the equivalent extended MKCOL request with + the same request and response XML elements. + + >> MKCOL Request << + + MKCOL /home/lisa/calendars/events/ HTTP/1.1 + Host: calendar.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + Lisa's Events + + + + + + + + + + + + +Daboo Standards Track [Page 9] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + + >> MKCOL Response << + + HTTP/1.1 201 Created + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + HTTP/1.1 200 OK + + + +5. XML Element Definitions + +5.1. mkcol XML Element + + Name: mkcol + + Namespace: DAV: + + Purpose: Used in a request to specify properties to be set in an + extended MKCOL request, as well as any additional information + needed when creating the resource. + + Description: This XML element is a container for the information + required to modify the properties on a collection resource as it + is created in an extended MKCOL request. + + Definition: + + + +5.2. mkcol-response XML Element + + Name: mkcol-response + + Namespace: DAV: + + + + + + +Daboo Standards Track [Page 10] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + + Purpose: Used in a response to indicate the status of properties + that were set or failed to be set during an extended MKCOL + request. + + Description: This XML element is a container for the information + returned about a resource that has been created in an extended + MKCOL request. + + Definition: + + + +6. Security Considerations + + This extension does not introduce any new security concerns beyond + those already described in HTTP [RFC2616] and WebDAV [RFC4918]. + +7. Acknowledgments + + Thanks to Bernard Desruisseaux, Mike Douglass, Alexey Melnikov, + Julian Reschke, and Simon Vaillancourt. + + +8. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault, + "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791, + March 2007. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + + [W3C.REC-xml-20081126] + Maler, E., Yergeau, F., Paoli, J., Bray, T., and C. + Sperberg-McQueen, "Extensible Markup Language (XML) 1.0 + (Fifth Edition)", World Wide Web Consortium + Recommendation REC-xml-20081126, November 2008, + . + + + + + + +Daboo Standards Track [Page 11] + +RFC 5689 Extended MKCOL for WebDAV September 2009 + + +Author's Address + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 12] + diff --git a/dav/SabreDAV/docs/rfc5789.txt b/dav/SabreDAV/docs/rfc5789.txt new file mode 100644 index 000000000..7a2c0614c --- /dev/null +++ b/dav/SabreDAV/docs/rfc5789.txt @@ -0,0 +1,563 @@ + + + + + + +Internet Engineering Task Force (IETF) L. Dusseault +Request for Comments: 5789 Linden Lab +Category: Standards Track J. Snell +ISSN: 2070-1721 March 2010 + + + PATCH Method for HTTP + +Abstract + + Several applications extending the Hypertext Transfer Protocol (HTTP) + require a feature to do partial resource modification. The existing + HTTP PUT method only allows a complete replacement of a document. + This proposal adds a new HTTP method, PATCH, to modify an existing + HTTP resource. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc5789. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + + + +Dusseault & Snell Standards Track [Page 1] + +RFC 5789 HTTP PATCH March 2010 + + +Table of Contents + + 1. Introduction ....................................................2 + 2. The PATCH Method ................................................2 + 2.1. A Simple PATCH Example .....................................4 + 2.2. Error Handling .............................................5 + 3. Advertising Support in OPTIONS ..................................7 + 3.1. The Accept-Patch Header ....................................7 + 3.2. Example OPTIONS Request and Response .......................7 + 4. IANA Considerations .............................................8 + 4.1. The Accept-Patch Response Header ...........................8 + 5. Security Considerations .........................................8 + 6. References ......................................................9 + 6.1. Normative References .......................................9 + 6.2. Informative References .....................................9 + Appendix A. Acknowledgements .....................................10 + +1. Introduction + + This specification defines the new HTTP/1.1 [RFC2616] method, PATCH, + which is used to apply partial modifications to a resource. + + A new method is necessary to improve interoperability and prevent + errors. The PUT method is already defined to overwrite a resource + with a complete new body, and cannot be reused to do partial changes. + Otherwise, proxies and caches, and even clients and servers, may get + confused as to the result of the operation. POST is already used but + without broad interoperability (for one, there is no standard way to + discover patch format support). PATCH was mentioned in earlier HTTP + specifications, but not completely defined. + + In this document, the key words "MUST", "MUST NOT", "REQUIRED", + "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", + and "OPTIONAL" are to be interpreted as described in [RFC2119]. + + Furthermore, this document uses the ABNF syntax defined in Section + 2.1 of [RFC2616]. + +2. The PATCH Method + + The PATCH method requests that a set of changes described in the + request entity be applied to the resource identified by the Request- + URI. The set of changes is represented in a format called a "patch + document" identified by a media type. If the Request-URI does not + point to an existing resource, the server MAY create a new resource, + depending on the patch document type (whether it can logically modify + a null resource) and permissions, etc. + + + + +Dusseault & Snell Standards Track [Page 2] + +RFC 5789 HTTP PATCH March 2010 + + + The difference between the PUT and PATCH requests is reflected in the + way the server processes the enclosed entity to modify the resource + identified by the Request-URI. In a PUT request, the enclosed entity + is considered to be a modified version of the resource stored on the + origin server, and the client is requesting that the stored version + be replaced. With PATCH, however, the enclosed entity contains a set + of instructions describing how a resource currently residing on the + origin server should be modified to produce a new version. The PATCH + method affects the resource identified by the Request-URI, and it + also MAY have side effects on other resources; i.e., new resources + may be created, or existing ones modified, by the application of a + PATCH. + + PATCH is neither safe nor idempotent as defined by [RFC2616], Section + 9.1. + + A PATCH request can be issued in such a way as to be idempotent, + which also helps prevent bad outcomes from collisions between two + PATCH requests on the same resource in a similar time frame. + Collisions from multiple PATCH requests may be more dangerous than + PUT collisions because some patch formats need to operate from a + known base-point or else they will corrupt the resource. Clients + using this kind of patch application SHOULD use a conditional request + such that the request will fail if the resource has been updated + since the client last accessed the resource. For example, the client + can use a strong ETag [RFC2616] in an If-Match header on the PATCH + request. + + There are also cases where patch formats do not need to operate from + a known base-point (e.g., appending text lines to log files, or non- + colliding rows to database tables), in which case the same care in + client requests is not needed. + + The server MUST apply the entire set of changes atomically and never + provide (e.g., in response to a GET during this operation) a + partially modified representation. If the entire patch document + cannot be successfully applied, then the server MUST NOT apply any of + the changes. The determination of what constitutes a successful + PATCH can vary depending on the patch document and the type of + resource(s) being modified. For example, the common 'diff' utility + can generate a patch document that applies to multiple files in a + directory hierarchy. The atomicity requirement holds for all + directly affected files. See "Error Handling", Section 2.2, for + details on status codes and possible error conditions. + + If the request passes through a cache and the Request-URI identifies + one or more currently cached entities, those entries SHOULD be + treated as stale. A response to this method is only cacheable if it + + + +Dusseault & Snell Standards Track [Page 3] + +RFC 5789 HTTP PATCH March 2010 + + + contains explicit freshness information (such as an Expires header or + "Cache-Control: max-age" directive) as well as the Content-Location + header matching the Request-URI, indicating that the PATCH response + body is a resource representation. A cached PATCH response can only + be used to respond to subsequent GET and HEAD requests; it MUST NOT + be used to respond to other methods (in particular, PATCH). + + Note that entity-headers contained in the request apply only to the + contained patch document and MUST NOT be applied to the resource + being modified. Thus, a Content-Language header could be present on + the request, but it would only mean (for whatever that's worth) that + the patch document had a language. Servers SHOULD NOT store such + headers except as trace information, and SHOULD NOT use such header + values the same way they might be used on PUT requests. Therefore, + this document does not specify a way to modify a document's Content- + Type or Content-Language value through headers, though a mechanism + could well be designed to achieve this goal through a patch document. + + There is no guarantee that a resource can be modified with PATCH. + Further, it is expected that different patch document formats will be + appropriate for different types of resources and that no single + format will be appropriate for all types of resources. Therefore, + there is no single default patch document format that implementations + are required to support. Servers MUST ensure that a received patch + document is appropriate for the type of resource identified by the + Request-URI. + + Clients need to choose when to use PATCH rather than PUT. For + example, if the patch document size is larger than the size of the + new resource data that would be used in a PUT, then it might make + sense to use PUT instead of PATCH. A comparison to POST is even more + difficult, because POST is used in widely varying ways and can + encompass PUT and PATCH-like operations if the server chooses. If + the operation does not modify the resource identified by the Request- + URI in a predictable way, POST should be considered instead of PATCH + or PUT. + +2.1. A Simple PATCH Example + + PATCH /file.txt HTTP/1.1 + Host: www.example.com + Content-Type: application/example + If-Match: "e0023aa4e" + Content-Length: 100 + + [description of changes] + + + + + +Dusseault & Snell Standards Track [Page 4] + +RFC 5789 HTTP PATCH March 2010 + + + This example illustrates use of a hypothetical patch document on an + existing resource. + + Successful PATCH response to existing text file: + + HTTP/1.1 204 No Content + Content-Location: /file.txt + ETag: "e0023aa4f" + + The 204 response code is used because the response does not carry a + message body (which a response with the 200 code would have). Note + that other success codes could be used as well. + + Furthermore, the ETag response header field contains the ETag for the + entity created by applying the PATCH, available at + http://www.example.com/file.txt, as indicated by the Content-Location + response header field. + +2.2. Error Handling + + There are several known conditions under which a PATCH request can + fail. + + Malformed patch document: When the server determines that the patch + document provided by the client is not properly formatted, it + SHOULD return a 400 (Bad Request) response. The definition of + badly formatted depends on the patch document chosen. + + Unsupported patch document: Can be specified using a 415 + (Unsupported Media Type) response when the client sends a patch + document format that the server does not support for the resource + identified by the Request-URI. Such a response SHOULD include an + Accept-Patch response header as described in Section 3.1 to notify + the client what patch document media types are supported. + + Unprocessable request: Can be specified with a 422 (Unprocessable + Entity) response ([RFC4918], Section 11.2) when the server + understands the patch document and the syntax of the patch + document appears to be valid, but the server is incapable of + processing the request. This might include attempts to modify a + resource in a way that would cause the resource to become invalid; + for instance, a modification to a well-formed XML document that + would cause it to no longer be well-formed. There may also be + more specific errors like "Conflicting State" that could be + signaled with this status code, but the more specific error would + generally be more helpful. + + + + + +Dusseault & Snell Standards Track [Page 5] + +RFC 5789 HTTP PATCH March 2010 + + + Resource not found: Can be specified with a 404 (Not Found) status + code when the client attempted to apply a patch document to a non- + existent resource, but the patch document chosen cannot be applied + to a non-existent resource. + + Conflicting state: Can be specified with a 409 (Conflict) status + code when the request cannot be applied given the state of the + resource. For example, if the client attempted to apply a + structural modification and the structures assumed to exist did + not exist (with XML, a patch might specify changing element 'foo' + to element 'bar' but element 'foo' might not exist). + + Conflicting modification: When a client uses either the If-Match or + If-Unmodified-Since header to define a precondition, and that + precondition failed, then the 412 (Precondition Failed) error is + most helpful to the client. However, that response makes no sense + if there was no precondition on the request. In cases when the + server detects a possible conflicting modification and no + precondition was defined in the request, the server can return a + 409 (Conflict) response. + + Concurrent modification: Some applications of PATCH might require + the server to process requests in the order in which they are + received. If a server is operating under those restrictions, and + it receives concurrent requests to modify the same resource, but + is unable to queue those requests, the server can usefully + indicate this error by using a 409 (Conflict) response. + + Note that the 409 Conflict response gives reasonably consistent + information to clients. Depending on the application and the nature + of the patch format, the client might be able to reissue the request + as is (e.g., an instruction to append a line to a log file), have to + retrieve the resource content to recalculate a patch, or have to fail + the operation. + + Other HTTP status codes can also be used under the appropriate + circumstances. + + The entity body of error responses SHOULD contain enough information + to communicate the nature of the error to the client. The content- + type of the response entity can vary across implementations. + + + + + + + + + + +Dusseault & Snell Standards Track [Page 6] + +RFC 5789 HTTP PATCH March 2010 + + +3. Advertising Support in OPTIONS + + A server can advertise its support for the PATCH method by adding it + to the listing of allowed methods in the "Allow" OPTIONS response + header defined in HTTP/1.1. The PATCH method MAY appear in the + "Allow" header even if the Accept-Patch header is absent, in which + case the list of allowed patch documents is not advertised. + +3.1. The Accept-Patch Header + + This specification introduces a new response header Accept-Patch used + to specify the patch document formats accepted by the server. + Accept-Patch SHOULD appear in the OPTIONS response for any resource + that supports the use of the PATCH method. The presence of the + Accept-Patch header in response to any method is an implicit + indication that PATCH is allowed on the resource identified by the + Request-URI. The presence of a specific patch document format in + this header indicates that that specific format is allowed on the + resource identified by the Request-URI. + + Accept-Patch = "Accept-Patch" ":" 1#media-type + + The Accept-Patch header specifies a comma-separated listing of media- + types (with optional parameters) as defined by [RFC2616], Section + 3.7. + + Example: + + Accept-Patch: text/example;charset=utf-8 + +3.2. Example OPTIONS Request and Response + + [request] + + OPTIONS /example/buddies.xml HTTP/1.1 + Host: www.example.com + + [response] + + HTTP/1.1 200 OK + Allow: GET, PUT, POST, OPTIONS, HEAD, DELETE, PATCH + Accept-Patch: application/example, text/example + + The examples show a server that supports PATCH generally using two + hypothetical patch document formats. + + + + + + +Dusseault & Snell Standards Track [Page 7] + +RFC 5789 HTTP PATCH March 2010 + + +4. IANA Considerations + +4.1. The Accept-Patch Response Header + + The Accept-Patch response header has been added to the permanent + registry (see [RFC3864]). + + Header field name: Accept-Patch + + Applicable Protocol: HTTP + + Author/Change controller: IETF + + Specification document: this specification + +5. Security Considerations + + The security considerations for PATCH are nearly identical to the + security considerations for PUT ([RFC2616], Section 9.6). These + include authorizing requests (possibly through access control and/or + authentication) and ensuring that data is not corrupted through + transport errors or through accidental overwrites. Whatever + mechanisms are used for PUT can be used for PATCH as well. The + following considerations apply especially to PATCH. + + A document that is patched might be more likely to be corrupted than + a document that is overridden in entirety, but that concern can be + addressed through the use of mechanisms such as conditional requests + using ETags and the If-Match request header as described in + Section 2. If a PATCH request fails, the client can issue a GET + request to the resource to see what state it is in. In some cases, + the client might be able to check the contents of the resource to see + if the PATCH request can be resent, but in other cases, the attempt + will just fail and/or a user will have to verify intent. In the case + of a failure of the underlying transport channel, where a PATCH + response is not received before the channel fails or some other + timeout happens, the client might have to issue a GET request to see + whether the request was applied. The client might want to ensure + that the GET request bypasses caches using mechanisms described in + HTTP specifications (see, for example, Section 13.1.6 of [RFC2616]). + + Sometimes an HTTP intermediary might try to detect viruses being sent + via HTTP by checking the body of the PUT/POST request or GET + response. The PATCH method complicates such watch-keeping because + neither the source document nor the patch document might be a virus, + yet the result could be. This security consideration is not + + + + + +Dusseault & Snell Standards Track [Page 8] + +RFC 5789 HTTP PATCH March 2010 + + + materially different from those already introduced by byte-range + downloads, downloading patch documents, uploading zipped (compressed) + files, and so on. + + Individual patch documents will have their own specific security + considerations that will likely vary depending on the types of + resources being patched. The considerations for patched binary + resources, for instance, will be different than those for patched XML + documents. Servers MUST take adequate precautions to ensure that + malicious clients cannot consume excessive server resources (e.g., + CPU, disk I/O) through the client's use of PATCH. + +6. References + +6.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration + Procedures for Message Header Fields", BCP 90, RFC 3864, + September 2004. + +6.2. Informative References + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + + + + + + + + + + + + + + + + + + + + +Dusseault & Snell Standards Track [Page 9] + +RFC 5789 HTTP PATCH March 2010 + + +Appendix A. Acknowledgements + + PATCH is not a new concept, it first appeared in HTTP in drafts of + version 1.1 written by Roy Fielding and Henrik Frystyk and also + appears in Section 19.6.1.1 of RFC 2068. + + Thanks to Adam Roach, Chris Sharp, Julian Reschke, Geoff Clemm, Scott + Lawrence, Jeffrey Mogul, Roy Fielding, Greg Stein, Jim Luther, Alex + Rousskov, Jamie Lokier, Joe Hildebrand, Mark Nottingham, Michael + Balloni, Cyrus Daboo, Brian Carpenter, John Klensin, Eliot Lear, SM, + and Bernie Hoeneisen for review and advice on this document. In + particular, Julian Reschke did repeated reviews, made many useful + suggestions, and was critical to the publication of this document. + +Authors' Addresses + + Lisa Dusseault + Linden Lab + 945 Battery Street + San Francisco, CA 94111 + USA + + EMail: lisa.dusseault@gmail.com + + + James M. Snell + + EMail: jasnell@gmail.com + URI: http://www.snellspace.com + + + + + + + + + + + + + + + + + + + + + + +Dusseault & Snell Standards Track [Page 10] + diff --git a/dav/SabreDAV/docs/rfc6047.txt b/dav/SabreDAV/docs/rfc6047.txt new file mode 100644 index 000000000..c839c8ed3 --- /dev/null +++ b/dav/SabreDAV/docs/rfc6047.txt @@ -0,0 +1,1235 @@ + + + + + + +Internet Engineering Task Force (IETF) A. Melnikov, Ed. +Request for Comments: 6047 Isode Ltd +Obsoletes: 2447 December 2010 +Category: Standards Track +ISSN: 2070-1721 + + + iCalendar Message-Based Interoperability Protocol (iMIP) + +Abstract + + This document, "iCalendar Message-Based Interoperability Protocol + (iMIP)", specifies a binding from the iCalendar Transport-independent + Interoperability Protocol (iTIP) to Internet email-based transports. + Calendaring entries defined by the iCalendar Object Model (iCalendar) + are wrapped using constructs from RFC 5322 and MIME (RFC 2045, RFC + 2046, RFC 2047, and RFC 2049), and then transported over SMTP. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6047. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + +Melnikov Standards Track [Page 1] + +RFC 6047 iMIP December 2010 + + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction ....................................................3 + 1.1. Related Memos ..............................................3 + 1.2. Formatting Conventions .....................................3 + 1.3. Terminology ................................................4 + 2. MIME Message Format Binding .....................................4 + 2.1. MIME Media Type ............................................4 + 2.2. Security ...................................................5 + 2.2.1. Authorization .......................................5 + 2.2.2. Authentication ......................................5 + 2.2.3. Confidentiality .....................................5 + 2.3. Email Addresses ............................................6 + 2.4. Content-Type Header Field ..................................6 + 2.5. Content-Transfer-Encoding Header Field .....................7 + 2.6. Content-Disposition Header Field ...........................8 + 3. Security Considerations .........................................8 + 4. Examples .......................................................11 + 4.1. Single Component with an ATTACH Property ..................11 + 4.2. Using multipart/alternative for Low-Fidelity Clients ......11 + 4.3. Single Component with an ATTACH Property and + Inline Attachment .........................................12 + 4.4. Multiple Similar Components ...............................14 + 4.5. Multiple Mixed Components .................................15 + 4.6. Detailed Components with an ATTACH Property ...............16 + 5. Recommended Practices ..........................................18 + 5.1. Use of Content and Message IDs ............................18 + 6. IANA Considerations ............................................18 + 7. References .....................................................19 + 7.1. Normative References ......................................19 + 7.2. Informative References ....................................20 + Appendix A. Changes since RFC 2447 ................................21 + Appendix B. Acknowledgements ......................................22 + + + + + + +Melnikov Standards Track [Page 2] + +RFC 6047 iMIP December 2010 + + +1. Introduction + + This document provides the transport-specific information ("binding") + necessary to convey iCalendar Transport-independent Interoperability + Protocol (iTIP) [iTIP] over Internet email (using MIME) as defined in + [RFC5322] and [RFC2045]. Therefore, this document defines the + iCalendar Message-Based Interoperability Protocol (iMIP). + +1.1. Related Memos + + Implementers will need to be familiar with several other memos that, + along with this memo, form a framework for Internet calendaring and + scheduling standards. + + This document specifies an Internet email binding for iTIP. + + [iCAL] specifies a core specification of objects, data types, + properties, and property parameters. + + [iTIP] specifies an interoperability protocol for scheduling between + different implementations. + + This memo does not attempt to repeat the specification of concepts or + definitions from these other memos. Where possible, references are + made to the memo that provides for the specification of these + concepts or definitions. + +1.2. Formatting Conventions + + The mechanisms defined in this memo are defined in prose. In order + to refer to elements of the calendaring and scheduling model, core + object, or interoperability protocol defined in [iCAL] and [iTIP], + some formatting conventions have been used. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [RFC2119]. + + Calendaring and scheduling roles are referred to in quoted strings of + text with the first character of each word in uppercase. For + example, "Organizer" refers to a role of a "Calendar User" within the + scheduling protocol defined by [iTIP]. + + Calendar components defined by [iCAL] are referred to with + capitalized, quoted strings of text. All calendar components start + with the letter "V". For example, "VEVENT" refers to the event + calendar component, "VTODO" refers to the to-do calendar component, + and "VJOURNAL" refers to the daily journal calendar component. + + + +Melnikov Standards Track [Page 3] + +RFC 6047 iMIP December 2010 + + + Scheduling methods defined by [iTIP] are referred to with + capitalized, quoted strings of text. For example, "REQUEST" refers + to the method for requesting a scheduling calendar component be + created or modified; "REPLY" refers to the method a recipient of a + request uses to update their status with the "Organizer" of the + calendar component. + + Properties defined by [iCAL] are referred to with capitalized, quoted + strings of text, followed by the word "property". For example, + "ATTENDEE" property refers to the iCalendar property used to convey + the calendar address of a "Calendar User". + + Property parameters defined by [iCAL] are referred to with lowercase, + quoted strings of text, followed by the word "parameter". For + example, "value" parameter refers to the iCalendar property parameter + used to override the default data type for a property value. + +1.3. Terminology + + The email terms used in this memo are defined in [RFC5322] and + [RFC2045]. The calendaring and scheduling terms used in this memo + are defined in [iCAL] and [iTIP]. + +2. MIME Message Format Binding + + This section defines the message binding to the MIME electronic mail + transport. + + The sections below refer to the "originator" and the "recipient" of + an iMIP message. In the case of a "request" method, the originator + is the "Organizer" and the recipient is an "Attendee" of the event. + In the case of a "response" method, the originator is an "Attendee" + and the recipient is the "Organizer" of the event. + + The [RFC5322] "Reply-To" header field typically contains the email + address of the originator of the scheduling message. However, this + cannot be guaranteed because the sender of the iMIP message might not + be the originator of the scheduling message and the sender's "Mail + User Agent" (MUA) might not enforce iMIP semantics by translating the + originator's address into the "Reply-To" email header field. + +2.1. MIME Media Type + + A MIME entity containing content information formatted according to + this document will be referenced as a "text/calendar" content type + [iCAL]. It is assumed that this content type will be transported + through a MIME electronic mail transport. + + + + +Melnikov Standards Track [Page 4] + +RFC 6047 iMIP December 2010 + + +2.2. Security + + This section addresses several aspects of security including + authentication, authorization, and confidentiality. Authentication + and confidentiality can be achieved using Secure/MIME (S/MIME) + [RFC5750] [RFC5751], which uses the Security Multiparts framework for + MIME [RFC1847]. + +2.2.1. Authorization + + In iTIP messages [iTIP], only the "Organizer" is authorized to modify + or cancel calendar entries she organizes. That is, + spoof@xyz.example.net is not allowed to modify or cancel a meeting + that was organized by a@example.com. Furthermore, only the + respondent has the authorization to indicate their status to the + "Organizer". That is, the "Organizer" MUST ignore an iTIP message + from spoof@xyz.example.net that declines a meeting invitation for + b@example.com. + + Implementations of iMIP SHOULD verify the authenticity of the creator + of an iCalendar object before taking any action. Methods for doing + this are presented later in this document. + + [RFC1847] message flow in iTIP supports someone working on behalf of + a "Calendar User" through use of the "sent-by" parameter that is + associated with the "ATTENDEE" and "ORGANIZER" properties. However, + there is no mechanism to verify whether or not a "Calendar User" has + authorized someone to work on their behalf. It is left to + implementations to provide mechanisms for the "Calendar Users" to + make that decision. + +2.2.2. Authentication + + Authentication MUST be performed using S/MIME [RFC5750] [RFC5751]. + Authentication is possible only on messages that have been signed. + Unauthenticated messages (i.e., unsigned messages) may not be + trusted. + +2.2.3. Confidentiality + + To ensure confidentiality using iMIP, implementations SHOULD utilize + encryption specified in S/MIME [RFC5750] [RFC5751]. iMIP does not + restrict a "Calendar User Agent" (CUA) from forwarding iCalendar + objects to other users or agents. + + + + + + + +Melnikov Standards Track [Page 5] + +RFC 6047 iMIP December 2010 + + +2.3. Email Addresses + + The calendar address specified within the "ORGANIZER" and "ATTENDEE" + properties in an iCalendar object sent using iMIP MUST be a proper + "mailto:" [MAILTO] URI specification for the corresponding + "Organizer" or "Attendee" of the "VEVENT" or "VTODO". + + Because [iTIP] does not preclude "Attendees" from forwarding + "VEVENT"s or "VTODO"s to others, the [RFC5322] "Sender" value may not + equal that of the "Organizer". Additionally, the "Organizer" or + "Attendee" cannot be reliably inferred by the [RFC5322] "Sender" or + "Reply-To" header field values of an iMIP message. The relevant + address MUST be ascertained by opening the "text/calendar" MIME body + part and examining the "ATTENDEE" and "ORGANIZER" properties. + +2.4. Content-Type Header Field + + A MIME body part containing content information that conforms to this + document MUST have an [RFC2045] "Content-Type" value of + "text/calendar". The [RFC2045] "Content-Type" header field MUST also + include the MIME parameter "method". The value MUST be the same + (ignoring case) as the value of the "METHOD" property within the + iCalendar object. + + Note 1: A MIME message containing multiple iCalendar objects with + different "method" values MUST be further encapsulated with a + "multipart/mixed" MIME entity [RFC2046]. This will allow each of + the iCalendar objects to be encapsulated within their own + "text/calendar" MIME entity. + + Note 2: A MIME body part with a "Content-Type" value of + "text/calendar" that lacks the "method" parameter is not + considered to be an iMIP body part and thus is not subject to the + requirements specified in this document. + + Note that according to [iCAL] the default character set for iCalendar + objects is UTF-8 [UTF-8]. However, the default character set for a + "text/*" MIME entity according to [RFC2046] is US-ASCII. Thus, a + "charset" MIME parameter MUST be present if the iCalendar object + contains characters that can't be represented in the US-ASCII + character set and, as specified in [iCAL], it MUST have the value + "UTF-8". + + The optional "component" MIME parameter defines the iCalendar + component type contained within the iCalendar object. + + + + + + +Melnikov Standards Track [Page 6] + +RFC 6047 iMIP December 2010 + + + The following is an example of this header field with a value that + indicates an event message. + + Content-Type: text/calendar; method=REQUEST; charset=UTF-8; + component=vevent + + The "text/calendar" content type allows for the scheduling message + type to be included in a MIME message with other content information + (i.e., "multipart/mixed") or included in a MIME message with a clear- + text, human-readable form of the scheduling message (i.e., + "multipart/alternative" [RFC2046]). + + In order to permit the information in the scheduling message to be + understood by MIME User Agents (UAs) that do not support the + "text/calendar" content type, scheduling messages SHOULD be sent with + an alternative, human-readable form of the information. + + Note that "multipart/alternative" MUST NOT be used to represent two + slightly different iCalendar objects, for example, two "VEVENT"s with + alternative starting times. + + CUAs can use other MIME parameters of the "Content-Type" header + field, as well as a language specified in the Content-Language header + field [RFC3282], to pick a "text/calendar" part for processing if a + "multipart/alternative" MIME message contains more than one + "text/calendar" part. + + Any receiving UA compliant with this specification MUST be able to + process "text/calendar" body parts enclosed within "multipart/*". + Note that a "multipart/mixed" MIME message can include multiple + "text/calendar" components. The receiving UA MUST be able to process + all of them. + +2.5. Content-Transfer-Encoding Header Field + + Unless an iMIP message is transported over 8-bit clean transport + (such as SMTP [8BITMIME]), a transfer encoding such as quoted- + printable or base64 [RFC2045] MUST be used for iCalendar objects + containing any characters that can't be represented in the US-ASCII + character set. For example: + + + + + + + + + + + +Melnikov Standards Track [Page 7] + +RFC 6047 iMIP December 2010 + + + From: user1@example.com + To: user2@example.com + Subject: Phone Conference + Mime-Version: 1.0 + Date: Wed, 07 May 2008 21:30:25 +0400 + Message-ID: <4821E731.5040506@laptop1.example.com> + Content-Type: text/calendar; method=REQUEST; charset=UTF-8 + Content-Transfer-Encoding: quoted-printable + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:user1@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:user1@example.com + ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:user2@example.com + DTSTAMP:20080507T170000Z + DTSTART:20080701T160000Z + DTEND:20080701T163000Z + SUMMARY:Phone call to discuss your last visit + DESCRIPTION:=D1=82=D1=8B =D0=BA=D0=B0=D0=BA - =D0=B4=D0=BE=D0= + =B2=D0=BE=D0=BB=D0=B5=D0=BD =D0=BF=D0=BE=D0=B5=D0=B7=D0=B4=D0=BA=D0 + =BE=D0=B9? + UID:calsvr.example.com-8739701987387998 + SEQUENCE:0 + STATUS:TENTATIVE + END:VEVENT + END:VCALENDAR + +2.6. Content-Disposition Header Field + + Implementations MAY include a "Content-Disposition" header field to + define a file name for an iCalendar object. However, the handling of + a MIME part MUST be based on its [RFC2045] "Content-Type" and not on + the extension specified in the "Content-Disposition", as different + email malware is known to trick User Agents into misinterpreting + content of messages by specifying a file extension in the Content- + Disposition header field that doesn't correspond to the value of the + "Content-Type" header field. + +3. Security Considerations + + The security threats that applications must address when implementing + iTIP are detailed in [iTIP]. In particular, two spoofing threats are + identified in Section 6.1 of [iTIP]: spoofing the "Organizer", and + spoofing an "Attendee". To address these threats, the originator of + an iCalendar object must be authenticated by a recipient. Once + + + +Melnikov Standards Track [Page 8] + +RFC 6047 iMIP December 2010 + + + authenticated, a determination can be made as to whether or not the + originator is authorized to perform the requested operation. + Compliant applications MUST support signing and encrypting + "text/calendar" body parts using a mechanism based on S/MIME + [RFC5750] [RFC5751] in order to facilitate the authentication of the + originator of the iCalendar object (see Sections 2.2.2 and 2.2.3). + The steps for processing a signed iMIP message are described below: + + 1. Using S/MIME, determine who signed the "text/calendar" body part + containing the iCalendar object. This is the "signer". (Note + that the email address of the signer MUST be specified in the + rfc822Name field of the "subject alternative name" extension of + the signer certificate, as specified in [RFC5280], + Section 4.1.2.6.) Note that the signer is not necessarily the + person sending an e-mail message, since an e-mail message can be + forwarded. + + 2. Correlate the signer to either an "ATTENDEE" property or to the + "ORGANIZER" property in the iCalendar object, based on the method + and the calendar component specified in the iCalendar object, as + defined in Section 1.4 of [iTIP]. If the signer cannot be + correlated to an "ATTENDEE"/"ORGANIZER" property, then actively + warn the user controlling the "Calendar User Agent" that the + iCalendar object is untrusted, and encourage the user to ignore + the message, but give advanced users the option to (a) view the + certificate of the signer and the entire certificate chain (if + any) in order to help decide if the signer should be trusted to + send the message, and then (b) allow the CUA to accept and process + the iCalendar object. + + 3. Determine whether or not the "ATTENDEE"/"ORGANIZER" is authorized + to perform the operation as defined by [iTIP]. If the conditions + are not met, ignore the message. + + 4. If all the above conditions are met, the message can be processed. + + S/MIME signing also protects against malicious changes to messages in + transit. + + If calendar confidentiality is required by the sender, signed iMIP + messages SHOULD be encrypted by a mechanism based on S/MIME [RFC5750] + [RFC5751]. If iMIP is used within a single ADministrative Management + Domain (ADMD) [RFC5598], SMTP STARTTLS [SMTP-TLS] (together with + STARTTLS in IMAP/POP [IMAP-POP-TLS]) MAY alternatively be used to + provide calendar confidentiality. + + + + + + +Melnikov Standards Track [Page 9] + +RFC 6047 iMIP December 2010 + + + Once a signed and/or encrypted iMIP message is received and + successfully verified (as detailed above) by a CUA, the CUA SHOULD + remember whether the sender of the message is using signing and/or + encrypting. If an unsigned iMIP message is received from the same + sender later on, the receiving CUA SHOULD warn the receiving user + about a possible man-in-the-middle attack and SHOULD ignore the + message, unless explicitly overridden by the user. + + Implementations MAY provide means for users to disable signing and + encrypting. + + It is possible to receive iMIP messages sent by someone working on + behalf of another "Calendar User". This is determined by examining + the "sent-by" parameter in the relevant "ORGANIZER" or "ATTENDEE" + property. [iCAL] and [iTIP] provide no mechanism to verify that a + "Calendar User" has authorized someone else to work on their behalf. + To address this security issue, implementations MUST provide + mechanisms for the "Calendar Users" to make that decision before + applying changes from someone working on behalf of a "Calendar User". + One way to achieve this is to reject iMIP messages sent by users + other than the "ORGANIZER" or the "ATTENDEE"s. Alternatively, the + receiver could have a list of trusted proxies in + its local security policy. And yet another way is to prompt the user + for confirmation. + + iMIP-based calendaring is frequently deployed within a single ADMD, + with boundary filtering employed to restrict email calendaring flows + to be inside the ADMD. This can help in minimizing malicious changes + to calendaring messages in transit, as well as in making + authorization decisions less risky. + + A security consideration associated with the use of the Content- + Disposition header field is described in Section 2.6. + + Use of S/MIME makes the security considerations discussed in + [RFC5750] [RFC5751] relevant to this document. For additional + security considerations regarding certificate and Certificate + Revocation List (CRL) verification, please see [RFC5280]. + + + + + + + + + + + + + +Melnikov Standards Track [Page 10] + +RFC 6047 iMIP December 2010 + + +4. Examples + +4.1. Single Component with an ATTACH Property + + This minimal message shows how an iCalendar object references an + attachment. The attachment is accessible via its URL. + + From: sman@netscape.example.com + To: stevesil@microsoft.example.com + Subject: Phone Conference + Mime-Version: 1.0 + Content-Type: text/calendar; method=REQUEST; charset=US-ASCII + Content-Transfer-Encoding: 7bit + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:man@netscape.example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:man@netscape.example.com + ATTENDEE;RSVP=YES:mailto:stevesil@microsoft.example.com + DTSTAMP:19970611T190000Z + DTSTART:19970701T210000Z + DTEND:19970701T230000Z + SUMMARY:Phone Conference + DESCRIPTION:Please review the attached document. + UID:calsvr.example.com-873970198738777 + ATTACH:ftp://ftp.bar.example.com/pub/docs/foo.doc + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + +4.2. Using multipart/alternative for Low-Fidelity Clients + + This example shows how a client can emit a multipart message that + includes both a plain text version and the full iCalendar object. + Clients that do not support "text/calendar" will still be capable of + rendering the plain text representation. + + + + + + + + + + + + +Melnikov Standards Track [Page 11] + +RFC 6047 iMIP December 2010 + + + From: foo1@example.com + To: foo2@example.com + Subject: Phone Conference + Mime-Version: 1.0 + Content-Type: multipart/alternative; boundary="01BD3665.3AF0D360" + + --01BD3665.3AF0D360 + Content-Type: text/plain; charset=us-ascii + Content-Transfer-Encoding: 7bit + + This is an alternative representation of a "text/calendar" + MIME object. + + When: 7/1/1997 10:00AM PDT - 7/1/97 10:30AM PDT + Where: + Organizer: foo1@example.com + Summary: Phone Conference + + --01BD3665.3AF0D360 + Content-Type: text/calendar; method=REQUEST; charset=US-ASCII + Content-Transfer-Encoding: 7bit + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:foo1@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:foo1@example.com + ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:foo2@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970701T170000Z + DTEND:19970701T173000Z + SUMMARY:Phone Conference + UID:calsvr.example.com-8739701987387771 + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + --01BD3665.3AF0D360 + +4.3. Single Component with an ATTACH Property and Inline Attachment + + This example shows how a message containing an iCalendar object + references an attached document. The reference is made using a + Content-ID (CID). Thus, the iCalendar object and the document are + packaged in a "multipart/related" encapsulation. + + + +Melnikov Standards Track [Page 12] + +RFC 6047 iMIP December 2010 + + + From: foo1@example.com + To: foo2@example.com + Subject: Phone Conference + Mime-Version: 1.0 + Content-Type: multipart/related; boundary="boundary-example-1" + + --boundary-example-1 + + Content-Type: text/calendar; method=REQUEST; charset=US-ASCII + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment; filename="event.ics" + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:foo1@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:foo1@example.com + ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:foo2@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970701T180000Z + DTEND:19970701T183000Z + SUMMARY:Phone Conference + UID:calsvr.example.com-8739701987387771 + ATTACH:cid:123456789@example.com + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + --boundary-example-1 + Content-Type: application/msword; name="FieldReport.doc" + Content-Transfer-Encoding: base64 + Content-Disposition: inline; filename="FieldReport.doc" + Content-ID: <123456789@example.com> + + 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAABAAAARAAAAAAA + AAAAEAAAQAAAAAEAAAD+////AAAAAEUAAAD///////////////////////////////// + ... + + --boundary-example-1-- + + + + + + + + + +Melnikov Standards Track [Page 13] + +RFC 6047 iMIP December 2010 + + +4.4. Multiple Similar Components + + Multiple iCalendar components of the same type can be included in the + iCalendar object when the "METHOD" is the same for each component. + + From: foo1@example.com + To: foo2@example.com + Subject: Summer Company Holidays + Mime-Version: 1.0 + Content-Type: text/calendar; method=PUBLISH; charset=US-ASCII + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment; filename="event.ics" + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:PUBLISH + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:foo1@example.com + DTSTAMP:19970611T150000Z + DTSTART:19970701T150000Z + DTEND:19970701T230000Z + SUMMARY:Company Picnic + DESCRIPTION:Food and drink will be provided + UID:calsvr.example.com-873970198738777-1 + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + BEGIN:VEVENT + ORGANIZER:mailto:foo1@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970715T150000Z + DTEND:19970715T230000Z + SUMMARY:Company Bowling Tournament + DESCRIPTION:We have 10 lanes reserved + UID:calsvr.example.com-873970198738777-2 + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + + + + + + + + + + +Melnikov Standards Track [Page 14] + +RFC 6047 iMIP December 2010 + + +4.5. Multiple Mixed Components + + Different component types must be encapsulated in separate iCalendar + objects. + + From: foo1@example.com + To: foo2@example.com + Subject: Phone Conference + Mime-Version: 1.0 + Content-Type: multipart/mixed; + boundary="--FEE3790DC7E35189CA67CE2C" + + This is a multi-part message in MIME format. + + ----FEE3790DC7E35189CA67CE2C + Content-Type: text/calendar; method=REQUEST; charset=US-ASCII + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment; filename="event1.ics" + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:mailto:foo1@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:foo1@example.com + ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:foo2@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970701T210000Z + DTEND:19970701T230000Z + SUMMARY:Phone Conference + DESCRIPTION:Discuss what happened at the last meeting + UID:calsvr.example.com-8739701987387772 + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + + + + + + + + + + + + + +Melnikov Standards Track [Page 15] + +RFC 6047 iMIP December 2010 + + + ----FEE3790DC7E35189CA67CE2C + Content-Type: text/calendar; method=REQUEST; charset=US-ASCII + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment; filename="todo1.ics" + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VTODO + DUE:19970701T160000Z + ORGANIZER:mailto:foo1@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:foo1@example.com + ATTENDEE;RSVP=YES:mailto:foo2@example.com + SUMMARY:Phone Conference + DESCRIPTION:Discuss a new location for the company picnic + UID:calsvr.example.com-td-8739701987387773 + SEQUENCE:0 + STATUS:NEEDS-ACTION + END:VEVENT + END:VCALENDAR + + ----FEE3790DC7E35189CA67CE2C + +4.6. Detailed Components with an ATTACH Property + + This example shows the format of a message containing a group meeting + between three individuals. The "multipart/related" encapsulation is + used because the iCalendar object contains an ATTACH property that + uses a CID to reference the attachment. + + From: foo1@example.com + MIME-Version: 1.0 + To: foo2@example.com,foo3@example.com + Subject: REQUEST - Phone Conference + Content-Type: multipart/related; + boundary="--FEE3790DC7E35189CA67CE2C" + + ----FEE3790DC7E35189CA67CE2C + Content-Type: multipart/alternative; + boundary="--00FEE3790DC7E35189CA67CE2C00" + + + + + + + + + + +Melnikov Standards Track [Page 16] + +RFC 6047 iMIP December 2010 + + + ----00FEE3790DC7E35189CA67CE2C00 + Content-Type: text/plain; charset=us-ascii + Content-Transfer-Encoding: 7bit + + When: 7/1/1997 10:00PM PDT - 7/1/97 10:30 PM PDT + Where: + Organizer: foo1@example.com + Summary: Let's discuss the attached document + + ----00FEE3790DC7E35189CA67CE2C00 + + Content-Type: text/calendar; method=REQUEST; charset=US-ASCII; + Component=vevent + Content-Transfer-Encoding: 7bit + Content-Disposition: attachment; filename="event.ics" + + BEGIN:VCALENDAR + PRODID:-//Example/ExampleCalendarClient//EN + METHOD:REQUEST + VERSION:2.0 + BEGIN:VEVENT + ORGANIZER:foo1@example.com + ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:foo1@example.com + ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:foo2@example.com + ATTENDEE;RSVP=YES;CUTYPE=INDIVIDUAL:mailto:foo3@example.com + DTSTAMP:19970611T190000Z + DTSTART:19970621T170000Z + DTEND:199706211T173000Z + SUMMARY:Let's discuss the attached document + UID:calsvr.example.com-873970198738777-8aa + ATTACH:cid:calsvr.example.com-12345aaa + SEQUENCE:0 + STATUS:CONFIRMED + END:VEVENT + END:VCALENDAR + + ----00FEE3790DC7E35189CA67CE2C00 + + + + + + + + + + + + + + +Melnikov Standards Track [Page 17] + +RFC 6047 iMIP December 2010 + + + ----FEE3790DC7E35189CA67CE2C + Content-Type: application/msword; name="FieldReport.doc" + Content-Transfer-Encoding: base64 + Content-Disposition: inline; filename="FieldReport.doc" + Content-ID: + + R0lGODdhTAQZAJEAAFVVVd3d3e4AAP///ywAAAAATAQZAAAC/5yPOSLhD6OctNqLs94Xq + AG4kiW5omm6sq27gvH8kzX9o1y+s73/g8MCofEovGITCoxKMbyCR16cNSq9YrNarfcrvd + riIH5LL5jE6rxc3G+v2cguf0uv2Oz+v38L7/DxgoOKjURnjIIbe3yNjo+AgZWYVIWWl5i + ZnJY6J + ... + + ----FEE3790DC7E35189CA67CE2C + +5. Recommended Practices + + This section outlines a series of recommended practices when using a + messaging transport to exchange iCalendar objects. + +5.1. Use of Content and Message IDs + + The [iCAL] specification makes frequent use of the URI for data types + in properties such as "DESCRIPTION", "ATTACH", "CONTACT", and others. + Two forms of URIs are the Message ID (MID) and the Content-ID (CID). + These are defined in [RFC2392]. Although [RFC2392] allows + referencing messages or MIME body parts in other MIME entities or + stores, it is strongly RECOMMENDED that iMIP implementations include + all referenced messages and body parts in a single MIME entity. + Simply put, if an iCalendar object contains CID or MID references to + other messages or body parts, implementations should ensure that + these messages and/or body parts are transmitted with the iCalendar + object. If they are not, there is no guarantee that the receiving + CUA will have the access or the authorization to view those objects. + +6. IANA Considerations + + The "text/calendar" MIME media type was registered in [iCAL]. + + + + + + + + + + + + + + +Melnikov Standards Track [Page 18] + +RFC 6047 iMIP December 2010 + + +7. References + +7.1. Normative References + + [iCAL] Desruisseaux, B., Ed., "Internet Calendaring and + Scheduling Core Object Specification (iCalendar)", + RFC 5545, September 2009. + + [iTIP] Daboo, C., Ed., "iCalendar Transport-Independent + Interoperability Protocol (iTIP)", RFC 5546, December + 2009. + + [RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, + October 2008. + + [MAILTO] Duerst, M., Masinter, L., and J. Zawinski, "The 'mailto' + URI Scheme", RFC 6068, October 2010. + + [RFC1847] Galvin, J., Murphy, S., Crocker, S., and N. Freed, + "Security Multiparts for MIME: Multipart/Signed and + Multipart/Encrypted", RFC 1847, October 1995. + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2046] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part Two: Media Types", RFC 2046, + November 1996. + + [RFC2392] Levinson, E., "Content-ID and Message-ID Uniform Resource + Locators", RFC 2392, August 1998. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", STD 63, RFC 3629, November 2003. + + [SMTP-TLS] Hoffman, P., "SMTP Service Extension for Secure SMTP over + Transport Layer Security", RFC 3207, February 2002. + + [IMAP-POP-TLS] + Newman, C., "Using TLS with IMAP, POP3 and ACAP", + RFC 2595, June 1999. + + + + + + +Melnikov Standards Track [Page 19] + +RFC 6047 iMIP December 2010 + + + [RFC5750] Ramsdell, B. and S. Turner, "Secure/Multipurpose Internet + Mail Extensions (S/MIME) Version 3.2 Certificate + Handling", RFC 5750, January 2010. + + [RFC5751] Ramsdell, B. and S. Turner, "Secure/Multipurpose Internet + Mail Extensions (S/MIME) Version 3.2 Message + Specification", RFC 5751, January 2010. + + [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., + Housley, R., and W. Polk, "Internet X.509 Public Key + Infrastructure Certificate and Certificate Revocation + List (CRL) Profile", RFC 5280, May 2008. + +7.2. Informative References + + [8BITMIME] Klensin, J., Freed, N., Rose, M., Stefferud, E., and D. + Crocker, "SMTP Service Extension for 8bit-MIMEtransport", + RFC 1652, July 1994. + + [RFC5598] Crocker, D., "Internet Mail Architecture", RFC 5598, July + 2009. + + [RFC3282] Alvestrand, H., "Content Language Headers", RFC 3282, May + 2002. + + + + + + + + + + + + + + + + + + + + + + + + + + + +Melnikov Standards Track [Page 20] + +RFC 6047 iMIP December 2010 + + +Appendix A. Changes since RFC 2447 + + Updated references. Split them into Normative and Informative. + + Updated examples to use example.com/example.net domains. + + Corrected usage of RFC 2119 language. + + Clarified that charset=UTF-8 is required, unless the calendar can be + entirely represented in US-ASCII. + + Clarified that 7-bit content transfer encodings should be used unless + the calendar object is known to be transferred over 8-bit clean + transport. + + Clarified that file extension specified in the Content-Disposition + header field is not to be used to override the "Content-Type" MIME + type. + + Disallowed use of "multipart/alternative" for slightly different + representations of the same calendar. + + Clarified handling of the "method" MIME parameter of the "Content- + Type" header field. + + Clarified that in an iMIP message an ORGANIZER/ATTENDEE property + contains a mailto: URI. + + Fixed examples with ATTENDEE property to use "CUTYPE=" instead of + "TYPE=". + + Clarified that message integrity/confidentiality should be achieved + using S/MIME. + + Provided additional examples. + + Improved the Security Considerations section. + + Made multiple editorial changes to different sections of the + document. + + + + + + + + + + + +Melnikov Standards Track [Page 21] + +RFC 6047 iMIP December 2010 + + +Appendix B. Acknowledgements + + The editor of this document wishes to thank Frank Dawson, Steve + Mansour, and Steve Silverberg, the original authors of RFC 2447, as + well as the following individuals who have participated in the + drafting, review, and discussion of this memo: + + Reinhold Kainhofer, Cyrus Daboo, Bernard Desruisseaux, Eliot Lear, + and Peter Saint-Andre. + +Author's Address + + Alexey Melnikov (editor) + Isode Ltd + 5 Castle Business Village + 36 Station Road + Hampton, Middlesex TW12 2BX + UK + + EMail: Alexey.Melnikov@isode.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Melnikov Standards Track [Page 22] + diff --git a/dav/SabreDAV/docs/rfc6321.txt b/dav/SabreDAV/docs/rfc6321.txt new file mode 100644 index 000000000..c038c64cf --- /dev/null +++ b/dav/SabreDAV/docs/rfc6321.txt @@ -0,0 +1,3027 @@ + + + + + + +Internet Engineering Task Force (IETF) C. Daboo +Request for Comments: 6321 Apple, Inc. +Category: Standards Track M. Douglass +ISSN: 2070-1721 RPI + S. Lees + Microsoft + August 2011 + + + xCal: The XML Format for iCalendar + +Abstract + + This specification defines "xCal", an XML format for iCalendar data. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6321. + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + + + + +Daboo, et al. Standards Track [Page 1] + +RFC 6321 xCal August 2011 + + +Table of Contents + + 1. Introduction ....................................................3 + 2. Conventions Used in This Document ...............................4 + 3. Converting from iCalendar to xCal ...............................4 + 3.1. Pre-Processing .............................................4 + 3.2. iCalendar Stream (RFC 5545, Section 3.4) ...................5 + 3.3. Components (RFC 5545, Section 3.6) .........................6 + 3.4. Properties (RFC 5545, Sections 3.7 and 3.8) ................6 + 3.4.1. Special Cases for Properties ........................8 + 3.4.1.1. Multi-Valued Properties ....................8 + 3.4.1.2. GEO Property ...............................9 + 3.4.1.3. REQUEST-STATUS Property ....................9 + 3.5. Parameters (RFC 5545, Section 3.2) ........................10 + 3.5.1. VALUE Parameter ....................................11 + 3.6. Values (RFC 5545, Section 3.3) ............................11 + 3.6.1. Binary (RFC 5545, Section 3.3.1) ...................12 + 3.6.2. Boolean (RFC 5545, Section 3.3.2) .................12 + 3.6.3. Calendar User Address (RFC 5545, Section 3.3.3) ....12 + 3.6.4. Date (RFC 5545, Section 3.3.4) .....................12 + 3.6.5. Date-Time (RFC 5545, Section 3.3.5) ................13 + 3.6.6. Duration (RFC 5545, Section 3.3.6) .................13 + 3.6.7. Float (RFC 5545, Section 3.3.7) ....................13 + 3.6.8. Integer (RFC 5545, Section 3.3.8) ..................14 + 3.6.9. Period of Time (RFC 5545, Section 3.3.9) ...........14 + 3.6.10. Recurrence Rule (RFC 5545, Section 3.3.10) ........14 + 3.6.11. Text (RFC 5545, Section 3.3.11) ...................15 + 3.6.12. Time (RFC 5545, Section 3.3.12) ...................15 + 3.6.13. URI (RFC 5545, Section 3.3.13) ....................15 + 3.6.14. UTC Offset (RFC 5545, Section 3.3.14) .............16 + 3.7. Extensions ................................................16 + 4. Converting from xCal into iCalendar ............................16 + 4.1. Converting XML Extensions into iCalendar ..................16 + 4.2. The XML Property for iCalendar ............................17 + 5. Handling Unrecognized Properties or Parameters .................18 + 6. Security Considerations ........................................19 + 7. IANA Considerations ............................................20 + 7.1. Namespace Registration ....................................20 + 7.2. Media Type ................................................20 + 7.3. iCalendar Property Registrations ..........................21 + 8. Acknowledgments ................................................22 + 9. References .....................................................22 + 9.1. Normative References ......................................22 + 9.2. Informative References ....................................22 + + + + + + + +Daboo, et al. Standards Track [Page 2] + +RFC 6321 xCal August 2011 + + + Appendix A. RELAX NG Schema .......................................23 + Appendix B. Examples ..............................................49 + B.1. Example 1 ..................................................49 + B.1.1. iCalendar Data .........................................49 + B.1.2. XML Data ...............................................49 + B.2. Example 2 ..................................................50 + B.2.1. iCalendar Data .........................................50 + B.2.2. XML Data ...............................................51 + +1. Introduction + + The iCalendar data format [RFC5545] is a widely deployed interchange + format for calendaring and scheduling data. While many applications + and services consume and generate calendar data, iCalendar is a + specialized format that requires its own parser/generator. In + contrast, XML-based formats are widely used for interoperability + between applications, and the many tools that generate, parse, and + manipulate XML make it easier to work with than iCalendar. + + The purpose of this specification is to define "xCal", an XML format + for iCalendar data. xCal is defined as a straightforward mapping into + XML from iCalendar, so that iCalendar data can be converted to XML, + and then back to iCalendar, without losing any semantic meaning in + the data. Anyone creating xCal calendar data according to this + specification will know that their data can be converted to a valid + iCalendar representation as well. + + Key design considerations are: + + Round-tripping (converting an iCalendar instance to xCal and back) + will give the same semantic result as the starting point. That + is, all components, properties, and property parameters are + guaranteed to be preserved, with the exception of those that have + default values. + + xCal preserves the semantics of the iCalendar data. While a + simple consumer can easily browse the calendar data in xCal, a + full understanding of iCalendar is still required in order to + modify and/or fully comprehend the calendar data. + + xCal has the ability to handle many extensions to the underlying + iCalendar specification without requiring an update to this + document. + + + + + + + + +Daboo, et al. Standards Track [Page 3] + +RFC 6321 xCal August 2011 + + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + When XML element types in the namespace + "urn:ietf:params:xml:ns:icalendar-2.0" are referenced in this + document outside of the context of an XML fragment, the string "IC:" + will be prefixed to the element types. + + Some examples in this document contain "partial" XML documents used + for illustrative purposes. In these examples, three periods "..." + are used to indicate a portion of the document that has been removed + for compactness. + +3. Converting from iCalendar to xCal + + This section describes how iCalendar data is converted to xCal using + a simple mapping between the iCalendar data model and XML elements. + +3.1. Pre-Processing + + iCalendar uses a line folding mechanism to limit lines of data to a + maximum line length (typically 72 characters) to ensure maximum + likelihood of preserving data integrity as it is transported via + various means (e.g., email) -- see Section 3.1 of [RFC5545]. Prior + to converting iCalendar data into xCal, all folded lines MUST be + unfolded. + + iCalendar data uses an "escape" character sequence for text values + and property parameter values. When such text elements are converted + into xCal, the escaping MUST be removed. + + iCalendar uses a base64 encoding for binary data. However, it does + not restrict the encoding from being applied to non-binary value + types. So, the following rules MUST be applied when processing a + property with the "ENCODING" property parameter set to "BASE64": + + o If the property value type is "BINARY", the base64 encoding MUST + be preserved. + + o If the value type is not "BINARY", the "ENCODING" property + parameter MUST be removed, and the value MUST be base64 decoded. + + When base64 encoding and decoding are used, they MUST conform to + Section 4 of [RFC4648], which is the base64 method used in [RFC5545]. + + + + +Daboo, et al. Standards Track [Page 4] + +RFC 6321 xCal August 2011 + + + One key difference in the formatting of values used in iCalendar and + xCal is that, in xCal, the specification uses date/time and UTC + offset values aligned with the syntax of + [W3C.REC-xmlschema-2-20041028] to aid with XML processing. + +3.2. iCalendar Stream (RFC 5545, Section 3.4) + + At the top level of the iCalendar object model is an "iCalendar + stream". This object encompasses multiple "iCalendar objects". In + xCal, the entire stream is contained in the root IC:icalendar XML + element. + + An iCalendar stream can contain one or more iCalendar objects. Each + iCalendar object, delimited by "BEGIN:VCALENDAR" and "END:VCALENDAR", + is enclosed by the IC:vcalendar XML element. + + Example: + + + + + ... + + + + iCalendar objects are comprised of a set of "components", + "properties", "parameters", and "values". A "component" can contain + other "components" or "properties". A "property" has a value and a + set of zero or more "parameters". + + In xCal, component elements, for example, IC:vevent and IC:vtodo, are + contained within an IC:components XML element. Within the component + element, another IC:components element could appear (representing + components nested within components) or the IC:properties XML element + could appear. IC:properties is used to encapsulate iCalendar + properties. + + Each iCalendar property will be mapped to its own XML element as + described below. Within each of these elements, there is zero or one + IC:parameters XML element used to encapsulate any iCalendar property + parameters. Additionally there will be one or more XML elements + representing the value of the iCalendar property. + + + + + + + + + +Daboo, et al. Standards Track [Page 5] + +RFC 6321 xCal August 2011 + + + Example: + + + + + + ... + + + ... + + + + + +------------------+--------------+------------------+ + | Item | XML element | XML Definition | + +------------------+--------------+------------------+ + | iCalendar Stream | IC:icalendar | Appendix A # 3.4 | + | VCALENDAR | IC:vcalendar | Appendix A # 3.6 | + +------------------+--------------+------------------+ + +3.3. Components (RFC 5545, Section 3.6) + + Each calendar component in the "VCALENDAR" object, delimited by + "BEGIN" and "END", will be converted to an enclosing XML element with + the same name, but in lowercase. As an example, the table below + shows iCalendar-to-xCal mappings for current iCalendar components. + Any new iCalendar components added in the future will be converted in + the same way. + + +-----------+--------------+--------------------+ + | Component | XML element | XML Definition | + +-----------+--------------+--------------------+ + | VEVENT | IC:vevent | Appendix A # 3.6.1 | + | VTODO | IC:vtodo | Appendix A # 3.6.2 | + | VJOURNAL | IC:vjournal | Appendix A # 3.6.3 | + | VFREEBUSY | IC:vfreebusy | Appendix A # 3.6.4 | + | VTIMEZONE | IC:vtimezone | Appendix A # 3.6.5 | + | STANDARD | IC:standard | Appendix A # 3.6.5 | + | DAYLIGHT | IC:daylight | Appendix A # 3.6.5 | + | VALARM | IC:valarm | Appendix A # 3.6.6 | + +-----------+--------------+--------------------+ + +3.4. Properties (RFC 5545, Sections 3.7 and 3.8) + + iCalendar properties, whether they apply to the "VCALENDAR" object or + to a component, are handled in a consistent way in the xCal format. + + + + +Daboo, et al. Standards Track [Page 6] + +RFC 6321 xCal August 2011 + + + iCalendar properties are enclosed in the XML element IC:properties. + + Each individual iCalendar property is represented in xCal by an + element of the same name as the iCalendar property, but in lowercase. + For example, the "CALSCALE" property is represented in xCal by the + IC:calscale element. + + Example: + + + + + + ... + ... + ... + + + ... + + + + + Each property can contain an IC:parameters XML element encapsulating + any iCalendar property parameters associated with the iCalendar + property. + + Each property will contain one or more "value" XML elements as + described below representing the value of the iCalendar property. + + As an example, the table below shows iCalendar-to-xCal mappings for + current iCalendar properties. Any new iCalendar properties added in + the future will be converted in the same way. + + +------------------+---------------------+-----------------------+ + | Property | XML element | XML Definition | + +------------------+---------------------+-----------------------+ + | CALSCALE | IC:calscale | Appendix A # 3.7.1 | + | METHOD | IC:method | Appendix A # 3.7.2 | + | PRODID | IC:prodid | Appendix A # 3.7.3 | + | VERSION | IC:version | Appendix A # 3.7.4 | + | ATTACH | IC:attach | Appendix A # 3.8.1.1 | + | CATEGORIES | IC:categories | Appendix A # 3.8.1.2 | + | CLASS | IC:class | Appendix A # 3.8.1.3 | + | COMMENT | IC:comment | Appendix A # 3.8.1.4 | + | DESCRIPTION | IC:description | Appendix A # 3.8.1.5 | + | GEO | IC:geo | Appendix A # 3.8.1.6 | + | LOCATION | IC:location | Appendix A # 3.8.1.7 | + + + +Daboo, et al. Standards Track [Page 7] + +RFC 6321 xCal August 2011 + + + | PERCENT-COMPLETE | IC:percent-complete | Appendix A # 3.8.1.8 | + | PRIORITY | IC:priority | Appendix A # 3.8.1.9 | + | RESOURCES | IC:resources | Appendix A # 3.8.1.10 | + | STATUS | IC:status | Appendix A # 3.8.1.11 | + | SUMMARY | IC:summary | Appendix A # 3.8.1.12 | + | COMPLETED | IC:completed | Appendix A # 3.8.2.1 | + | DTEND | IC:dtend | Appendix A # 3.8.2.2 | + | DUE | IC:due | Appendix A # 3.8.2.3 | + | DTSTART | IC:dtstart | Appendix A # 3.8.2.4 | + | DURATION | IC:duration | Appendix A # 3.8.2.5 | + | FREEBUSY | IC:freebusy | Appendix A # 3.8.2.6 | + | TRANSP | IC:transp | Appendix A # 3.8.2.7 | + | TZID | IC:tzid | Appendix A # 3.8.3.1 | + | TZNAME | IC:tzname | Appendix A # 3.8.3.2 | + | TZOFFSETFROM | IC:tzoffsetfrom | Appendix A # 3.8.3.3 | + | TZOFFSETTO | IC:tzoffsetto | Appendix A # 3.8.3.4 | + | TZURL | IC:tzurl | Appendix A # 3.8.3.5 | + | ATTENDEE | IC:attendee | Appendix A # 3.8.4.1 | + | CONTACT | IC:contact | Appendix A # 3.8.4.2 | + | ORGANIZER | IC:organizer | Appendix A # 3.8.4.3 | + | RECURRENCE-ID | IC:recurrence-id | Appendix A # 3.8.4.4 | + | RELATED-TO | IC:related-to | Appendix A # 3.8.4.5 | + | URL | IC:url | Appendix A # 3.8.4.6 | + | UID | IC:uid | Appendix A # 3.8.4.7 | + | EXDATE | IC:exdate | Appendix A # 3.8.5.1 | + | RDATE | IC:rdate | Appendix A # 3.8.5.2 | + | RRULE | IC:rrule | Appendix A # 3.8.5.3 | + | ACTION | IC:action | Appendix A # 3.8.6.1 | + | REPEAT | IC:repeat | Appendix A # 3.8.6.2 | + | TRIGGER | IC:trigger | Appendix A # 3.8.6.3 | + | CREATED | IC:created | Appendix A # 3.8.7.1 | + | DTSTAMP | IC:dtstamp | Appendix A # 3.8.7.2 | + | LAST-MODIFIED | IC:last-modified | Appendix A # 3.8.7.3 | + | SEQUENCE | IC:sequence | Appendix A # 3.8.7.4 | + | REQUEST-STATUS | IC:request-status | Appendix A # 3.8.8.3 | + +------------------+---------------------+-----------------------+ + +3.4.1. Special Cases for Properties + + This section describes some properties that have special handling + when converting to xCal. + +3.4.1.1. Multi-Valued Properties + + The following iCalendar properties can have values that consist of a + list of "standard" iCalendar values separated by a specific + delimiter. In xCal, these properties are represented by an XML + element that contains multiple "value" elements (Section 3.6). + + + +Daboo, et al. Standards Track [Page 8] + +RFC 6321 xCal August 2011 + + + +------------+---------------+-----------------------+ + | Property | XML element | XML Definition | + +------------+---------------+-----------------------+ + | CATEGORIES | IC:categories | Appendix A # 3.8.1.2 | + | RESOURCES | IC:resources | Appendix A # 3.8.1.10 | + | FREEBUSY | IC:freebusy | Appendix A # 3.8.2.6 | + | EXDATE | IC:exdate | Appendix A # 3.8.5.1 | + | RDATE | IC:rdate | Appendix A # 3.8.5.2 | + +------------+---------------+-----------------------+ + +3.4.1.2. GEO Property + + In iCalendar, the "GEO" property value is defined as a semicolon- + separated list of two "FLOAT" values; the first representing latitude + and the second longitude. + + In xCal, the value for the IC:geo element is represented by two XML + elements. These are an IC:latitude element and an IC:longitude + element, each of which contains float values. See Appendix A # + 3.8.1.6. + + Example: + + + + ... + + 37.386013 + -122.082932 + + ... + + +3.4.1.3. REQUEST-STATUS Property + + In iCalendar, the "REQUEST-STATUS" property value is defined as a + semicolon-separated list of two or three "TEXT" values. The first + represents a code, the second a description, and the third any + additional data. + + In xCal, the value for the IC:request-status element is represented + by two or three XML elements. These are an IC:code element, an IC: + description element, and an IC:data element, each of which contains + the corresponding "TEXT" values. If there is no additional data in + the iCalendar value, the IC:data element (which would be empty) + SHOULD NOT be present. See Appendix A # 3.8.8.3. + + + + + +Daboo, et al. Standards Track [Page 9] + +RFC 6321 xCal August 2011 + + + Example: + + + + ... + + 2.0 + Success + + ... + + +3.5. Parameters (RFC 5545, Section 3.2) + + iCalendar property parameters are enclosed in the XML element IC: + parameters, which occurs in each property XML element. If there are + no iCalendar property parameters, the IC:parameters element (which + would be empty) SHOULD NOT be present. + + Each individual iCalendar property parameter is represented in xCal + by an element of the same name as the iCalendar property parameter, + but in lowercase. For example, the "PARTSTAT" property parameter is + represented in xCal by the IC:partstat element. + + Example: + + + + + ... + + ... + + + NEEDS-ACTION + + ... + + ... + + + + + Each XML parameter element contains one or more child XML elements + representing iCalendar value types. + + + + + + +Daboo, et al. Standards Track [Page 10] + +RFC 6321 xCal August 2011 + + + As an example, the table below shows iCalendar-to-xCal mappings for + current iCalendar parameters. Any new iCalendar parameters added in + the future will be converted in the same way. + + +----------------+-------------------+---------------------+ + | Parameter | XML element | XML Definition | + +----------------+-------------------+---------------------+ + | ALTREP | IC:altrep | Appendix A # 3.2.1 | + | CN | IC:cn | Appendix A # 3.2.2 | + | CUTYPE | IC:cutype | Appendix A # 3.2.3 | + | DELEGATED-FROM | IC:delegated-from | Appendix A # 3.2.4 | + | DELEGATED-TO | IC:delegated-to | Appendix A # 3.2.5 | + | DIR | IC:dir | Appendix A # 3.2.6 | + | ENCODING | IC:encoding | Appendix A # 3.2.7 | + | FMTTYPE | IC:fmttype | Appendix A # 3.2.8 | + | FBTYPE | IC:fbtype | Appendix A # 3.2.9 | + | LANGUAGE | IC:language | Appendix A # 3.2.10 | + | MEMBER | IC:member | Appendix A # 3.2.11 | + | PARTSTAT | IC:partstat | Appendix A # 3.2.12 | + | RANGE | IC:range | Appendix A # 3.2.13 | + | RELATED | IC:related | Appendix A # 3.2.14 | + | RELTYPE | IC:reltype | Appendix A # 3.2.15 | + | ROLE | IC:role | Appendix A # 3.2.16 | + | RSVP | IC:rsvp | Appendix A # 3.2.17 | + | SENT-BY | IC:sent-by | Appendix A # 3.2.18 | + | TZID | IC:tzid | Appendix A # 3.2.19 | + +----------------+-------------------+---------------------+ + +3.5.1. VALUE Parameter + + iCalendar defines a "VALUE" property parameter (Section 3.2.20 of + [RFC5545]). This property parameter is not mapped to an xCal XML + element. Instead, the value type is handled by having different XML + elements for each value, and these appear inside of property + elements. Thus, when converting from iCalendar to xCal, any "VALUE" + property parameters are skipped. When converting from xCal into + iCalendar, the appropriate "VALUE" property parameter MUST be + included in the iCalendar property if the value type is not the + default value type for that property. + +3.6. Values (RFC 5545, Section 3.3) + + In the typical case, iCalendar value types are mapped into XML + elements with a matching name in all lowercase. In the case of the + value for a recurrence rule (see below), iCalendar defines + "structured" values, and these are mapped into separate child + elements for each value element. + + + + +Daboo, et al. Standards Track [Page 11] + +RFC 6321 xCal August 2011 + + +3.6.1. Binary (RFC 5545, Section 3.3.1) + + Description: iCalendar "BINARY" property values are represented by + the IC:binary XML element. The content of the element is base64 + encoded data, conforming to Section 4 of [RFC4648], which is the + base64 method used in [RFC5545]. Whitespace MAY be inserted into + the data at any point to "wrap" the data to reasonable line + lengths. When converting back to iCalendar, the whitespace MUST + first be removed. + + XML Definition: Appendix A # 3.3.1 + + Example: + + SGVsbG8gV29ybGQh + +3.6.2. Boolean (RFC 5545, Section 3.3.2) + + Description: iCalendar "BOOLEAN" property values are represented by + the IC:boolean XML element. The content of the element is a + boolean value. + + XML Definition: Appendix A # 3.3.2 + + Example: + + true + +3.6.3. Calendar User Address (RFC 5545, Section 3.3.3) + + Description: iCalendar "CAL-ADDRESS" property values are represented + by the IC:cal-address XML element. The content of the element is + a URI. + + XML Definition: Appendix A # 3.3.3 + + Example: + + mailto:cyrus@example.com + +3.6.4. Date (RFC 5545, Section 3.3.4) + + Description: iCalendar "DATE" property values are represented by the + IC:date XML element. The content of the element is the same date + value specified by [RFC5545], with the exception that the date + components are separated by "-" characters, for consistency with + [W3C.REC-xmlschema-2-20041028]. + + + + +Daboo, et al. Standards Track [Page 12] + +RFC 6321 xCal August 2011 + + + XML Definition: Appendix A # 3.3.4 + + Example: + + 2011-05-17 + +3.6.5. Date-Time (RFC 5545, Section 3.3.5) + + Description: iCalendar "DATE-TIME" property values are represented + by the IC:date-time XML element. The content of the element is + the same date-time value specified by [RFC5545], with the + exception that the date components are separated by "-" + characters, and the time components are separated by ":" + characters, for consistency with [W3C.REC-xmlschema-2-20041028]. + Note that while [W3C.REC-xmlschema-2-20041028] allows for a UTC + offset to be included in date/time values, xCal does not use that, + and instead follows the iCalendar behavior of using time zone + definitions via the "TZID" property parameter. + + XML Definition: Appendix A # 3.3.5 + + Example: + + 2011-05-17T12:00:00 + +3.6.6. Duration (RFC 5545, Section 3.3.6) + + Description: iCalendar "DURATION" property values are represented by + the IC:duration XML element. The content of the element is the + same duration value specified by [RFC5545]. + + XML Definition: Appendix A # 3.3.6 + + Example: + + P1D + +3.6.7. Float (RFC 5545, Section 3.3.7) + + Description: iCalendar "FLOAT" property values are represented by + the IC:float XML element. The content of the element is a text + representation of a floating point number. + + XML Definition: Appendix A # 3.3.7 + + Example: + + 0.5 + + + +Daboo, et al. Standards Track [Page 13] + +RFC 6321 xCal August 2011 + + +3.6.8. Integer (RFC 5545, Section 3.3.8) + + Description: iCalendar "INTEGER" property values are represented by + the IC:integer XML element. The content of the element is a text + representation of an integer number. + + XML Definition: Appendix A # 3.3.8 + + Examples: + + 50 + -100 + +3.6.9. Period of Time (RFC 5545, Section 3.3.9) + + Description: iCalendar "PERIOD" property values are represented by + the IC:period XML element. The content of the element is child + elements representing the start, end, or duration components of + the period. + + XML Definition: Appendix A # 3.3.9 + + Example: + + + 2011-05-17T12:00:00 + P1H + + +3.6.10. Recurrence Rule (RFC 5545, Section 3.3.10) + + Description: iCalendar "RECUR" property values are represented by + the IC:recur XML element. The content of the element is child + elements representing the various components of a recurrence rule. + + XML Definition: Appendix A # 3.3.10 + + Example: + + + YEARLY + 5 + -1SU + 10 + + + + + + + +Daboo, et al. Standards Track [Page 14] + +RFC 6321 xCal August 2011 + + +3.6.11. Text (RFC 5545, Section 3.3.11) + + Description: iCalendar "TEXT" property values are represented by the + IC:text XML element. The content of the element is simple text. + + XML Definition: Appendix A # 3.3.11 + + Example: + + Hello World! + +3.6.12. Time (RFC 5545, Section 3.3.12) + + Description: iCalendar "TIME" property values are represented by the + IC:time XML element. The content of the element is the same time + value specified by [RFC5545], with the exception that the time + components are separated by ":" characters, for consistency with + [W3C.REC-xmlschema-2-20041028]. Note that while + [W3C.REC-xmlschema-2-20041028] allows for a UTC offset to be + included in date/time values, xCal does not use that, and instead + follows the iCalendar behavior of using time zone definitions via + the "TZID" property parameter. + + XML Definition: Appendix A # 3.3.12 + + Example: + + + +3.6.13. URI (RFC 5545, Section 3.3.13) + + Description: iCalendar "URI" property values are represented by the + IC:uri XML element. The content of the element is a URI. + + XML Definition: Appendix A # 3.3.13 + + Example: + + http://calendar.example.com + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 15] + +RFC 6321 xCal August 2011 + + +3.6.14. UTC Offset (RFC 5545, Section 3.3.14) + + Description: iCalendar "UTC-OFFSET" property values are represented + by the IC:utc-offset XML element. The content of the element is + the same UTC offset value specified by [RFC5545], with the + exception that the hour, minute, and second components are + separated by a ":" character, for consistency with + [W3C.REC-xmlschema-2-20041028]. + + XML Definition: Appendix A # 3.3.14 + + Example: + + -05:00 + +3.7. Extensions + + iCalendar extension properties and property parameters (those with an + "X-" prefix in their name) are handled in the same way as other + properties and property parameters: the property or property + parameter is represented by an XML element with the same name, but in + lowercase, e.g., the "X-FOO" property in iCalendar turns into the IC: + x-foo element in xCal. However, see Section 5 for how to deal with + default values for unrecognized extension properties or property + parameters. + +4. Converting from xCal into iCalendar + + When converting component, property, and property parameter values, + the names SHOULD be converted to uppercase. Although iCalendar names + are case insensitive, common practice is to keep them all uppercase + following the actual definitions in [RFC5545]. + + BACKSLASH character encoding and line folding MUST be applied to the + resulting iCalendar data as required by [RFC5545]. + + Non-binary value types MUST NOT be base64 encoded. + +4.1. Converting XML Extensions into iCalendar + + XML extensions are converted back to iCalendar in one of two ways, + depending on whether the extensions are in the iCalendar XML + namespace or in an external namespace. + + Extensions that are part of the iCalendar XML namespace MUST have + element names that begin with "x-", and will be converted back to the + equivalent extension property in iCalendar. For example, the "x-foo" + element will convert to the "X-FOO" iCalendar property. + + + +Daboo, et al. Standards Track [Page 16] + +RFC 6321 xCal August 2011 + + + Extensions that are in a namespace other than the iCalendar XML + namespace SHOULD be preserved in the iCalendar representation using + the "XML" iCalendar property described in Section 4.2. Only those + extension elements that are immediate child elements of the IC: + properties element are converted, any others are ignored. + +4.2. The XML Property for iCalendar + + This section describes an extension property for iCalendar, as + covered in Section 8.2.3 of [RFC5545]. + + Property name: XML + + Purpose: To embed extended XML-encoded iCalendar data in the + iCalendar format. + + Value type: The default value type is "TEXT". The value type can + also be set to "BINARY" to indicate base64 encoded content. + + Property parameters: IANA, non-standard, inline encoding, and value + data type property parameters can be specified on this property. + + Conformance: The property can be specified multiple times in any + calendar component. + + Description: The value of this property is a single XML 1.0 + [W3C.REC-xml-20081126] element. The "XML" property MUST NOT be used + to contain properties that are already defined in iCalendar. Since + all elements in the urn:ietf:params:xml:ns:icalendar-2.0 namespace + convert to a well-defined iCalendar object, the elements in this + property MUST NOT be in the urn:ietf:params:xml:ns:icalendar-2.0 + namespace. The XML element that is the value of this property MUST + have an XML namespace declaration. + + The default value type for this property is "TEXT", and normal + BACKSLASH character encoding rules for that value MUST be applied. + Note that the source XML can contain characters not allowed in "TEXT" + property values. If this is the case, then the XML data MUST be + base64 encoded. As required by [RFC5545], the "ENCODING" property + parameter MUST be present and set to "BASE64", and the "VALUE" + property parameter MUST be present and set to "BINARY". + + The ordering of "XML" properties is not preserved in the conversion + between xCal and iCalendar. + + Format definition: This property is defined by the following + notation: + + + + +Daboo, et al. Standards Track [Page 17] + +RFC 6321 xCal August 2011 + + + xml = "XML" xmlparam ( ":" text ) / + ( + ";" "ENCODING" "=" "BASE64" + ";" "VALUE" "=" "BINARY" + ":" binary + ) + CRLF + + xmlparam = *(";" other-param) + + Example: The following is an example of a location embedded in KML + markup inside the "XML" property. + + XML:\n + \n + KML Sample\n + 1\n + An incomplete example of a KML docum + ent - used as an example!\n + \n + + +5. Handling Unrecognized Properties or Parameters + + In iCalendar, properties have a default value type specified by their + definition, e.g., "SUMMARY"'s value type is "TEXT" and "DURATION"'s + is "DURATION". When a property uses its default value type, the + "VALUE" property parameter does not need to be specified on the + property. + + When new properties are defined or "X-" properties are used, an + iCalendar<->xCal converter might not recognize them, and know what + the appropriate default value types are, yet they need to be able to + preserve the values. A similar issue arises for unrecognized + property parameters. As a result, the following rules are applied + when dealing with unrecognized properties and property parameters: + + o When converting iCalendar into xCal: + + * Any property that does not include a "VALUE" property parameter + and whose default value type is not known MUST be converted + using the value type XML element IC:unknown. The content of + that element is the unprocessed value text. + + * Any unrecognized property parameter MUST be converted using the + value type XML element IC:unknown, with its content set to the + property parameter value text, treated as if it were a "TEXT" + value or list of "TEXT" values. + + + +Daboo, et al. Standards Track [Page 18] + +RFC 6321 xCal August 2011 + + + o When converting xCal into iCalendar: + + * Any IC:unknown property value XML elements are converted + directly into iCalendar values. The containing property MUST + NOT have a "VALUE" property parameter. + + * Any IC:unknown parameter value XML elements are converted as if + they were IC:text value type XML elements. + + Example: The following is an example of an unrecognized iCalendar + property (that uses a "DATE-TIME" value as its default) and the + equivalent xCal representation of that property. + + iCalendar: + + X-PROPERTY:20110512T120000Z + + xCal: + + + 20110512T120000Z + + + Example: The following is an example of an unrecognized iCalendar + property parameter (that uses a "DURATION" value as its default) + specified on a recognized iCalendar property, and the equivalent xCal + representation of that property and property parameter. + + iCalendar: + + DTSTART;X-PARAM=PT30M:20110512T130000Z + + xCal: + + + + PT30M + + 2011-05-12T13:00:00Z + + +6. Security Considerations + + For security considerations specific to calendar data, see Section 7 + of [RFC5545]. Since this specification is a mapping from iCalendar, + no new security concerns are introduced related to calendar data. + + + + + +Daboo, et al. Standards Track [Page 19] + +RFC 6321 xCal August 2011 + + + The use of XML as a format does have security risks. Section 7 of + [RFC3470] discusses these risks. See also the security discussion + for the application/xml type in [RFC3023]. + +7. IANA Considerations + + This document defines a new URN to identify a new XML namespace for + iCalendar data. The URN conforms to a registry mechanism described + in [RFC3688]. + + This document defines a new media type. The registration is in + Section 7.2. + + This document defines a new property for iCalendar. The registration + is in Section 7.3. + +7.1. Namespace Registration + + Registration request for the iCalendar namespace: + + URI: urn:ietf:params:xml:ns:icalendar-2.0 + + Registrant Contact: See the "Authors' Addresses" section of this + document. + + XML: None. Namespace URIs do not represent an XML specification. + +7.2. Media Type + + This section defines the MIME media type for use with iCalendar in + XML data. + + Type name: application + + Subtype name: calendar+xml + + Required parameters: None + + Optional parameters: method, component, and optinfo as defined for + the text/calendar media type in [RFC5545]; charset as defined for + application/xml in [RFC3023]; per [RFC3023], use of the charset + property parameter with the value "utf-8" is STRONGLY RECOMMENDED. + + Encoding considerations: Same as encoding considerations of + application/xml as specified in [RFC3023]. + + + + + + +Daboo, et al. Standards Track [Page 20] + +RFC 6321 xCal August 2011 + + + Security considerations: See Section 6. + + Interoperability considerations: This media type provides an + alternative format for iCalendar data based on XML. + + Published specification: This specification. + + Applications that use this media type: Applications that currently + make use of the text/calendar media type can use this as an + alternative. + + Additional information: + + Magic number(s): None + + File extension(s): xcs + + Macintosh file type code(s): None specified. + + Person & email address to contact for further information: + calsify@ietf.org + + Intended usage: COMMON + + Restrictions on usage: There are no restrictions on where this media + type can be used. + + Author: See the "Authors' Addresses" section of this document. + + Change controller: IETF + +7.3. iCalendar Property Registrations + + This document defines the following new iCalendar property to be + added to the registry defined in Section 8.2.3 of [RFC5545]: + + +----------+---------+-----------------------+ + | Property | Status | Reference | + +----------+---------+-----------------------+ + | XML | Current | RFC 6321, Section 4.2 | + +----------+---------+-----------------------+ + + + + + + + + + + +Daboo, et al. Standards Track [Page 21] + +RFC 6321 xCal August 2011 + + +8. Acknowledgments + + The authors would like to thank the following for their valuable + contributions: Toby Considine, Bernard Desruisseaux, Keith Moore, + Filip Navara, Simon Perreault, Arnaud Quillaud, Peter Saint-Andre, + and Dave Thewlis. This specification originated from the work of the + XML technical committee of the Calendaring and Scheduling Consortium. + +9. References + +9.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC3023] Murata, M., St. Laurent, S., and D. Kohn, "XML Media + Types", RFC 3023, January 2001. + + [RFC3470] Hollenbeck, S., Rose, M., and L. Masinter, "Guidelines for + the Use of Extensible Markup Language (XML) + within IETF Protocols", BCP 70, RFC 3470, January 2003. + + [RFC3688] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, + January 2004. + + [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data + Encodings", RFC 4648, October 2006. + + [RFC5545] Desruisseaux, B., "Internet Calendaring and Scheduling + Core Object Specification (iCalendar)", RFC 5545, + September 2009. + + [W3C.REC-xml-20081126] + Sperberg-McQueen, C., Yergeau, F., Bray, T., Paoli, J., + and E. Maler, "Extensible Markup Language (XML) 1.0 (Fifth + Edition)", World Wide Web Consortium Recommendation REC- + xml-20081126, November 2008, + . + +9.2. Informative References + + [W3C.REC-xmlschema-2-20041028] + Malhotra, A. and P. Biron, "XML Schema Part 2: Datatypes + Second Edition", World Wide Web Consortium + Recommendation REC-xmlschema-2-20041028, October 2004, + . + + + + + +Daboo, et al. Standards Track [Page 22] + +RFC 6321 xCal August 2011 + + +Appendix A. RELAX NG Schema + + Below is a RELAX NG schema for iCalendar in XML. The schema is non- + normative and given for reference only. + + This schema uses the compact notation of RELAX NG. The numeric + section numbers given in the comments refer to sections in [RFC5545]. + The ordering of elements follows the section ordering of [RFC5545]. + + The RELAX NG compact notation "?" operator is used to indicate an + unordered list of items. However, that operator, as defined, allows + "mixing" each element that it operates on at any depth within the + other elements, rather than just allowing "mixing" of siblings only. + As a result, the schema provided allows certain constructs that are + not allowed in iCalendar. Given that there is no sibling-only + unordered list operator in RELAX NG, this is the best representation + that can be given. + + Patterns for date/time, duration, and UTC offset values are given + because those differ from the values used in iCalendar. More + restrictive schema with patterns and numerical limits could be + derived from the example schema here if more comprehensive schema + validation is required. + + # RELAX NG Schema for iCalendar in XML + + default namespace = "urn:ietf:params:xml:ns:icalendar-2.0" + + # 3.2 Property Parameters + + # 3.2.1 Alternate Text Representation + + altrepparam = element altrep { + value-uri + } + + # 3.2.2 Common Name + + cnparam = element cn { + value-text + } + + + + + + + + + + +Daboo, et al. Standards Track [Page 23] + +RFC 6321 xCal August 2011 + + + # 3.2.3 Calendar User Type + + cutypeparam = element cutype { + element text { + "INDIVIDUAL" | + "GROUP" | + "RESOURCE" | + "ROOM" | + "UNKNOWN" + } + } + + # 3.2.4 Delegators + + delfromparam = element delegated-from { + value-cal-address+ + } + + # 3.2.5 Delegatees + + deltoparam = element delegated-to { + value-cal-address+ + } + + # 3.2.6 Directory Entry Reference + + dirparam = element dir { + value-uri + } + + # 3.2.7 Inline Encoding + + encodingparam = element encoding { + element text { + "8BIT" | + "BASE64" + } + } + + # 3.2.8 Format Type + + fmttypeparam = element fmttype { + value-text + } + + + + + + + +Daboo, et al. Standards Track [Page 24] + +RFC 6321 xCal August 2011 + + + # 3.2.9 Free/Busy Time Type + + fbtypeparam = element fbtype { + element text { + "FREE" | + "BUSY" | + "BUSY-UNAVAILABLE" | + "BUSY-TENTATIVE" + } + } + + # 3.2.10 Language + + languageparam = element language { + value-text + } + + # 3.2.11 Group or List Membership + + memberparam = element member { + value-cal-address+ + } + + # 3.2.12 Participation Status + + partstatparam = element partstat { + type-partstat-event | + type-partstat-todo | + type-partstat-jour + } + + type-partstat-event = ( + element text { + "NEEDS-ACTION" | + "ACCEPTED" | + "DECLINED" | + "TENTATIVE" | + "DELEGATED" + } + ) + + + + + + + + + + + +Daboo, et al. Standards Track [Page 25] + +RFC 6321 xCal August 2011 + + + type-partstat-todo = ( + element text { + "NEEDS-ACTION" | + "ACCEPTED" | + "DECLINED" | + "TENTATIVE" | + "DELEGATED" | + "COMPLETED" | + "IN-PROCESS" + } + ) + + type-partstat-jour = ( + element text { + "NEEDS-ACTION" | + "ACCEPTED" | + "DECLINED" + } + ) + + # 3.2.13 Recurrence Identifier Range + + rangeparam = element range { + element text { + "THISANDFUTURE" + } + } + + # 3.2.14 Alarm Trigger Relationship + + trigrelparam = element related { + element text { + "START" | + "END" + } + } + + # 3.2.15 Relationship Type + + reltypeparam = element reltype { + element text { + "PARENT" | + "CHILD" | + "SIBLING" + } + } + + + + + +Daboo, et al. Standards Track [Page 26] + +RFC 6321 xCal August 2011 + + + # 3.2.16 Participation Role + + roleparam = element role { + element text { + "CHAIR" | + "REQ-PARTICIPANT" | + "OPT-PARTICIPANT" | + "NON-PARTICIPANT" + } + } + + # 3.2.17 RSVP Expectation + + rsvpparam = element rsvp { + value-boolean + } + + # 3.2.18 Sent By + + sentbyparam = element sent-by { + value-cal-address + } + + # 3.2.19 Time Zone Identifier + + tzidparam = element tzid { + value-text + } + + # 3.3 Property Value Data Types + + # 3.3.1 BINARY + + value-binary = element binary { + xsd:string + } + + # 3.3.2 BOOLEAN + + value-boolean = element boolean { + xsd:boolean + } + + # 3.3.3 CAL-ADDRESS + + value-cal-address = element cal-address { + xsd:anyURI + } + + + +Daboo, et al. Standards Track [Page 27] + +RFC 6321 xCal August 2011 + + + # 3.3.4 DATE + + pattern-date = xsd:string { + pattern = "\d\d\d\d-\d\d-\d\d" + } + + value-date = element date { + pattern-date + } + + # 3.3.5 DATE-TIME + + pattern-date-time = xsd:string { + pattern = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ?" + } + + value-date-time = element date-time { + pattern-date-time + } + + # 3.3.6 DURATION + + pattern-duration = xsd:string { + pattern = "(+|-)?P(\d+W)|(\d+D)?" + ~ "(T(\d+H(\d+M)?(\d+S)?)|" + ~ "(\d+M(\d+S)?)|" + ~ "(\d+S))?" + } + + value-duration = element duration { + pattern-duration + } + + # 3.3.7 FLOAT + + value-float = element float { + xsd:float + } + + # 3.3.8 INTEGER + + value-integer = element integer { + xsd:integer + } + + + + + + + +Daboo, et al. Standards Track [Page 28] + +RFC 6321 xCal August 2011 + + + # 3.3.9 PERIOD + + value-period = element period { + element start { + pattern-date-time + }, + ( + element end { + pattern-date-time + } | + element duration { + pattern-duration + } + ) + } + + # 3.3.10 RECUR + + value-recur = element recur { + type-freq, + (type-until | type-count)?, + element interval { + xsd:positiveInteger + }?, + type-bysecond*, + type-byminute*, + type-byhour*, + type-byday*, + type-bymonthday*, + type-byyearday*, + type-byweekno*, + type-bymonth*, + type-bysetpos*, + element wkst { type-weekday }? + } + + type-freq = element freq { + "SECONDLY" | + "MINUTELY" | + "HOURLY" | + "DAILY" | + "WEEKLY" | + "MONTHLY" | + "YEARLY" + } + + + + + + +Daboo, et al. Standards Track [Page 29] + +RFC 6321 xCal August 2011 + + + type-until = element until { + type-date | + type-date-time + } + + type-count = element count { + xsd:positiveInteger + } + + type-bysecond = element bysecond { + xsd:positiveInteger + } + + type-byminute = element byminute { + xsd:positiveInteger + } + + type-byhour = element byhour { + xsd:positiveInteger + } + + type-weekday = ( + "SU" | + "MO" | + "TU" | + "WE" | + "TH" | + "FR" | + "SA" + ) + + type-byday = element byday { + xsd:integer?, + type-weekday + } + + type-bymonthday = element bymonthday { + xsd:integer + } + + type-byyearday = element byyearday { + xsd:integer + } + + type-byweekno = element byweekno { + xsd:integer + } + + + + +Daboo, et al. Standards Track [Page 30] + +RFC 6321 xCal August 2011 + + + type-bymonth = element bymonth { + xsd:positiveInteger + } + + type-bysetpos = element bysetpos { + xsd:integer + } + + # 3.3.11 TEXT + + value-text = element text { + xsd:string + } + + # 3.3.12 TIME + + pattern-time = xsd:string { + pattern = "\d\d:\d\d:\d\dZ?" + } + + value-time = element time { + pattern-time + } + + # 3.3.13 URI + + value-uri = element uri { + xsd:anyURI + } + + # 3.3.14 UTC-OFFSET + + value-utc-offset = element utc-offset { + xsd:string { pattern = "(+|-)\d\d:\d\d(:\d\d)?" } + } + + # UNKNOWN + + value-unknown = element unknown { + xsd:string + } + + # 3.4 iCalendar Stream + + start = element icalendar { + vcalendar+ + } + + + + +Daboo, et al. Standards Track [Page 31] + +RFC 6321 xCal August 2011 + + + # 3.6 Calendar Components + + vcalendar = element vcalendar { + type-calprops, + type-component + } + + type-calprops = element properties { + property-prodid & + property-version & + property-calscale? & + property-method? + } + + type-component = element components { + ( + component-vevent | + component-vtodo | + component-vjournal | + component-vfreebusy | + component-vtimezone + )* + } + + # 3.6.1 Event Component + + component-vevent = element vevent { + type-eventprop, + element components { + component-valarm+ + }? + } + + type-eventprop = element properties { + property-dtstamp & + property-dtstart & + property-uid & + + property-class? & + property-created? & + property-description? & + property-geo? & + property-last-mod? & + property-location? & + property-organizer? & + property-priority? & + property-seq? & + property-status-event? & + + + +Daboo, et al. Standards Track [Page 32] + +RFC 6321 xCal August 2011 + + + property-summary? & + property-transp? & + property-url? & + property-recurid? & + + property-rrule? & + + (property-dtend | property-duration)? & + + property-attach* & + property-attendee* & + property-categories* & + property-comment* & + property-contact* & + property-exdate* & + property-rstatus* & + property-related* & + property-resources* & + property-rdate* + } + + # 3.6.2 To-do Component + + component-vtodo = element vtodo { + type-todoprop, + element components { + component-valarm+ + }? + } + + type-todoprop = element properties { + property-dtstamp & + property-uid & + + property-class? & + property-completed? & + property-created? & + property-description? & + property-geo? & + property-last-mod? & + property-location? & + property-organizer? & + property-percent? & + property-priority? & + property-recurid? & + property-seq? & + property-status-todo? & + property-summary? & + + + +Daboo, et al. Standards Track [Page 33] + +RFC 6321 xCal August 2011 + + + property-url? & + + property-rrule? & + + ( + (property-dtstart?, property-dtend? ) | + (property-dtstart, property-duration)? + ) & + + property-attach* & + property-attendee* & + property-categories* & + property-comment* & + property-contact* & + property-exdate* & + property-rstatus* & + property-related* & + property-resources* & + property-rdate* + } + + # 3.6.3 Journal Component + + component-vjournal = element vjournal { + type-jourprop + } + + type-jourprop = element properties { + property-dtstamp & + property-uid & + + property-class? & + property-created? & + property-dtstart? & + property-last-mod? & + property-organizer? & + property-recurid? & + property-seq? & + property-status-jour? & + property-summary? & + property-url? & + + property-rrule? & + + property-attach* & + property-attendee* & + property-categories* & + property-comment* & + + + +Daboo, et al. Standards Track [Page 34] + +RFC 6321 xCal August 2011 + + + property-contact* & + property-description? & + property-exdate* & + property-related* & + property-rdate* & + property-rstatus* + } + + # 3.6.4 Free/Busy Component + + component-vfreebusy = element vfreebusy { + type-fbprop + } + + type-fbprop = element properties { + property-dtstamp & + property-uid & + + property-contact? & + property-dtstart? & + property-dtend? & + property-duration? & + property-organizer? & + property-url? & + + property-attendee* & + property-comment* & + property-freebusy* & + property-rstatus* + } + + # 3.6.5 Time Zone Component + + component-vtimezone = element vtimezone { + element properties { + property-tzid & + + property-last-mod? & + property-tzuurl? + }, + element components { + (component-standard | component-daylight) & + component-standard* & + component-daylight* + } + } + + + + + +Daboo, et al. Standards Track [Page 35] + +RFC 6321 xCal August 2011 + + + component-standard = element standard { + type-tzprop + } + + component-daylight = element daylight { + type-tzprop + } + + type-tzprop = element properties { + property-dtstart & + property-tzoffsetto & + property-tzoffsetfrom & + + property-rrule? & + + property-comment* & + property-rdate* & + property-tzname* + } + + # 3.6.6 Alarm Component + + component-valarm = element valarm { + audioprop | dispprop | emailprop + } + + type-audioprop = element properties { + property-action & + + property-trigger & + + (property-duration, property-repeat)? & + + property-attach? + } + + type-dispprop = element properties { + property-action & + property-description & + property-trigger & + property-summary & + + property-attendee+ & + + (property-duration, property-repeat)? & + + property-attach* + } + + + +Daboo, et al. Standards Track [Page 36] + +RFC 6321 xCal August 2011 + + + type-emailprop = element properties { + property-action & + property-description & + property-trigger & + + (property-duration, property-repeat)? + } + + # 3.7 Calendar Properties + + # 3.7.1 Calendar Scale + + property-calscale = element calscale { + + element parameters { empty }?, + + element text { "GREGORIAN" } + } + + # 3.7.2 Method + + property-method = element method { + + element parameters { empty }?, + + value-text + } + + # 3.7.3 Product Identifier + + property-prodid = element prodid { + + element parameters { empty }?, + + value-text + } + + # 3.7.4 Version + + property-version = element version { + + element parameters { empty }?, + + element text { "2.0" } + } + + + + + + +Daboo, et al. Standards Track [Page 37] + +RFC 6321 xCal August 2011 + + + # 3.8 Component Properties + + # 3.8.1 Descriptive Component Properties + + # 3.8.1.1 Attachment + + property-attach = element attach { + + element parameters { + fmttypeparam? & + encodingparam? + }?, + + value-uri | value-binary + } + + # 3.8.1.2 Categories + + property-categories = element categories { + + element parameters { + languageparam? & + }?, + + value-text+ + } + + # 3.8.1.3 Classification + + property-class = element class { + + element parameters { empty }?, + + element text { + "PUBLIC" | + "PRIVATE" | + "CONFIDENTIAL" + } + } + + # 3.8.1.4 Comment + + property-comment = element comment { + + element parameters { + altrepparam? & + languageparam? + }?, + + + +Daboo, et al. Standards Track [Page 38] + +RFC 6321 xCal August 2011 + + + value-text + } + + # 3.8.1.5 Description + + property-description = element description { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text + } + + # 3.8.1.6 Geographic Position + + property-geo = element geo { + + element parameters { empty }?, + + element latitude { xsd:float }, + element longitude { xsd:float } + } + + # 3.8.1.7 Location + + property-location = element location { + + element parameters { + + altrepparam? & + languageparam? + }?, + + value-text + } + + # 3.8.1.8 Percent Complete + + property-percent = element percent-complete { + + element parameters { empty }?, + + value-integer + } + + + + + +Daboo, et al. Standards Track [Page 39] + +RFC 6321 xCal August 2011 + + + # 3.8.1.9 Priority + + property-priority = element priority { + + element parameters { empty }?, + + value-integer + } + + # 3.8.1.10 Resources + + property-resources = element resources { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text+ + } + + # 3.8.1.11 Status + + property-status-event = element status { + + element parameters { empty }?, + + element text { + "TENTATIVE" | + "CONFIRMED" | + "CANCELLED" + } + } + + property-status-todo = element status { + + element parameters { empty }?, + + element text { + "NEEDS-ACTION" | + "COMPLETED" | + "IN-PROCESS" | + "CANCELLED" + } + } + + + + + + +Daboo, et al. Standards Track [Page 40] + +RFC 6321 xCal August 2011 + + + property-status-jour = element status { + + element parameters { empty }?, + + element text { + "DRAFT" | + "FINAL" | + "CANCELLED" + } + } + + # 3.8.1.12 Summary + + property-summary = element summary { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text + } + + # 3.8.2 Date and Time Component Properties + + # 3.8.2.1 Date/Time Completed + + property-completed = element completed { + + element parameters { empty }?, + + value-date-time + } + + # 3.8.2.2 Date/Time End + + property-dtend = element dtend { + + element parameters { + tzidparam? + }?, + + value-date-time | + value-date + } + + + + + + +Daboo, et al. Standards Track [Page 41] + +RFC 6321 xCal August 2011 + + + # 3.8.2.3 Date/Time Due + + property-due = element due { + + element parameters { + tzidparam? + }?, + + value-date-time | + value-date + } + + # 3.8.2.4 Date/Time Start + + property-dtstart = element dtstart { + + element parameters { + tzidparam? + }?, + + value-date-time | + value-date + } + + # 3.8.2.5 Duration + + property-duration = element duration { + + element parameters { empty }?, + + value-duration + } + + # 3.8.2.6 Free/Busy Time + + property-freebusy = element freebusy { + + element parameters { + fbtypeparam? + }?, + + + value-period+ + } + + # 3.8.2.7 Time Transparency + + property-transp = element transp { + + + +Daboo, et al. Standards Track [Page 42] + +RFC 6321 xCal August 2011 + + + element parameters { empty }?, + + element text { + "OPAQUE" | + "TRANSPARENT" + } + } + + # 3.8.3 Time Zone Component Properties + + # 3.8.3.1 Time Zone Identifier + + property-tzid = element tzid { + + element parameters { empty }?, + + value-text + } + + # 3.8.3.2 Time Zone Name + + property-tzname = element tzname { + + element parameters { + languageparam? + }?, + + value-text + } + + # 3.8.3.3 Time Zone Offset From + + property-tzoffsetfrom = element tzoffsetfrom { + + element parameters { empty }?, + + value-utc-offset + } + + # 3.8.3.4 Time Zone Offset To + + property-tzoffsetto = element tzoffsetto { + + element parameters { empty }?, + + value-utc-offset + } + + + + +Daboo, et al. Standards Track [Page 43] + +RFC 6321 xCal August 2011 + + + # 3.8.3.5 Time Zone URL + + property-tzurl = element tzurl { + + element parameters { empty }?, + + value-uri + } + + # 3.8.4 Relationship Component Properties + + # 3.8.4.1 Attendee + + property-attendee = element attendee { + + element parameters { + cutypeparam? & + memberparam? & + roleparam? & + partstatparam? & + rsvpparam? & + deltoparam? & + delfromparam? & + sentbyparam? & + cnparam? & + dirparam? & + languageparam? + }?, + + value-cal-address + } + + # 3.8.4.2 Contact + + property-contact = element contact { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text + } + + # 3.8.4.3 Organizer + + property-organizer = element organizer { + + + + +Daboo, et al. Standards Track [Page 44] + +RFC 6321 xCal August 2011 + + + element parameters { + cnparam? & + dirparam? & + sentbyparam? & + languageparam? + }?, + + value-cal-address + } + + # 3.8.4.4 Recurrence ID + + property-recurid = element recurrence-id { + + element parameters { + tzidparam? & + rangeparam? + }?, + + value-date-time | + value-date + } + + # 3.8.4.5 Related-To + + property-related = element related-to { + + element parameters { + reltypeparam? + }?, + + value-text + } + + # 3.8.4.6 Uniform Resource Locator + + property-url = element url { + + element parameters { empty }?, + + value-uri + } + + # 3.8.4.7 Unique Identifier + + property-uid = element uid { + + element parameters { empty }?, + + + +Daboo, et al. Standards Track [Page 45] + +RFC 6321 xCal August 2011 + + + value-text + } + + # 3.8.5 Recurrence Component Properties + + # 3.8.5.1 Exception Date/Times + + property-exdate = element exdate { + + element parameters { + tzidparam? + }?, + + value-date-time+ | + value-date+ + } + + # 3.8.5.2 Recurrence Date/Times + + property-rdate = element rdate { + + element parameters { + tzidparam? + }?, + + value-date-time+ | + value-date+ | + value-period+ + } + + # 3.8.5.3 Recurrence Rule + + property-rrule = element rrule { + + element parameters { empty }?, + + value-recur + } + + # 3.8.6 Alarm Component Properties + + # 3.8.6.1 Action + + property-action = element action { + + element parameters { empty }?, + + + + + +Daboo, et al. Standards Track [Page 46] + +RFC 6321 xCal August 2011 + + + element text { + "AUDIO" | + "DISPLAY" | + "EMAIL" + } + } + + # 3.8.6.2 Repeat Count + + property-repeat = element repeat { + + element parameters { empty }?, + + value-integer + } + + # 3.8.6.3 Trigger + + property-trigger = element trigger { + + ( + element parameters { + trigrelparam? + }?, + + value-duration + ) | + ( + element parameters { empty }?, + + value-date-time + ) + } + + # 3.8.7 Change Management Component Properties + + # 3.8.7.1 Date/Time Created + + property-created = element created { + + element parameters { empty }?, + + value-date-time + } + + # 3.8.7.2 Date/Time Stamp + + property-dtstamp = element dtstamp { + + + +Daboo, et al. Standards Track [Page 47] + +RFC 6321 xCal August 2011 + + + element parameters { empty }?, + + value-date-time + } + + # 3.8.7.3 Last Modified + + property-last-mod = element last-modified { + + element parameters { empty }?, + + value-date-time + } + + # 3.8.7.4 Sequence Number + + property-seq = element sequence { + + element parameters { empty }?, + + value-integer + } + + # 3.8.8 Miscellaneous Component Properties + + # 3.8.8.3 Request Status + + property-rstatus = element request-status { + + element parameters { + languageparam? + }?, + + element code { xsd:string }, + element description { xsd:string }, + element data { xsd:string }? + } + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 48] + +RFC 6321 xCal August 2011 + + +Appendix B. Examples + + This section contains two examples of iCalendar objects with their + xCal representation. + +B.1. Example 1 + +B.1.1. iCalendar Data + + BEGIN:VCALENDAR + CALSCALE:GREGORIAN + PRODID:-//Example Inc.//Example Calendar//EN + VERSION:2.0 + BEGIN:VEVENT + DTSTAMP:20080205T191224Z + DTSTART:20081006 + SUMMARY:Planning meeting + UID:4088E990AD89CB3DBB484909 + END:VEVENT + END:VCALENDAR + +B.1.2. XML Data + + + + + + + GREGORIAN + + + -//Example Inc.//Example Calendar//EN + + + 2.0 + + + + + + + 2008-02-05T19:12:24Z + + + 2008-10-06 + + + Planning meeting + + + +Daboo, et al. Standards Track [Page 49] + +RFC 6321 xCal August 2011 + + + + + 4088E990AD89CB3DBB484909 + + + + + + + +B.2. Example 2 + +B.2.1. iCalendar Data + + VERSION:2.0 + PRODID:-//Example Corp.//Example Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + RDATE;TZID=US/Eastern;VALUE=PERIOD:20060102T150000/PT2H + SUMMARY:Event #2 + DESCRIPTION:We are having a meeting all this week at 12 pm fo + r one hour\, with an additional meeting on the first day 2 h + ours long.\nPlease bring your own lunch for the 12 pm meetin + gs. + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + + + +Daboo, et al. Standards Track [Page 50] + +RFC 6321 xCal August 2011 + + + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + +B.2.2. XML Data + + + + + + + -//Example Inc.//Example Client//EN + + + 2.0 + + + + + + + 2004-01-10T03:28:45Z + + US/Eastern + + + + + + 2000-04-04T02:00:00 + + + + YEARLY + 1SU + 4 + + + + EDT + + + -05:00 + + + +Daboo, et al. Standards Track [Page 51] + +RFC 6321 xCal August 2011 + + + + + -04:00 + + + + + + + 2000-10-26T02:00:00 + + + + YEARLY + -1SU + 10 + + + + EST + + + -04:00 + + + -05:00 + + + + + + + + + 2006-02-06T00:11:21Z + + + + US/Eastern + + 2006-01-02T12:00:00 + + + PT1H + + + + DAILY + + + +Daboo, et al. Standards Track [Page 52] + +RFC 6321 xCal August 2011 + + + 5 + + + + + US/Eastern + + + 2006-01-02T15:00:00 + PT2H + + + + Event #2 + + + We are having a meeting all this week at 12 + pm for one hour, with an additional meeting on the first day + 2 hours long. Please bring your own lunch for the 12 pm + meetings. + + + 00959BC664CA650E933C892C@example.com + + + + + + + 2006-02-06T00:11:21Z + + + + US/Eastern + + 2006-01-04T14:00:00 + + + PT1H + + + + US/Eastern + + 2006-01-04T12:00:00 + + + Event #2 bis + + + +Daboo, et al. Standards Track [Page 53] + +RFC 6321 xCal August 2011 + + + + + 00959BC664CA650E933C892C@example.com + + + + + + + +Authors' Addresses + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + Mike Douglass + Rensselaer Polytechnic Institute + 110 8th Street + Troy, NY 12180 + USA + + EMail: douglm@rpi.edu + URI: http://www.rpi.edu/ + + + Steven Lees + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052 + USA + + EMail: steven.lees@microsoft.com + URI: http://www.microsoft.com/ + + + + + + + + + + + +Daboo, et al. Standards Track [Page 54] + diff --git a/dav/SabreDAV/docs/rfc6350.txt b/dav/SabreDAV/docs/rfc6350.txt new file mode 100644 index 000000000..d853cbc6c --- /dev/null +++ b/dav/SabreDAV/docs/rfc6350.txt @@ -0,0 +1,4147 @@ + + + + + + +Internet Engineering Task Force (IETF) S. Perreault +Request for Comments: 6350 Viagenie +Obsoletes: 2425, 2426, 4770 August 2011 +Updates: 2739 +Category: Standards Track +ISSN: 2070-1721 + + + vCard Format Specification + +Abstract + + This document defines the vCard data format for representing and + exchanging a variety of information about individuals and other + entities (e.g., formatted and structured name and delivery addresses, + email address, multiple telephone numbers, photograph, logo, audio + clips, etc.). This document obsoletes RFCs 2425, 2426, and 4770, and + updates RFC 2739. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6350. + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + +Perreault Standards Track [Page 1] + +RFC 6350 vCard August 2011 + + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 3. vCard Format Specification . . . . . . . . . . . . . . . . . . 5 + 3.1. Charset . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 3.2. Line Delimiting and Folding . . . . . . . . . . . . . . . 5 + 3.3. ABNF Format Definition . . . . . . . . . . . . . . . . . . 6 + 3.4. Property Value Escaping . . . . . . . . . . . . . . . . . 9 + 4. Property Value Data Types . . . . . . . . . . . . . . . . . . 9 + 4.1. TEXT . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 + 4.2. URI . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 4.3. DATE, TIME, DATE-TIME, DATE-AND-OR-TIME, and TIMESTAMP . . 12 + 4.3.1. DATE . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 4.3.2. TIME . . . . . . . . . . . . . . . . . . . . . . . . . 13 + 4.3.3. DATE-TIME . . . . . . . . . . . . . . . . . . . . . . 13 + 4.3.4. DATE-AND-OR-TIME . . . . . . . . . . . . . . . . . . . 14 + 4.3.5. TIMESTAMP . . . . . . . . . . . . . . . . . . . . . . 14 + 4.4. BOOLEAN . . . . . . . . . . . . . . . . . . . . . . . . . 14 + 4.5. INTEGER . . . . . . . . . . . . . . . . . . . . . . . . . 15 + 4.6. FLOAT . . . . . . . . . . . . . . . . . . . . . . . . . . 15 + 4.7. UTC-OFFSET . . . . . . . . . . . . . . . . . . . . . . . . 15 + 4.8. LANGUAGE-TAG . . . . . . . . . . . . . . . . . . . . . . . 16 + 5. Property Parameters . . . . . . . . . . . . . . . . . . . . . 16 + 5.1. LANGUAGE . . . . . . . . . . . . . . . . . . . . . . . . . 16 + 5.2. VALUE . . . . . . . . . . . . . . . . . . . . . . . . . . 16 + 5.3. PREF . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 + 5.4. ALTID . . . . . . . . . . . . . . . . . . . . . . . . . . 18 + 5.5. PID . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 + 5.6. TYPE . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 + 5.7. MEDIATYPE . . . . . . . . . . . . . . . . . . . . . . . . 20 + 5.8. CALSCALE . . . . . . . . . . . . . . . . . . . . . . . . . 20 + 5.9. SORT-AS . . . . . . . . . . . . . . . . . . . . . . . . . 21 + 5.10. GEO . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 + 5.11. TZ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 + + + + +Perreault Standards Track [Page 2] + +RFC 6350 vCard August 2011 + + + 6. vCard Properties . . . . . . . . . . . . . . . . . . . . . . . 23 + 6.1. General Properties . . . . . . . . . . . . . . . . . . . . 23 + 6.1.1. BEGIN . . . . . . . . . . . . . . . . . . . . . . . . 23 + 6.1.2. END . . . . . . . . . . . . . . . . . . . . . . . . . 23 + 6.1.3. SOURCE . . . . . . . . . . . . . . . . . . . . . . . . 24 + 6.1.4. KIND . . . . . . . . . . . . . . . . . . . . . . . . . 25 + 6.1.5. XML . . . . . . . . . . . . . . . . . . . . . . . . . 27 + 6.2. Identification Properties . . . . . . . . . . . . . . . . 28 + 6.2.1. FN . . . . . . . . . . . . . . . . . . . . . . . . . . 28 + 6.2.2. N . . . . . . . . . . . . . . . . . . . . . . . . . . 29 + 6.2.3. NICKNAME . . . . . . . . . . . . . . . . . . . . . . . 29 + 6.2.4. PHOTO . . . . . . . . . . . . . . . . . . . . . . . . 30 + 6.2.5. BDAY . . . . . . . . . . . . . . . . . . . . . . . . . 30 + 6.2.6. ANNIVERSARY . . . . . . . . . . . . . . . . . . . . . 31 + 6.2.7. GENDER . . . . . . . . . . . . . . . . . . . . . . . . 32 + 6.3. Delivery Addressing Properties . . . . . . . . . . . . . . 32 + 6.3.1. ADR . . . . . . . . . . . . . . . . . . . . . . . . . 32 + 6.4. Communications Properties . . . . . . . . . . . . . . . . 34 + 6.4.1. TEL . . . . . . . . . . . . . . . . . . . . . . . . . 34 + 6.4.2. EMAIL . . . . . . . . . . . . . . . . . . . . . . . . 36 + 6.4.3. IMPP . . . . . . . . . . . . . . . . . . . . . . . . . 36 + 6.4.4. LANG . . . . . . . . . . . . . . . . . . . . . . . . . 37 + 6.5. Geographical Properties . . . . . . . . . . . . . . . . . 37 + 6.5.1. TZ . . . . . . . . . . . . . . . . . . . . . . . . . . 37 + 6.5.2. GEO . . . . . . . . . . . . . . . . . . . . . . . . . 38 + 6.6. Organizational Properties . . . . . . . . . . . . . . . . 39 + 6.6.1. TITLE . . . . . . . . . . . . . . . . . . . . . . . . 39 + 6.6.2. ROLE . . . . . . . . . . . . . . . . . . . . . . . . . 39 + 6.6.3. LOGO . . . . . . . . . . . . . . . . . . . . . . . . . 40 + 6.6.4. ORG . . . . . . . . . . . . . . . . . . . . . . . . . 40 + 6.6.5. MEMBER . . . . . . . . . . . . . . . . . . . . . . . . 41 + 6.6.6. RELATED . . . . . . . . . . . . . . . . . . . . . . . 42 + 6.7. Explanatory Properties . . . . . . . . . . . . . . . . . . 43 + 6.7.1. CATEGORIES . . . . . . . . . . . . . . . . . . . . . . 43 + 6.7.2. NOTE . . . . . . . . . . . . . . . . . . . . . . . . . 44 + 6.7.3. PRODID . . . . . . . . . . . . . . . . . . . . . . . . 44 + 6.7.4. REV . . . . . . . . . . . . . . . . . . . . . . . . . 45 + 6.7.5. SOUND . . . . . . . . . . . . . . . . . . . . . . . . 45 + 6.7.6. UID . . . . . . . . . . . . . . . . . . . . . . . . . 46 + 6.7.7. CLIENTPIDMAP . . . . . . . . . . . . . . . . . . . . . 47 + 6.7.8. URL . . . . . . . . . . . . . . . . . . . . . . . . . 47 + 6.7.9. VERSION . . . . . . . . . . . . . . . . . . . . . . . 48 + 6.8. Security Properties . . . . . . . . . . . . . . . . . . . 48 + 6.8.1. KEY . . . . . . . . . . . . . . . . . . . . . . . . . 48 + 6.9. Calendar Properties . . . . . . . . . . . . . . . . . . . 49 + 6.9.1. FBURL . . . . . . . . . . . . . . . . . . . . . . . . 49 + 6.9.2. CALADRURI . . . . . . . . . . . . . . . . . . . . . . 50 + 6.9.3. CALURI . . . . . . . . . . . . . . . . . . . . . . . . 50 + + + +Perreault Standards Track [Page 3] + +RFC 6350 vCard August 2011 + + + 6.10. Extended Properties and Parameters . . . . . . . . . . . . 51 + 7. Synchronization . . . . . . . . . . . . . . . . . . . . . . . 51 + 7.1. Mechanisms . . . . . . . . . . . . . . . . . . . . . . . . 51 + 7.1.1. Matching vCard Instances . . . . . . . . . . . . . . . 51 + 7.1.2. Matching Property Instances . . . . . . . . . . . . . 52 + 7.1.3. PID Matching . . . . . . . . . . . . . . . . . . . . . 52 + 7.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 53 + 7.2.1. Creation . . . . . . . . . . . . . . . . . . . . . . . 53 + 7.2.2. Initial Sharing . . . . . . . . . . . . . . . . . . . 53 + 7.2.3. Adding and Sharing a Property . . . . . . . . . . . . 54 + 7.2.4. Simultaneous Editing . . . . . . . . . . . . . . . . . 54 + 7.2.5. Global Context Simplification . . . . . . . . . . . . 56 + 8. Example: Author's vCard . . . . . . . . . . . . . . . . . . . 56 + 9. Security Considerations . . . . . . . . . . . . . . . . . . . 57 + 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 58 + 10.1. Media Type Registration . . . . . . . . . . . . . . . . . 58 + 10.2. Registering New vCard Elements . . . . . . . . . . . . . . 59 + 10.2.1. Registration Procedure . . . . . . . . . . . . . . . . 59 + 10.2.2. Vendor Namespace . . . . . . . . . . . . . . . . . . . 60 + 10.2.3. Registration Template for Properties . . . . . . . . . 61 + 10.2.4. Registration Template for Parameters . . . . . . . . . 61 + 10.2.5. Registration Template for Value Data Types . . . . . . 62 + 10.2.6. Registration Template for Values . . . . . . . . . . . 62 + 10.3. Initial vCard Elements Registries . . . . . . . . . . . . 63 + 10.3.1. Properties Registry . . . . . . . . . . . . . . . . . 64 + 10.3.2. Parameters Registry . . . . . . . . . . . . . . . . . 65 + 10.3.3. Value Data Types Registry . . . . . . . . . . . . . . 65 + 10.3.4. Values Registries . . . . . . . . . . . . . . . . . . 66 + 11. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 69 + 12. References . . . . . . . . . . . . . . . . . . . . . . . . . . 69 + 12.1. Normative References . . . . . . . . . . . . . . . . . . . 69 + 12.2. Informative References . . . . . . . . . . . . . . . . . . 71 + Appendix A. Differences from RFCs 2425 and 2426 . . . . . . . . . 73 + A.1. New Structure . . . . . . . . . . . . . . . . . . . . . . 73 + A.2. Removed Features . . . . . . . . . . . . . . . . . . . . . 73 + A.3. New Properties and Parameters . . . . . . . . . . . . . . 73 + + + + + + + + + + + + + + + +Perreault Standards Track [Page 4] + +RFC 6350 vCard August 2011 + + +1. Introduction + + Electronic address books have become ubiquitous. Their increased + presence on portable, connected devices as well as the diversity of + platforms that exchange contact data call for a standard. This memo + defines the vCard format, which allows the capture and exchange of + information normally stored within an address book or directory + application. + + A high-level overview of the differences from RFCs 2425 and 2426 can + be found in Appendix A. + +2. Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and + "OPTIONAL" in this document are to be interpreted as described in + [RFC2119]. + +3. vCard Format Specification + + The text/vcard MIME content type (hereafter known as "vCard"; see + Section 10.1) contains contact information, typically pertaining to a + single contact or group of contacts. The content consists of one or + more lines in the format given below. + +3.1. Charset + + The charset (see [RFC3536] for internationalization terminology) for + vCard is UTF-8 as defined in [RFC3629]. There is no way to override + this. It is invalid to specify a value other than "UTF-8" in the + "charset" MIME parameter (see Section 10.1). + +3.2. Line Delimiting and Folding + + Individual lines within vCard are delimited by the [RFC5322] line + break, which is a CRLF sequence (U+000D followed by U+000A). Long + logical lines of text can be split into a multiple-physical-line + representation using the following folding technique. Content lines + SHOULD be folded to a maximum width of 75 octets, excluding the line + break. Multi-octet characters MUST remain contiguous. The rationale + for this folding process can be found in [RFC5322], Section 2.1.1. + + A logical line MAY be continued on the next physical line anywhere + between two characters by inserting a CRLF immediately followed by a + single white space character (space (U+0020) or horizontal tab + (U+0009)). The folded line MUST contain at least one character. Any + sequence of CRLF followed immediately by a single white space + + + +Perreault Standards Track [Page 5] + +RFC 6350 vCard August 2011 + + + character is ignored (removed) when processing the content type. For + example, the line: + + NOTE:This is a long description that exists on a long line. + + can be represented as: + + NOTE:This is a long description + that exists on a long line. + + It could also be represented as: + + NOTE:This is a long descrip + tion that exists o + n a long line. + + The process of moving from this folded multiple-line representation + of a property definition to its single-line representation is called + unfolding. Unfolding is accomplished by regarding CRLF immediately + followed by a white space character (namely, HTAB (U+0009) or SPACE + (U+0020)) as equivalent to no characters at all (i.e., the CRLF and + single white space character are removed). + + Note: It is possible for very simple implementations to generate + improperly folded lines in the middle of a UTF-8 multi-octet + sequence. For this reason, implementations SHOULD unfold lines in + such a way as to properly restore the original sequence. + + Note: Unfolding is done differently than in [RFC5322]. Unfolding + in [RFC5322] only removes the CRLF, not the space following it. + + Folding is done after any content encoding of a type value. + Unfolding is done before any decoding of a type value in a content + line. + +3.3. ABNF Format Definition + + The following ABNF uses the notation of [RFC5234], which also defines + CRLF, WSP, DQUOTE, VCHAR, ALPHA, and DIGIT. + + vcard-entity = 1*vcard + + vcard = "BEGIN:VCARD" CRLF + "VERSION:4.0" CRLF + 1*contentline + "END:VCARD" CRLF + ; A vCard object MUST include the VERSION and FN properties. + ; VERSION MUST come immediately after BEGIN:VCARD. + + + +Perreault Standards Track [Page 6] + +RFC 6350 vCard August 2011 + + + contentline = [group "."] name *(";" param) ":" value CRLF + ; When parsing a content line, folded lines must first + ; be unfolded according to the unfolding procedure + ; described in Section 3.2. + ; When generating a content line, lines longer than 75 + ; characters SHOULD be folded according to the folding + ; procedure described in Section 3.2. + + group = 1*(ALPHA / DIGIT / "-") + name = "SOURCE" / "KIND" / "FN" / "N" / "NICKNAME" + / "PHOTO" / "BDAY" / "ANNIVERSARY" / "GENDER" / "ADR" / "TEL" + / "EMAIL" / "IMPP" / "LANG" / "TZ" / "GEO" / "TITLE" / "ROLE" + / "LOGO" / "ORG" / "MEMBER" / "RELATED" / "CATEGORIES" + / "NOTE" / "PRODID" / "REV" / "SOUND" / "UID" / "CLIENTPIDMAP" + / "URL" / "KEY" / "FBURL" / "CALADRURI" / "CALURI" / "XML" + / iana-token / x-name + ; Parsing of the param and value is based on the "name" as + ; defined in ABNF sections below. + ; Group and name are case-insensitive. + + iana-token = 1*(ALPHA / DIGIT / "-") + ; identifier registered with IANA + + x-name = "x-" 1*(ALPHA / DIGIT / "-") + ; Names that begin with "x-" or "X-" are + ; reserved for experimental use, not intended for released + ; products, or for use in bilateral agreements. + + param = language-param / value-param / pref-param / pid-param + / type-param / geo-parameter / tz-parameter / sort-as-param + / calscale-param / any-param + ; Allowed parameters depend on property name. + + param-value = *SAFE-CHAR / DQUOTE *QSAFE-CHAR DQUOTE + + any-param = (iana-token / x-name) "=" param-value *("," param-value) + + NON-ASCII = UTF8-2 / UTF8-3 / UTF8-4 + ; UTF8-{2,3,4} are defined in [RFC3629] + + QSAFE-CHAR = WSP / "!" / %x23-7E / NON-ASCII + ; Any character except CTLs, DQUOTE + + SAFE-CHAR = WSP / "!" / %x23-39 / %x3C-7E / NON-ASCII + ; Any character except CTLs, DQUOTE, ";", ":" + + VALUE-CHAR = WSP / VCHAR / NON-ASCII + ; Any textual character + + + +Perreault Standards Track [Page 7] + +RFC 6350 vCard August 2011 + + + A line that begins with a white space character is a continuation of + the previous line, as described in Section 3.2. The white space + character and immediately preceeding CRLF should be discarded when + reconstructing the original line. Note that this line-folding + convention differs from that found in [RFC5322], in that the sequence + found anywhere in the content indicates a continued line + and should be removed. + + Property names and parameter names are case-insensitive (e.g., the + property name "fn" is the same as "FN" and "Fn"). Parameter values + MAY be case-sensitive or case-insensitive, depending on their + definition. Parameter values that are not explicitly defined as + being case-sensitive are case-insensitive. Based on experience with + vCard 3 interoperability, it is RECOMMENDED that property and + parameter names be upper-case on output. + + The group construct is used to group related properties together. + The group name is a syntactic convention used to indicate that all + property names prefaced with the same group name SHOULD be grouped + together when displayed by an application. It has no other + significance. Implementations that do not understand or support + grouping MAY simply strip off any text before a "." to the left of + the type name and present the types and values as normal. + + Property cardinalities are indicated using the following notation, + which is based on ABNF (see [RFC5234], Section 3.6): + + +-------------+--------------------------------------------------+ + | Cardinality | Meaning | + +-------------+--------------------------------------------------+ + | 1 | Exactly one instance per vCard MUST be present. | + | *1 | Exactly one instance per vCard MAY be present. | + | 1* | One or more instances per vCard MUST be present. | + | * | One or more instances per vCard MAY be present. | + +-------------+--------------------------------------------------+ + + Properties defined in a vCard instance may have multiple values + depending on the property cardinality. The general rule for encoding + multi-valued properties is to simply create a new content line for + each value (including the property name). However, it should be + noted that some value types support encoding multiple values in a + single content line by separating the values with a comma ",". This + approach has been taken for several of the content types defined + below (date, time, integer, float). + + + + + + + +Perreault Standards Track [Page 8] + +RFC 6350 vCard August 2011 + + +3.4. Property Value Escaping + + Some properties may contain one or more values delimited by a COMMA + character (U+002C). Therefore, a COMMA character in a value MUST be + escaped with a BACKSLASH character (U+005C), even for properties that + don't allow multiple instances (for consistency). + + Some properties (e.g., N and ADR) comprise multiple fields delimited + by a SEMICOLON character (U+003B). Therefore, a SEMICOLON in a field + of such a "compound" property MUST be escaped with a BACKSLASH + character. SEMICOLON characters in non-compound properties MAY be + escaped. On input, an escaped SEMICOLON character is never a field + separator. An unescaped SEMICOLON character may be a field + separator, depending on the property in which it appears. + + Furthermore, some fields of compound properties may contain a list of + values delimited by a COMMA character. Therefore, a COMMA character + in one of a field's values MUST be escaped with a BACKSLASH + character, even for fields that don't allow multiple values (for + consistency). Compound properties allowing multiple instances MUST + NOT be encoded in a single content line. + + Finally, BACKSLASH characters in values MUST be escaped with a + BACKSLASH character. NEWLINE (U+000A) characters in values MUST be + encoded by two characters: a BACKSLASH followed by either an 'n' + (U+006E) or an 'N' (U+004E). + + In all other cases, escaping MUST NOT be used. + +4. Property Value Data Types + + Standard value types are defined below. + + value = text + / text-list + / date-list + / time-list + / date-time-list + / date-and-or-time-list + / timestamp-list + / boolean + / integer-list + / float-list + / URI ; from Section 3 of [RFC3986] + / utc-offset + / Language-Tag + / iana-valuespec + ; Actual value type depends on property name and VALUE parameter. + + + +Perreault Standards Track [Page 9] + +RFC 6350 vCard August 2011 + + + text = *TEXT-CHAR + + TEXT-CHAR = "\\" / "\," / "\n" / WSP / NON-ASCII + / %x21-2B / %x2D-5B / %x5D-7E + ; Backslashes, commas, and newlines must be encoded. + + component = "\\" / "\," / "\;" / "\n" / WSP / NON-ASCII + / %x21-2B / %x2D-3A / %x3C-5B / %x5D-7E + list-component = component *("," component) + + text-list = text *("," text) + date-list = date *("," date) + time-list = time *("," time) + date-time-list = date-time *("," date-time) + date-and-or-time-list = date-and-or-time *("," date-and-or-time) + timestamp-list = timestamp *("," timestamp) + integer-list = integer *("," integer) + float-list = float *("," float) + + boolean = "TRUE" / "FALSE" + integer = [sign] 1*DIGIT + float = [sign] 1*DIGIT ["." 1*DIGIT] + + sign = "+" / "-" + + year = 4DIGIT ; 0000-9999 + month = 2DIGIT ; 01-12 + day = 2DIGIT ; 01-28/29/30/31 depending on month and leap year + hour = 2DIGIT ; 00-23 + minute = 2DIGIT ; 00-59 + second = 2DIGIT ; 00-58/59/60 depending on leap second + zone = utc-designator / utc-offset + utc-designator = %x5A ; uppercase "Z" + + date = year [month day] + / year "-" month + / "--" month [day] + / "--" "-" day + date-noreduc = year month day + / "--" month day + / "--" "-" day + date-complete = year month day + + time = hour [minute [second]] [zone] + / "-" minute [second] [zone] + / "-" "-" second [zone] + time-notrunc = hour [minute [second]] [zone] + time-complete = hour minute second [zone] + + + +Perreault Standards Track [Page 10] + +RFC 6350 vCard August 2011 + + + time-designator = %x54 ; uppercase "T" + date-time = date-noreduc time-designator time-notrunc + timestamp = date-complete time-designator time-complete + + date-and-or-time = date-time / date / time-designator time + + utc-offset = sign hour [minute] + + Language-Tag = + + iana-valuespec = + ; a publicly defined valuetype format, registered + ; with IANA, as defined in Section 12 of this + ; document. + +4.1. TEXT + + "text": The "text" value type should be used to identify values that + contain human-readable text. As for the language, it is controlled + by the LANGUAGE property parameter defined in Section 5.1. + + Examples for "text": + + this is a text value + this is one value,this is another + this is a single value\, with a comma encoded + + A formatted text line break in a text value type MUST be represented + as the character sequence backslash (U+005C) followed by a Latin + small letter n (U+006E) or a Latin capital letter N (U+004E), that + is, "\n" or "\N". + + For example, a multiple line NOTE value of: + + Mythical Manager + Hyjinx Software Division + BabsCo, Inc. + + could be represented as: + + NOTE:Mythical Manager\nHyjinx Software Division\n + BabsCo\, Inc.\n + + demonstrating the \n literal formatted line break technique, the + CRLF-followed-by-space line folding technique, and the backslash + escape technique. + + + + + +Perreault Standards Track [Page 11] + +RFC 6350 vCard August 2011 + + +4.2. URI + + "uri": The "uri" value type should be used to identify values that + are referenced by a Uniform Resource Identifier (URI) instead of + encoded in-line. These value references might be used if the value + is too large, or otherwise undesirable to include directly. The + format for the URI is as defined in Section 3 of [RFC3986]. Note + that the value of a property of type "uri" is what the URI points to, + not the URI itself. + + Examples for "uri": + + http://www.example.com/my/picture.jpg + ldap://ldap.example.com/cn=babs%20jensen + +4.3. DATE, TIME, DATE-TIME, DATE-AND-OR-TIME, and TIMESTAMP + + "date", "time", "date-time", "date-and-or-time", and "timestamp": + Each of these value types is based on the definitions in + [ISO.8601.2004]. Multiple such values can be specified using the + comma-separated notation. + + Only the basic format is supported. + +4.3.1. DATE + + A calendar date as specified in [ISO.8601.2004], Section 4.1.2. + + Reduced accuracy, as specified in [ISO.8601.2004], Sections 4.1.2.3 + a) and b), but not c), is permitted. + + Expanded representation, as specified in [ISO.8601.2004], Section + 4.1.4, is forbidden. + + Truncated representation, as specified in [ISO.8601.2000], Sections + 5.2.1.3 d), e), and f), is permitted. + + Examples for "date": + + 19850412 + 1985-04 + 1985 + --0412 + ---12 + + + + + + + +Perreault Standards Track [Page 12] + +RFC 6350 vCard August 2011 + + + Note the use of YYYY-MM in the second example above. YYYYMM is + disallowed to prevent confusion with YYMMDD. Note also that + YYYY-MM-DD is disallowed since we are using the basic format instead + of the extended format. + +4.3.2. TIME + + A time of day as specified in [ISO.8601.2004], Section 4.2. + + Reduced accuracy, as specified in [ISO.8601.2004], Section 4.2.2.3, + is permitted. + + Representation with decimal fraction, as specified in + [ISO.8601.2004], Section 4.2.2.4, is forbidden. + + The midnight hour is always represented by 00, never 24 (see + [ISO.8601.2004], Section 4.2.3). + + Truncated representation, as specified in [ISO.8601.2000], Sections + 5.3.1.4 a), b), and c), is permitted. + + Examples for "time": + + 102200 + 1022 + 10 + -2200 + --00 + 102200Z + 102200-0800 + +4.3.3. DATE-TIME + + A date and time of day combination as specified in [ISO.8601.2004], + Section 4.3. + + Truncation of the date part, as specified in [ISO.8601.2000], Section + 5.4.2 c), is permitted. + + Examples for "date-time": + + 19961022T140000 + --1022T1400 + ---22T14 + + + + + + + +Perreault Standards Track [Page 13] + +RFC 6350 vCard August 2011 + + +4.3.4. DATE-AND-OR-TIME + + Either a DATE-TIME, a DATE, or a TIME value. To allow unambiguous + interpretation, a stand-alone TIME value is always preceded by a "T". + + Examples for "date-and-or-time": + + 19961022T140000 + --1022T1400 + ---22T14 + 19850412 + 1985-04 + 1985 + --0412 + ---12 + T102200 + T1022 + T10 + T-2200 + T--00 + T102200Z + T102200-0800 + +4.3.5. TIMESTAMP + + A complete date and time of day combination as specified in + [ISO.8601.2004], Section 4.3.2. + + Examples for "timestamp": + + 19961022T140000 + 19961022T140000Z + 19961022T140000-05 + 19961022T140000-0500 + +4.4. BOOLEAN + + "boolean": The "boolean" value type is used to express boolean + values. These values are case-insensitive. + + Examples: + + TRUE + false + True + + + + + + +Perreault Standards Track [Page 14] + +RFC 6350 vCard August 2011 + + +4.5. INTEGER + + "integer": The "integer" value type is used to express signed + integers in decimal format. If sign is not specified, the value is + assumed positive "+". Multiple "integer" values can be specified + using the comma-separated notation. The maximum value is + 9223372036854775807, and the minimum value is -9223372036854775808. + These limits correspond to a signed 64-bit integer using two's- + complement arithmetic. + + Examples: + + 1234567890 + -1234556790 + +1234556790,432109876 + +4.6. FLOAT + + "float": The "float" value type is used to express real numbers. If + sign is not specified, the value is assumed positive "+". Multiple + "float" values can be specified using the comma-separated notation. + Implementations MUST support a precision equal or better than that of + the IEEE "binary64" format [IEEE.754.2008]. + + Note: Scientific notation is disallowed. Implementers wishing to + use their favorite language's %f formatting should be careful. + + Examples: + + 20.30 + 1000000.0000001 + 1.333,3.14 + +4.7. UTC-OFFSET + + "utc-offset": The "utc-offset" value type specifies that the property + value is a signed offset from UTC. This value type can be specified + in the TZ property. + + The value type is an offset from Coordinated Universal Time (UTC). + It is specified as a positive or negative difference in units of + hours and minutes (e.g., +hhmm). The time is specified as a 24-hour + clock. Hour values are from 00 to 23, and minute values are from 00 + to 59. Hour and minutes are 2 digits with high-order zeroes required + to maintain digit count. The basic format for ISO 8601 UTC offsets + MUST be used. + + + + + +Perreault Standards Track [Page 15] + +RFC 6350 vCard August 2011 + + +4.8. LANGUAGE-TAG + + "language-tag": A single language tag, as defined in [RFC5646]. + +5. Property Parameters + + A property can have attributes associated with it. These "property + parameters" contain meta-information about the property or the + property value. In some cases, the property parameter can be multi- + valued in which case the property parameter value elements are + separated by a COMMA (U+002C). + + Property parameter value elements that contain the COLON (U+003A), + SEMICOLON (U+003B), or COMMA (U+002C) character separators MUST be + specified as quoted-string text values. Property parameter values + MUST NOT contain the DQUOTE (U+0022) character. The DQUOTE character + is used as a delimiter for parameter values that contain restricted + characters or URI text. + + Applications MUST ignore x-param and iana-param values they don't + recognize. + +5.1. LANGUAGE + + The LANGUAGE property parameter is used to identify data in multiple + languages. There is no concept of "default" language, except as + specified by any "Content-Language" MIME header parameter that is + present [RFC3282]. The value of the LANGUAGE property parameter is a + language tag as defined in Section 2 of [RFC5646]. + + Examples: + + ROLE;LANGUAGE=tr:hoca + + ABNF: + + language-param = "LANGUAGE=" Language-Tag + ; Language-Tag is defined in section 2.1 of RFC 5646 + +5.2. VALUE + + The VALUE parameter is OPTIONAL, used to identify the value type + (data type) and format of the value. The use of these predefined + formats is encouraged even if the value parameter is not explicitly + used. By defining a standard set of value types and their formats, + existing parsing and processing code can be leveraged. The + + + + + +Perreault Standards Track [Page 16] + +RFC 6350 vCard August 2011 + + + predefined data type values MUST NOT be repeated in COMMA-separated + value lists except within the N, NICKNAME, ADR, and CATEGORIES + properties. + + ABNF: + + value-param = "VALUE=" value-type + + value-type = "text" + / "uri" + / "date" + / "time" + / "date-time" + / "date-and-or-time" + / "timestamp" + / "boolean" + / "integer" + / "float" + / "utc-offset" + / "language-tag" + / iana-token ; registered as described in section 12 + / x-name + +5.3. PREF + + The PREF parameter is OPTIONAL and is used to indicate that the + corresponding instance of a property is preferred by the vCard + author. Its value MUST be an integer between 1 and 100 that + quantifies the level of preference. Lower values correspond to a + higher level of preference, with 1 being most preferred. + + When the parameter is absent, the default MUST be to interpret the + property instance as being least preferred. + + Note that the value of this parameter is to be interpreted only in + relation to values assigned to other instances of the same property + in the same vCard. A given value, or the absence of a value, MUST + NOT be interpreted on its own. + + This parameter MAY be applied to any property that allows multiple + instances. + + ABNF: + + pref-param = "PREF=" (1*2DIGIT / "100") + ; An integer between 1 and 100. + + + + + +Perreault Standards Track [Page 17] + +RFC 6350 vCard August 2011 + + +5.4. ALTID + + The ALTID parameter is used to "tag" property instances as being + alternative representations of the same logical property. For + example, translations of a property in multiple languages generates + multiple property instances having different LANGUAGE (Section 5.1) + parameter that are tagged with the same ALTID value. + + This parameter's value is treated as an opaque string. Its sole + purpose is to be compared for equality against other ALTID parameter + values. + + Two property instances are considered alternative representations of + the same logical property if and only if their names as well as the + value of their ALTID parameters are identical. Property instances + without the ALTID parameter MUST NOT be considered an alternative + representation of any other property instance. Values for the ALTID + parameter are not globally unique: they MAY be reused for different + property names. + + Property instances having the same ALTID parameter value count as 1 + toward cardinality. Therefore, since N (Section 6.2.2) has + cardinality *1 and TITLE (Section 6.6.1) has cardinality *, these + three examples would be legal: + + N;ALTID=1;LANGUAGE=jp:;;;; + N;ALTID=1;LANGUAGE=en:Yamada;Taro;;; + ( denotes a UTF8-encoded Unicode character.) + + TITLE;ALTID=1;LANGUAGE=fr:Patron + TITLE;ALTID=1;LANGUAGE=en:Boss + + TITLE;ALTID=1;LANGUAGE=fr:Patron + TITLE;ALTID=1;LANGUAGE=en:Boss + TITLE;ALTID=2;LANGUAGE=en:Chief vCard Evangelist + + while this one would not: + + N;ALTID=1;LANGUAGE=jp:;;;; + N:Yamada;Taro;;; + (Two instances of the N property.) + + and these three would be legal but questionable: + + TITLE;ALTID=1;LANGUAGE=fr:Patron + TITLE;ALTID=2;LANGUAGE=en:Boss + (Should probably have the same ALTID value.) + + + + +Perreault Standards Track [Page 18] + +RFC 6350 vCard August 2011 + + + TITLE;ALTID=1;LANGUAGE=fr:Patron + TITLE:LANGUAGE=en:Boss + (Second line should probably have ALTID=1.) + + N;ALTID=1;LANGUAGE=jp:;;;; + N;ALTID=1;LANGUAGE=en:Yamada;Taro;;; + N;ALTID=1;LANGUAGE=en:Smith;John;;; + (The last line should probably have ALTID=2. But that would be + illegal because N has cardinality *1.) + + The ALTID property MAY also be used in may contexts other than with + the LANGUAGE parameter. Here's an example with two representations + of the same photo in different file formats: + + PHOTO;ALTID=1:data:image/jpeg;base64,... + PHOTO;ALTID=1;data:image/jp2;base64,... + + ABNF: + + altid-param = "ALTID=" param-value + +5.5. PID + + The PID parameter is used to identify a specific property among + multiple instances. It plays a role analogous to the UID property + (Section 6.7.6) on a per-property instead of per-vCard basis. It MAY + appear more than once in a given property. It MUST NOT appear on + properties that may have only one instance per vCard. Its value is + either a single small positive integer or a pair of small positive + integers separated by a dot. Multiple values may be encoded in a + single PID parameter by separating the values with a comma ",". See + Section 7 for more details on its usage. + + ABNF: + + pid-param = "PID=" pid-value *("," pid-value) + pid-value = 1*DIGIT ["." 1*DIGIT] + +5.6. TYPE + + The TYPE parameter has multiple, different uses. In general, it is a + way of specifying class characteristics of the associated property. + Most of the time, its value is a comma-separated subset of a + predefined enumeration. In this document, the following properties + make use of this parameter: FN, NICKNAME, PHOTO, ADR, TEL, EMAIL, + IMPP, LANG, TZ, GEO, TITLE, ROLE, LOGO, ORG, RELATED, CATEGORIES, + + + + + +Perreault Standards Track [Page 19] + +RFC 6350 vCard August 2011 + + + NOTE, SOUND, URL, KEY, FBURL, CALADRURI, and CALURI. The TYPE + parameter MUST NOT be applied on other properties defined in this + document. + + The "work" and "home" values act like tags. The "work" value implies + that the property is related to an individual's work place, while the + "home" value implies that the property is related to an individual's + personal life. When neither "work" nor "home" is present, it is + implied that the property is related to both an individual's work + place and personal life in the case that the KIND property's value is + "individual", or to none in other cases. + + ABNF: + + type-param = "TYPE=" type-value *("," type-value) + + type-value = "work" / "home" / type-param-tel + / type-param-related / iana-token / x-name + ; This is further defined in individual property sections. + +5.7. MEDIATYPE + + The MEDIATYPE parameter is used with properties whose value is a URI. + Its use is OPTIONAL. It provides a hint to the vCard consumer + application about the media type [RFC2046] of the resource identified + by the URI. Some URI schemes do not need this parameter. For + example, the "data" scheme allows the media type to be explicitly + indicated as part of the URI [RFC2397]. Another scheme, "http", + provides the media type as part of the URI resolution process, with + the Content-Type HTTP header [RFC2616]. The MEDIATYPE parameter is + intended to be used with URI schemes that do not provide such + functionality (e.g., "ftp" [RFC1738]). + + ABNF: + + mediatype-param = "MEDIATYPE=" mediatype + mediatype = type-name "/" subtype-name *( ";" attribute "=" value ) + ; "attribute" and "value" are from [RFC2045] + ; "type-name" and "subtype-name" are from [RFC4288] + +5.8. CALSCALE + + The CALSCALE parameter is identical to the CALSCALE property in + iCalendar (see [RFC5545], Section 3.7.1). It is used to define the + calendar system in which a date or date-time value is expressed. The + only value specified by iCalendar is "gregorian", which stands for + the Gregorian system. It is the default when the parameter is + absent. Additional values may be defined in extension documents and + + + +Perreault Standards Track [Page 20] + +RFC 6350 vCard August 2011 + + + registered with IANA (see Section 10.3.4). A vCard implementation + MUST ignore properties with a CALSCALE parameter value that it does + not understand. + + ABNF: + + calscale-param = "CALSCALE=" calscale-value + + calscale-value = "gregorian" / iana-token / x-name + +5.9. SORT-AS + + The "sort-as" parameter is used to specify the string to be used for + national-language-specific sorting. Without this information, + sorting algorithms could incorrectly sort this vCard within a + sequence of sorted vCards. When this property is present in a vCard, + then the given strings are used for sorting the vCard. + + This parameter's value is a comma-separated list that MUST have as + many or fewer elements as the corresponding property value has + components. This parameter's value is case-sensitive. + + ABNF: + + sort-as-param = "SORT-AS=" sort-as-value + + sort-as-value = param-value *("," param-value) + + Examples: For the case of surname and given name sorting, the + following examples define common sort string usage with the N + property. + + FN:Rene van der Harten + N;SORT-AS="Harten,Rene":van der Harten;Rene,J.;Sir;R.D.O.N. + + FN:Robert Pau Shou Chang + N;SORT-AS="Pau Shou Chang,Robert":Shou Chang;Robert,Pau;; + + FN:Osamu Koura + N;SORT-AS="Koura,Osamu":Koura;Osamu;; + + FN:Oscar del Pozo + N;SORT-AS="Pozo,Oscar":del Pozo Triscon;Oscar;; + + FN:Chistine d'Aboville + N;SORT-AS="Aboville,Christine":d'Aboville;Christine;; + + + + + +Perreault Standards Track [Page 21] + +RFC 6350 vCard August 2011 + + + FN:H. James de Mann + N;SORT-AS="Mann,James":de Mann;Henry,James;; + + If sorted by surname, the results would be: + + Christine d'Aboville + Rene van der Harten + Osamu Koura + H. James de Mann + Robert Pau Shou Chang + Oscar del Pozo + + If sorted by given name, the results would be: + + Christine d'Aboville + H. James de Mann + Osamu Koura + Oscar del Pozo + Rene van der Harten + Robert Pau Shou Chang + +5.10. GEO + + The GEO parameter can be used to indicate global positioning + information that is specific to an address. Its value is the same as + that of the GEO property (see Section 6.5.2). + + ABNF: + + geo-parameter = "GEO=" DQUOTE URI DQUOTE + +5.11. TZ + + The TZ parameter can be used to indicate time zone information that + is specific to an address. Its value is the same as that of the TZ + property. + + ABNF: + + tz-parameter = "TZ=" (param-value / DQUOTE URI DQUOTE) + + + + + + + + + + + +Perreault Standards Track [Page 22] + +RFC 6350 vCard August 2011 + + +6. vCard Properties + + What follows is an enumeration of the standard vCard properties. + +6.1. General Properties + +6.1.1. BEGIN + + Purpose: To denote the beginning of a syntactic entity within a + text/vcard content-type. + + Value type: text + + Cardinality: 1 + + Special notes: The content entity MUST begin with the BEGIN property + with a value of "VCARD". The value is case-insensitive. + + The BEGIN property is used in conjunction with the END property to + delimit an entity containing a related set of properties within a + text/vcard content-type. This construct can be used instead of + including multiple vCards as body parts inside of a multipart/ + alternative MIME message. It is provided for applications that + wish to define content that can contain multiple entities within + the same text/vcard content-type or to define content that can be + identifiable outside of a MIME environment. + + ABNF: + + BEGIN-param = 0" " ; no parameter allowed + BEGIN-value = "VCARD" + + Example: + + BEGIN:VCARD + +6.1.2. END + + Purpose: To denote the end of a syntactic entity within a text/vcard + content-type. + + Value type: text + + Cardinality: 1 + + Special notes: The content entity MUST end with the END type with a + value of "VCARD". The value is case-insensitive. + + + + +Perreault Standards Track [Page 23] + +RFC 6350 vCard August 2011 + + + The END property is used in conjunction with the BEGIN property to + delimit an entity containing a related set of properties within a + text/vcard content-type. This construct can be used instead of or + in addition to wrapping separate sets of information inside + additional MIME headers. It is provided for applications that + wish to define content that can contain multiple entities within + the same text/vcard content-type or to define content that can be + identifiable outside of a MIME environment. + + ABNF: + + END-param = 0" " ; no parameter allowed + END-value = "VCARD" + + Example: + + END:VCARD + +6.1.3. SOURCE + + Purpose: To identify the source of directory information contained + in the content type. + + Value type: uri + + Cardinality: * + + Special notes: The SOURCE property is used to provide the means by + which applications knowledgable in the given directory service + protocol can obtain additional or more up-to-date information from + the directory service. It contains a URI as defined in [RFC3986] + and/or other information referencing the vCard to which the + information pertains. When directory information is available + from more than one source, the sending entity can pick what it + considers to be the best source, or multiple SOURCE properties can + be included. + + ABNF: + + SOURCE-param = "VALUE=uri" / pid-param / pref-param / altid-param + / mediatype-param / any-param + SOURCE-value = URI + + Examples: + + SOURCE:ldap://ldap.example.com/cn=Babs%20Jensen,%20o=Babsco,%20c=US + + + + + +Perreault Standards Track [Page 24] + +RFC 6350 vCard August 2011 + + + SOURCE:http://directory.example.com/addressbooks/jdoe/ + Jean%20Dupont.vcf + +6.1.4. KIND + + Purpose: To specify the kind of object the vCard represents. + + Value type: A single text value. + + Cardinality: *1 + + Special notes: The value may be one of the following: + + "individual" for a vCard representing a single person or entity. + This is the default kind of vCard. + + "group" for a vCard representing a group of persons or entities. + The group's member entities can be other vCards or other types + of entities, such as email addresses or web sites. A group + vCard will usually contain MEMBER properties to specify the + members of the group, but it is not required to. A group vCard + without MEMBER properties can be considered an abstract + grouping, or one whose members are known empirically (perhaps + "IETF Participants" or "Republican U.S. Senators"). + + All properties in a group vCard apply to the group as a whole, + and not to any particular MEMBER. For example, an EMAIL + property might specify the address of a mailing list associated + with the group, and an IMPP property might refer to a group + chat room. + + "org" for a vCard representing an organization. An organization + vCard will not (in fact, MUST NOT) contain MEMBER properties, + and so these are something of a cross between "individual" and + "group". An organization is a single entity, but not a person. + It might represent a business or government, a department or + division within a business or government, a club, an + association, or the like. + + All properties in an organization vCard apply to the + organization as a whole, as is the case with a group vCard. + For example, an EMAIL property might specify the address of a + contact point for the organization. + + + + + + + + +Perreault Standards Track [Page 25] + +RFC 6350 vCard August 2011 + + + "location" for a named geographical place. A location vCard will + usually contain a GEO property, but it is not required to. A + location vCard without a GEO property can be considered an + abstract location, or one whose definition is known empirically + (perhaps "New England" or "The Seashore"). + + All properties in a location vCard apply to the location + itself, and not with any entity that might exist at that + location. For example, in a vCard for an office building, an + ADR property might give the mailing address for the building, + and a TEL property might specify the telephone number of the + receptionist. + + An x-name. vCards MAY include private or experimental values for + KIND. Remember that x-name values are not intended for general + use and are unlikely to interoperate. + + An iana-token. Additional values may be registered with IANA (see + Section 10.3.4). A new value's specification document MUST + specify which properties make sense for that new kind of vCard + and which do not. + + Implementations MUST support the specific string values defined + above. If this property is absent, "individual" MUST be assumed + as the default. If this property is present but the + implementation does not understand its value (the value is an + x-name or iana-token that the implementation does not support), + the implementation SHOULD act in a neutral way, which usually + means treating the vCard as though its kind were "individual". + The presence of MEMBER properties MAY, however, be taken as an + indication that the unknown kind is an extension of "group". + + Clients often need to visually distinguish contacts based on what + they represent, and the KIND property provides a direct way for + them to do so. For example, when displaying contacts in a list, + an icon could be displayed next to each one, using distinctive + icons for the different kinds; a client might use an outline of a + single person to represent an "individual", an outline of multiple + people to represent a "group", and so on. Alternatively, or in + addition, a client might choose to segregate different kinds of + vCards to different panes, tabs, or selections in the user + interface. + + Some clients might also make functional distinctions among the + kinds, ignoring "location" vCards for some purposes and + considering only "location" vCards for others. + + + + + +Perreault Standards Track [Page 26] + +RFC 6350 vCard August 2011 + + + When designing those sorts of visual and functional distinctions, + client implementations have to decide how to fit unsupported kinds + into the scheme. What icon is used for them? The one for + "individual"? A unique one, such as an icon of a question mark? + Which tab do they go into? It is beyond the scope of this + specification to answer these questions, but these are things + implementers need to consider. + + ABNF: + + KIND-param = "VALUE=text" / any-param + KIND-value = "individual" / "group" / "org" / "location" + / iana-token / x-name + + Example: + + This represents someone named Jane Doe working in the marketing + department of the North American division of ABC Inc. + + BEGIN:VCARD + VERSION:4.0 + KIND:individual + FN:Jane Doe + ORG:ABC\, Inc.;North American Division;Marketing + END:VCARD + + This represents the department itself, commonly known as ABC + Marketing. + + BEGIN:VCARD + VERSION:4.0 + KIND:org + FN:ABC Marketing + ORG:ABC\, Inc.;North American Division;Marketing + END:VCARD + +6.1.5. XML + + Purpose: To include extended XML-encoded vCard data in a plain + vCard. + + Value type: A single text value. + + Cardinality: * + + Special notes: The content of this property is a single XML 1.0 + [W3C.REC-xml-20081126] element whose namespace MUST be explicitly + specified using the xmlns attribute and MUST NOT be the vCard 4 + + + +Perreault Standards Track [Page 27] + +RFC 6350 vCard August 2011 + + + namespace ("urn:ietf:params:xml:ns:vcard-4.0"). (This implies + that it cannot duplicate a standard vCard property.) The element + is to be interpreted as if it was contained in a element, + as defined in [RFC6351]. + + The fragment is subject to normal line folding and escaping, i.e., + replace all backslashes with "\\", then replace all newlines with + "\n", then fold long lines. + + Support for this property is OPTIONAL, but implementations of this + specification MUST preserve instances of this property when + propagating vCards. + + See [RFC6351] for more information on the intended use of this + property. + + ABNF: + + XML-param = "VALUE=text" / altid-param + XML-value = text + +6.2. Identification Properties + + These types are used to capture information associated with the + identification and naming of the entity associated with the vCard. + +6.2.1. FN + + Purpose: To specify the formatted text corresponding to the name of + the object the vCard represents. + + Value type: A single text value. + + Cardinality: 1* + + Special notes: This property is based on the semantics of the X.520 + Common Name attribute [CCITT.X520.1988]. The property MUST be + present in the vCard object. + + ABNF: + + FN-param = "VALUE=text" / type-param / language-param / altid-param + / pid-param / pref-param / any-param + FN-value = text + + Example: + + FN:Mr. John Q. Public\, Esq. + + + +Perreault Standards Track [Page 28] + +RFC 6350 vCard August 2011 + + +6.2.2. N + + Purpose: To specify the components of the name of the object the + vCard represents. + + Value type: A single structured text value. Each component can have + multiple values. + + Cardinality: *1 + + Special note: The structured property value corresponds, in + sequence, to the Family Names (also known as surnames), Given + Names, Additional Names, Honorific Prefixes, and Honorific + Suffixes. The text components are separated by the SEMICOLON + character (U+003B). Individual text components can include + multiple text values separated by the COMMA character (U+002C). + This property is based on the semantics of the X.520 individual + name attributes [CCITT.X520.1988]. The property SHOULD be present + in the vCard object when the name of the object the vCard + represents follows the X.520 model. + + The SORT-AS parameter MAY be applied to this property. + + ABNF: + + N-param = "VALUE=text" / sort-as-param / language-param + / altid-param / any-param + N-value = list-component 4(";" list-component) + + Examples: + + N:Public;John;Quinlan;Mr.;Esq. + + N:Stevenson;John;Philip,Paul;Dr.;Jr.,M.D.,A.C.P. + +6.2.3. NICKNAME + + Purpose: To specify the text corresponding to the nickname of the + object the vCard represents. + + Value type: One or more text values separated by a COMMA character + (U+002C). + + Cardinality: * + + + + + + + +Perreault Standards Track [Page 29] + +RFC 6350 vCard August 2011 + + + Special note: The nickname is the descriptive name given instead of + or in addition to the one belonging to the object the vCard + represents. It can also be used to specify a familiar form of a + proper name specified by the FN or N properties. + + ABNF: + + NICKNAME-param = "VALUE=text" / type-param / language-param + / altid-param / pid-param / pref-param / any-param + NICKNAME-value = text-list + + Examples: + + NICKNAME:Robbie + + NICKNAME:Jim,Jimmie + + NICKNAME;TYPE=work:Boss + +6.2.4. PHOTO + + Purpose: To specify an image or photograph information that + annotates some aspect of the object the vCard represents. + + Value type: A single URI. + + Cardinality: * + + ABNF: + + PHOTO-param = "VALUE=uri" / altid-param / type-param + / mediatype-param / pref-param / pid-param / any-param + PHOTO-value = URI + + Examples: + + PHOTO:http://www.example.com/pub/photos/jqpublic.gif + + PHOTO:data:image/jpeg;base64,MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhv + AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm + ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 + <...remainder of base64-encoded data...> + +6.2.5. BDAY + + Purpose: To specify the birth date of the object the vCard + represents. + + + + +Perreault Standards Track [Page 30] + +RFC 6350 vCard August 2011 + + + Value type: The default is a single date-and-or-time value. It can + also be reset to a single text value. + + Cardinality: *1 + + ABNF: + + BDAY-param = BDAY-param-date / BDAY-param-text + BDAY-value = date-and-or-time / text + ; Value and parameter MUST match. + + BDAY-param-date = "VALUE=date-and-or-time" + BDAY-param-text = "VALUE=text" / language-param + + BDAY-param =/ altid-param / calscale-param / any-param + ; calscale-param can only be present when BDAY-value is + ; date-and-or-time and actually contains a date or date-time. + + Examples: + + BDAY:19960415 + BDAY:--0415 + BDAY;19531015T231000Z + BDAY;VALUE=text:circa 1800 + +6.2.6. ANNIVERSARY + + Purpose: The date of marriage, or equivalent, of the object the + vCard represents. + + Value type: The default is a single date-and-or-time value. It can + also be reset to a single text value. + + Cardinality: *1 + + ABNF: + + ANNIVERSARY-param = "VALUE=" ("date-and-or-time" / "text") + ANNIVERSARY-value = date-and-or-time / text + ; Value and parameter MUST match. + + ANNIVERSARY-param =/ altid-param / calscale-param / any-param + ; calscale-param can only be present when ANNIVERSARY-value is + ; date-and-or-time and actually contains a date or date-time. + + Examples: + + ANNIVERSARY:19960415 + + + +Perreault Standards Track [Page 31] + +RFC 6350 vCard August 2011 + + +6.2.7. GENDER + + Purpose: To specify the components of the sex and gender identity of + the object the vCard represents. + + Value type: A single structured value with two components. Each + component has a single text value. + + Cardinality: *1 + + Special notes: The components correspond, in sequence, to the sex + (biological), and gender identity. Each component is optional. + + Sex component: A single letter. M stands for "male", F stands + for "female", O stands for "other", N stands for "none or not + applicable", U stands for "unknown". + + Gender identity component: Free-form text. + + ABNF: + + GENDER-param = "VALUE=text" / any-param + GENDER-value = sex [";" text] + + sex = "" / "M" / "F" / "O" / "N" / "U" + + Examples: + + GENDER:M + GENDER:F + GENDER:M;Fellow + GENDER:F;grrrl + GENDER:O;intersex + GENDER:;it's complicated + +6.3. Delivery Addressing Properties + + These types are concerned with information related to the delivery + addressing or label for the vCard object. + +6.3.1. ADR + + Purpose: To specify the components of the delivery address for the + vCard object. + + Value type: A single structured text value, separated by the + SEMICOLON character (U+003B). + + + + +Perreault Standards Track [Page 32] + +RFC 6350 vCard August 2011 + + + Cardinality: * + + Special notes: The structured type value consists of a sequence of + address components. The component values MUST be specified in + their corresponding position. The structured type value + corresponds, in sequence, to + the post office box; + the extended address (e.g., apartment or suite number); + the street address; + the locality (e.g., city); + the region (e.g., state or province); + the postal code; + the country name (full name in the language specified in + Section 5.1). + + When a component value is missing, the associated component + separator MUST still be specified. + + Experience with vCard 3 has shown that the first two components + (post office box and extended address) are plagued with many + interoperability issues. To ensure maximal interoperability, + their values SHOULD be empty. + + The text components are separated by the SEMICOLON character + (U+003B). Where it makes semantic sense, individual text + components can include multiple text values (e.g., a "street" + component with multiple lines) separated by the COMMA character + (U+002C). + + The property can include the "PREF" parameter to indicate the + preferred delivery address when more than one address is + specified. + + The GEO and TZ parameters MAY be used with this property. + + The property can also include a "LABEL" parameter to present a + delivery address label for the address. Its value is a plain-text + string representing the formatted address. Newlines are encoded + as \n, as they are for property values. + + ABNF: + + label-param = "LABEL=" param-value + + ADR-param = "VALUE=text" / label-param / language-param + / geo-parameter / tz-parameter / altid-param / pid-param + / pref-param / type-param / any-param + + + + +Perreault Standards Track [Page 33] + +RFC 6350 vCard August 2011 + + + ADR-value = ADR-component-pobox ";" ADR-component-ext ";" + ADR-component-street ";" ADR-component-locality ";" + ADR-component-region ";" ADR-component-code ";" + ADR-component-country + ADR-component-pobox = list-component + ADR-component-ext = list-component + ADR-component-street = list-component + ADR-component-locality = list-component + ADR-component-region = list-component + ADR-component-code = list-component + ADR-component-country = list-component + + Example: In this example, the post office box and the extended + address are absent. + + ADR;GEO="geo:12.3457,78.910";LABEL="Mr. John Q. Public, Esq.\n + Mail Drop: TNE QB\n123 Main Street\nAny Town, CA 91921-1234\n + U.S.A.":;;123 Main Street;Any Town;CA;91921-1234;U.S.A. + +6.4. Communications Properties + + These properties describe information about how to communicate with + the object the vCard represents. + +6.4.1. TEL + + Purpose: To specify the telephone number for telephony communication + with the object the vCard represents. + + Value type: By default, it is a single free-form text value (for + backward compatibility with vCard 3), but it SHOULD be reset to a + URI value. It is expected that the URI scheme will be "tel", as + specified in [RFC3966], but other schemes MAY be used. + + Cardinality: * + + Special notes: This property is based on the X.520 Telephone Number + attribute [CCITT.X520.1988]. + + The property can include the "PREF" parameter to indicate a + preferred-use telephone number. + + The property can include the parameter "TYPE" to specify intended + use for the telephone number. The predefined values for the TYPE + parameter are: + + + + + + +Perreault Standards Track [Page 34] + +RFC 6350 vCard August 2011 + + + +-----------+-------------------------------------------------------+ + | Value | Description | + +-----------+-------------------------------------------------------+ + | text | Indicates that the telephone number supports text | + | | messages (SMS). | + | voice | Indicates a voice telephone number. | + | fax | Indicates a facsimile telephone number. | + | cell | Indicates a cellular or mobile telephone number. | + | video | Indicates a video conferencing telephone number. | + | pager | Indicates a paging device telephone number. | + | textphone | Indicates a telecommunication device for people with | + | | hearing or speech difficulties. | + +-----------+-------------------------------------------------------+ + + The default type is "voice". These type parameter values can be + specified as a parameter list (e.g., TYPE=text;TYPE=voice) or as a + value list (e.g., TYPE="text,voice"). The default can be + overridden to another set of values by specifying one or more + alternate values. For example, the default TYPE of "voice" can be + reset to a VOICE and FAX telephone number by the value list + TYPE="voice,fax". + + If this property's value is a URI that can also be used for + instant messaging, the IMPP (Section 6.4.3) property SHOULD be + used in addition to this property. + + ABNF: + + TEL-param = TEL-text-param / TEL-uri-param + TEL-value = TEL-text-value / TEL-uri-value + ; Value and parameter MUST match. + + TEL-text-param = "VALUE=text" + TEL-text-value = text + + TEL-uri-param = "VALUE=uri" / mediatype-param + TEL-uri-value = URI + + TEL-param =/ type-param / pid-param / pref-param / altid-param + / any-param + + type-param-tel = "text" / "voice" / "fax" / "cell" / "video" + / "pager" / "textphone" / iana-token / x-name + ; type-param-tel MUST NOT be used with a property other than TEL. + + + + + + + +Perreault Standards Track [Page 35] + +RFC 6350 vCard August 2011 + + + Example: + + TEL;VALUE=uri;PREF=1;TYPE="voice,home":tel:+1-555-555-5555;ext=5555 + TEL;VALUE=uri;TYPE=home:tel:+33-01-23-45-67 + +6.4.2. EMAIL + + Purpose: To specify the electronic mail address for communication + with the object the vCard represents. + + Value type: A single text value. + + Cardinality: * + + Special notes: The property can include tye "PREF" parameter to + indicate a preferred-use email address when more than one is + specified. + + Even though the value is free-form UTF-8 text, it is likely to be + interpreted by a Mail User Agent (MUA) as an "addr-spec", as + defined in [RFC5322], Section 3.4.1. Readers should also be aware + of the current work toward internationalized email addresses + [RFC5335bis]. + + ABNF: + + EMAIL-param = "VALUE=text" / pid-param / pref-param / type-param + / altid-param / any-param + EMAIL-value = text + + Example: + + EMAIL;TYPE=work:jqpublic@xyz.example.com + + EMAIL;PREF=1:jane_doe@example.com + +6.4.3. IMPP + + Purpose: To specify the URI for instant messaging and presence + protocol communications with the object the vCard represents. + + Value type: A single URI. + + Cardinality: * + + Special notes: The property may include the "PREF" parameter to + indicate that this is a preferred address and has the same + semantics as the "PREF" parameter in a TEL property. + + + +Perreault Standards Track [Page 36] + +RFC 6350 vCard August 2011 + + + If this property's value is a URI that can be used for voice + and/or video, the TEL property (Section 6.4.1) SHOULD be used in + addition to this property. + + This property is adapted from [RFC4770], which is made obsolete by + this document. + + ABNF: + + IMPP-param = "VALUE=uri" / pid-param / pref-param / type-param + / mediatype-param / altid-param / any-param + IMPP-value = URI + + Example: + + IMPP;PREF=1:xmpp:alice@example.com + +6.4.4. LANG + + Purpose: To specify the language(s) that may be used for contacting + the entity associated with the vCard. + + Value type: A single language-tag value. + + Cardinality: * + + ABNF: + + LANG-param = "VALUE=language-tag" / pid-param / pref-param + / altid-param / type-param / any-param + LANG-value = Language-Tag + + Example: + + LANG;TYPE=work;PREF=1:en + LANG;TYPE=work;PREF=2:fr + LANG;TYPE=home:fr + +6.5. Geographical Properties + + These properties are concerned with information associated with + geographical positions or regions associated with the object the + vCard represents. + +6.5.1. TZ + + Purpose: To specify information related to the time zone of the + object the vCard represents. + + + +Perreault Standards Track [Page 37] + +RFC 6350 vCard August 2011 + + + Value type: The default is a single text value. It can also be + reset to a single URI or utc-offset value. + + Cardinality: * + + Special notes: It is expected that names from the public-domain + Olson database [TZ-DB] will be used, but this is not a + restriction. See also [IANA-TZ]. + + Efforts are currently being directed at creating a standard URI + scheme for expressing time zone information. Usage of such a + scheme would ensure a high level of interoperability between + implementations that support it. + + Note that utc-offset values SHOULD NOT be used because the UTC + offset varies with time -- not just because of the usual daylight + saving time shifts that occur in may regions, but often entire + regions will "re-base" their overall offset. The actual offset + may be +/- 1 hour (or perhaps a little more) than the one given. + + ABNF: + + TZ-param = "VALUE=" ("text" / "uri" / "utc-offset") + TZ-value = text / URI / utc-offset + ; Value and parameter MUST match. + + TZ-param =/ altid-param / pid-param / pref-param / type-param + / mediatype-param / any-param + + Examples: + + TZ:Raleigh/North America + + TZ;VALUE=utc-offset:-0500 + ; Note: utc-offset format is NOT RECOMMENDED. + +6.5.2. GEO + + Purpose: To specify information related to the global positioning of + the object the vCard represents. + + Value type: A single URI. + + Cardinality: * + + Special notes: The "geo" URI scheme [RFC5870] is particularly well + suited for this property, but other schemes MAY be used. + + + + +Perreault Standards Track [Page 38] + +RFC 6350 vCard August 2011 + + + ABNF: + + GEO-param = "VALUE=uri" / pid-param / pref-param / type-param + / mediatype-param / altid-param / any-param + GEO-value = URI + + Example: + + GEO:geo:37.386013,-122.082932 + +6.6. Organizational Properties + + These properties are concerned with information associated with + characteristics of the organization or organizational units of the + object that the vCard represents. + +6.6.1. TITLE + + Purpose: To specify the position or job of the object the vCard + represents. + + Value type: A single text value. + + Cardinality: * + + Special notes: This property is based on the X.520 Title attribute + [CCITT.X520.1988]. + + ABNF: + + TITLE-param = "VALUE=text" / language-param / pid-param + / pref-param / altid-param / type-param / any-param + TITLE-value = text + + Example: + + TITLE:Research Scientist + +6.6.2. ROLE + + Purpose: To specify the function or part played in a particular + situation by the object the vCard represents. + + Value type: A single text value. + + Cardinality: * + + + + + +Perreault Standards Track [Page 39] + +RFC 6350 vCard August 2011 + + + Special notes: This property is based on the X.520 Business Category + explanatory attribute [CCITT.X520.1988]. This property is + included as an organizational type to avoid confusion with the + semantics of the TITLE property and incorrect usage of that + property when the semantics of this property is intended. + + ABNF: + + ROLE-param = "VALUE=text" / language-param / pid-param / pref-param + / type-param / altid-param / any-param + ROLE-value = text + + Example: + + ROLE:Project Leader + +6.6.3. LOGO + + Purpose: To specify a graphic image of a logo associated with the + object the vCard represents. + + Value type: A single URI. + + Cardinality: * + + ABNF: + + LOGO-param = "VALUE=uri" / language-param / pid-param / pref-param + / type-param / mediatype-param / altid-param / any-param + LOGO-value = URI + + Examples: + + LOGO:http://www.example.com/pub/logos/abccorp.jpg + + LOGO:data:image/jpeg;base64,MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvc + AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm + ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 + <...the remainder of base64-encoded data...> + +6.6.4. ORG + + Purpose: To specify the organizational name and units associated + with the vCard. + + Value type: A single structured text value consisting of components + separated by the SEMICOLON character (U+003B). + + + + +Perreault Standards Track [Page 40] + +RFC 6350 vCard August 2011 + + + Cardinality: * + + Special notes: The property is based on the X.520 Organization Name + and Organization Unit attributes [CCITT.X520.1988]. The property + value is a structured type consisting of the organization name, + followed by zero or more levels of organizational unit names. + + The SORT-AS parameter MAY be applied to this property. + + ABNF: + + ORG-param = "VALUE=text" / sort-as-param / language-param + / pid-param / pref-param / altid-param / type-param + / any-param + ORG-value = component *(";" component) + + Example: A property value consisting of an organizational name, + organizational unit #1 name, and organizational unit #2 name. + + ORG:ABC\, Inc.;North American Division;Marketing + +6.6.5. MEMBER + + Purpose: To include a member in the group this vCard represents. + + Value type: A single URI. It MAY refer to something other than a + vCard object. For example, an email distribution list could + employ the "mailto" URI scheme [RFC6068] for efficiency. + + Cardinality: * + + Special notes: This property MUST NOT be present unless the value of + the KIND property is "group". + + ABNF: + + MEMBER-param = "VALUE=uri" / pid-param / pref-param / altid-param + / mediatype-param / any-param + MEMBER-value = URI + + + + + + + + + + + + +Perreault Standards Track [Page 41] + +RFC 6350 vCard August 2011 + + + Examples: + + BEGIN:VCARD + VERSION:4.0 + KIND:group + FN:The Doe family + MEMBER:urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af + MEMBER:urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519 + END:VCARD + BEGIN:VCARD + VERSION:4.0 + FN:John Doe + UID:urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af + END:VCARD + BEGIN:VCARD + VERSION:4.0 + FN:Jane Doe + UID:urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519 + END:VCARD + + BEGIN:VCARD + VERSION:4.0 + KIND:group + FN:Funky distribution list + MEMBER:mailto:subscriber1@example.com + MEMBER:xmpp:subscriber2@example.com + MEMBER:sip:subscriber3@example.com + MEMBER:tel:+1-418-555-5555 + END:VCARD + +6.6.6. RELATED + + Purpose: To specify a relationship between another entity and the + entity represented by this vCard. + + Value type: A single URI. It can also be reset to a single text + value. The text value can be used to specify textual information. + + Cardinality: * + + Special notes: The TYPE parameter MAY be used to characterize the + related entity. It contains a comma-separated list of values that + are registered with IANA as described in Section 10.2. The + registry is pre-populated with the values defined in [xfn]. This + document also specifies two additional values: + + agent: an entity who may sometimes act on behalf of the entity + associated with the vCard. + + + +Perreault Standards Track [Page 42] + +RFC 6350 vCard August 2011 + + + emergency: indicates an emergency contact + + ABNF: + + RELATED-param = RELATED-param-uri / RELATED-param-text + RELATED-value = URI / text + ; Parameter and value MUST match. + + RELATED-param-uri = "VALUE=uri" / mediatype-param + RELATED-param-text = "VALUE=text" / language-param + + RELATED-param =/ pid-param / pref-param / altid-param / type-param + / any-param + + type-param-related = related-type-value *("," related-type-value) + ; type-param-related MUST NOT be used with a property other than + ; RELATED. + + related-type-value = "contact" / "acquaintance" / "friend" / "met" + / "co-worker" / "colleague" / "co-resident" + / "neighbor" / "child" / "parent" + / "sibling" / "spouse" / "kin" / "muse" + / "crush" / "date" / "sweetheart" / "me" + / "agent" / "emergency" + + Examples: + + RELATED;TYPE=friend:urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + RELATED;TYPE=contact:http://example.com/directory/jdoe.vcf + RELATED;TYPE=co-worker;VALUE=text:Please contact my assistant Jane + Doe for any inquiries. + +6.7. Explanatory Properties + + These properties are concerned with additional explanations, such as + that related to informational notes or revisions specific to the + vCard. + +6.7.1. CATEGORIES + + Purpose: To specify application category information about the + vCard, also known as "tags". + + Value type: One or more text values separated by a COMMA character + (U+002C). + + Cardinality: * + + + + +Perreault Standards Track [Page 43] + +RFC 6350 vCard August 2011 + + + ABNF: + + CATEGORIES-param = "VALUE=text" / pid-param / pref-param + / type-param / altid-param / any-param + CATEGORIES-value = text-list + + Example: + + CATEGORIES:TRAVEL AGENT + + CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY + +6.7.2. NOTE + + Purpose: To specify supplemental information or a comment that is + associated with the vCard. + + Value type: A single text value. + + Cardinality: * + + Special notes: The property is based on the X.520 Description + attribute [CCITT.X520.1988]. + + ABNF: + + NOTE-param = "VALUE=text" / language-param / pid-param / pref-param + / type-param / altid-param / any-param + NOTE-value = text + + Example: + + NOTE:This fax number is operational 0800 to 1715 + EST\, Mon-Fri. + +6.7.3. PRODID + + Purpose: To specify the identifier for the product that created the + vCard object. + + Type value: A single text value. + + Cardinality: *1 + + Special notes: Implementations SHOULD use a method such as that + specified for Formal Public Identifiers in [ISO9070] or for + Universal Resource Names in [RFC3406] to ensure that the text + value is unique. + + + +Perreault Standards Track [Page 44] + +RFC 6350 vCard August 2011 + + + ABNF: + + PRODID-param = "VALUE=text" / any-param + PRODID-value = text + + Example: + + PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN + +6.7.4. REV + + Purpose: To specify revision information about the current vCard. + + Value type: A single timestamp value. + + Cardinality: *1 + + Special notes: The value distinguishes the current revision of the + information in this vCard for other renditions of the information. + + ABNF: + + REV-param = "VALUE=timestamp" / any-param + REV-value = timestamp + + Example: + + REV:19951031T222710Z + +6.7.5. SOUND + + Purpose: To specify a digital sound content information that + annotates some aspect of the vCard. This property is often used + to specify the proper pronunciation of the name property value of + the vCard. + + Value type: A single URI. + + Cardinality: * + + ABNF: + + SOUND-param = "VALUE=uri" / language-param / pid-param / pref-param + / type-param / mediatype-param / altid-param + / any-param + SOUND-value = URI + + + + + +Perreault Standards Track [Page 45] + +RFC 6350 vCard August 2011 + + + Example: + + SOUND:CID:JOHNQPUBLIC.part8.19960229T080000.xyzMail@example.com + + SOUND:data:audio/basic;base64,MIICajCCAdOgAwIBAgICBEUwDQYJKoZIh + AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm + ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 + <...the remainder of base64-encoded data...> + +6.7.6. UID + + Purpose: To specify a value that represents a globally unique + identifier corresponding to the entity associated with the vCard. + + Value type: A single URI value. It MAY also be reset to free-form + text. + + Cardinality: *1 + + Special notes: This property is used to uniquely identify the object + that the vCard represents. The "uuid" URN namespace defined in + [RFC4122] is particularly well suited to this task, but other URI + schemes MAY be used. Free-form text MAY also be used. + + ABNF: + + UID-param = UID-uri-param / UID-text-param + UID-value = UID-uri-value / UID-text-value + ; Value and parameter MUST match. + + UID-uri-param = "VALUE=uri" + UID-uri-value = URI + + UID-text-param = "VALUE=text" + UID-text-value = text + + UID-param =/ any-param + + Example: + + UID:urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + + + + + + + + + + +Perreault Standards Track [Page 46] + +RFC 6350 vCard August 2011 + + +6.7.7. CLIENTPIDMAP + + Purpose: To give a global meaning to a local PID source identifier. + + Value type: A semicolon-separated pair of values. The first field + is a small integer corresponding to the second field of a PID + parameter instance. The second field is a URI. The "uuid" URN + namespace defined in [RFC4122] is particularly well suited to this + task, but other URI schemes MAY be used. + + Cardinality: * + + Special notes: PID source identifiers (the source identifier is the + second field in a PID parameter instance) are small integers that + only have significance within the scope of a single vCard + instance. Each distinct source identifier present in a vCard MUST + have an associated CLIENTPIDMAP. See Section 7 for more details + on the usage of CLIENTPIDMAP. + + PID source identifiers MUST be strictly positive. Zero is not + allowed. + + As a special exception, the PID parameter MUST NOT be applied to + this property. + + ABNF: + + CLIENTPIDMAP-param = any-param + CLIENTPIDMAP-value = 1*DIGIT ";" URI + + Example: + + TEL;PID=3.1,4.2;VALUE=uri:tel:+1-555-555-5555 + EMAIL;PID=4.1,5.2:jdoe@example.com + CLIENTPIDMAP:1;urn:uuid:3df403f4-5924-4bb7-b077-3c711d9eb34b + CLIENTPIDMAP:2;urn:uuid:d89c9c7a-2e1b-4832-82de-7e992d95faa5 + +6.7.8. URL + + Purpose: To specify a uniform resource locator associated with the + object to which the vCard refers. Examples for individuals + include personal web sites, blogs, and social networking site + identifiers. + + Cardinality: * + + Value type: A single uri value. + + + + +Perreault Standards Track [Page 47] + +RFC 6350 vCard August 2011 + + + ABNF: + + URL-param = "VALUE=uri" / pid-param / pref-param / type-param + / mediatype-param / altid-param / any-param + URL-value = URI + + Example: + + URL:http://example.org/restaurant.french/~chezchic.html + +6.7.9. VERSION + + Purpose: To specify the version of the vCard specification used to + format this vCard. + + Value type: A single text value. + + Cardinality: 1 + + Special notes: This property MUST be present in the vCard object, + and it must appear immediately after BEGIN:VCARD. The value MUST + be "4.0" if the vCard corresponds to this specification. Note + that earlier versions of vCard allowed this property to be placed + anywhere in the vCard object, or even to be absent. + + ABNF: + + VERSION-param = "VALUE=text" / any-param + VERSION-value = "4.0" + + Example: + + VERSION:4.0 + +6.8. Security Properties + + These properties are concerned with the security of communication + pathways or access to the vCard. + +6.8.1. KEY + + Purpose: To specify a public key or authentication certificate + associated with the object that the vCard represents. + + Value type: A single URI. It can also be reset to a text value. + + Cardinality: * + + + + +Perreault Standards Track [Page 48] + +RFC 6350 vCard August 2011 + + + ABNF: + + KEY-param = KEY-uri-param / KEY-text-param + KEY-value = KEY-uri-value / KEY-text-value + ; Value and parameter MUST match. + + KEY-uri-param = "VALUE=uri" / mediatype-param + KEY-uri-value = URI + + KEY-text-param = "VALUE=text" + KEY-text-value = text + + KEY-param =/ altid-param / pid-param / pref-param / type-param + / any-param + + Examples: + + KEY:http://www.example.com/keys/jdoe.cer + + KEY;MEDIATYPE=application/pgp-keys:ftp://example.com/keys/jdoe + + KEY:data:application/pgp-keys;base64,MIICajCCAdOgAwIBAgICBE + UwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05l + <... remainder of base64-encoded data ...> + +6.9. Calendar Properties + + These properties are further specified in [RFC2739]. + +6.9.1. FBURL + + Purpose: To specify the URI for the busy time associated with the + object that the vCard represents. + + Value type: A single URI value. + + Cardinality: * + + Special notes: Where multiple FBURL properties are specified, the + default FBURL property is indicated with the PREF parameter. The + FTP [RFC1738] or HTTP [RFC2616] type of URI points to an iCalendar + [RFC5545] object associated with a snapshot of the next few weeks + or months of busy time data. If the iCalendar object is + represented as a file or document, its file extension should be + ".ifb". + + + + + + +Perreault Standards Track [Page 49] + +RFC 6350 vCard August 2011 + + + ABNF: + + FBURL-param = "VALUE=uri" / pid-param / pref-param / type-param + / mediatype-param / altid-param / any-param + FBURL-value = URI + + Examples: + + FBURL;PREF=1:http://www.example.com/busy/janedoe + FBURL;MEDIATYPE=text/calendar:ftp://example.com/busy/project-a.ifb + +6.9.2. CALADRURI + + Purpose: To specify the calendar user address [RFC5545] to which a + scheduling request [RFC5546] should be sent for the object + represented by the vCard. + + Value type: A single URI value. + + Cardinality: * + + Special notes: Where multiple CALADRURI properties are specified, + the default CALADRURI property is indicated with the PREF + parameter. + + ABNF: + + CALADRURI-param = "VALUE=uri" / pid-param / pref-param / type-param + / mediatype-param / altid-param / any-param + CALADRURI-value = URI + + Example: + + CALADRURI;PREF=1:mailto:janedoe@example.com + CALADRURI:http://example.com/calendar/jdoe + +6.9.3. CALURI + + Purpose: To specify the URI for a calendar associated with the + object represented by the vCard. + + Value type: A single URI value. + + Cardinality: * + + Special notes: Where multiple CALURI properties are specified, the + default CALURI property is indicated with the PREF parameter. The + property should contain a URI pointing to an iCalendar [RFC5545] + + + +Perreault Standards Track [Page 50] + +RFC 6350 vCard August 2011 + + + object associated with a snapshot of the user's calendar store. + If the iCalendar object is represented as a file or document, its + file extension should be ".ics". + + ABNF: + + CALURI-param = "VALUE=uri" / pid-param / pref-param / type-param + / mediatype-param / altid-param / any-param + CALURI-value = URI + + Examples: + + CALURI;PREF=1:http://cal.example.com/calA + CALURI;MEDIATYPE=text/calendar:ftp://ftp.example.com/calA.ics + +6.10. Extended Properties and Parameters + + The properties and parameters defined by this document can be + extended. Non-standard, private properties and parameters with a + name starting with "X-" may be defined bilaterally between two + cooperating agents without outside registration or standardization. + +7. Synchronization + + vCard data often needs to be synchronized between devices. In this + context, synchronization is defined as the intelligent merging of two + representations of the same object. vCard 4.0 includes mechanisms to + aid this process. + +7.1. Mechanisms + + Two mechanisms are available: the UID property is used to match + multiple instances of the same vCard, while the PID parameter is used + to match multiple instances of the same property. + + The term "matching" is used here to mean recognizing that two + instances are in fact representations of the same object. For + example, a single vCard that is shared with someone results in two + vCard instances. After they have evolved separately, they still + represent the same object, and therefore may be matched by a + synchronization engine. + +7.1.1. Matching vCard Instances + + vCard instances for which the UID properties (Section 6.7.6) are + equivalent MUST be matched. Equivalence is determined as specified + in [RFC3986], Section 6. + + + + +Perreault Standards Track [Page 51] + +RFC 6350 vCard August 2011 + + + In all other cases, vCard instances MAY be matched at the discretion + of the synchronization engine. + +7.1.2. Matching Property Instances + + Property instances belonging to unmatched vCards MUST NOT be matched. + + Property instances whose name (e.g., EMAIL, TEL, etc.) is not the + same MUST NOT be matched. + + Property instances whose name is CLIENTPIDMAP are handled separately + and MUST NOT be matched. The synchronization MUST ensure that there + is consistency of CLIENTPIDMAPs among matched vCard instances. + + Property instances belonging to matched vCards, whose name is the + same, and whose maximum cardinality is 1, MUST be matched. + + Property instances belonging to matched vCards, whose name is the + same, and whose PID parameters match, MUST be matched. See + Section 7.1.3 for details on PID matching. + + In all other cases, property instances MAY be matched at the + discretion of the synchronization engine. + +7.1.3. PID Matching + + Two PID values for which the first fields are equivalent represent + the same local value. + + Two PID values representing the same local value and for which the + second fields point to CLIENTPIDMAP properties whose second field + URIs are equivalent (as specified in [RFC3986], Section 6) also + represent the same global value. + + PID parameters for which at least one pair of their values represent + the same global value MUST be matched. + + In all other cases, PID parameters MAY be matched at the discretion + of the synchronization engine. + + For example, PID value "5.1", in the first vCard below, and PID value + "5.2", in the second vCard below, represent the same global value. + + + + + + + + + +Perreault Standards Track [Page 52] + +RFC 6350 vCard August 2011 + + + BEGIN:VCARD + VERSION:4.0 + EMAIL;PID=4.2,5.1:jdoe@example.com + CLIENTPIDMAP:1;urn:uuid:3eef374e-7179-4196-a914-27358c3e6527 + CLIENTPIDMAP:2;urn:uuid:42bcd5a7-1699-4514-87b4-056edf68e9cc + END:VCARD + + BEGIN:VCARD + VERSION:4.0 + EMAIL;PID=5.1,5.2:john@example.com + CLIENTPIDMAP:1;urn:uuid:0c75c629-6a8d-4d5e-a07f-1bb35846854d + CLIENTPIDMAP:2;urn:uuid:3eef374e-7179-4196-a914-27358c3e6527 + END:VCARD + +7.2. Example + +7.2.1. Creation + + The following simple vCard is first created on a given device. + + BEGIN:VCARD + VERSION:4.0 + UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1 + FN;PID=1.1:J. Doe + N:Doe;J.;;; + EMAIL;PID=1.1:jdoe@example.com + CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556 + END:VCARD + + This new vCard is assigned the UID + "urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1" by the creating + device. The FN and EMAIL properties are assigned the same local + value of 1, and this value is given global context by associating it + with "urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556", which + represents the creating device. We are at liberty to reuse the same + local value since instances of different properties will never be + matched. The N property has no PID because it is forbidden by its + maximum cardinality of 1. + +7.2.2. Initial Sharing + + This vCard is shared with a second device. Upon inspecting the UID + property, the second device understands that this is a new vCard + (i.e., unmatched) and thus the synchronization results in a simple + copy. + + + + + + +Perreault Standards Track [Page 53] + +RFC 6350 vCard August 2011 + + +7.2.3. Adding and Sharing a Property + + A new phone number is created on the first device, then the vCard is + shared with the second device. This is what the second device + receives: + + BEGIN:VCARD + VERSION:4.0 + UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1 + FN;PID=1.1:J. Doe + N:Doe;J.;;; + EMAIL;PID=1.1:jdoe@example.com + TEL;PID=1.1;VALUE=uri:tel:+1-555-555-5555 + CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556 + END:VCARD + + Upon inspecting the UID property, the second device matches the vCard + it received to the vCard that it already has stored. It then starts + comparing the properties of the two vCards in same-named pairs. + + The FN properties are matched because the PID parameters have the + same global value. Since the property value is the same, no update + takes place. + + The N properties are matched automatically because their maximum + cardinality is 1. Since the property value is the same, no update + takes place. + + The EMAIL properties are matched because the PID parameters have the + same global value. Since the property value is the same, no update + takes place. + + The TEL property in the new vCard is not matched to any in the stored + vCard because no property in the stored vCard has the same name. + Therefore, this property is copied from the new vCard to the stored + vCard. + + The CLIENTPIDMAP property is handled separately by the + synchronization engine. It ensures that it is consistent with the + stored one. If it was not, the results would be up to the + synchronization engine, and thus undefined by this document. + +7.2.4. Simultaneous Editing + + A new email address and a new phone number are added to the vCard on + each of the two devices, and then a new synchronization event + happens. Here are the vCards that are communicated to each other: + + + + +Perreault Standards Track [Page 54] + +RFC 6350 vCard August 2011 + + + BEGIN:VCARD + VERSION:4.0 + UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1 + FN;PID=1.1:J. Doe + N:Doe;J.;;; + EMAIL;PID=1.1:jdoe@example.com + EMAIL;PID=2.1:boss@example.com + TEL;PID=1.1;VALUE=uri:tel:+1-555-555-5555 + TEL;PID=2.1;VALUE=uri:tel:+1-666-666-6666 + CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556 + END:VCARD + + BEGIN:VCARD + VERSION:4.0 + UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1 + FN;PID=1.1:J. Doe + N:Doe;J.;;; + EMAIL;PID=1.1:jdoe@example.com + EMAIL;PID=2.2:ceo@example.com + TEL;PID=1.1;VALUE=uri:tel:+1-555-555-5555 + TEL;PID=2.2;VALUE=uri:tel:+1-666-666-6666 + CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556 + CLIENTPIDMAP:2;urn:uuid:1f762d2b-03c4-4a83-9a03-75ff658a6eee + END:VCARD + + On the first device, the same PID source identifier (1) is reused for + the new EMAIL and TEL properties. On the second device, a new source + identifier (2) is generated, and a corresponding CLIENTPIDMAP + property is created. It contains the second device's identifier, + "urn:uuid:1f762d2b-03c4-4a83-9a03-75ff658a6eee". + + The new EMAIL properties are unmatched on both sides since the PID + global value is new in both cases. The sync thus results in a copy + on both sides. + + Although the situation appears to be the same for the TEL properties, + in this case, the synchronization engine is particularly smart and + matches the two new TEL properties even though their PID global + values are different. Note that in this case, the rules of + Section 7.1.2 state that two properties MAY be matched at the + discretion of the synchronization engine. Therefore, the two + properties are merged. + + All this results in the following vCard, which is stored on both + devices: + + + + + + +Perreault Standards Track [Page 55] + +RFC 6350 vCard August 2011 + + + BEGIN:VCARD + VERSION:4.0 + UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1 + FN:J. Doe + N:Doe;J.;;; + EMAIL;PID=1.1:jdoe@example.com + EMAIL;PID=2.1:boss@example.com + EMAIL;PID=2.2:ceo@example.com + TEL;PID=1.1;VALUE=uri:tel:+1-555-555-5555 + TEL;PID=2.1,2.2;VALUE=uri:tel:+1-666-666-6666 + CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556 + CLIENTPIDMAP:2;urn:uuid:1f762d2b-03c4-4a83-9a03-75ff658a6eee + END:VCARD + +7.2.5. Global Context Simplification + + The two devices finish their synchronization procedure by simplifying + their global contexts. Since they haven't talked to any other + device, the following vCard is for all purposes equivalent to the + above. It is also shorter. + + BEGIN:VCARD + VERSION:4.0 + UID:urn:uuid:4fbe8971-0bc3-424c-9c26-36c3e1eff6b1 + FN:J. Doe + N:Doe;J.;;; + EMAIL;PID=1.1:jdoe@example.com + EMAIL;PID=2.1:boss@example.com + EMAIL;PID=3.1:ceo@example.com + TEL;PID=1.1;VALUE=uri:tel:+1-555-555-5555 + TEL;PID=2.1;VALUE=uri:tel:+1-666-666-6666 + CLIENTPIDMAP:1;urn:uuid:53e374d9-337e-4727-8803-a1e9c14e0556 + END:VCARD + + The details of global context simplification are unspecified by this + document. They are left up to the synchronization engine. This + example is merely intended to illustrate the possibility, which + investigating would be, in the author's opinion, worthwhile. + +8. Example: Author's vCard + + BEGIN:VCARD + VERSION:4.0 + FN:Simon Perreault + N:Perreault;Simon;;;ing. jr,M.Sc. + BDAY:--0203 + ANNIVERSARY:20090808T1430-0500 + GENDER:M + + + +Perreault Standards Track [Page 56] + +RFC 6350 vCard August 2011 + + + LANG;PREF=1:fr + LANG;PREF=2:en + ORG;TYPE=work:Viagenie + ADR;TYPE=work:;Suite D2-630;2875 Laurier; + Quebec;QC;G1V 2M2;Canada + TEL;VALUE=uri;TYPE="work,voice";PREF=1:tel:+1-418-656-9254;ext=102 + TEL;VALUE=uri;TYPE="work,cell,voice,video,text":tel:+1-418-262-6501 + EMAIL;TYPE=work:simon.perreault@viagenie.ca + GEO;TYPE=work:geo:46.772673,-71.282945 + KEY;TYPE=work;VALUE=uri: + http://www.viagenie.ca/simon.perreault/simon.asc + TZ:-0500 + URL;TYPE=home:http://nomis80.org + END:VCARD + +9. Security Considerations + + o Internet mail is often used to transport vCards and is subject to + many well-known security attacks, including monitoring, replay, + and forgery. Care should be taken by any directory service in + allowing information to leave the scope of the service itself, + where any access controls or confidentiality can no longer be + guaranteed. Applications should also take care to display + directory data in a "safe" environment. + + o vCards can carry cryptographic keys or certificates, as described + in Section 6.8.1. + + o vCards often carry information that can be sensitive (e.g., + birthday, address, and phone information). Although vCards have + no inherent authentication or confidentiality provisions, they can + easily be carried by any security mechanism that transfers MIME + objects to address authentication or confidentiality (e.g., S/MIME + [RFC5751], OpenPGP [RFC4880]). In cases where the confidentiality + or authenticity of information contained in vCard is a concern, + the vCard SHOULD be transported using one of these secure + mechanisms. The KEY property (Section 6.8.1) can be used to + transport the public key used by these mechanisms. + + o The information in a vCard may become out of date. In cases where + the vitality of data is important to an originator of a vCard, the + SOURCE property (Section 6.1.3) SHOULD be specified. In addition, + the "REV" type described in Section 6.7.4 can be specified to + indicate the last time that the vCard data was updated. + + o Many vCard properties may be used to transport URIs. Please refer + to [RFC3986], Section 7, for considerations related to URIs. + + + + +Perreault Standards Track [Page 57] + +RFC 6350 vCard August 2011 + + +10. IANA Considerations + +10.1. Media Type Registration + + IANA has registered the following Media Type (in + ) and marked the text/directory Media Type as + DEPRECATED. + + To: ietf-types@iana.org + + Subject: Registration of media type text/vcard + + Type name: text + + Subtype name: vcard + + Required parameters: none + + Optional parameters: version + + The "version" parameter is to be interpreted identically as the + VERSION vCard property. If this parameter is present, all vCards + in a text/vcard body part MUST have a VERSION property with value + identical to that of this MIME parameter. + + "charset": as defined for text/plain [RFC2046]; encodings other + than UTF-8 [RFC3629] MUST NOT be used. + + Encoding considerations: 8bit + + Security considerations: See Section 9. + + Interoperability considerations: The text/vcard media type is + intended to identify vCard data of any version. There are older + specifications of vCard [RFC2426][vCard21] still in common use. + While these formats are similar, they are not strictly compatible. + In general, it is necessary to inspect the value of the VERSION + property (see Section 6.7.9) for identifying the standard to which + a given vCard object conforms. + + In addition, the following media types are known to have been used + to refer to vCard data. They should be considered deprecated in + favor of text/vcard. + + * text/directory + * text/directory; profile=vcard + * text/x-vcard + + + + +Perreault Standards Track [Page 58] + +RFC 6350 vCard August 2011 + + + Published specification: RFC 6350 + + Applications that use this media type: They are numerous, diverse, + and include mail user agents, instant messaging clients, address + book applications, directory servers, and customer relationship + management software. + + Additional information: + + Magic number(s): + + File extension(s): .vcf .vcard + + Macintosh file type code(s): + + Person & email address to contact for further information: vCard + discussion mailing list + + Intended usage: COMMON + + Restrictions on usage: none + + Author: Simon Perreault + + Change controller: IETF + +10.2. Registering New vCard Elements + + This section defines the process for registering new or modified + vCard elements (i.e., properties, parameters, value data types, and + values) with IANA. + +10.2.1. Registration Procedure + + The IETF has created a mailing list, vcarddav@ietf.org, which can be + used for public discussion of vCard element proposals prior to + registration. Use of the mailing list is strongly encouraged. The + IESG has appointed a designated expert who will monitor the + vcarddav@ietf.org mailing list and review registrations. + + Registration of new vCard elements MUST be reviewed by the designated + expert and published in an RFC. A Standards Track RFC is REQUIRED + for the registration of new value data types that modify existing + properties. A Standards Track RFC is also REQUIRED for registration + of vCard elements that modify vCard elements previously documented in + a Standards Track RFC. + + + + + +Perreault Standards Track [Page 59] + +RFC 6350 vCard August 2011 + + + The registration procedure begins when a completed registration + template, defined in the sections below, is sent to vcarddav@ietf.org + and iana@iana.org. Within two weeks, the designated expert is + expected to tell IANA and the submitter of the registration whether + the registration is approved, approved with minor changes, or + rejected with cause. When a registration is rejected with cause, it + can be re-submitted if the concerns listed in the cause are + addressed. Decisions made by the designated expert can be appealed + to the IESG Applications Area Director, then to the IESG. They + follow the normal appeals procedure for IESG decisions. + + Once the registration procedure concludes successfully, IANA creates + or modifies the corresponding record in the vCard registry. The + completed registration template is discarded. + + An RFC specifying new vCard elements MUST include the completed + registration templates, which MAY be expanded with additional + information. These completed templates are intended to go in the + body of the document, not in the IANA Considerations section. + + Finally, note that there is an XML representation for vCard defined + in [RFC6351]. An XML representation SHOULD be defined for new vCard + elements. + +10.2.2. Vendor Namespace + + The vendor namespace is used for vCard elements associated with + commercially available products. "Vendor" or "producer" are + construed as equivalent and very broadly in this context. + + A registration may be placed in the vendor namespace by anyone who + needs to interchange files associated with the particular product. + However, the registration formally belongs to the vendor or + organization handling the vCard elements in the namespace being + registered. Changes to the specification will be made at their + request, as discussed in subsequent sections. + + vCard elements belonging to the vendor namespace will be + distinguished by the "VND-" prefix. This is followed by an IANA- + registered Private Enterprise Number (PEN), a dash, and a vCard + element designation of the vendor's choosing (e.g., "VND-123456- + MUDPIE"). + + While public exposure and review of vCard elements to be registered + in the vendor namespace are not required, using the vcarddav@ietf.org + mailing list for review is strongly encouraged to improve the quality + of those specifications. Registrations in the vendor namespace may + be submitted directly to the IANA. + + + +Perreault Standards Track [Page 60] + +RFC 6350 vCard August 2011 + + +10.2.3. Registration Template for Properties + + A property is defined by completing the following template. + + Namespace: Empty for the global namespace, "VND-NNNN-" for a vendor- + specific property (where NNNN is replaced by the vendor's PEN). + + Property name: The name of the property. + + Purpose: The purpose of the property. Give a short but clear + description. + + Value type: Any of the valid value types for the property value + needs to be specified. The default value type also needs to be + specified. + + Cardinality: See Section 6. + + Property parameters: Any of the valid property parameters for the + property MUST be specified. + + Description: Any special notes about the property, how it is to be + used, etc. + + Format definition: The ABNF for the property definition needs to be + specified. + + Example(s): One or more examples of instances of the property need + to be specified. + +10.2.4. Registration Template for Parameters + + A parameter is defined by completing the following template. + + Namespace: Empty for the global namespace, "VND-NNNN-" for a vendor- + specific property (where NNNN is replaced by the vendor's PEN). + + Parameter name: The name of the parameter. + + Purpose: The purpose of the parameter. Give a short but clear + description. + + Description: Any special notes about the parameter, how it is to be + used, etc. + + Format definition: The ABNF for the parameter definition needs to be + specified. + + + + +Perreault Standards Track [Page 61] + +RFC 6350 vCard August 2011 + + + Example(s): One or more examples of instances of the parameter need + to be specified. + +10.2.5. Registration Template for Value Data Types + + A value data type is defined by completing the following template. + + Value name: The name of the value type. + + Purpose: The purpose of the value type. Give a short but clear + description. + + Description: Any special notes about the value type, how it is to be + used, etc. + + Format definition: The ABNF for the value type definition needs to + be specified. + + Example(s): One or more examples of instances of the value type need + to be specified. + +10.2.6. Registration Template for Values + + A value is defined by completing the following template. + + Value: The value literal. + + Purpose: The purpose of the value. Give a short but clear + description. + + Conformance: The vCard properties and/or parameters that can take + this value needs to be specified. + + Example(s): One or more examples of instances of the value need to + be specified. + + The following is a fictitious example of a registration of a vCard + value: + + Value: supervisor + + Purpose: It means that the related entity is the direct hierarchical + superior (i.e., supervisor or manager) of the entity this vCard + represents. + + Conformance: This value can be used with the "TYPE" parameter + applied on the "RELATED" property. + + + + +Perreault Standards Track [Page 62] + +RFC 6350 vCard August 2011 + + + Example(s): + + RELATED;TYPE=supervisor:urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + +10.3. Initial vCard Elements Registries + + The IANA has created and will maintain the following registries for + vCard elements with pointers to appropriate reference documents. The + registries are grouped together under the heading "vCard Elements". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Perreault Standards Track [Page 63] + +RFC 6350 vCard August 2011 + + +10.3.1. Properties Registry + + The following table has been used to initialize the properties + registry. + + +-----------+--------------+-------------------------+ + | Namespace | Property | Reference | + +-----------+--------------+-------------------------+ + | | SOURCE | RFC 6350, Section 6.1.3 | + | | KIND | RFC 6350, Section 6.1.4 | + | | XML | RFC 6350, Section 6.1.5 | + | | FN | RFC 6350, Section 6.2.1 | + | | N | RFC 6350, Section 6.2.2 | + | | NICKNAME | RFC 6350, Section 6.2.3 | + | | PHOTO | RFC 6350, Section 6.2.4 | + | | BDAY | RFC 6350, Section 6.2.5 | + | | ANNIVERSARY | RFC 6350, Section 6.2.6 | + | | GENDER | RFC 6350, Section 6.2.7 | + | | ADR | RFC 6350, Section 6.3.1 | + | | TEL | RFC 6350, Section 6.4.1 | + | | EMAIL | RFC 6350, Section 6.4.2 | + | | IMPP | RFC 6350, Section 6.4.3 | + | | LANG | RFC 6350, Section 6.4.4 | + | | TZ | RFC 6350, Section 6.5.1 | + | | GEO | RFC 6350, Section 6.5.2 | + | | TITLE | RFC 6350, Section 6.6.1 | + | | ROLE | RFC 6350, Section 6.6.2 | + | | LOGO | RFC 6350, Section 6.6.3 | + | | ORG | RFC 6350, Section 6.6.4 | + | | MEMBER | RFC 6350, Section 6.6.5 | + | | RELATED | RFC 6350, Section 6.6.6 | + | | CATEGORIES | RFC 6350, Section 6.7.1 | + | | NOTE | RFC 6350, Section 6.7.2 | + | | PRODID | RFC 6350, Section 6.7.3 | + | | REV | RFC 6350, Section 6.7.4 | + | | SOUND | RFC 6350, Section 6.7.5 | + | | UID | RFC 6350, Section 6.7.6 | + | | CLIENTPIDMAP | RFC 6350, Section 6.7.7 | + | | URL | RFC 6350, Section 6.7.8 | + | | VERSION | RFC 6350, Section 6.7.9 | + | | KEY | RFC 6350, Section 6.8.1 | + | | FBURL | RFC 6350, Section 6.9.1 | + | | CALADRURI | RFC 6350, Section 6.9.2 | + | | CALURI | RFC 6350, Section 6.9.3 | + +-----------+--------------+-------------------------+ + + + + + + +Perreault Standards Track [Page 64] + +RFC 6350 vCard August 2011 + + +10.3.2. Parameters Registry + + The following table has been used to initialize the parameters + registry. + + +-----------+-----------+------------------------+ + | Namespace | Parameter | Reference | + +-----------+-----------+------------------------+ + | | LANGUAGE | RFC 6350, Section 5.1 | + | | VALUE | RFC 6350, Section 5.2 | + | | PREF | RFC 6350, Section 5.3 | + | | ALTID | RFC 6350, Section 5.4 | + | | PID | RFC 6350, Section 5.5 | + | | TYPE | RFC 6350, Section 5.6 | + | | MEDIATYPE | RFC 6350, Section 5.7 | + | | CALSCALE | RFC 6350, Section 5.8 | + | | SORT-AS | RFC 6350, Section 5.9 | + | | GEO | RFC 6350, Section 5.10 | + | | TZ | RFC 6350, Section 5.11 | + +-----------+-----------+------------------------+ + +10.3.3. Value Data Types Registry + + The following table has been used to initialize the parameters + registry. + + +------------------+-------------------------+ + | Value Data Type | Reference | + +------------------+-------------------------+ + | BOOLEAN | RFC 6350, Section 4.4 | + | DATE | RFC 6350, Section 4.3.1 | + | DATE-AND-OR-TIME | RFC 6350, Section 4.3.4 | + | DATE-TIME | RFC 6350, Section 4.3.3 | + | FLOAT | RFC 6350, Section 4.6 | + | INTEGER | RFC 6350, Section 4.5 | + | LANGUAGE-TAG | RFC 6350, Section 4.8 | + | TEXT | RFC 6350, Section 4.1 | + | TIME | RFC 6350, Section 4.3.2 | + | TIMESTAMP | RFC 6350, Section 4.3.5 | + | URI | RFC 6350, Section 4.2 | + | UTC-OFFSET | RFC 6350, Section 4.7 | + +------------------+-------------------------+ + + + + + + + + + +Perreault Standards Track [Page 65] + +RFC 6350 vCard August 2011 + + +10.3.4. Values Registries + + Separate tables are used for property and parameter values. + + The following table is to be used to initialize the property values + registry. + + +----------+------------+-------------------------+ + | Property | Value | Reference | + +----------+------------+-------------------------+ + | BEGIN | VCARD | RFC 6350, Section 6.1.1 | + | END | VCARD | RFC 6350, Section 6.1.2 | + | KIND | individual | RFC 6350, Section 6.1.4 | + | KIND | group | RFC 6350, Section 6.1.4 | + | KIND | org | RFC 6350, Section 6.1.4 | + | KIND | location | RFC 6350, Section 6.1.4 | + +----------+------------+-------------------------+ + + The following table has been used to initialize the parameter values + registry. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Perreault Standards Track [Page 66] + +RFC 6350 vCard August 2011 + + + +------------------------+-----------+--------------+---------------+ + | Property | Parameter | Value | Reference | + +------------------------+-----------+--------------+---------------+ + | FN, NICKNAME, PHOTO, | TYPE | work | RFC 6350, | + | ADR, TEL, EMAIL, IMPP, | | | Section 5.6 | + | LANG, TZ, GEO, TITLE, | | | | + | ROLE, LOGO, ORG, | | | | + | RELATED, CATEGORIES, | | | | + | NOTE, SOUND, URL, KEY, | | | | + | FBURL, CALADRURI, and | | | | + | CALURI | | | | + | FN, NICKNAME, PHOTO, | TYPE | home | RFC 6350, | + | ADR, TEL, EMAIL, IMPP, | | | Section 5.6 | + | LANG, TZ, GEO, TITLE, | | | | + | ROLE, LOGO, ORG, | | | | + | RELATED, CATEGORIES, | | | | + | NOTE, SOUND, URL, KEY, | | | | + | FBURL, CALADRURI, and | | | | + | CALURI | | | | + | TEL | TYPE | text | RFC 6350, | + | | | | Section 6.4.1 | + | TEL | TYPE | voice | RFC 6350, | + | | | | Section 6.4.1 | + | TEL | TYPE | fax | RFC 6350, | + | | | | Section 6.4.1 | + | TEL | TYPE | cell | RFC 6350, | + | | | | Section 6.4.1 | + | TEL | TYPE | video | RFC 6350, | + | | | | Section 6.4.1 | + | TEL | TYPE | pager | RFC 6350, | + | | | | Section 6.4.1 | + | TEL | TYPE | textphone | RFC 6350, | + | | | | Section 6.4.1 | + | BDAY, ANNIVERSARY | CALSCALE | gregorian | RFC 6350, | + | | | | Section 5.8 | + | RELATED | TYPE | contact | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | acquaintance | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | friend | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | met | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + + + + +Perreault Standards Track [Page 67] + +RFC 6350 vCard August 2011 + + + | RELATED | TYPE | co-worker | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | colleague | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | co-resident | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | neighbor | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | child | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | parent | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | sibling | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | spouse | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | kin | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | muse | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | crush | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | date | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | sweetheart | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | me | RFC 6350, | + | | | | Section 6.6.6 | + | | | | and [xfn] | + | RELATED | TYPE | agent | RFC 6350, | + | | | | Section 6.6.6 | + | RELATED | TYPE | emergency | RFC 6350, | + | | | | Section 6.6.6 | + +------------------------+-----------+--------------+---------------+ + + + + +Perreault Standards Track [Page 68] + +RFC 6350 vCard August 2011 + + +11. Acknowledgments + + The authors would like to thank Tim Howes, Mark Smith, and Frank + Dawson, the original authors of [RFC2425] and [RFC2426], Pete + Resnick, who got this effort started and provided help along the way, + as well as the following individuals who have participated in the + drafting, review, and discussion of this memo: + + Aki Niemi, Andy Mabbett, Alexander Mayrhofer, Alexey Melnikov, Anil + Srivastava, Barry Leiba, Ben Fortuna, Bernard Desruisseaux, Bernie + Hoeneisen, Bjoern Hoehrmann, Caleb Richardson, Chris Bryant, Chris + Newman, Cyrus Daboo, Daisuke Miyakawa, Dan Brickley, Dan Mosedale, + Dany Cauchie, Darryl Champagne, Dave Thewlis, Filip Navara, Florian + Zeitz, Helge Hess, Jari Urpalainen, Javier Godoy, Jean-Luc Schellens, + Joe Hildebrand, Jose Luis Gayosso, Joseph Smarr, Julian Reschke, + Kepeng Li, Kevin Marks, Kevin Wu Won, Kurt Zeilenga, Lisa Dusseault, + Marc Blanchet, Mark Paterson, Markus Lorenz, Michael Haardt, Mike + Douglass, Nick Levinson, Peter K. Sheerin, Peter Mogensen, Peter + Saint-Andre, Renato Iannella, Rohit Khare, Sly Gryphon, Stephane + Bortzmeyer, Tantek Celik, and Zoltan Ordogh. + +12. References + +12.1. Normative References + + [CCITT.X520.1988] + International Telephone and Telegraph Consultative + Committee, "Information Technology - Open Systems + Interconnection - The Directory: Selected Attribute + Types", CCITT Recommendation X.520, November 1988. + + [IEEE.754.2008] + Institute of Electrical and Electronics Engineers, + "Standard for Binary Floating-Point Arithmetic", + IEEE Standard 754, August 2008. + + [ISO.8601.2000] + International Organization for Standardization, "Data + elements and interchange formats - Information interchange + - Representation of dates and times", ISO Standard 8601, + December 2000. + + [ISO.8601.2004] + International Organization for Standardization, "Data + elements and interchange formats - Information interchange + - Representation of dates and times", ISO Standard 8601, + December 2004. + + + + +Perreault Standards Track [Page 69] + +RFC 6350 vCard August 2011 + + + [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message + Bodies", RFC 2045, November 1996. + + [RFC2046] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part Two: Media Types", RFC 2046, + November 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2739] Small, T., Hennessy, D., and F. Dawson, "Calendar + Attributes for vCard and LDAP", RFC 2739, January 2000. + + [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", STD 63, RFC 3629, November 2003. + + [RFC3966] Schulzrinne, H., "The tel URI for Telephone Numbers", + RFC 3966, December 2004. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform + Resource Identifier (URI): Generic Syntax", STD 66, + RFC 3986, January 2005. + + [RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally + Unique IDentifier (UUID) URN Namespace", RFC 4122, + July 2005. + + [RFC4288] Freed, N. and J. Klensin, "Media Type Specifications and + Registration Procedures", BCP 13, RFC 4288, December 2005. + + [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", STD 68, RFC 5234, January 2008. + + [RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, + October 2008. + + [RFC5545] Desruisseaux, B., "Internet Calendaring and Scheduling + Core Object Specification (iCalendar)", RFC 5545, + September 2009. + + [RFC5546] Daboo, C., "iCalendar Transport-Independent + Interoperability Protocol (iTIP)", RFC 5546, + December 2009. + + [RFC5646] Phillips, A. and M. Davis, "Tags for Identifying + Languages", BCP 47, RFC 5646, September 2009. + + + + +Perreault Standards Track [Page 70] + +RFC 6350 vCard August 2011 + + + [RFC5870] Mayrhofer, A. and C. Spanring, "A Uniform Resource + Identifier for Geographic Locations ('geo' URI)", + RFC 5870, June 2010. + + [RFC6351] Perreault, S., "xCard: vCard XML Representation", + RFC 6351, August 2011. + + [W3C.REC-xml-20081126] + Maler, E., Yergeau, F., Sperberg-McQueen, C., Paoli, J., + and T. Bray, "Extensible Markup Language (XML) 1.0 (Fifth + Edition)", World Wide Web Consortium Recommendation REC- + xml-20081126, November 2008, + . + + [xfn] Celik, T., Mullenweg, M., and E. Meyer, "XFN 1.1 profile", + . + +12.2. Informative References + + [IANA-TZ] Lear, E. and P. Eggert, "IANA Procedures for Maintaining + the Timezone Database", Work in Progress, May 2011. + + [ISO9070] International Organization for Standardization, + "Information Processing - SGML support facilities - + Registration Procedures for Public Text Owner + Identifiers", ISO 9070, April 1991. + + [RFC1738] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform + Resource Locators (URL)", RFC 1738, December 1994. + + [RFC2397] Masinter, L., "The "data" URL scheme", RFC 2397, + August 1998. + + [RFC2425] Howes, T., Smith, M., and F. Dawson, "A MIME Content-Type + for Directory Information", RFC 2425, September 1998. + + [RFC2426] Dawson, F. and T. Howes, "vCard MIME Directory Profile", + RFC 2426, September 1998. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC3282] Alvestrand, H., "Content Language Headers", RFC 3282, + May 2002. + + + + + + +Perreault Standards Track [Page 71] + +RFC 6350 vCard August 2011 + + + [RFC3406] Daigle, L., van Gulik, D., Iannella, R., and P. Faltstrom, + "Uniform Resource Names (URN) Namespace Definition + Mechanisms", BCP 66, RFC 3406, October 2002. + + [RFC3536] Hoffman, P., "Terminology Used in Internationalization in + the IETF", RFC 3536, May 2003. + + [RFC4770] Jennings, C. and J. Reschke, Ed., "vCard Extensions for + Instant Messaging (IM)", RFC 4770, January 2007. + + [RFC4880] Callas, J., Donnerhacke, L., Finney, H., Shaw, D., and R. + Thayer, "OpenPGP Message Format", RFC 4880, November 2007. + + [RFC5335bis] + Yang, A. and S. Steele, "Internationalized Email Headers", + Work in Progress, July 2011. + + [RFC5751] Ramsdell, B. and S. Turner, "Secure/Multipurpose Internet + Mail Extensions (S/MIME) Version 3.2 Message + Specification", RFC 5751, January 2010. + + [RFC6068] Duerst, M., Masinter, L., and J. Zawinski, "The 'mailto' + URI Scheme", RFC 6068, October 2010. + + [TZ-DB] Olson, A., "Time zone code and data", + . + + [vCard21] Internet Mail Consortium, "vCard - The Electronic Business + Card Version 2.1", September 1996. + + + + + + + + + + + + + + + + + + + + + + +Perreault Standards Track [Page 72] + +RFC 6350 vCard August 2011 + + +Appendix A. Differences from RFCs 2425 and 2426 + + This appendix contains a high-level overview of the major changes + that have been made in the vCard specification from RFCs 2425 and + 2426. It is incomplete, as it only lists the most important changes. + +A.1. New Structure + + o [RFC2425] and [RFC2426] have been merged. + + o vCard is now not only a MIME type but a stand-alone format. + + o A proper MIME type registration form has been included. + + o UTF-8 is now the only possible character set. + + o New vCard elements can be registered from IANA. + +A.2. Removed Features + + o The CONTEXT and CHARSET parameters are no more. + + o The NAME, MAILER, LABEL, and CLASS properties are no more. + + o The "intl", "dom", "postal", and "parcel" TYPE parameter values + for the ADR property have been removed. + + o In-line vCards (such as the value of the AGENT property) are no + longer supported. + +A.3. New Properties and Parameters + + o The KIND, GENDER, LANG, ANNIVERSARY, XML, and CLIENTPIDMAP + properties have been added. + + o [RFC2739], which defines the FBURL, CALADRURI, CAPURI, and CALURI + properties, has been merged in. + + o [RFC4770], which defines the IMPP property, has been merged in. + + o The "work" and "home" TYPE parameter values are now applicable to + many more properties. + + o The "pref" value of the TYPE parameter is now a parameter of its + own, with a positive integer value indicating the level of + preference. + + o The ALTID and PID parameters have been added. + + + +Perreault Standards Track [Page 73] + +RFC 6350 vCard August 2011 + + + o The MEDIATYPE parameter has been added and replaces the TYPE + parameter when it was used for indicating the media type of the + property's content. + +Author's Address + + Simon Perreault + Viagenie + 2875 Laurier, suite D2-630 + Quebec, QC G1V 2M2 + Canada + + Phone: +1 418 656 9254 + EMail: simon.perreault@viagenie.ca + URI: http://www.viagenie.ca + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Perreault Standards Track [Page 74] + diff --git a/dav/SabreDAV/docs/rfc6351.txt b/dav/SabreDAV/docs/rfc6351.txt new file mode 100644 index 000000000..5ae0fa3f2 --- /dev/null +++ b/dav/SabreDAV/docs/rfc6351.txt @@ -0,0 +1,1235 @@ + + + + + + +Internet Engineering Task Force (IETF) S. Perreault +Request for Comments: 6351 Viagenie +Category: Standards Track August 2011 +ISSN: 2070-1721 + + + xCard: vCard XML Representation + +Abstract + + This document defines the XML schema of the vCard data format. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6351. + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + + + + + + + +Perreault Standards Track [Page 1] + +RFC 6351 xCard August 2011 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 3. The Schema . . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 4. Example: Author's XML vCard . . . . . . . . . . . . . . . . . 3 + 5. Design Considerations . . . . . . . . . . . . . . . . . . . . 4 + 5.1. Extensibility . . . . . . . . . . . . . . . . . . . . . . 6 + 5.2. Limitations . . . . . . . . . . . . . . . . . . . . . . . 7 + 6. Format Conversions . . . . . . . . . . . . . . . . . . . . . . 8 + 7. Security Considerations . . . . . . . . . . . . . . . . . . . 10 + 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 11 + 8.1. Registration of the XML Namespace . . . . . . . . . . . . 11 + 8.2. Media Type . . . . . . . . . . . . . . . . . . . . . . . . 11 + 9. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 12 + 10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 10.1. Normative References . . . . . . . . . . . . . . . . . . . 12 + 10.2. Informative References . . . . . . . . . . . . . . . . . . 13 + Appendix A. Relax NG Schema . . . . . . . . . . . . . . . . . . . 14 + +1. Introduction + + vCard [RFC6350] is a data format for representing and exchanging + information about individuals and other entities. It is a text-based + format (as opposed to a binary format). This document defines xCard, + an XML [W3C.REC-xml-20081126] representation for vCard. The + underlying data structure is exactly the same, enabling a 1-to-1 + mapping between the original vCard format and the XML representation. + The XML formatting may be preferred in some contexts where an XML + engine is readily available and may be reused instead of writing a + standalone vCard parser. + + Earlier work on an XML format for vCard was started in 1998 by Frank + Dawson [VCARD-DTD]. Sadly, it did not take over the world. + +2. Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + +3. The Schema + + The schema is expressed in the RELAX NG language [ISO.19757-2.2008] + and is found in Appendix A. + + + + + + +Perreault Standards Track [Page 2] + +RFC 6351 xCard August 2011 + + +4. Example: Author's XML vCard + + + + + Simon Perreault + + Perreault + Simon + + + ing. jr + M.Sc. + + --0203 + + 20090808T1430-0500 + + M + + 1 + fr + + + 2 + en + + + work + Viagenie + + + + work + + + + + 2875 boul. Laurier, suite D2-630 + Quebec + QC + G1V 2M2 + Canada + + + + + +Perreault Standards Track [Page 3] + +RFC 6351 xCard August 2011 + + + + + work + voice + + + tel:+1-418-656-9254;ext=102 + + + + + work + text + voice + cell + video + + + tel:+1-418-262-6501 + + + work + simon.perreault@viagenie.ca + + + work + geo:46.766336,-71.28955 + + + work + http://www.viagenie.ca/simon.perreault/simon.asc + + America/Montreal + + home + http://nomis80.org + + + + +5. Design Considerations + + The general idea is to map vCard parameters, properties, and value + types to XML elements. For example, the "FN" property is mapped to + the "fn" element. In turn, that element contains a text element + whose content corresponds to the vCard property's value. + + + + + +Perreault Standards Track [Page 4] + +RFC 6351 xCard August 2011 + + + vCard parameters are also mapped to XML elements. They are contained + in the element, which is contained in property elements. + For example, the "TYPE" parameter applied to the "TEL" property would + look like the following in XML: + + + + + voice + video + + + tel:+1-555-555-555 + + + Parameters taking a list of values are simply repeated multiple + times, once for each value in the list. + + Properties having structured values (e.g., the "N" property) are + expressed by XML element trees. Element names in that tree (e.g., + "surname", "given", etc.) do not have a vCard equivalent since they + are identified by position in plain vCard. + + Line folding is a non-issue in XML. Therefore, the mapping from + vCard to XML is done after the unfolding procedure is carried out. + Conversely, the mapping from XML to vCard is done before the folding + procedure is carried out. + + A top-level element is used as root. It contains one or + more elements, each representing a complete vCard. The + element MUST be present even when only a single vCard is + present in an XML document. + + The group construct (Section 3.2 in [RFC6350]) is represented with + the element. The "name" attribute contains the group's name. + For example: + + + + + + + + + + + + + + + +Perreault Standards Track [Page 5] + +RFC 6351 xCard August 2011 + + + + + + ... + ... + + + ... + + ... + + + + is equivalent to: + + BEGIN:VCARD + VERSION:4.0 + contact.FN=... + contact.EMAIL=... + media.PHOTO=... + CATEGORIES=... + END:VCARD + +5.1. Extensibility + + The original vCard format is extensible. New properties, parameters, + data types and values (collectively known as vCard elements, not to + be confused with XML elements) can be registered with IANA (see + [RFC6350], Section 10.2). It is expected that these vCard extensions + will also specify extensions to the XML format described in this + document. + + New XML vCard property and parameter element names MUST be lower- + case. This is necessary to ensure that round-tripping between XML + and plain-text vCard works correctly. + + Unregistered extensions (i.e., those starting with "X-" and + "VND-...-") are expressed in XML by using elements starting with "x-" + and "vnd-...-". Usage of XML namespaces [W3C.REC-xml-names-20091208] + for extensibility is RECOMMENDED for extensions that have no + equivalent in plain-text vCard. Refer to Section 6 for the + implications when converting between plain-text vCard and XML. + + + + + + + + + +Perreault Standards Track [Page 6] + +RFC 6351 xCard August 2011 + + + Examples: + + + + 1 + + value goes here + + + + + 1 + + value goes here + + + Note that extension elements do not need the "X-" or "VND-" prefix in + XML. The XML namespace mechanism is sufficient. + + A vCard XML parser MUST ignore XML elements and attributes for which + it doesn't recognize the expanded name. The normal behavior of + ignoring XML processing instructions whose target is not recognized + MUST also be followed. + + In the original vCard format, the "VERSION" property was mandatory + and played a role in extensibility. In XML, this property is absent. + Its role is played by the vCard core namespace identifier, which + includes the version number. vCard revisions will use a different + namespace. + + Parameters containing a list of values are expressed using a list of + elements in XML (e.g., the element). + +5.2. Limitations + + The schema does not validate the cardinality of properties. This is + a limitation of the schema definition language. Cardinalities of the + original vCard format [RFC6350] MUST still be respected. + + Some constructs (e.g., value enumerations in type parameters) have + additional ordering constraints in XML. This is a result of + limitations of the schema definition language, and the order is + arbitrary. The order MUST be respected in XML for the vCard to be + valid. However, reordering as part of conversion to or from plain + vCard MAY happen. + + + + + +Perreault Standards Track [Page 7] + +RFC 6351 xCard August 2011 + + +6. Format Conversions + + When new properties or "X-" properties are used, a vCard<->xCard + converter might not recognize them or know what the appropriate + default value types are, yet they need to be able to preserve the + values. A similar issue arises for unrecognized property parameters. + As a result, the following rules are applied when dealing with + unrecognized properties and property parameters: + + o When converting from vCard to xCard: + + * Any property that does not include a "VALUE" parameter and + whose default value type is not known MUST be converted using + the value type XML element . The content of that + element is the unprocessed value text. + + * Any unrecognized property parameter MUST be converted using the + value type XML element , with its content set to the + parameter value text, treated as if it were a text value, or + list of text values. + + * The content of "XML" properties is copied as is to XML. + + * Property and parameter XML element names are converted to + lower-case. + + * Property value escaping is undone. For example, "\n" becomes a + NEWLINE character (ASCII decimal 10). + + * Double-quoting of parameter values, as well as backslash + escaping in parameter values, is undone. For example, + PARAM="\"foo\",\"bar\"" becomes "foo","bar". + + o When converting xCard to vCard: + + * Properties in the vCard 4 namespace: + + + If the converter knows of a specific plain-text + representation for this property, it uses it. For example, + the element corresponds to the "ADR" property, which + is encoded using comma-separated lists separated by + semicolons. + + + Otherwise, the property name is taken from the element name, + property parameters are taken from the element, + and the content of the property is taken from the content of + the value element. If the property element has attributes + or contains other XML elements, they are dropped. + + + +Perreault Standards Track [Page 8] + +RFC 6351 xCard August 2011 + + + + If a standard property's XML element contains XML elements + and attributes for which the converter doesn't recognize the + expanded name, they are dropped. Therefore, it is + RECOMMENDED to limit extensions to the property level to + ensure that all data is preserved intact in round-trip + conversions. + + * Properties in other namespaces are wrapped as is inside an + "XML" property. + + * Any property value XML elements are converted + directly into vCard values. The containing property MUST NOT + have a "VALUE" parameter. + + * Any parameter value XML elements are converted as if + they were value type XML elements. + + * Property and parameter names are converted to upper-case. + + * Property value escaping (Section 3.3 of [RFC6350]) is carried + out. For example, a NEWLINE character (ASCII decimal 10) + becomes "\n". + + * Double-quoting of parameter values, as well as backslash + escaping in parameter values, is carried out. For example, + "foo","bar" becomes PARAM="\"foo\",\"bar\"". + + + + + + + + + + + + + + + + + + + + + + + + + +Perreault Standards Track [Page 9] + +RFC 6351 xCard August 2011 + + + For example, these two vCards are equivalent: + + + + + J. Doe + + Doe + J. + + + + + + + image/jpeg + + alien.jpg + + My web page! + + + + + BEGIN:VCARD + VERSION:4.0 + FN:J. Doe + N:Doe;J.;; + X-FILE;MEDIATYPE=image/jpeg:alien.jpg + XML:My web page! + END:VCARD + +7. Security Considerations + + All the security considerations applicable to plain vCard [RFC6350] + are applicable to this document as well. + + XML Signature [W3C.CR-xmldsig-core1-20110303] and XML Encryption + [W3C.CR-xmlenc-core1-20110303] can be used with xCard to provide + authentication and confidentiality. + + + + + + + + + +Perreault Standards Track [Page 10] + +RFC 6351 xCard August 2011 + + +8. IANA Considerations + +8.1. Registration of the XML Namespace + + URI: urn:ietf:params:xml:ns:vcard-4.0 + + Registrant Contact: The IESG + + XML: None. Namespace URIs do not represent an XML specification. + +8.2. Media Type + + This section defines the MIME media type [RFC4288] for use with + vCard-in-XML data. + + To: ietf-types@iana.org + + Subject: Registration of media type application/vcard+xml + + Type name: application + + Subtype name: vcard+xml + + Required parameters: none + + Optional parameters: charset as defined for application/xml in + [RFC3023]; per [RFC3023], use of the charset parameter with the + value "utf-8" is "STRONGLY RECOMMENDED". + + Encoding considerations: Same as encoding considerations of + application/xml as specified in [RFC3023]. + + Security considerations: This media type has all of the security + considerations described in [RFC3023], plus those listed in + Section 7. + + Interoperability considerations: This media type provides an + alternative syntax to vCard data [RFC6350] based on XML. + + Published specification: This specification. + + Applications that use this media type: Applications that currently + make use of the text/vcard media type can use this as an + alternative. In general, applications that maintain or process + contact information can use this media type. + + + + + + +Perreault Standards Track [Page 11] + +RFC 6351 xCard August 2011 + + + Additional information: + + Magic number(s): none + + File extension(s): XML data should use ".xml" as the file + extension. + + Macintosh file type code(s): none + + Person & email address to contact for further information: Simon + Perreault + + Intended usage: COMMON + + Restrictions on usage: none + + Author: Simon Perreault + + Change controller: IETF + +9. Acknowledgments + + Thanks to the following people for their input: + + Alexey Melnikov, Barry Leiba, Bjorn Hoehrmann, Cyrus Daboo, Joe + Hildebrand, Joseph Smarr, Marc Blanchet, Mike Douglass, Peter Saint- + Andre, Robins George, Zahhar Kirillov, Zoltan Ordogh. + + +10. References + +10.1. Normative References + + [ISO.19757-2.2008] + International Organization for Standardization, + "Information technology -- Document Schema Definition + Language (DSDL) -- Part 2: Regular-grammar-based + validation -- RELAX NG", ISO International + Standard 19757-2, October 2008. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC3023] Murata, M., St. Laurent, S., and D. Kohn, "XML Media + Types", RFC 3023, January 2001. + + [RFC6350] Perreault, S., "vCard Format Specification", RFC 6350, + August 2011. + + + +Perreault Standards Track [Page 12] + +RFC 6351 xCard August 2011 + + + [W3C.REC-xml-20081126] + Paoli, J., Yergeau, F., Maler, E., Bray, T., and C. + Sperberg-McQueen, "Extensible Markup Language (XML) 1.0 + (Fifth Edition)", World Wide Web Consortium + Recommendation REC-xml-20081126, November 2008, + . + + [W3C.REC-xml-names-20091208] + Bray, T., Hollander, D., Layman, A., Tobin, R., and H. + Thompson, "Namespaces in XML 1.0 (Third Edition)", World + Wide Web Consortium Recommendation REC-xml-names-20091208, + December 2009, + . + +10.2. Informative References + + [RFC4288] Freed, N. and J. Klensin, "Media Type Specifications and + Registration Procedures", BCP 13, RFC 4288, December 2005. + + [VCARD-DTD] + Dawson, F., "The vCard v3.0 XML DTD", Work in Progress, + June 1998. + + [W3C.CR-xmldsig-core1-20110303] + Roessler, T., Solo, D., Yiu, K., Reagle, J., Hirsch, F., + Eastlake, D., and M. Nystroem, "XML Signature Syntax and + Processing Version 1.1", World Wide Web Consortium CR CR- + xmldsig-core1-20110303, March 2011, + . + + [W3C.CR-xmlenc-core1-20110303] + Eastlake, D., Reagle, J., Roessler, T., and F. Hirsch, + "XML Encryption Syntax and Processing Version 1.1", World + Wide Web Consortium CR CR-xmlenc-core1-20110303, + March 2011, + . + + + + + + + + + + + + + + + +Perreault Standards Track [Page 13] + +RFC 6351 xCard August 2011 + + +Appendix A. Relax NG Schema + +default namespace = "urn:ietf:params:xml:ns:vcard-4.0" + +### Section 3.3: vCard Format Specification +# +# 3.3 +iana-token = xsd:string { pattern = "[a-zA-Z0-9-]+" } +x-name = xsd:string { pattern = "x-[a-zA-Z0-9-]+" } + +### Section 4: Value types +# +# 4.1 +value-text = element text { text } +value-text-list = value-text+ + +# 4.2 +value-uri = element uri { xsd:anyURI } + +# 4.3.1 +value-date = element date { + xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" } + } + +# 4.3.2 +value-time = element time { + xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d?)|--\d\d)" + ~ "(Z|[+\-]\d\d(\d\d)?)?" } + } + +# 4.3.3 +value-date-time = element date-time { + xsd:string { pattern = "(\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?" + ~ "(Z|[+\-]\d\d(\d\d)?)?" } + } + +# 4.3.4 +value-date-and-or-time = value-date | value-date-time | value-time + +# 4.3.5 +value-timestamp = element timestamp { + xsd:string { pattern = "\d{8}T\d{6}(Z|[+\-]\d\d(\d\d)?)?" } + } + +# 4.4 +value-boolean = element boolean { xsd:boolean } + + + + + +Perreault Standards Track [Page 14] + +RFC 6351 xCard August 2011 + + +# 4.5 +value-integer = element integer { xsd:integer } + +# 4.6 +value-float = element float { xsd:float } + +# 4.7 +value-utc-offset = element utc-offset { + xsd:string { pattern = "[+\-]\d\d(\d\d)?" } + } + +# 4.8 +value-language-tag = element language-tag { + xsd:string { pattern = "([a-z]{2,3}((-[a-z]{3}){0,3})?|[a-z]{4,8})" + ~ "(-[a-z]{4})?(-([a-z]{2}|\d{3}))?" + ~ "(-([0-9a-z]{5,8}|\d[0-9a-z]{3}))*" + ~ "(-[0-9a-wyz](-[0-9a-z]{2,8})+)*" + ~ "(-x(-[0-9a-z]{1,8})+)?|x(-[0-9a-z]{1,8})+|" + ~ "[a-z]{1,3}(-[0-9a-z]{2,8}){1,2}" } + } + +### Section 5: Parameters +# +# 5.1 +param-language = element language { value-language-tag }? + +# 5.2 +param-pref = element pref { + element integer { + xsd:integer { minInclusive = "1" maxInclusive = "100" } + } + }? + +# 5.4 +param-altid = element altid { value-text }? + +# 5.5 +param-pid = element pid { + element text { xsd:string { pattern = "\d+(\.\d+)?" } }+ + }? + +# 5.6 +param-type = element type { element text { "work" | "home" }+ }? + +# 5.7 +param-mediatype = element mediatype { value-text }? + + + + + +Perreault Standards Track [Page 15] + +RFC 6351 xCard August 2011 + + +# 5.8 +param-calscale = element calscale { element text { "gregorian" } }? + +# 5.9 +param-sort-as = element sort-as { value-text+ }? + +# 5.10 +param-geo = element geo { value-uri }? + +# 5.11 +param-tz = element tz { value-text | value-uri }? + +### Section 6: Properties +# +# 6.1.3 +property-source = element source { + element parameters { param-altid, param-pid, param-pref, + param-mediatype }, + value-uri + } + +# 6.1.4 +property-kind = element kind { + element text { "individual" | "group" | "org" | "location" | + x-name | iana-token }* + } + +# 6.2.1 +property-fn = element fn { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.2.2 +property-n = element n { + element parameters { param-language, param-sort-as, param-altid }?, + element surname { text }+, + element given { text }+, + element additional { text }+, + element prefix { text }+, + element suffix { text }+ + } + + + + + + + + +Perreault Standards Track [Page 16] + +RFC 6351 xCard August 2011 + + +# 6.2.3 +property-nickname = element nickname { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text-list + } + +# 6.2.4 +property-photo = element photo { + element parameters { param-altid, param-pid, param-pref, param-type, + param-mediatype }?, + value-uri + } + +# 6.2.5 +property-bday = element bday { + element parameters { param-altid, param-calscale }?, + (value-date-and-or-time | value-text) + } + +# 6.2.6 +property-anniversary = element anniversary { + element parameters { param-altid, param-calscale }?, + (value-date-and-or-time | value-text) + } + +# 6.2.7 +property-gender = element gender { + element sex { "" | "M" | "F" | "O" | "N" | "U" }, + element identity { text }? + } + +# 6.3.1 +param-label = element label { value-text }? +property-adr = element adr { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-geo, param-tz, + param-label }?, + element pobox { text }+, + element ext { text }+, + element street { text }+, + element locality { text }+, + element region { text }+, + element code { text }+, + element country { text }+ + } + + + + + +Perreault Standards Track [Page 17] + +RFC 6351 xCard August 2011 + + +# 6.4.1 +property-tel = element tel { + element parameters { + param-altid, + param-pid, + param-pref, + element type { + element text { "work" | "home" | "text" | "voice" + | "fax" | "cell" | "video" | "pager" + | "textphone" }+ + }?, + param-mediatype + }?, + (value-text | value-uri) + } + +# 6.4.2 +property-email = element email { + element parameters { param-altid, param-pid, param-pref, + param-type }?, + value-text + } + +# 6.4.3 +property-impp = element impp { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.4.4 +property-lang = element lang { + element parameters { param-altid, param-pid, param-pref, + param-type }?, + value-language-tag + } + +# 6.5.1 +property-tz = element tz { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + (value-text | value-uri | value-utc-offset) + } + + + + + + + + +Perreault Standards Track [Page 18] + +RFC 6351 xCard August 2011 + + +# 6.5.2 +property-geo = element geo { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.6.1 +property-title = element title { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.6.2 +property-role = element role { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.6.3 +property-logo = element logo { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-mediatype }?, + value-uri + } + +# 6.6.4 +property-org = element org { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-sort-as }?, + value-text-list + } + +# 6.6.5 +property-member = element member { + element parameters { param-altid, param-pid, param-pref, + param-mediatype }?, + value-uri + } + + + + + + + + + + +Perreault Standards Track [Page 19] + +RFC 6351 xCard August 2011 + + +# 6.6.6 +property-related = element related { + element parameters { + param-altid, + param-pid, + param-pref, + element type { + element text { + "work" | "home" | "contact" | "acquaintance" | + "friend" | "met" | "co-worker" | "colleague" | "co-resident" | + "neighbor" | "child" | "parent" | "sibling" | "spouse" | + "kin" | "muse" | "crush" | "date" | "sweetheart" | "me" | + "agent" | "emergency" + }+ + }?, + param-mediatype + }?, + (value-uri | value-text) + } + +# 6.7.1 +property-categories = element categories { + element parameters { param-altid, param-pid, param-pref, + param-type }?, + value-text-list + } + +# 6.7.2 +property-note = element note { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.7.3 +property-prodid = element prodid { value-text } + +# 6.7.4 +property-rev = element rev { value-timestamp } + +# 6.7.5 +property-sound = element sound { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-mediatype }?, + value-uri + } + + + + + +Perreault Standards Track [Page 20] + +RFC 6351 xCard August 2011 + + +# 6.7.6 +property-uid = element uid { value-uri } + +# 6.7.7 +property-clientpidmap = element clientpidmap { + element sourceid { xsd:positiveInteger }, + value-uri + } + +# 6.7.8 +property-url = element url { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.8.1 +property-key = element key { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + (value-uri | value-text) + } + +# 6.9.1 +property-fburl = element fburl { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.9.2 +property-caladruri = element caladruri { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.9.3 +property-caluri = element caluri { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + + + + + + + + +Perreault Standards Track [Page 21] + +RFC 6351 xCard August 2011 + + +# Top-level grammar +property = property-adr | property-anniversary | property-bday + | property-caladruri | property-caluri | property-categories + | property-clientpidmap | property-email | property-fburl + | property-fn | property-geo | property-impp | property-key + | property-kind | property-lang | property-logo + | property-member | property-n | property-nickname + | property-note | property-org | property-photo + | property-prodid | property-related | property-rev + | property-role | property-gender | property-sound + | property-source | property-tel | property-title + | property-tz | property-uid | property-url +start = element vcards { + element vcard { + (property + | element group { + attribute name { text }, + property* + })+ + }+ + } + +Author's Address + + Simon Perreault + Viagenie + 2600 boul. Laurier, Suite 625 + Quebec, QC G1V 4W1 + Canada + + Phone: +1 418 656 9254 + EMail: simon.perreault@viagenie.ca + URI: http://www.viagenie.ca + + + + + + + + + + + + + + + + + + +Perreault Standards Track [Page 22] + diff --git a/dav/SabreDAV/docs/rfc6352.txt b/dav/SabreDAV/docs/rfc6352.txt new file mode 100644 index 000000000..cb03747b4 --- /dev/null +++ b/dav/SabreDAV/docs/rfc6352.txt @@ -0,0 +1,2691 @@ + + + + + + +Internet Engineering Task Force (IETF) C. Daboo +Request for Comments: 6352 Apple +Category: Standards Track August 2011 +ISSN: 2070-1721 + + + CardDAV: vCard Extensions to + Web Distributed Authoring and Versioning (WebDAV) + +Abstract + + This document defines extensions to the Web Distributed Authoring and + Versioning (WebDAV) protocol to specify a standard way of accessing, + managing, and sharing contact information based on the vCard format. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6352. + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + + + +Daboo Standards Track [Page 1] + +RFC 6352 CardDAV August 2011 + + + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction and Overview . . . . . . . . . . . . . . . . . . 4 + 2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 3. Requirements Overview . . . . . . . . . . . . . . . . . . . . 6 + 4. Address Book Data Model . . . . . . . . . . . . . . . . . . . 7 + 4.1. Address Book Server . . . . . . . . . . . . . . . . . . . 7 + 5. Address Book Resources . . . . . . . . . . . . . . . . . . . . 7 + 5.1. Address Object Resources . . . . . . . . . . . . . . . . . 7 + 5.1.1. Data Type Conversion . . . . . . . . . . . . . . . . . 8 + 5.1.1.1. Additional Precondition for GET . . . . . . . . . 8 + 5.2. Address Book Collections . . . . . . . . . . . . . . . . . 9 + 6. Address Book Feature . . . . . . . . . . . . . . . . . . . . . 10 + 6.1. Address Book Support . . . . . . . . . . . . . . . . . . . 10 + 6.1.1. Example: Using OPTIONS for the Discovery of + Support for CardDAV . . . . . . . . . . . . . . . . . 10 + 6.2. Address Book Properties . . . . . . . . . . . . . . . . . 10 + 6.2.1. CARDDAV:addressbook-description Property . . . . . . . 10 + 6.2.2. CARDDAV:supported-address-data Property . . . . . . . 11 + 6.2.3. CARDDAV:max-resource-size Property . . . . . . . . . . 12 + 6.3. Creating Resources . . . . . . . . . . . . . . . . . . . . 13 + 6.3.1. Extended MKCOL Method . . . . . . . . . . . . . . . . 13 + 6.3.1.1. Example - Successful MKCOL Request . . . . . . . . 14 + 6.3.2. Creating Address Object Resources . . . . . . . . . . 15 + 6.3.2.1. Additional Preconditions for PUT, COPY, and + MOVE . . . . . . . . . . . . . . . . . . . . . . . 16 + 6.3.2.2. Non-Standard vCard Properties and Parameters . . . 17 + 6.3.2.3. Address Object Resource Entity Tag . . . . . . . . 18 + 7. Address Book Access Control . . . . . . . . . . . . . . . . . 18 + 7.1. Additional Principal Properties . . . . . . . . . . . . . 18 + 7.1.1. CARDDAV:addressbook-home-set Property . . . . . . . . 19 + 7.1.2. CARDDAV:principal-address Property . . . . . . . . . . 19 + 8. Address Book Reports . . . . . . . . . . . . . . . . . . . . . 20 + 8.1. REPORT Method . . . . . . . . . . . . . . . . . . . . . . 20 + 8.2. Ordinary Collections . . . . . . . . . . . . . . . . . . . 21 + 8.3. Searching Text: Collations . . . . . . . . . . . . . . . . 21 + 8.3.1. CARDDAV:supported-collation-set Property . . . . . . . 22 + 8.4. Partial Retrieval . . . . . . . . . . . . . . . . . . . . 23 + 8.5. Non-Standard Properties and Parameters . . . . . . . . . . 23 + + + + +Daboo Standards Track [Page 2] + +RFC 6352 CardDAV August 2011 + + + 8.6. CARDDAV:addressbook-query Report . . . . . . . . . . . . . 23 + 8.6.1. Limiting Results . . . . . . . . . . . . . . . . . . . 25 + 8.6.2. Truncation of Results . . . . . . . . . . . . . . . . 25 + 8.6.3. Example: Partial Retrieval of vCards Matching + NICKNAME . . . . . . . . . . . . . . . . . . . . . . . 26 + 8.6.4. Example: Partial Retrieval of vCards Matching a + Full Name or Email Address . . . . . . . . . . . . . . 27 + 8.6.5. Example: Truncated Results . . . . . . . . . . . . . . 29 + 8.7. CARDDAV:addressbook-multiget Report . . . . . . . . . . . 31 + 8.7.1. Example: CARDDAV:addressbook-multiget Report . . . . . 32 + 8.7.2. Example: CARDDAV:addressbook-multiget Report . . . . . 33 + 9. Client Guidelines . . . . . . . . . . . . . . . . . . . . . . 34 + 9.1. Restrict the Properties Returned . . . . . . . . . . . . . 34 + 9.2. Avoiding Lost Updates . . . . . . . . . . . . . . . . . . 35 + 9.3. Client Configuration . . . . . . . . . . . . . . . . . . . 35 + 9.4. Finding Other Users' Address Books . . . . . . . . . . . . 35 + 10. XML Element Definitions . . . . . . . . . . . . . . . . . . . 36 + 10.1. CARDDAV:addressbook XML Element . . . . . . . . . . . . . 36 + 10.2. CARDDAV:supported-collation XML Element . . . . . . . . . 36 + 10.3. CARDDAV:addressbook-query XML Element . . . . . . . . . . 37 + 10.4. CARDDAV:address-data XML Element . . . . . . . . . . . . . 37 + 10.4.1. CARDDAV:allprop XML Element . . . . . . . . . . . . . 39 + 10.4.2. CARDDAV:prop XML Element . . . . . . . . . . . . . . . 39 + 10.5. CARDDAV:filter XML Element . . . . . . . . . . . . . . . . 40 + 10.5.1. CARDDAV:prop-filter XML Element . . . . . . . . . . . 40 + 10.5.2. CARDDAV:param-filter XML Element . . . . . . . . . . . 41 + 10.5.3. CARDDAV:is-not-defined XML Element . . . . . . . . . . 42 + 10.5.4. CARDDAV:text-match XML Element . . . . . . . . . . . . 42 + 10.6. CARDDAV:limit XML Element . . . . . . . . . . . . . . . . 43 + 10.6.1. CARDDAV:nresults XML Element . . . . . . . . . . . . . 44 + 10.7. CARDDAV:addressbook-multiget XML Element . . . . . . . . . 44 + 11. Service Discovery via SRV Records . . . . . . . . . . . . . . 45 + 12. Internationalization Considerations . . . . . . . . . . . . . 45 + 13. Security Considerations . . . . . . . . . . . . . . . . . . . 45 + 14. IANA Consideration . . . . . . . . . . . . . . . . . . . . . . 46 + 14.1. Namespace Registration . . . . . . . . . . . . . . . . . . 46 + 15. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 46 + 16. References . . . . . . . . . . . . . . . . . . . . . . . . . . 47 + 16.1. Normative References . . . . . . . . . . . . . . . . . . . 47 + 16.2. Informative References . . . . . . . . . . . . . . . . . . 48 + + + + + + + + + + + +Daboo Standards Track [Page 3] + +RFC 6352 CardDAV August 2011 + + +1. Introduction and Overview + + Address books containing contact information are a key component of + personal information management tools, such as email, calendaring and + scheduling, and instant messaging clients. To date several protocols + have been used for remote access to contact data, including the + Lightweight Directory Access Protocol (LDAP) [RFC4510], Internet + Message Support Protocol [IMSP], and Application Configuration Access + Protocol (ACAP) [RFC2244], together with SyncML used for + synchronization of such data. + + WebDAV [RFC4918] offers a number of advantages as a framework or + basis for address book access and management. Most of these + advantages boil down to a significant reduction in the costs of + design, implementation, interoperability testing, and deployment. + + The key features of address book support with WebDAV are: + + 1. Ability to use multiple address books with hierarchical layout. + + 2. Ability to control access to individual address books and address + entries as per WebDAV Access Control List (ACL) [RFC3744]. + + 3. Principal collections can be used to enumerate and query other + users on the system as per WebDAV ACL [RFC3744]. + + 4. Server-side searching of address data, avoiding the need for + clients to download an entire address book in order to do a quick + address 'expansion' operation. + + 5. Well-defined internationalization support through WebDAV's use of + XML. + + 6. Use of vCards [RFC2426] for well-defined address schema to + enhance client interoperability. + + 7. Many limited clients (e.g., mobile devices) contain an HTTP stack + that makes implementing WebDAV much easier than other protocols. + + The key disadvantage of address book support in WebDAV is: + + 1. Lack of change notification. Many of the alternative protocols + also lack this ability. However, an extension for push + notifications could easily be developed. + + vCard is a MIME directory profile aimed at encapsulating personal + addressing and contact information about people. The specification + of vCard was originally done by the Versit consortium, with a + + + +Daboo Standards Track [Page 4] + +RFC 6352 CardDAV August 2011 + + + subsequent 3.0 version standardized by the IETF [RFC2426]. vCard is + in widespread use in email clients and mobile devices as a means of + encapsulating address information for transport via email or for + import/export and synchronization operations. + + An update to vCard -- vCard v4 -- is currently being developed + [RFC6350] and is compatible with this specification. + +2. Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The term "protected" is used in the Conformance field of property + definitions as defined in Section 15 of [RFC4918]. + + This document uses XML DTD fragments ([W3C.REC-xml-20081126], Section + 3.2) as a purely notational convention. WebDAV request and response + bodies cannot be validated by a DTD due to the specific extensibility + rules defined in Section 17 of [RFC4918] and due to the fact that all + XML elements defined by that specification use the XML namespace name + "DAV:". In particular: + + 1. Element names use the "DAV:" namespace. + + 2. Element ordering is irrelevant unless explicitly stated. + + 3. Extension elements (elements not already defined as valid child + elements) may be added anywhere, except when explicitly stated + otherwise. + + 4. Extension attributes (attributes not already defined as valid for + this element) may be added anywhere, except when explicitly + stated otherwise. + + The namespace "urn:ietf:params:xml:ns:carddav" is reserved for the + XML elements defined in this specification, its revisions, and + related CardDAV specifications. XML elements defined by individual + implementations MUST NOT use the "urn:ietf:params:xml:ns:carddav" + namespace, and instead should use a namespace that they control. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:carddav" are referenced in this document + outside of the context of an XML fragment, the strings "DAV:" and + "CARDDAV:" will be prefixed to the element types, respectively. + + + + + +Daboo Standards Track [Page 5] + +RFC 6352 CardDAV August 2011 + + + This document inherits, and sometimes extends, DTD productions from + Section 14 of [RFC4918]. + + Also, note that some CardDAV XML element names are identical to + WebDAV XML element names, though their namespace differs. Care must + be taken not to confuse the two sets of names. + +3. Requirements Overview + + This section lists what functionality is required of a CardDAV + server. To advertise support for CardDAV, a server: + + o MUST support vCard v3 [RFC2426] as a media type for the address + object resource format; + + o MUST support WebDAV Class 3 [RFC4918]; + + o MUST support WebDAV ACL [RFC3744]; + + o MUST support secure transport as defined in [RFC2818] using + Transport Layer Security (TLS) [RFC5246] and using the certificate + validation procedures described in [RFC5280]; + + o MUST support ETags [RFC2616] with additional requirements + specified in Section 6.3.2.3 of this document; + + o MUST support all address book reports defined in Section 8 of this + document; and + + o MUST advertise support on all address book collections and address + object resources for the address book reports in the + DAV:supported-report-set property, as defined in Versioning + Extensions to WebDAV [RFC3253]. + + In addition, a server: + + o SHOULD support vCard v4 [RFC6350] as a media type for the address + object resource format; + + o SHOULD support the extended MKCOL method [RFC5689] to create + address book collections as defined in Section 6.3.1 of this + document. + + o SHOULD support the DAV:current-user-principal-URL property as + defined in [RFC5397] to give clients a fast way to locate user + principals. + + + + + +Daboo Standards Track [Page 6] + +RFC 6352 CardDAV August 2011 + + +4. Address Book Data Model + + As a brief overview, a CardDAV address book is modeled as a WebDAV + collection with a well-defined structure; each of these address book + collections contains a number of resources representing address + objects as their direct child resources. Each resource representing + an address object is called an "address object resource". Each + address object resource and each address book collection can be + individually locked and have individual WebDAV properties. + Requirements derived from this model are provided in Sections 5.1 and + 5.2. + +4.1. Address Book Server + + A CardDAV server is an address-aware engine combined with a WebDAV + server. The server may include address data in some parts of its URL + namespace and non-address data in other parts. + + A WebDAV server can advertise itself as a CardDAV server if it + supports the functionality defined in this specification at any point + within the root of its repository. That might mean that address data + is spread throughout the repository and mixed with non-address data + in nearby collections (e.g., address data may be found in /lisa/ + addressbook/ as well as in /bernard/addressbook/, and non-address + data in /lisa/calendars/). Or, it might mean that address data can + be found only in certain sections of the repository (e.g., + /addressbooks/user/). Address book features are only required in the + repository sections that are or contain address objects. So, a + repository confining address data to the /carddav/ collection would + only need to support the CardDAV required features within that + collection. + + The CardDAV server is the canonical location for address data and + state information. Clients may submit requests to change data or + download data. Clients may store address objects offline and attempt + to synchronize at a later time. Address data on the server can + change between the time of last synchronization and when attempting + an update, as address book collections may be shared and accessible + via multiple clients. Entity tags and locking help this work. + +5. Address Book Resources + +5.1. Address Object Resources + + This specification uses vCard as the default format for address or + contact information being stored on the server. However, this + specification does allow other formats for address data provided that + the server advertises support for those additional formats as + + + +Daboo Standards Track [Page 7] + +RFC 6352 CardDAV August 2011 + + + described below. The requirements in this section pertain to vCard + address data or formats that follow the semantics of vCard data. + + Address object resources contained in address book collections MUST + contain a single vCard component only. + + vCard components in an address book collection MUST have a UID + property value that MUST be unique in the scope of the address book + collection in which it is contained. + +5.1.1. Data Type Conversion + + Servers might support more than one primary media type for address + object resources, for example, vCard v3.0 and vCard v4.0. In such + cases, servers have to accept all media types that they advertise via + the CARDDAV:supported-address-data WebDAV property (see + Section 6.2.2). + + However, clients can use standard HTTP content negotiation behavior + (the Accept request header defined in Section 14.1 of [RFC2616]) to + request that an address object resource's data be returned in a + specific media type format. For example, a client merely capable of + handling vCard v3.0 would only want to have address object resources + returned in v3.0 format. + + Additionally, REPORT requests, defined later in this specification, + allow for the return of address object resource data within an XML + response body. Again, the client can use content negotiation to + request that data be returned in a specific media type by specifying + appropriate attributes on the CARDDAV:address-data XML element used + in the request body (see Section 10.4). + + In some cases, it might not be possible for a server to convert from + one media type to another. When that happens, the server MUST return + the CARDDAV:supported-address-data-conversion precondition (see + below) in the response body (when the failure to convert applies to + the entire response) or use that same precondition code in the + DAV:response XML element in the response for the targeted address + object resource when one of the REPORTs defined below is used. See + Section 8.7.2 for an example of this. + +5.1.1.1. Additional Precondition for GET + + This specification creates additional preconditions for the GET + method. + + + + + + +Daboo Standards Track [Page 8] + +RFC 6352 CardDAV August 2011 + + + The new precondition is: + + (CARDDAV:supported-address-data-conversion): The resource targeted + by the GET request can be converted to the media type specified in + the Accept request header included with the request. + +5.2. Address Book Collections + + Address book collections appear to clients as a WebDAV collection + resource, identified by a URL. An address book collection MUST + report the DAV:collection and CARDDAV:addressbook XML elements in the + value of the DAV:resourcetype property. The element type declaration + for CARDDAV:addressbook is: + + + + An address book collection can be created through provisioning (e.g., + automatically created when a user's account is provisioned), or it + can be created with the extended MKCOL method (see Section 6.3.1). + This can be used by a user to create additional address books (e.g., + "soccer team members") or for users to share an address book (e.g., + "sales team contacts"). However, note that this document doesn't + define what extra address book collections are for. Users must rely + on non-standard cues to find out what an address book collection is + for, or use the CARDDAV:addressbook-description property defined in + Section 6.2.1 to provide such a cue. + + The following restrictions are applied to the resources within an + address book collection: + + a. Address book collections MUST only contain address object + resources and collections that are not address book collections. + That is, the only "top-level" non-collection resources allowed in + an address book collection are address object resources. This + ensures that address book clients do not have to deal with non- + address data in an address book collection, though they do have + to distinguish between address object resources and collections + when using standard WebDAV techniques to examine the contents of + a collection. + + b. Collections contained in address book collections MUST NOT + contain address book collections at any depth. That is, + "nesting" of address book collections within other address book + collections at any depth is not allowed. This specification does + not define how collections contained in an address book + collection are used or how they relate to any address object + resources contained in the address book collection. + + + + +Daboo Standards Track [Page 9] + +RFC 6352 CardDAV August 2011 + + + Multiple address book collections MAY be children of the same + collection. + +6. Address Book Feature + +6.1. Address Book Support + + A server supporting the features described in this document MUST + include "addressbook" as a field in the DAV response header from an + OPTIONS request on any resource that supports any address book + properties, reports, or methods. A value of "addressbook" in the DAV + response header MUST indicate that the server supports all MUST level + requirements and REQUIRED features specified in this document. + +6.1.1. Example: Using OPTIONS for the Discovery of Support for CardDAV + + >> Request << + + OPTIONS /addressbooks/users/ HTTP/1.1 + Host: addressbook.example.com + + >> Response << + + HTTP/1.1 200 OK + Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE + Allow: MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL + DAV: 1, 2, 3, access-control, addressbook + DAV: extended-mkcol + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Length: 0 + + In this example, the OPTIONS response indicates that the server + supports CardDAV in this namespace; therefore, the '/addressbooks/ + users/' collection may be used as a parent for address book + collections as the extended MKCOL method is available and as a + possible target for REPORT requests for address book reports. + +6.2. Address Book Properties + +6.2.1. CARDDAV:addressbook-description Property + + Name: addressbook-description + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Provides a human-readable description of the address book + collection. + + + + +Daboo Standards Track [Page 10] + +RFC 6352 CardDAV August 2011 + + + Value: Any text. + + Protected: SHOULD NOT be protected so that users can specify a + description. + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: This property contains a description of the address + book collection that is suitable for presentation to a user. The + xml:lang attribute can be used to add a language tag for the value + of this property. + + Definition: + + + + + Example: + + Adresses de Oliver Daboo + +6.2.2. CARDDAV:supported-address-data Property + + Name: supported-address-data + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies what media types are allowed for address object + resources in an address book collection. + + Protected: MUST be protected as it indicates the level of support + provided by the server. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:supported-address-data property is used to + specify the media type supported for the address object resources + contained in a given address book collection (e.g., vCard version + + + +Daboo Standards Track [Page 11] + +RFC 6352 CardDAV August 2011 + + + 3.0). Any attempt by the client to store address object resources + with a media type not listed in this property MUST result in an + error, with the CARDDAV:supported-address-data precondition + (Section 6.3.2.1) being violated. In the absence of this + property, the server MUST only accept data with the media type + "text/vcard" and vCard version 3.0, and clients can assume that is + all the server will accept. + + Definition: + + + + + + + + + Example: + + + + + +6.2.3. CARDDAV:max-resource-size Property + + Name: max-resource-size + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Provides a numeric value indicating the maximum size in + octets of a resource that the server is willing to accept when an + address object resource is stored in an address book collection. + + Value: Any text representing a numeric value. + + Protected: MUST be protected as it indicates limits provided by the + server. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + + + + + +Daboo Standards Track [Page 12] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:max-resource-size is used to specify a + numeric value that represents the maximum size in octets that the + server is willing to accept when an address object resource is + stored in an address book collection. Any attempt to store an + address book object resource exceeding this size MUST result in an + error, with the CARDDAV:max-resource-size precondition + (Section 6.3.2.1) being violated. In the absence of this + property, the client can assume that the server will allow storing + a resource of any reasonable size. + + Definition: + + + + + Example: + + 102400 + +6.3. Creating Resources + + Address book collections and address object resources may be created + by either a CardDAV client or the CardDAV server. This specification + defines restrictions and a data model that both clients and servers + MUST adhere to when manipulating such address data. + +6.3.1. Extended MKCOL Method + + An HTTP request using the extended MKCOL method [RFC5689] can be used + to create a new address book collection resource. A server MAY + restrict address book collection creation to particular collections. + + To create an address book, the client sends an extended MKCOL request + to the server and in the body of the request sets the + DAV:resourcetype property to the resource type for an address book + collection as defined in Section 5.2. + + Support for creating address books on the server is only RECOMMENDED + and not REQUIRED because some address book stores only support one + address book per user (or principal), and those are typically pre- + created for each account. However, servers and clients are strongly + encouraged to support address book creation whenever possible to + allow users to create multiple address book collections to help + organize their data better. + + + + + + +Daboo Standards Track [Page 13] + +RFC 6352 CardDAV August 2011 + + + The DAV:displayname property can be used for a human-readable name of + the address book. Clients can either specify the value of the + DAV:displayname property in the request body of the extended MKCOL + request or, alternatively, issue a PROPPATCH request to change the + DAV:displayname property to the appropriate value immediately after + using the extended MKCOL request. When displaying address book + collections to users, clients SHOULD check the DAV:displayname + property and use that value as the name of the address book. In the + event that the DAV:displayname property is not set, the client MAY + use the last part of the address book collection URI as the name; + however, that path segment may be "opaque" and not represent any + meaningful human-readable text. + +6.3.1.1. Example - Successful MKCOL Request + + This example creates an address book collection called /home/lisa/ + addressbook/ on the server addressbook.example.com with specific + values for the properties DAV:resourcetype, DAV:displayname, and + CARDDAV:addressbook-description. + + >> Request << + + MKCOL /home/lisa/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + + + + + + Lisa's Contacts + My primary address book. + + + + + + + + + + + + +Daboo Standards Track [Page 14] + +RFC 6352 CardDAV August 2011 + + + >> Response << + + HTTP/1.1 201 Created + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + HTTP/1.1 200 OK + + + +6.3.2. Creating Address Object Resources + + Clients populate address book collections with address object + resources. The URL for each address object resource is entirely + arbitrary and does not need to bear a specific relationship (but + might) to the address object resource's vCard properties or other + metadata. New address object resources MUST be created with a PUT + request targeted at an unmapped URI. A PUT request targeted at a + mapped URI updates an existing address object resource. + + When servers create new resources, it's not hard for the server to + choose a unique URL. It's slightly tougher for clients, because a + client might not want to examine all resources in the collection and + might not want to lock the entire collection to ensure that a new one + isn't created with a name collision. However, there is an HTTP + feature to mitigate this. If the client intends to create a new + address resource, the client SHOULD use the HTTP header "If-None- + Match: *" on the PUT request. The Request-URI on the PUT request + MUST include the target collection, where the resource is to be + created, plus the name of the resource in the last path segment. The + "If-None-Match" header ensures that the client will not inadvertently + overwrite an existing resource even if the last path segment turned + out to already be used. + + + + + + + +Daboo Standards Track [Page 15] + +RFC 6352 CardDAV August 2011 + + + >> Request << + + PUT /lisa/addressbook/newvcard.vcf HTTP/1.1 + If-None-Match: * + Host: addressbook.example.com + Content-Type: text/vcard + Content-Length: xxx + + BEGIN:VCARD + VERSION:3.0 + FN:Cyrus Daboo + N:Daboo;Cyrus + ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA + EMAIL;TYPE=INTERNET,PREF:cyrus@example.com + NICKNAME:me + NOTE:Example VCard. + ORG:Self Employed + TEL;TYPE=WORK,VOICE:412 605 0499 + TEL;TYPE=FAX:412 605 0705 + URL:http://www.example.com + UID:1234-5678-9000-1 + END:VCARD + + >> Response << + + HTTP/1.1 201 Created + Date: Thu, 02 Sep 2004 16:53:32 GMT + Content-Length: 0 + ETag: "123456789-000-111" + + The request to change an existing address object resource without + overwriting a change made on the server uses a specific ETag in an + "If-Match" header, rather than the "If-None-Match" header. + + File names for vCards are commonly suffixed by ".vcf", and clients + may choose to use the same convention for URLs. + +6.3.2.1. Additional Preconditions for PUT, COPY, and MOVE + + This specification creates additional preconditions for the PUT, + COPY, and MOVE methods. These preconditions apply: + + o When a PUT operation of an address object resource into an address + book collection occurs. + + o When a COPY or MOVE operation of an address object resource into + an address book collection occurs. + + + + +Daboo Standards Track [Page 16] + +RFC 6352 CardDAV August 2011 + + + The new preconditions are: + + (CARDDAV:supported-address-data): The resource submitted in the + PUT request, or targeted by a COPY or MOVE request, MUST be a + supported media type (i.e., vCard) for address object resources. + + (CARDDAV:valid-address-data): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST be valid data + for the media type being specified (i.e., MUST contain valid vCard + data). + + (CARDDAV:no-uid-conflict): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST NOT specify a + vCard UID property value already in use in the targeted address + book collection or overwrite an existing address object resource + with one that has a different UID property value. Servers SHOULD + report the URL of the resource that is already making use of the + same UID property value in the DAV:href element. + + + + (CARDDAV:addressbook-collection-location-ok): In a COPY or MOVE + request, when the Request-URI is an address book collection, the + URI targeted by the Destination HTTP Request header MUST identify + a location where an address book collection can be created. + + (CARDDAV:max-resource-size): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST have a size + in octets less than or equal to the value of the + CARDDAV:max-resource-size property value (Section 6.2.3) on the + address book collection where the resource will be stored. + +6.3.2.2. Non-Standard vCard Properties and Parameters + + vCard provides a "standard mechanism for doing non-standard things". + This extension support allows implementers to make use of non- + standard vCard properties and parameters whose names are prefixed + with the text "X-". + + Servers MUST support the use of non-standard properties and + parameters in address object resources stored via the PUT method. + + Servers may need to enforce rules for their own "private" properties + or parameters, so servers MAY reject any attempt by the client to + change those or use values for those outside of any restrictions the + server may have. A server SHOULD ensure that any "private" + + + + + +Daboo Standards Track [Page 17] + +RFC 6352 CardDAV August 2011 + + + properties or parameters it uses follow the convention of including a + vendor ID in the "X-" name, as described in Section 3.8 of [RFC2426], + e.g., "X-ABC-PRIVATE". + +6.3.2.3. Address Object Resource Entity Tag + + The DAV:getetag property MUST be defined and set to a strong entity + tag on all address object resources. + + A response to a GET request targeted at an address object resource + MUST contain an ETag response header field indicating the current + value of the strong entity tag of the address object resource. + + Servers SHOULD return a strong entity tag (ETag header) in a PUT + response when the stored address object resource is equivalent by + octet equality to the address object resource submitted in the body + of the PUT request. This allows clients to reliably use the returned + strong entity tag for data synchronization purposes. For instance, + the client can do a PROPFIND request on the stored address object + resource, have the DAV:getetag property returned, compare that value + with the strong entity tag it received on the PUT response, and know + that if they are equal, then the address object resource on the + server has not been changed. + + In the case where the data stored by a server as a result of a PUT + request is not equivalent by octet equality to the submitted address + object resource, the behavior of the ETag response header is not + specified here, with the exception that a strong entity tag MUST NOT + be returned in the response. As a result, a client may need to + retrieve the modified address object resource (and ETag) as a basis + for further changes, rather than use the address object resource it + had sent with the PUT request. + +7. Address Book Access Control + + CardDAV servers MUST support and adhere to the requirements of WebDAV + ACL [RFC3744]. WebDAV ACL provides a framework for an extensible set + of privileges that can be applied to WebDAV collections and ordinary + resources. + +7.1. Additional Principal Properties + + This section defines additional properties for WebDAV principal + resources as defined in [RFC3744]. + + + + + + + +Daboo Standards Track [Page 18] + +RFC 6352 CardDAV August 2011 + + +7.1.1. CARDDAV:addressbook-home-set Property + + Name: addressbook-home-set + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies the URL of any WebDAV collections that contain + address book collections owned by the associated principal + resource. + + Protected: MAY be protected if the server has fixed locations in + which address books are created. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:addressbook-home-set property is meant to + allow users to easily find the address book collections owned by + the principal. Typically, users will group all the address book + collections that they own under a common collection. This + property specifies the URL of collections that are either address + book collections or ordinary collections that have child or + descendant address book collections owned by the principal. + + Definition: + + + + Example: + + + /bernard/addresses/ + + +7.1.2. CARDDAV:principal-address Property + + Name: principal-address + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies the URL of an address object resource that + corresponds to the user represented by the principal. + + + + + +Daboo Standards Track [Page 19] + +RFC 6352 CardDAV August 2011 + + + Protected: MAY be protected if the server provides a fixed location + for principal addresses. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:principal-address property is meant to + allow users to easily find contact information for users + represented by principals on the system. This property specifies + the URL of the resource containing the corresponding contact + information. The resource could be an address object resource in + an address book collection, or it could be a resource in a + "regular" collection. + + Definition: + + + + Example: + + + /system/cyrus.vcf + + +8. Address Book Reports + + This section defines the reports that CardDAV servers MUST support on + address book collections and address object resources. + + CardDAV servers MUST advertise support for these reports on all + address book collections and address object resources with the + DAV:supported-report-set property defined in Section 3.1.5 of + [RFC3253]. CardDAV servers MAY also advertise support for these + reports on ordinary collections. + + Some of these reports allow address data (from possibly multiple + resources) to be returned. + +8.1. REPORT Method + + The REPORT method (defined in Section 3.6 of [RFC3253]) provides an + extensible mechanism for obtaining information about a resource. + Unlike the PROPFIND method, which returns the value of one or more + named properties, the REPORT method can involve more complex + + + +Daboo Standards Track [Page 20] + +RFC 6352 CardDAV August 2011 + + + processing. REPORT is valuable in cases where the server has access + to all of the information needed to perform the complex request (such + as a query), and where it would require multiple requests for the + client to retrieve the information needed to perform the same + request. + + A server that supports this specification MUST support the + DAV:expand-property report (defined in Section 3.8 of [RFC3253]). + +8.2. Ordinary Collections + + Servers MAY support the reports defined in this document on ordinary + collections (collections that are not address book collections) in + addition to address book collections or address object resources. In + computing responses to the reports on ordinary collections, servers + MUST only consider address object resources contained in address book + collections that are targeted by the REPORT based on the value of the + Depth request header. + +8.3. Searching Text: Collations + + Some of the reports defined in this section do text matches of + character strings provided by the client and compared to stored + address data. Since vCard data is by default encoded in the UTF-8 + charset and may include characters outside of the US-ASCII charset + range in some property and parameter values, there is a need to + ensure that text matching follows well-defined rules. + + To deal with this, this specification makes use of the IANA Collation + Registry defined in [RFC4790] to specify collations that may be used + to carry out the text comparison operations with a well-defined rule. + + Collations supported by the server MUST support "equality" and + "substring" match operations as per [RFC4790], Section 4.2, including + the "prefix" and "suffix" options for "substring" matching. CardDAV + uses these match options for "equals", "contains", "starts-with", and + "ends-with" match operations. + + CardDAV servers are REQUIRED to support the "i;ascii-casemap" + [RFC4790] and "i;unicode-casemap" [RFC5051] collations and MAY + support other collations. + + Servers MUST advertise the set of collations that they support via + the CARDDAV:supported-collation-set property defined on any resource + that supports reports that use collations. + + + + + + +Daboo Standards Track [Page 21] + +RFC 6352 CardDAV August 2011 + + + In the absence of a collation explicitly specified by the client, or + if the client specifies the "default" collation identifier (as + defined in [RFC4790], Section 3.1), the server MUST default to using + "i;unicode-casemap" as the collation. + + Wildcards (as defined in [RFC4790], Section 3.2) MUST NOT be used in + the collation identifier. + + If the client chooses a collation not supported by the server, the + server MUST respond with a CARDDAV:supported-collation precondition + error response. + +8.3.1. CARDDAV:supported-collation-set Property + + Name: supported-collation-set + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies the set of collations supported by the server + for text matching operations. + + Protected: MUST be protected as it indicates support provided by the + server. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:supported-collation-set property contains + two or more CARDDAV:supported-collation elements that specify the + identifiers of the collations supported by the server. + + Definition: + + + + + + + + + + + + +Daboo Standards Track [Page 22] + +RFC 6352 CardDAV August 2011 + + + Example: + + + i;ascii-casemap + i;octet + i;unicode-casemap + + +8.4. Partial Retrieval + + Some address book reports defined in this document allow partial + retrieval of address object resources. A CardDAV client can specify + what information to return in the body of an address book REPORT + request. + + A CardDAV client can request particular WebDAV property values, all + WebDAV property values, or a list of the names of the resource's + WebDAV properties. A CardDAV client can also request address data to + be returned and whether all vCard properties should be returned or + only particular ones. See CARDDAV:address-data in Section 10.4. + +8.5. Non-Standard Properties and Parameters + + Servers MUST support the use of non-standard vCard property or + parameter names in the CARDDAV:address-data XML element in address + book REPORT requests to allow clients to request that non-standard + properties and parameters be returned in the address data provided in + the response. + + Servers MAY support the use of non-standard vCard property or + parameter names in the CARDDAV:prop-filter and CARDDAV:param-filter + XML elements specified in the CARDDAV:filter XML element of address + book REPORT requests. + + Servers MUST fail with the CARDDAV:supported-filter precondition if + an address book REPORT request uses a CARDDAV:prop-filter or + CARDDAV:param-filter XML element that makes reference to a non- + standard vCard property or parameter name on which the server does + not support queries. + +8.6. CARDDAV:addressbook-query Report + + The CARDDAV:addressbook-query REPORT performs a search for all + address object resources that match a specified filter. The response + of this report will contain all the WebDAV properties and address + object resource data specified in the request. In the case of the + + + + +Daboo Standards Track [Page 23] + +RFC 6352 CardDAV August 2011 + + + CARDDAV:address-data XML element, one can explicitly specify the + vCard properties that should be returned in the address object + resource data that matches the filter. + + The format of this report is modeled on the PROPFIND method. The + request and response bodies of the CARDDAV:addressbook-query report + use XML elements that are also used by PROPFIND. In particular, the + request can include XML elements to request WebDAV properties to be + returned. When that occurs, the response should follow the same + behavior as PROPFIND with respect to the DAV:multistatus response + elements used to return specific WebDAV property results. For + instance, a request to retrieve the value of a WebDAV property that + does not exist is an error and MUST be noted with a response XML + element that contains a 404 (Not Found) status value. + + Support for the CARDDAV:addressbook-query REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CARDDAV:addressbook-query XML element + as defined in Section 10.3. + + The request MUST include a Depth header. The scope of the query + is determined by the value of the Depth header. For example, to + query all address object resources in an address book collection, + the REPORT would use the address book collection as the Request- + URI and specify a Depth of 1 or infinity. + + The response body for a successful request MUST be a + DAV:multistatus XML element (i.e., the response uses the same + format as the response for PROPFIND). In the case where there are + no response elements, the returned DAV:multistatus XML element is + empty. + + The response body for a successful CARDDAV:addressbook-query + REPORT request MUST contain a DAV:response element for each + address object that matched the search filter. Address data is + returned in the CARDDAV:address-data XML element inside the + DAV:propstat XML element. + + Preconditions: + + (CARDDAV:supported-address-data): The attributes "content-type" + and "version" of the CARDDAV:address-data XML element (see + Section 10.4) specify a media type supported by the server for + address object resources. + + + + + +Daboo Standards Track [Page 24] + +RFC 6352 CardDAV August 2011 + + + (CARDDAV:supported-filter): The CARDDAV:prop-filter (see + Section 10.5.1) and CARDDAV:param-filter (see Section 10.5.2) XML + elements used in the CARDDAV:filter XML element (see Section 10.5) + in the REPORT request only make reference to vCard properties and + parameters for which queries are supported by the server. That + is, if the CARDDAV:filter element attempts to reference an + unsupported vCard property or parameter, this precondition is + violated. A server SHOULD report the CARDDAV:prop-filter or + CARDDAV:param-filter for which it does not provide support. + + + + (CARDDAV:supported-collation): Any XML attribute specifying a + collation MUST specify a collation supported by the server as + described in Section 8.3. + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + address object resources must fall within server-specific, + predefined limits. For example, this condition might be triggered + if a search specification would cause the return of an extremely + large number of responses. + +8.6.1. Limiting Results + + A client can limit the number of results returned by the server + through use of the CARDDAV:limit element in the request body. This + is useful when clients are only interested in a few matches or only + have limited space to display results to users and thus don't need + the overhead of receiving more than that. When the results are + truncated by the server, the server MUST follow the rules below for + indicating a result set truncation to the client. + +8.6.2. Truncation of Results + + A server MAY limit the number of resources in a response, for + example, to limit the amount of work expended in processing a query, + or as the result of an explicit limit set by the client. If the + result set is truncated because of such a limit, the response MUST + use status code 207 (Multi-Status), return a DAV:multistatus response + body, and indicate a status of 507 (Insufficient Storage) for the + Request-URI. That DAV:response element SHOULD include a DAV:error + element with the DAV:number-of-matches-within-limits precondition, as + defined in [RFC3744], Section 9.2. + + + + + +Daboo Standards Track [Page 25] + +RFC 6352 CardDAV August 2011 + + + The server SHOULD also include the partial results in additional + DAV:response elements. If a client-requested limit is being applied, + the 507 response for the Request-URI MUST NOT be included in + calculating the limit (e.g., if the client requests that only a + single result be returned, and multiple matches are present, then the + DAV:multistatus response will include one DAV:response for the + matching resource and one DAV:response for the 507 status on the + Request-URI). + +8.6.3. Example: Partial Retrieval of vCards Matching NICKNAME + + In this example, the client requests that the server search for + address object resources that contain a NICKNAME property whose value + equals some specific text and return specific vCard properties for + those vCards found. In addition, the DAV:getetag property is also + requested and returned as part of the response. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + me + + + + + + + + +Daboo Standards Track [Page 26] + +RFC 6352 CardDAV August 2011 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/v102.vcf + + + "23ba4d-ff11fb" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:me + UID:34222-232@example.com + FN:Cyrus Daboo + EMAIL:daboo@example.com + END:VCARD + + + HTTP/1.1 200 OK + + + + +8.6.4. Example: Partial Retrieval of vCards Matching a Full Name or + Email Address + + In this example, the client requests that the server search for + address object resources that contain a FN property whose value + contains some specific text or that contain an EMAIL property whose + value contains other text and return specific vCard properties for + those vCards found. In addition, the DAV:getetag property is also + requested and returned as part of the response. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + +Daboo Standards Track [Page 27] + +RFC 6352 CardDAV August 2011 + + + + + + + + + + + + + + + + + daboo + + + daboo + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/v102.vcf + + + "23ba4d-ff11fb" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:me + UID:34222-232@example.com + FN:David Boo + EMAIL:daboo@example.com + + + +Daboo Standards Track [Page 28] + +RFC 6352 CardDAV August 2011 + + + END:VCARD + + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/v104.vcf + + + "23ba4d-ff11fc" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:oliver + UID:34222-23222@example.com + FN:Oliver Daboo + EMAIL:oliver@example.com + END:VCARD + + + HTTP/1.1 200 OK + + + + +8.6.5. Example: Truncated Results + + In this example, the client requests that the server search for + address object resources that contain a FN property whose value + contains some specific text and return the DAV:getetag property for + two results only. The server response includes a 507 status for the + Request-URI indicating that there were more than two resources that + matched the query, but that the server truncated the result set as + requested by the client. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + +Daboo Standards Track [Page 29] + +RFC 6352 CardDAV August 2011 + + + + + + + + daboo + + + + 2 + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/ + HTTP/1.1 507 Insufficient Storage + + + Only two matching records were returned + + + + /home/bernard/addressbook/v102.vcf + + + "23ba4d-ff11fb" + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/v104.vcf + + + "23ba4d-ff11fc" + + + + +Daboo Standards Track [Page 30] + +RFC 6352 CardDAV August 2011 + + + HTTP/1.1 200 OK + + + + +8.7. CARDDAV:addressbook-multiget Report + + The CARDDAV:addressbook-multiget REPORT is used to retrieve specific + address object resources from within a collection, if the Request-URI + is a collection, or to retrieve a specific address object resource, + if the Request-URI is an address object resource. This report is + similar to the CARDDAV:addressbook-query REPORT (see Section 8.6), + except that it takes a list of DAV:href elements instead of a + CARDDAV:filter element to determine which address object resources to + return. + + Support for the addressbook-multiget REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CARDDAV:addressbook-multiget XML + element (see Section 10.7), which MUST contain at least one + DAV:href XML element and one optional CARDDAV:address-data element + as defined in Section 10.4. If DAV:href elements are present, the + scope of the request is the set of resources identified by these + elements, which all need to be members (not necessarily internal + members) of the resource identified by the Request-URI. + Otherwise, the scope is the resource identified by the Request-URI + itself. + + The request MUST include a Depth: 0 header; however, the actual + scope of the REPORT is determined as described above. + + The response body for a successful request MUST be a + DAV:multistatus XML element. + + The response body for a successful CARDDAV:addressbook-multiget + REPORT request MUST contain a DAV:response element for each + address object resource referenced by the provided set of DAV:href + elements. Address data is returned in the CARDDAV:address-data + element inside the DAV:prop element. + + In the case of an error accessing any of the provided DAV:href + resources, the server MUST return the appropriate error status + code in the DAV:status element of the corresponding DAV:response + element. + + + + + +Daboo Standards Track [Page 31] + +RFC 6352 CardDAV August 2011 + + + Preconditions: + + (CARDDAV:supported-address-data): The attributes "content-type" + and "version" of the CARDDAV:address-data XML elements (see + Section 10.4) specify a media type supported by the server for + address object resources. + + Postconditions: + + None. + +8.7.1. Example: CARDDAV:addressbook-multiget Report + + In this example, the client requests the server to return specific + vCard properties of the address components referenced by specific + URIs. In addition, the DAV:getetag property is also requested and + returned as part of the response. Note that, in this example, the + resource at + http://addressbook.example.com/home/bernard/addressbook/vcf1.vcf does + not exist, resulting in an error status response. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + /home/bernard/addressbook/vcf102.vcf + /home/bernard/addressbook/vcf1.vcf + + + + + + + +Daboo Standards Track [Page 32] + +RFC 6352 CardDAV August 2011 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/vcf102.vcf + + + "23ba4d-ff11fb" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:me + UID:34222-232@example.com + FN:Cyrus Daboo + EMAIL:daboo@example.com + END:VCARD + + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/vcf1.vcf + HTTP/1.1 404 Resource not found + + + +8.7.2. Example: CARDDAV:addressbook-multiget Report + + In this example, the client requests the server to return vCard v4.0 + data of the address components referenced by specific URIs. In + addition, the DAV:getetag property is also requested and returned as + part of the response. Note that, in this example, the resource at + http://addressbook.example.com/home/bernard/addressbook/vcf3.vcf + exists but in a media type format that the server is unable to + convert, resulting in an error status response. + + + + + + + + + +Daboo Standards Track [Page 33] + +RFC 6352 CardDAV August 2011 + + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + /home/bernard/addressbook/vcf3.vcf + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/vcf3.vcf + HTTP/1.1 415 Unsupported Media Type + + Unable to convert from vCard v3.0 + to vCard v4.0 + + + +9. Client Guidelines + +9.1. Restrict the Properties Returned + + Clients may not need all the properties in a vCard object when + presenting information to the user, or looking up specific items for + their email address, for example. Since some property data can be + large (e.g., PHOTO or SOUND with in-line content) clients can choose + to ignore those by only requesting the specific items it knows it + will use, through use of the CARDDAV:address-data XML element in the + relevant reports. + + + +Daboo Standards Track [Page 34] + +RFC 6352 CardDAV August 2011 + + + However, if a client needs to make a change to a vCard, it can only + change the entire vCard data via a PUT request. There is no way to + incrementally make a change to a set of properties within a vCard + object resource. As a result, the client will have to cache the + entire set of properties on a resource that is being changed. + +9.2. Avoiding Lost Updates + + When resources are accessed by multiple clients, the possibility of + clients overwriting each other's changes exists. To alleviate this, + clients SHOULD use the If-Match request header on PUT requests with + the ETag of the previously retrieved resource data to check whether + the resource was modified since it was previously retrieved. If a + precondition failure occurs, clients need to reload the resource and + go through their own merge or conflict resolution process before + writing back the data (again using the If-Match check). + +9.3. Client Configuration + + When CardDAV clients need to be configured, the key piece of + information that they require is the principal-URL of the user whose + address book information is desired. Servers SHOULD support the + DAV:current-user-principal-URL property as defined in [RFC5397] to + give clients a fast way to locate user principals. + + Given support for SRV records (Section 11) and DAV:current-user- + principal-URL [RFC5397], users only need enter a user identifier, + host name, and password to configure their client. The client would + take the host name and do an SRV lookup to locate the CardDAV server, + then execute an authenticated PROPFIND on the root/resource looking + for the DAV:current-user-principal-URL property. The value returned + gives the client direct access to the user's principal-URL and from + there all the related CardDAV properties needed to locate address + books. + +9.4. Finding Other Users' Address Books + + For use cases of address book sharing, one might wish to find the + address book belonging to another user. To find other users' address + books on the same server, the DAV:principal-property-search REPORT + [RFC3744] can be used to search principals for matching properties + and return specified properties for the matching principal resources. + To search for an address book owned by a user named "Laurie", the + REPORT request body would look like this: + + + + + + + +Daboo Standards Track [Page 35] + +RFC 6352 CardDAV August 2011 + + + + + + + + + Laurie + + + + + + + + The server performs a case-sensitive or caseless search for a + matching string subset of "Laurie" within the DAV:displayname + property. Thus, the server might return "Laurie Dusseault", "Laurier + Desruisseaux", or "Wilfrid Laurier" all as matching DAV:displayname + values, and the address books for each of these. + +10. XML Element Definitions + +10.1. CARDDAV:addressbook XML Element + + Name: addressbook + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies the resource type of an address book collection. + + Description: See Section 5.2. + + Definition: + + + +10.2. CARDDAV:supported-collation XML Element + + Name: supported-collation + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies a single collation via its collation identifier + as defined by [RFC4790]. + + Description: The CARDDAV:supported-collation contains the text of a + collation identifier as described in Section 8.3.1. + + + +Daboo Standards Track [Page 36] + +RFC 6352 CardDAV August 2011 + + + Definition: + + + + +10.3. CARDDAV:addressbook-query XML Element + + Name: addressbook-query + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Defines a report for querying address book data + + Description: See Section 8.6. + + Definition: + + + +10.4. CARDDAV:address-data XML Element + + Name: address-data + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies one of the following: + + 1. The parts of an address object resource that should be + returned by a given address book REPORT request, and the media + type and version for the returned data; or + + 2. The content of an address object resource in a response to an + address book REPORT request. + + Description: When used in an address book REPORT request, the + CARDDAV:address-data XML element specifies which parts of address + object resources need to be returned in the response. If the + CARDDAV:address-data XML element doesn't contain any CARDDAV:prop + elements, address object resources will be returned in their + entirety. Additionally, a media type and version can be specified + to request that the server return the data in that format if + possible. + + Finally, when used in an address book REPORT response, the + CARDDAV:address-data XML element specifies the content of an + address object resource. Given that XML parsers normalize the + + + +Daboo Standards Track [Page 37] + +RFC 6352 CardDAV August 2011 + + + two-character sequence CRLF (US-ASCII decimal 13 and US-ASCII + decimal 10) to a single LF character (US-ASCII decimal 10), the CR + character (US-ASCII decimal 13) MAY be omitted in address object + resources specified in the CARDDAV:address-data XML element. + Furthermore, address object resources specified in the + CARDDAV:address-data XML element MAY be invalid per their media + type specification if the CARDDAV:address-data XML element part of + the address book REPORT request did not specify required vCard + properties (e.g., UID, etc.) or specified a CARDDAV:prop XML + element with the "novalue" attribute set to "yes". + + Note: The CARDDAV:address-data XML element is specified in requests + and responses inside the DAV:prop XML element as if it were a + WebDAV property. However, the CARDDAV:address-data XML element is + not a WebDAV property and as such it is not returned in PROPFIND + responses nor used in PROPPATCH requests. + + Note: The address data embedded within the CARDDAV:address-data XML + element MUST follow the standard XML character data encoding + rules, including use of <, >, & etc., entity encoding or + the use of a construct. In the latter case, the + vCard data cannot contain the character sequence "]]>", which is + the end delimiter for the CDATA section. + + Definition: + + + + when nested in the DAV:prop XML element in an address book + REPORT request to specify which parts of address object + resources should be returned in the response; + + + + + when nested in the DAV:prop XML element in an address book + REPORT response to specify the content of a returned + address object resource. + + + + + + attributes can be used on each variant of the + CALDAV:address-data XML element. + + + + + +Daboo Standards Track [Page 38] + +RFC 6352 CardDAV August 2011 + + +10.4.1. CARDDAV:allprop XML Element + + Name: allprop + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies that all vCard properties shall be returned. + + Description: This element can be used when the client wants all + vCard properties of components returned by a report. + + Definition: + + + + Note: The CARDDAV:allprop element defined here has the same name as + the DAV:allprop element defined in WebDAV. However, the + CARDDAV:allprop element defined here uses the + "urn:ietf:params:xml:ns:carddav" namespace, as opposed to the "DAV:" + namespace used for the DAV:allprop element defined in WebDAV. + +10.4.2. CARDDAV:prop XML Element + + Name: prop + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Defines which vCard properties to return in the response. + + Description: The "name" attribute specifies the name of the vCard + property to return (e.g., "NICKNAME"). The "novalue" attribute + can be used by clients to request that the actual value of the + property not be returned (if the "novalue" attribute is set to + "yes"). In that case, the server will return just the vCard + property name and any vCard parameters and a trailing ":" without + the subsequent value data. + + vCard allows a "group" prefix to appear before a property name in + the vCard data. When the "name" attribute does not specify a + group prefix, it MUST match properties in the vCard data without a + group prefix or with any group prefix. When the "name" attribute + includes a group prefix, it MUST match properties that have + exactly the same group prefix and name. For example, a "name" set + to "TEL" will match "TEL", "X-ABC.TEL", and "X-ABC-1.TEL" vCard + properties. A "name" set to "X-ABC.TEL" will match an "X-ABC.TEL" + vCard property only; it will not match "TEL" or "X-ABC-1.TEL". + + + + + +Daboo Standards Track [Page 39] + +RFC 6352 CardDAV August 2011 + + + Definition: + + + + + + + + Note: The CARDDAV:prop element defined here has the same name as the + DAV:prop element defined in WebDAV. However, the CARDDAV:prop + element defined here uses the "urn:ietf:params:xml:ns:carddav" + namespace, as opposed to the "DAV:" namespace used for the DAV:prop + element defined in WebDAV. + +10.5. CARDDAV:filter XML Element + + Name: filter + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Determines which matching objects are returned. + + Description: The "filter" element specifies the search filter used + to match address objects that should be returned by a report. The + "test" attribute specifies whether any (logical OR) or all + (logical AND) of the prop-filter tests need to match in order for + the overall filter to match. + + Definition: + + + + + + +10.5.1. CARDDAV:prop-filter XML Element + + Name: prop-filter + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Limits the search to specific vCard properties. + + + + + + +Daboo Standards Track [Page 40] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:prop-filter XML element specifies search + criteria on a specific vCard property (e.g., "NICKNAME"). An + address object is said to match a CARDDAV:prop-filter if: + + * A vCard property of the type specified by the "name" attribute + exists, and the CARDDAV:prop-filter is empty, or it matches any + specified CARDDAV:text-match or CARDDAV:param-filter + conditions. The "test" attribute specifies whether any + (logical OR) or all (logical AND) of the text-filter and param- + filter tests need to match in order for the overall filter to + match. + + or: + + * A vCard property of the type specified by the "name" attribute + does not exist, and the CARDDAV:is-not-defined element is + specified. + + vCard allows a "group" prefix to appear before a property name in + the vCard data. When the "name" attribute does not specify a + group prefix, it MUST match properties in the vCard data without a + group prefix or with any group prefix. When the "name" attribute + includes a group prefix, it MUST match properties that have + exactly the same group prefix and name. For example, a "name" set + to "TEL" will match "TEL", "X-ABC.TEL", "X-ABC-1.TEL" vCard + properties. A "name" set to "X-ABC.TEL" will match an "X-ABC.TEL" + vCard property only, it will not match "TEL" or "X-ABC-1.TEL". + + Definition: + + + + + + +10.5.2. CARDDAV:param-filter XML Element + + Name: param-filter + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Limits the search to specific parameter values. + + + + +Daboo Standards Track [Page 41] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:param-filter XML element specifies search + criteria on a specific vCard property parameter (e.g., TYPE) in + the scope of a given CARDDAV:prop-filter. A vCard property is + said to match a CARDDAV:param-filter if: + + * A parameter of the type specified by the "name" attribute + exists, and the CARDDAV:param-filter is empty, or it matches + the CARDDAV:text-match conditions if specified. + + or: + + * A parameter of the type specified by the "name" attribute does + not exist, and the CARDDAV:is-not-defined element is specified. + + Definition: + + + + + + +10.5.3. CARDDAV:is-not-defined XML Element + + Name: is-not-defined + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies that a match should occur if the enclosing vCard + property or parameter does not exist. + + Description: The CARDDAV:is-not-defined XML element specifies that a + match occurs if the enclosing vCard property or parameter value + specified in an address book REPORT request does not exist in the + address data being tested. + + Definition: + + + +10.5.4. CARDDAV:text-match XML Element + + Name: text-match + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies a substring match on a vCard property or + parameter value. + + + + +Daboo Standards Track [Page 42] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:text-match XML element specifies text used + for a substring match against the vCard property or parameter + value specified in an address book REPORT request. + + The "collation" attribute is used to select the collation that the + server MUST use for character string matching. In the absence of + this attribute, the server MUST use the "i;unicode-casemap" + collation. + + The "negate-condition" attribute is used to indicate that this + test returns a match if the text matches, when the attribute value + is set to "no", or return a match if the text does not match, if + the attribute value is set to "yes". For example, this can be + used to match components with a CATEGORIES property not set to + PERSON. + + The "match-type" attribute is used to indicate the type of match + operation to use. Possible choices are: + + "equals" - an exact match to the target string + + "contains" - a substring match, matching anywhere within the + target string + + "starts-with" - a substring match, matching only at the start + of the target string + + "ends-with" - a substring match, matching only at the end of + the target string + + Definition: + + + + + + +10.6. CARDDAV:limit XML Element + + Name: limit + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies different types of limits that can be applied to + the results returned by the server. + + + +Daboo Standards Track [Page 43] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:limit XML element can be used to specify + different types of limits that the client can request the server + to apply to the results returned by the server. Currently, only + the CARDDAV:nresults limit can be used; other types of limit could + be defined in the future. + + Definition: + + + +10.6.1. CARDDAV:nresults XML Element + + Name: nresults + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies a limit on the number of results returned by the + server. + + Description: The CARDDAV:nresults XML element contains a requested + maximum number of DAV:response elements to be returned in the + response body of a query. The server MAY disregard this limit. + The value of this element is an unsigned integer. + + Definition: + + + + +10.7. CARDDAV:addressbook-multiget XML Element + + Name: addressbook-multiget + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: CardDAV report used to retrieve specific address objects + via their URIs. + + Description: See Section 8.7. + + Definition: + + + + + + + +Daboo Standards Track [Page 44] + +RFC 6352 CardDAV August 2011 + + +11. Service Discovery via SRV Records + + [RFC2782] defines a DNS-based service discovery protocol that has + been widely adopted as a means of locating particular services within + a local area network and beyond, using SRV RRs. + + This specification adds two service types for use with SRV records: + + carddav: Identifies a CardDAV server that uses HTTP without TLS + [RFC2818]. + + carddavs: Identifies a CardDAV server that uses HTTP with TLS + [RFC2818]. + + Example: non-TLS service record + + _carddav._tcp SRV 0 1 80 addressbook.example.com. + + Example: TLS service + + _carddavs._tcp SRV 0 1 443 addressbook.example.com. + +12. Internationalization Considerations + + CardDAV allows internationalized strings to be stored and retrieved + for the description of address book collections (see Section 6.2.1). + + The CARDDAV:addressbook-query REPORT (Section 8.6) includes a text + searching option controlled by the CARDDAV:text-match element and + details of character handling are covered in the description of that + element (see Section 10.5.4). + +13. Security Considerations + + HTTP protocol transactions are sent in the clear over the network + unless protection from snooping is negotiated. This can be + accomplished by use of TLS as defined in [RFC2818]. In particular, + if HTTP Basic authentication [RFC2617] is available, the server MUST + allow TLS to be used at the same time, and it SHOULD prevent use of + Basic authentication when TLS is not in use. Clients SHOULD use TLS + whenever possible. + + With the ACL extension [RFC3744] present, WebDAV allows control over + who can access (read or write) any resource on the WebDAV server. In + addition, WebDAV ACL provides for an "inheritance" mechanism, whereby + resources may inherit access privileges from other resources. Often, + the "other" resource is a parent collection of the resource itself. + Servers are able to support address books that are "private" + + + +Daboo Standards Track [Page 45] + +RFC 6352 CardDAV August 2011 + + + (accessible only to the "owner"), "shared" (accessible to the owner + and other specified authenticated users), and "public" (accessible to + any authenticated or unauthenticated users). When provisioning + address books of a particular type, servers MUST ensure that the + correct privileges are applied on creation. In particular, private + and shared address books MUST NOT be accessible by unauthenticated + users (to prevent data from being automatically searched or indexed + by web "crawlers"). + + Clients SHOULD warn users in an appropriate fashion when they copy or + move address data from a private address book to a shared address + book or public address book. Clients SHOULD provide a clear + indication as to which address books are private, shared, or public. + Clients SHOULD provide an appropriate warning when changing access + privileges for a private or shared address book with data so as to + allow unauthenticated users access. + + This specification currently relies on standard HTTP authentication + mechanisms for identifying users. These comprise Basic and Digest + authentication [RFC2617] as well as TLS [RFC2818] using client-side + certificates. + +14. IANA Consideration + + This document uses a URN to describe a new XML namespace conforming + to the registry mechanism described in [RFC3688]. + +14.1. Namespace Registration + + Registration request for the carddav namespace: + + URI: urn:ietf:params:xml:ns:carddav + + Registrant Contact: The IESG + + XML: None - not applicable for namespace registrations. + +15. Acknowledgments + + Thanks go to Lisa Dusseault and Bernard Desruisseaux for their work + on CalDAV, on which CardDAV is heavily based. The following + individuals contributed their ideas and support for writing this + specification: Mike Douglass, Stefan Eissing, Helge Hess, Arnaud + Quillaud, Julian Reschke, Elias Sinderson, Greg Stein, Wilfredo + Sanchez, and Simon Vaillancourt. + + + + + + +Daboo Standards Track [Page 46] + +RFC 6352 CardDAV August 2011 + + +16. References + +16.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2426] Dawson, F. and T. Howes, "vCard MIME Directory Profile", + RFC 2426, September 1998. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., + Leach, P., Luotonen, A., and L. Stewart, "HTTP + Authentication: Basic and Digest Access Authentication", + RFC 2617, June 1999. + + [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for + specifying the location of services (DNS SRV)", RFC 2782, + February 2000. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000. + + [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, C., and J. + Whitehead, "Versioning Extensions to WebDAV + (Web Distributed Authoring and Versioning)", RFC 3253, + March 2002. + + [RFC3688] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, + January 2004. + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web + Distributed Authoring and Versioning (WebDAV) + Access Control Protocol", RFC 3744, May 2004. + + [RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet + Application Protocol Collation Registry", RFC 4790, + March 2007. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + + [RFC5051] Crispin, M., "i;unicode-casemap - Simple Unicode Collation + Algorithm", RFC 5051, October 2007. + + + + + +Daboo Standards Track [Page 47] + +RFC 6352 CardDAV August 2011 + + + [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security + (TLS) Protocol Version 1.2", RFC 5246, August 2008. + + [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., + Housley, R., and W. Polk, "Internet X.509 Public Key + Infrastructure Certificate and Certificate Revocation List + (CRL) Profile", RFC 5280, May 2008. + + [RFC5397] Sanchez, W. and C. Daboo, "WebDAV Current Principal + Extension", RFC 5397, December 2008. + + [RFC5689] Daboo, C., "Extended MKCOL for Web Distributed Authoring + and Versioning (WebDAV)", RFC 5689, September 2009. + + [RFC6350] Perreault, S., "vCard Format Specification", RFC 6350, + August 2011. + + [W3C.REC-xml-20081126] + Bray, T., Paoli, J., Sperberg-McQueen, C., Maler, E., and + F. Yergeau, "Extensible Markup Language (XML) 1.0 (Fifth + Edition)", World Wide Web Consortium Recommendation REC- + xml-20081126, November 2008, + . + +16.2. Informative References + + [IMSP] Myers, J., "IMSP - Internet Message Support Protocol", + Work in Progress, June 1995. + + [RFC2244] Newman, C. and J. Myers, "ACAP -- Application + Configuration Access Protocol", RFC 2244, November 1997. + + [RFC4510] Zeilenga, K., "Lightweight Directory Access Protocol + (LDAP): Technical Specification Road Map", RFC 4510, + June 2006. + +Author's Address + + Cyrus Daboo + Apple, Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + +Daboo Standards Track [Page 48] + diff --git a/dav/SabreDAV/examples/addressbookserver.php b/dav/SabreDAV/examples/addressbookserver.php new file mode 100644 index 000000000..22b30e613 --- /dev/null +++ b/dav/SabreDAV/examples/addressbookserver.php @@ -0,0 +1,56 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + +//Mapping PHP errors to exceptions +function exception_error_handler($errno, $errstr, $errfile, $errline ) { + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); +} +set_error_handler("exception_error_handler"); + +// Autoloader +require_once 'lib/Sabre/autoload.php'; + +// Backends +$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo); +$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); +$carddavBackend = new Sabre_CardDAV_Backend_PDO($pdo); +//$caldavBackend = new Sabre_CalDAV_Backend_PDO($pdo); + +// Setting up the directory tree // +$nodes = array( + new Sabre_DAVACL_PrincipalCollection($principalBackend), +// new Sabre_CalDAV_CalendarRootNode($authBackend, $caldavBackend), + new Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend), +); + +// The object tree needs in turn to be passed to the server class +$server = new Sabre_DAV_Server($nodes); +$server->setBaseUri($baseUri); + +// Plugins +$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV')); +$server->addPlugin(new Sabre_DAV_Browser_Plugin()); +//$server->addPlugin(new Sabre_CalDAV_Plugin()); +$server->addPlugin(new Sabre_CardDAV_Plugin()); +$server->addPlugin(new Sabre_DAVACL_Plugin()); + +// And off we go! +$server->exec(); diff --git a/dav/SabreDAV/examples/basicauth.php b/dav/SabreDAV/examples/basicauth.php new file mode 100644 index 000000000..75552f3f9 --- /dev/null +++ b/dav/SabreDAV/examples/basicauth.php @@ -0,0 +1,26 @@ +getUserPass(); + +if (!$result || $result[0]!=$u || $result[1]!=$p) { + + $auth->requireLogin(); + echo "Authentication required\n"; + die(); + +} diff --git a/dav/SabreDAV/examples/calendarserver.php b/dav/SabreDAV/examples/calendarserver.php new file mode 100644 index 000000000..b6cc5c314 --- /dev/null +++ b/dav/SabreDAV/examples/calendarserver.php @@ -0,0 +1,62 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + +//Mapping PHP errors to exceptions +function exception_error_handler($errno, $errstr, $errfile, $errline ) { + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); +} +set_error_handler("exception_error_handler"); + +// Files we need +require_once 'lib/Sabre/autoload.php'; + +// Backends +$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo); +$calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo); +$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + +// Directory structure +$tree = array( + new Sabre_CalDAV_Principal_Collection($principalBackend), + new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend), +); + +$server = new Sabre_DAV_Server($tree); + +if (isset($baseUri)) + $server->setBaseUri($baseUri); + +/* Server Plugins */ +$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV'); +$server->addPlugin($authPlugin); + +$aclPlugin = new Sabre_DAVACL_Plugin(); +$server->addPlugin($aclPlugin); + +$caldavPlugin = new Sabre_CalDAV_Plugin(); +$server->addPlugin($caldavPlugin); + +// Support for html frontend +$browser = new Sabre_DAV_Browser_Plugin(); +$server->addPlugin($browser); + +// And off we go! +$server->exec(); diff --git a/dav/SabreDAV/examples/digestauth.php b/dav/SabreDAV/examples/digestauth.php new file mode 100644 index 000000000..748f7c4f5 --- /dev/null +++ b/dav/SabreDAV/examples/digestauth.php @@ -0,0 +1,25 @@ +init(); + +if ($auth->getUsername() != $u || !$auth->validatePassword($p)) { + + $auth->requireLogin(); + echo "Authentication required\n"; + die(); + +} diff --git a/dav/SabreDAV/examples/fileserver.php b/dav/SabreDAV/examples/fileserver.php new file mode 100644 index 000000000..88a570872 --- /dev/null +++ b/dav/SabreDAV/examples/fileserver.php @@ -0,0 +1,60 @@ +setBaseUri($baseUri); + +// Support for LOCK and UNLOCK +$lockBackend = new Sabre_DAV_Locks_Backend_File($tmpDir . '/locksdb'); +$lockPlugin = new Sabre_DAV_Locks_Plugin($lockBackend); +$server->addPlugin($lockPlugin); + +// Support for html frontend +$browser = new Sabre_DAV_Browser_Plugin(); +$server->addPlugin($browser); + +// Automatically guess (some) contenttypes, based on extesion +$server->addPlugin(new Sabre_DAV_Browser_GuessContentType()); + +// Authentication backend +$authBackend = new Sabre_DAV_Auth_Backend_File('.htdigest'); +$auth = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV'); +$server->addPlugin($auth); + +// Temporary file filter +$tempFF = new Sabre_DAV_TemporaryFileFilterPlugin($tmpDir); +$server->addPlugin($tempFF); + +// And off we go! +$server->exec(); diff --git a/dav/SabreDAV/examples/groupwareserver.php b/dav/SabreDAV/examples/groupwareserver.php new file mode 100644 index 000000000..bf8e74307 --- /dev/null +++ b/dav/SabreDAV/examples/groupwareserver.php @@ -0,0 +1,91 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + +/** + * Mapping PHP errors to exceptions. + * + * While this is not strictly needed, it makes a lot of sense to do so. If an + * E_NOTICE or anything appears in your code, this allows SabreDAV to intercept + * the issue and send a proper response back to the client (HTTP/1.1 500). + */ +function exception_error_handler($errno, $errstr, $errfile, $errline ) { + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); +} +set_error_handler("exception_error_handler"); + +// Autoloader +require_once 'lib/Sabre/autoload.php'; + +/** + * The backends. Yes we do really need all of them. + * + * This allows any developer to subclass just any of them and hook into their + * own backend systems. + */ +$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo); +$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); +$carddavBackend = new Sabre_CardDAV_Backend_PDO($pdo); +$caldavBackend = new Sabre_CalDAV_Backend_PDO($pdo); + +/** + * The directory tree + * + * Basically this is an array which contains the 'top-level' directories in the + * WebDAV server. + */ +$nodes = array( + // /principals + new Sabre_CalDAV_Principal_Collection($principalBackend), + // /calendars + new Sabre_CalDAV_CalendarRootNode($principalBackend, $caldavBackend), + // /addressbook + new Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend), +); + +// The object tree needs in turn to be passed to the server class +$server = new Sabre_DAV_Server($nodes); +$server->setBaseUri($baseUri); + +// Plugins +$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV')); +$server->addPlugin(new Sabre_DAV_Browser_Plugin()); +$server->addPlugin(new Sabre_CalDAV_Plugin()); +$server->addPlugin(new Sabre_CardDAV_Plugin()); +$server->addPlugin(new Sabre_DAVACL_Plugin()); + +// And off we go! +$server->exec(); diff --git a/dav/SabreDAV/examples/simplefsserver.php b/dav/SabreDAV/examples/simplefsserver.php new file mode 100644 index 000000000..d7ea3f7c1 --- /dev/null +++ b/dav/SabreDAV/examples/simplefsserver.php @@ -0,0 +1,123 @@ +myPath = $myPath; + + } + + function getChildren() { + + $children = array(); + // Loop through the directory, and create objects for each node + foreach(scandir($this->myPath) as $node) { + + // Ignoring files staring with . + if ($node[0]==='.') continue; + + $children[] = $this->getChild($node); + + } + + return $children; + + } + + function getChild($name) { + + $path = $this->myPath . '/' . $name; + + // We have to throw a NotFound exception if the file didn't exist + if (!file_exists($this->myPath)) throw new Sabre_DAV_Exception_NotFound('The file with name: ' . $name . ' could not be found'); + // Some added security + + if ($name[0]=='.') throw new Sabre_DAV_Exception_NotFound('Access denied'); + + if (is_dir($path)) { + + return new MyDirectory($name); + + } else { + + return new MyFile($path); + + } + + } + + function getName() { + + return basename($this->myPath); + + } + +} + +class MyFile extends Sabre_DAV_File { + + private $myPath; + + function __construct($myPath) { + + $this->myPath = $myPath; + + } + + function getName() { + + return basename($this->myPath); + + } + + function get() { + + return fopen($this->myPath,'r'); + + } + + function getSize() { + + return filesize($this->myPath); + + } + +} + +// Make sure there is a directory in your current directory named 'public'. We will be exposing that directory to WebDAV +$rootNode = new MyDirectory($publicDir); + +// The rootNode needs to be passed to the server object. +$server = new Sabre_DAV_Server($rootNode); + +// And off we go! +$server->exec(); diff --git a/dav/SabreDAV/examples/sql/mysql.addressbook.sql b/dav/SabreDAV/examples/sql/mysql.addressbook.sql new file mode 100644 index 000000000..f603ad4c5 --- /dev/null +++ b/dav/SabreDAV/examples/sql/mysql.addressbook.sql @@ -0,0 +1,18 @@ +CREATE TABLE addressbooks ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(255), + displayname VARCHAR(255), + uri VARCHAR(200), + description TEXT, + ctag INT(11) UNSIGNED NOT NULL DEFAULT '1', + UNIQUE(principaluri, uri) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE cards ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + addressbookid INT(11) UNSIGNED NOT NULL, + carddata MEDIUMBLOB, + uri VARCHAR(200), + lastmodified INT(11) UNSIGNED +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + diff --git a/dav/SabreDAV/examples/sql/mysql.calendars.sql b/dav/SabreDAV/examples/sql/mysql.calendars.sql new file mode 100644 index 000000000..820253894 --- /dev/null +++ b/dav/SabreDAV/examples/sql/mysql.calendars.sql @@ -0,0 +1,27 @@ +CREATE TABLE calendarobjects ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + calendardata MEDIUMBLOB, + uri VARCHAR(200), + calendarid INTEGER UNSIGNED NOT NULL, + lastmodified INT(11) UNSIGNED, + etag VARCHAR(32), + size INT(11) UNSIGNED NOT NULL, + componenttype VARCHAR(8), + firstoccurence INT(11) UNSIGNED, + lastoccurence INT(11) UNSIGNED, + UNIQUE(calendarid, uri) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE calendars ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(100), + displayname VARCHAR(100), + uri VARCHAR(200), + ctag INTEGER UNSIGNED NOT NULL DEFAULT '0', + description TEXT, + calendarorder INTEGER UNSIGNED NOT NULL DEFAULT '0', + calendarcolor VARCHAR(10), + timezone TEXT, + components VARCHAR(20), + UNIQUE(principaluri, uri) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; diff --git a/dav/SabreDAV/examples/sql/mysql.locks.sql b/dav/SabreDAV/examples/sql/mysql.locks.sql new file mode 100644 index 000000000..2fafac9cb --- /dev/null +++ b/dav/SabreDAV/examples/sql/mysql.locks.sql @@ -0,0 +1,10 @@ +CREATE TABLE locks ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + owner VARCHAR(100), + timeout INTEGER UNSIGNED, + created INTEGER, + token VARCHAR(100), + scope TINYINT, + depth TINYINT, + uri text +); diff --git a/dav/SabreDAV/examples/sql/mysql.principals.sql b/dav/SabreDAV/examples/sql/mysql.principals.sql new file mode 100644 index 000000000..7a9e4669d --- /dev/null +++ b/dav/SabreDAV/examples/sql/mysql.principals.sql @@ -0,0 +1,22 @@ +CREATE TABLE principals ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + uri VARCHAR(200) NOT NULL, + email VARCHAR(80), + displayname VARCHAR(80), + vcardurl VARCHAR(80), + UNIQUE(uri) +); + +CREATE TABLE groupmembers ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principal_id INTEGER UNSIGNED NOT NULL, + member_id INTEGER UNSIGNED NOT NULL, + UNIQUE(principal_id, member_id) +); + + +INSERT INTO principals (uri,email,displayname) VALUES +('principals/admin', 'admin@example.org','Administrator'), +('principals/admin/calendar-proxy-read', null, null), +('principals/admin/calendar-proxy-write', null, null); + diff --git a/dav/SabreDAV/examples/sql/mysql.users.sql b/dav/SabreDAV/examples/sql/mysql.users.sql new file mode 100644 index 000000000..1244f596f --- /dev/null +++ b/dav/SabreDAV/examples/sql/mysql.users.sql @@ -0,0 +1,9 @@ +CREATE TABLE users ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(50), + digesta1 VARCHAR(32), + UNIQUE(username) +); + +INSERT INTO users (username,digesta1) VALUES +('admin', '87fd274b7b6c01e48d7c2f965da8ddf7'); diff --git a/dav/SabreDAV/examples/sql/pgsql.addressbook.sql b/dav/SabreDAV/examples/sql/pgsql.addressbook.sql new file mode 100644 index 000000000..c3ca8b291 --- /dev/null +++ b/dav/SabreDAV/examples/sql/pgsql.addressbook.sql @@ -0,0 +1,33 @@ +CREATE TABLE addressbooks ( + id SERIAL NOT NULL, + principaluri VARCHAR(255), + displayname VARCHAR(255), + uri VARCHAR(200), + description TEXT, + ctag INTEGER NOT NULL DEFAULT 1 +); + +ALTER TABLE ONLY addressbooks + ADD CONSTRAINT addressbooks_pkey PRIMARY KEY (id); + +CREATE UNIQUE INDEX addressbooks_ukey + ON addressbooks USING btree (principaluri, uri); + +CREATE TABLE cards ( + id SERIAL NOT NULL, + addressbookid INTEGER NOT NULL, + carddata TEXT, + uri VARCHAR(200), + lastmodified INTEGER +); + +ALTER TABLE ONLY cards + ADD CONSTRAINT cards_pkey PRIMARY KEY (id); + +CREATE UNIQUE INDEX cards_ukey + ON cards USING btree (addressbookid, uri); + +ALTER TABLE ONLY cards + ADD CONSTRAINT cards_addressbookid_fkey FOREIGN KEY (addressbookid) REFERENCES addressbooks(id) + ON DELETE CASCADE; + diff --git a/dav/SabreDAV/examples/sql/pgsql.calendars.sql b/dav/SabreDAV/examples/sql/pgsql.calendars.sql new file mode 100644 index 000000000..b97ef0291 --- /dev/null +++ b/dav/SabreDAV/examples/sql/pgsql.calendars.sql @@ -0,0 +1,41 @@ +CREATE TABLE calendars ( + id SERIAL NOT NULL, + principaluri VARCHAR(100), + displayname VARCHAR(100), + uri VARCHAR(200), + ctag INTEGER NOT NULL DEFAULT 0, + description TEXT, + calendarorder INTEGER NOT NULL DEFAULT 0, + calendarcolor VARCHAR(10), + timezone TEXT, + components VARCHAR(20) +); + +ALTER TABLE ONLY calendars + ADD CONSTRAINT calendars_pkey PRIMARY KEY (id); + +CREATE UNIQUE INDEX calendars_ukey + ON calendars USING btree (principaluri, uri); + +CREATE TABLE calendarobjects ( + id SERIAL NOT NULL, + calendarid INTEGER NOT NULL, + calendardata TEXT, + uri VARCHAR(200), + etag VARCHAR(32), + size INTEGER NOT NULL, + componenttype VARCHAR(8), + lastmodified INTEGER + firstoccurence INTEGER, + lastoccurence INTEGER +); + +ALTER TABLE ONLY calendarobjects + ADD CONSTRAINT calendarobjects_pkey PRIMARY KEY (id); + +CREATE UNIQUE INDEX calendarobjects_ukey + ON calendarobjects USING btree (calendarid, uri); + +ALTER TABLE ONLY calendarobjects + ADD CONSTRAINT calendarobjects_calendarid_fkey FOREIGN KEY (calendarid) REFERENCES calendars(id) + ON DELETE CASCADE; diff --git a/dav/SabreDAV/examples/sql/pgsql.locks.sql b/dav/SabreDAV/examples/sql/pgsql.locks.sql new file mode 100644 index 000000000..ca6c82e96 --- /dev/null +++ b/dav/SabreDAV/examples/sql/pgsql.locks.sql @@ -0,0 +1,13 @@ +CREATE TABLE locks ( + id SERIAL NOT NULL, + owner VARCHAR(100), + timeout INTEGER, + created INTEGER, + token VARCHAR(100), + scope smallint, + depth smallint, + uri text +); + +ALTER TABLE ONLY locks + ADD CONSTRAINT locks_pkey PRIMARY KEY (id); diff --git a/dav/SabreDAV/examples/sql/pgsql.principals.sql b/dav/SabreDAV/examples/sql/pgsql.principals.sql new file mode 100644 index 000000000..9e404fed4 --- /dev/null +++ b/dav/SabreDAV/examples/sql/pgsql.principals.sql @@ -0,0 +1,40 @@ +CREATE TABLE principals ( + id SERIAL NOT NULL, + uri VARCHAR(100) NOT NULL, + email VARCHAR(80), + displayname VARCHAR(80), + vcardurl VARCHAR(80) +); + +ALTER TABLE ONLY principals + ADD CONSTRAINT principals_pkey PRIMARY KEY (id); + +CREATE UNIQUE INDEX principals_ukey + ON principals USING btree (uri); + +CREATE TABLE groupmembers ( + id SERIAL NOT NULL, + principal_id INTEGER NOT NULL, + member_id INTEGER NOT NULL +); + +ALTER TABLE ONLY groupmembers + ADD CONSTRAINT groupmembers_pkey PRIMARY KEY (id); + +CREATE UNIQUE INDEX groupmembers_ukey + ON groupmembers USING btree (principal_id, member_id); + +ALTER TABLE ONLY groupmembers + ADD CONSTRAINT groupmembers_principal_id_fkey FOREIGN KEY (principal_id) REFERENCES principals(id) + ON DELETE CASCADE; + +-- Is this correct correct link ... or not? +-- ALTER TABLE ONLY groupmembers +-- ADD CONSTRAINT groupmembers_member_id_id_fkey FOREIGN KEY (member_id) REFERENCES users(id) +-- ON DELETE CASCADE; + +INSERT INTO principals (uri,email,displayname) VALUES +('principals/admin', 'admin@example.org','Administrator'), +('principals/admin/calendar-proxy-read', null, null), +('principals/admin/calendar-proxy-write', null, null); + diff --git a/dav/SabreDAV/examples/sql/pgsql.users.sql b/dav/SabreDAV/examples/sql/pgsql.users.sql new file mode 100644 index 000000000..939c931d8 --- /dev/null +++ b/dav/SabreDAV/examples/sql/pgsql.users.sql @@ -0,0 +1,15 @@ +CREATE TABLE users ( + id SERIAL NOT NULL, + username VARCHAR(50), + digesta1 VARCHAR(32), + UNIQUE(username) +); + +ALTER TABLE ONLY users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + +CREATE UNIQUE INDEX users_ukey + ON users USING btree (username); + +INSERT INTO users (username,digesta1) VALUES +('admin', '87fd274b7b6c01e48d7c2f965da8ddf7'); diff --git a/dav/SabreDAV/examples/sql/sqlite.addressbooks.sql b/dav/SabreDAV/examples/sql/sqlite.addressbooks.sql new file mode 100644 index 000000000..aa7639c5f --- /dev/null +++ b/dav/SabreDAV/examples/sql/sqlite.addressbooks.sql @@ -0,0 +1,17 @@ +CREATE TABLE addressbooks ( + id integer primary key asc, + principaluri text, + displayname text, + uri text, + description text, + ctag integer +); + +CREATE TABLE cards ( + id integer primary key asc, + addressbookid integer, + carddata blob, + uri text, + lastmodified integer +); + diff --git a/dav/SabreDAV/examples/sql/sqlite.calendars.sql b/dav/SabreDAV/examples/sql/sqlite.calendars.sql new file mode 100644 index 000000000..dce4f312e --- /dev/null +++ b/dav/SabreDAV/examples/sql/sqlite.calendars.sql @@ -0,0 +1,25 @@ +CREATE TABLE calendarobjects ( + id integer primary key asc, + calendardata blob, + uri text, + calendarid integer, + lastmodified integer, + etag text, + size integer, + componenttype text, + firstoccurence integer, + lastoccurence integer +); + +CREATE TABLE calendars ( + id integer primary key asc, + principaluri text, + displayname text, + uri text, + ctag integer, + description text, + calendarorder integer, + calendarcolor text, + timezone text, + components text +); diff --git a/dav/SabreDAV/examples/sql/sqlite.locks.sql b/dav/SabreDAV/examples/sql/sqlite.locks.sql new file mode 100644 index 000000000..fd89b41eb --- /dev/null +++ b/dav/SabreDAV/examples/sql/sqlite.locks.sql @@ -0,0 +1,12 @@ +BEGIN TRANSACTION; +CREATE TABLE locks ( + id integer primary key asc, + owner text, + timeout integer, + created integer, + token text, + scope integer, + depth integer, + uri text +); +COMMIT; diff --git a/dav/SabreDAV/examples/sql/sqlite.principals.sql b/dav/SabreDAV/examples/sql/sqlite.principals.sql new file mode 100644 index 000000000..09dbc4d24 --- /dev/null +++ b/dav/SabreDAV/examples/sql/sqlite.principals.sql @@ -0,0 +1,21 @@ +CREATE TABLE principals ( + id INTEGER PRIMARY KEY ASC, + uri TEXT, + email TEXT, + displayname TEXT, + vcardurl TEXT, + UNIQUE(uri) +); + +CREATE TABLE groupmembers ( + id INTEGER PRIMARY KEY ASC, + principal_id INTEGER, + member_id INTEGER, + UNIQUE(principal_id, member_id) +); + + +INSERT INTO principals (uri,email,displayname) VALUES ('principals/admin', 'admin@example.org','Administrator'); +INSERT INTO principals (uri,email,displayname) VALUES ('principals/admin/calendar-proxy-read', null, null); +INSERT INTO principals (uri,email,displayname) VALUES ('principals/admin/calendar-proxy-write', null, null); + diff --git a/dav/SabreDAV/examples/sql/sqlite.users.sql b/dav/SabreDAV/examples/sql/sqlite.users.sql new file mode 100644 index 000000000..f4b2c1674 --- /dev/null +++ b/dav/SabreDAV/examples/sql/sqlite.users.sql @@ -0,0 +1,9 @@ +CREATE TABLE users ( + id integer primary key asc, + username TEXT, + digesta1 TEXT, + UNIQUE(username) +); + +INSERT INTO users (username,digesta1) VALUES +('admin', '87fd274b7b6c01e48d7c2f965da8ddf7'); diff --git a/dav/SabreDAV/examples/webserver/apache2_htaccess.conf b/dav/SabreDAV/examples/webserver/apache2_htaccess.conf new file mode 100644 index 000000000..c5f29ba80 --- /dev/null +++ b/dav/SabreDAV/examples/webserver/apache2_htaccess.conf @@ -0,0 +1,16 @@ +RewriteEngine On +# This makes every request go to server.php +RewriteRule (.*) server.php [L] + +# Output buffering needs to be off, to prevent high memory usage +php_flag output_buffering off + +# This is also to prevent high memory usage +php_flag always_populate_raw_post_data off + +# This is almost a given, but magic quotes is *still* on on some +# linux distributions +php_flag magic_quotes_gpc off + +# SabreDAV is not compatible with mbstring function overloading +php_flag mbstring.func_overload off diff --git a/dav/SabreDAV/examples/webserver/apache2_vhost.conf b/dav/SabreDAV/examples/webserver/apache2_vhost.conf new file mode 100644 index 000000000..5a816e1f5 --- /dev/null +++ b/dav/SabreDAV/examples/webserver/apache2_vhost.conf @@ -0,0 +1,33 @@ +# This is a sample configuration to setup a dedicated Apache vhost for WebDAV. +# +# The main thing that should be configured is the servername, and the path to +# your SabreDAV installation (DocumentRoot). +# +# This configuration assumed mod_php5 is used, as it sets a few default php +# settings as well. + + + # Don't forget to change the server name + # ServerName dav.example.org + + # The DocumentRoot is also required + # DocumentRoot /home/sabredav/ + + RewriteEngine On + # This makes every request go to server.php + RewriteRule ^/(.*)$ /server.php [L] + + # Output buffering needs to be off, to prevent high memory usage + php_flag output_buffering off + + # This is also to prevent high memory usage + php_flag always_populate_raw_post_data off + + # This is almost a given, but magic quotes is *still* on on some + # linux distributions + php_flag magic_quotes_gpc off + + # SabreDAV is not compatible with mbstring function overloading + php_flag mbstring.func_overload off + + diff --git a/dav/SabreDAV/examples/webserver/apache2_vhost_cgi.conf b/dav/SabreDAV/examples/webserver/apache2_vhost_cgi.conf new file mode 100644 index 000000000..a034d7fca --- /dev/null +++ b/dav/SabreDAV/examples/webserver/apache2_vhost_cgi.conf @@ -0,0 +1,21 @@ +# This is a sample configuration to setup a dedicated Apache vhost for WebDAV. +# +# The main thing that should be configured is the servername, and the path to +# your SabreDAV installation (DocumentRoot). +# +# This configuration assumes CGI or FastCGI is used. + + + # Don't forget to change the server name + # ServerName dav.example.org + + # The DocumentRoot is also required + # DocumentRoot /home/sabredav/ + + # This makes every request go to server.php. This also makes sure + # the Authentication information is available. If your server script is + # not called server.php, be sure to change it. + RewriteEngine On + RewriteRule ^/(.*)$ /server.php [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + diff --git a/dav/SabreDAV/lib/Sabre.includes.php b/dav/SabreDAV/lib/Sabre.includes.php new file mode 100644 index 000000000..c13343736 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre.includes.php @@ -0,0 +1,26 @@ + array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param mixed $calendarId + * @param array $mutations + * @return bool|array + */ + public function updateCalendar($calendarId, array $mutations) { + + return false; + + } + + /** + * Delete a calendar and all it's objects + * + * @param mixed $calendarId + * @return void + */ + abstract function deleteCalendar($calendarId); + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * calendarid - The calendarid as it was passed to this function. + * * size - The size of the calendar objects, in bytes. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param mixed $calendarId + * @return array + */ + abstract function getCalendarObjects($calendarId); + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param mixed $calendarId + * @param string $objectUri + * @return array + */ + abstract function getCalendarObject($calendarId,$objectUri); + + /** + * Creates a new calendar object. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + abstract function createCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Updates an existing calendarobject, based on it's uri. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + abstract function updateCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Deletes an existing calendar object. + * + * @param mixed $calendarId + * @param string $objectUri + * @return void + */ + abstract function deleteCalendarObject($calendarId,$objectUri); + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * Note that it is extremely likely that getCalendarObject for every path + * returned from this method will be called almost immediately after. You + * may want to anticipate this to speed up these requests. + * + * This method provides a default implementation, which parses *all* the + * iCalendar objects in the specified calendar. + * + * This default may well be good enough for personal use, and calendars + * that aren't very large. But if you anticipate high usage, big calendars + * or high loads, you are strongly adviced to optimize certain paths. + * + * The best way to do so is override this method and to optimize + * specifically for 'common filters'. + * + * Requests that are extremely common are: + * * requests for just VEVENTS + * * requests for just VTODO + * * requests with a time-range-filter on either VEVENT or VTODO. + * + * ..and combinations of these requests. It may not be worth it to try to + * handle every possible situation and just rely on the (relatively + * easy to use) CalendarQueryValidator to handle the rest. + * + * Note that especially time-range-filters may be difficult to parse. A + * time-range filter specified on a VEVENT must for instance also handle + * recurrence rules correctly. + * A good example of how to interprete all these filters can also simply + * be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct + * as possible, so it gives you a good idea on what type of stuff you need + * to think of. + * + * @param mixed $calendarId + * @param array $filters + * @return array + */ + public function calendarQuery($calendarId, array $filters) { + + $result = array(); + $objects = $this->getCalendarObjects($calendarId); + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + + foreach($objects as $object) { + + if ($this->validateFilterForObject($object, $filters)) { + $result[] = $object['uri']; + } + + } + + return $result; + + } + + /** + * This method validates if a filters (as passed to calendarQuery) matches + * the given object. + * + * @param array $object + * @param array $filter + * @return bool + */ + protected function validateFilterForObject(array $object, array $filters) { + + // Unfortunately, setting the 'calendardata' here is optional. If + // it was excluded, we actually need another call to get this as + // well. + if (!isset($object['calendardata'])) { + $object = $this->getCalendarObject($object['calendarid'], $object['uri']); + } + + $vObject = Sabre_VObject_Reader::read($object['calendardata']); + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + return $validator->validate($vObject, $filters); + + } + + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Backend/PDO.php b/dav/SabreDAV/lib/Sabre/CalDAV/Backend/PDO.php new file mode 100644 index 000000000..aafea1c55 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Backend/PDO.php @@ -0,0 +1,657 @@ + 'displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', + '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', + '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + ); + + /** + * Creates the backend + * + * @param PDO $pdo + * @param string $calendarTableName + * @param string $calendarObjectTableName + */ + public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') { + + $this->pdo = $pdo; + $this->calendarTableName = $calendarTableName; + $this->calendarObjectTableName = $calendarObjectTableName; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principaluri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + public function getCalendarsForUser($principalUri) { + + $fields = array_values($this->propertyMap); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'ctag'; + $fields[] = 'components'; + $fields[] = 'principaluri'; + + // Making fields a comma-delimited list + $fields = implode(', ', $fields); + $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC"); + $stmt->execute(array($principalUri)); + + $calendars = array(); + while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + + $components = array(); + if ($row['components']) { + $components = explode(',',$row['components']); + } + + $calendar = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0', + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components), + ); + + + foreach($this->propertyMap as $xmlName=>$dbName) { + $calendar[$xmlName] = $row[$dbName]; + } + + $calendars[] = $calendar; + + } + + return $calendars; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return string + */ + public function createCalendar($principalUri, $calendarUri, array $properties) { + + $fieldNames = array( + 'principaluri', + 'uri', + 'ctag', + ); + $values = array( + ':principaluri' => $principalUri, + ':uri' => $calendarUri, + ':ctag' => 1, + ); + + // Default value + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $fieldNames[] = 'components'; + if (!isset($properties[$sccs])) { + $values[':components'] = 'VEVENT,VTODO'; + } else { + if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) { + throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet'); + } + $values[':components'] = implode(',',$properties[$sccs]->getValue()); + } + + foreach($this->propertyMap as $xmlName=>$dbName) { + if (isset($properties[$xmlName])) { + + $values[':' . $dbName] = $properties[$xmlName]; + $fieldNames[] = $dbName; + } + } + + $stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); + $stmt->execute($values); + + return $this->pdo->lastInsertId(); + + } + + /** + * Updates properties for a calendar. + * + * The mutations array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existent property is always successful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $mutations + * @return bool|array + */ + public function updateCalendar($calendarId, array $mutations) { + + $newValues = array(); + $result = array( + 200 => array(), // Ok + 403 => array(), // Forbidden + 424 => array(), // Failed Dependency + ); + + $hasError = false; + + foreach($mutations as $propertyName=>$propertyValue) { + + // We don't know about this property. + if (!isset($this->propertyMap[$propertyName])) { + $hasError = true; + $result[403][$propertyName] = null; + unset($mutations[$propertyName]); + continue; + } + + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + + } + + // If there were any errors we need to fail the request + if ($hasError) { + // Properties has the remaining properties + foreach($mutations as $propertyName=>$propertyValue) { + $result[424][$propertyName] = null; + } + + // Removing unused statuscodes for cleanliness + foreach($result as $status=>$properties) { + if (is_array($properties) && count($properties)===0) unset($result[$status]); + } + + return $result; + + } + + // Success + + // Now we're generating the sql query. + $valuesSql = array(); + foreach($newValues as $fieldName=>$value) { + $valuesSql[] = $fieldName . ' = ?'; + } + $valuesSql[] = 'ctag = ctag + 1'; + + $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?"); + $newValues['id'] = $calendarId; + $stmt->execute(array_values($newValues)); + + return true; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + public function deleteCalendar($calendarId) { + + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * calendarid - The calendarid as it was passed to this function. + * * size - The size of the calendar objects, in bytes. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param string $calendarId + * @return array + */ + public function getCalendarObjects($calendarId) { + + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + + $result = array(); + foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $result[] = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => $row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'calendarid' => $row['calendarid'], + 'size' => (int)$row['size'], + ); + } + + return $result; + + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + public function getCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId, $objectUri)); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + + if(!$row) return null; + + return array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => $row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'calendarid' => $row['calendarid'], + 'size' => (int)$row['size'], + 'calendardata' => $row['calendardata'], + ); + + } + + + /** + * Creates a new calendar object. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + public function createCalendarObject($calendarId,$objectUri,$calendarData) { + + $extraData = $this->getDenormalizedData($calendarData); + + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence) VALUES (?,?,?,?,?,?,?,?,?)'); + $stmt->execute(array( + $calendarId, + $objectUri, + $calendarData, + time(), + $extraData['etag'], + $extraData['size'], + $extraData['componentType'], + $extraData['firstOccurence'], + $extraData['lastOccurence'], + )); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + return '"' . $extraData['etag'] . '"'; + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + public function updateCalendarObject($calendarId,$objectUri,$calendarData) { + + $extraData = $this->getDenormalizedData($calendarData); + + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarData,time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'] ,$calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + return '"' . $extraData['etag'] . '"'; + + } + + /** + * Parses some information from calendar objects, used for optimized + * calendar-queries. + * + * Returns an array with the following keys: + * * etag + * * size + * * componentType + * * firstOccurence + * * lastOccurence + * + * @param string $calendarData + * @return array + */ + protected function getDenormalizedData($calendarData) { + + $vObject = Sabre_VObject_Reader::read($calendarData); + $componentType = null; + $component = null; + $firstOccurence = null; + $lastOccurence = null; + foreach($vObject->getComponents() as $component) { + if ($component->name!=='VTIMEZONE') { + $componentType = $component->name; + break; + } + } + if (!$componentType) { + throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); + } + if ($componentType === 'VEVENT') { + $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); + // Finding the last occurence is a bit harder + if (!isset($component->RRULE)) { + if (isset($component->DTEND)) { + $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); + } elseif (isset($component->DURATION)) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value)); + $lastOccurence = $endDate->getTimeStamp(); + } elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate->modify('+1 day'); + $lastOccurence = $endDate->getTimeStamp(); + } else { + $lastOccurence = $firstOccurence; + } + } else { + $it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID); + $maxDate = new DateTime(self::MAX_DATE); + if ($it->isInfinite()) { + $lastOccurence = $maxDate->getTimeStamp(); + } else { + $end = $it->getDtEnd(); + while($it->valid() && $end < $maxDate) { + $end = $it->getDtEnd(); + $it->next(); + + } + $lastOccurence = $end->getTimeStamp(); + } + + } + } + + return array( + 'etag' => md5($calendarData), + 'size' => strlen($calendarData), + 'componentType' => $componentType, + 'firstOccurence' => $firstOccurence, + 'lastOccurence' => $lastOccurence, + ); + + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + public function deleteCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * Note that it is extremely likely that getCalendarObject for every path + * returned from this method will be called almost immediately after. You + * may want to anticipate this to speed up these requests. + * + * This method provides a default implementation, which parses *all* the + * iCalendar objects in the specified calendar. + * + * This default may well be good enough for personal use, and calendars + * that aren't very large. But if you anticipate high usage, big calendars + * or high loads, you are strongly adviced to optimize certain paths. + * + * The best way to do so is override this method and to optimize + * specifically for 'common filters'. + * + * Requests that are extremely common are: + * * requests for just VEVENTS + * * requests for just VTODO + * * requests with a time-range-filter on a VEVENT. + * + * ..and combinations of these requests. It may not be worth it to try to + * handle every possible situation and just rely on the (relatively + * easy to use) CalendarQueryValidator to handle the rest. + * + * Note that especially time-range-filters may be difficult to parse. A + * time-range filter specified on a VEVENT must for instance also handle + * recurrence rules correctly. + * A good example of how to interprete all these filters can also simply + * be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct + * as possible, so it gives you a good idea on what type of stuff you need + * to think of. + * + * This specific implementation (for the PDO) backend optimizes filters on + * specific components, and VEVENT time-ranges. + * + * @param string $calendarId + * @param array $filters + * @return array + */ + public function calendarQuery($calendarId, array $filters) { + + $result = array(); + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + + $componentType = null; + $requirePostFilter = true; + $timeRange = null; + + // if no filters were specified, we don't need to filter after a query + if (!$filters['prop-filters'] && !$filters['comp-filters']) { + $requirePostFilter = false; + } + + // Figuring out if there's a component filter + if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) { + $componentType = $filters['comp-filters'][0]['name']; + + // Checking if we need post-filters + if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) { + $requirePostFilter = false; + } + // There was a time-range filter + if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { + $timeRange = $filters['comp-filters'][0]['time-range']; + } + + } + + if ($requirePostFilter) { + $query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; + } else { + $query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; + } + + $values = array( + 'calendarid' => $calendarId, + ); + + if ($componentType) { + $query.=" AND componenttype = :componenttype"; + $values['componenttype'] = $componentType; + } + + if ($timeRange && $timeRange['start']) { + $query.=" AND lastoccurence > :startdate"; + $values['startdate'] = $timeRange['start']->getTimeStamp(); + } + if ($timeRange && $timeRange['end']) { + $query.=" AND firstoccurence < :enddate"; + $values['enddate'] = $timeRange['end']->getTimeStamp(); + } + + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + $result = array(); + while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if ($requirePostFilter) { + if (!$this->validateFilterForObject($row, $filters)) { + continue; + } + } + $result[] = $row['uri']; + + } + + return $result; + + } +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Calendar.php b/dav/SabreDAV/lib/Sabre/CalDAV/Calendar.php new file mode 100644 index 000000000..81708198b --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Calendar.php @@ -0,0 +1,366 @@ +caldavBackend = $caldavBackend; + $this->principalBackend = $principalBackend; + $this->calendarInfo = $calendarInfo; + + + } + + /** + * Returns the name of the calendar + * + * @return string + */ + public function getName() { + + return $this->calendarInfo['uri']; + + } + + /** + * Updates properties such as the display name and description + * + * @param array $mutations + * @return array + */ + public function updateProperties($mutations) { + + return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations); + + } + + /** + * Returns the list of properties + * + * @param array $requestedProperties + * @return array + */ + public function getProperties($requestedProperties) { + + $response = array(); + + foreach($requestedProperties as $prop) switch($prop) { + + case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); + break; + case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet(); + break; + case '{DAV:}owner' : + $response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']); + break; + default : + if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; + break; + + } + return $response; + + } + + /** + * Returns a calendar object + * + * The contained calendar objects are for example Events or Todo's. + * + * @param string $name + * @return Sabre_CalDAV_ICalendarObject + */ + public function getChild($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found'); + return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + + } + + /** + * Returns the full list of calendar objects + * + * @return array + */ + public function getChildren() { + + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + } + return $children; + + } + + /** + * Checks if a child-node exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) + return false; + else + return true; + + } + + /** + * Creates a new directory + * + * We actually block this, as subdirectories are not allowed in calendars. + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed'); + + } + + /** + * Creates a new file + * + * The contents of the new file must be a valid ICalendar string. + * + * @param string $name + * @param resource $calendarData + * @return string|null + */ + public function createFile($name,$calendarData = null) { + + if (is_resource($calendarData)) { + $calendarData = stream_get_contents($calendarData); + } + return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); + + } + + /** + * Deletes the calendar. + * + * @return void + */ + public function delete() { + + $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); + + } + + /** + * Renames the calendar. Note that most calendars use the + * {DAV:}displayname to display a name to display a name. + * + * @param string $newName + * @return void + */ + public function setName($newName) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported'); + + } + + /** + * Returns the last modification date as a unix timestamp. + * + * @return void + */ + public function getLastModified() { + + return null; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->calendarInfo['principaluri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ), + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet(); + + // We need to inject 'read-free-busy' in the tree, aggregated under + // {DAV:}read. + foreach($default['aggregates'] as &$agg) { + + if ($agg['privilege'] !== '{DAV:}read') continue; + + $agg['aggregates'][] = array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + ); + + } + return $default; + + } + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * @param array $filters + * @return array + */ + public function calendarQuery(array $filters) { + + return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/CalendarObject.php b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarObject.php new file mode 100644 index 000000000..1adf8689c --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarObject.php @@ -0,0 +1,273 @@ +caldavBackend = $caldavBackend; + + if (!isset($objectData['calendarid'])) { + throw new InvalidArgumentException('The objectData argument must contain a \'calendarid\' property'); + } + if (!isset($objectData['uri'])) { + throw new InvalidArgumentException('The objectData argument must contain an \'uri\' property'); + } + + $this->calendarInfo = $calendarInfo; + $this->objectData = $objectData; + + } + + /** + * Returns the uri for this object + * + * @return string + */ + public function getName() { + + return $this->objectData['uri']; + + } + + /** + * Returns the ICalendar-formatted object + * + * @return string + */ + public function get() { + + // Pre-populating the 'calendardata' is optional, if we don't have it + // already we fetch it from the backend. + if (!isset($this->objectData['calendardata'])) { + $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']); + } + return $this->objectData['calendardata']; + + } + + /** + * Updates the ICalendar-formatted object + * + * @param string|resource $calendarData + * @return string + */ + public function put($calendarData) { + + if (is_resource($calendarData)) { + $calendarData = stream_get_contents($calendarData); + } + $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); + $this->objectData['calendardata'] = $calendarData; + $this->objectData['etag'] = $etag; + + return $etag; + + } + + /** + * Deletes the calendar object + * + * @return void + */ + public function delete() { + + $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']); + + } + + /** + * Returns the mime content-type + * + * @return string + */ + public function getContentType() { + + return 'text/calendar; charset=utf-8'; + + } + + /** + * Returns an ETag for this object. + * + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * @return string + */ + public function getETag() { + + if (isset($this->objectData['etag'])) { + return $this->objectData['etag']; + } else { + return '"' . md5($this->get()). '"'; + } + + } + + /** + * Returns the last modification date as a unix timestamp + * + * @return int + */ + public function getLastModified() { + + return $this->objectData['lastmodified']; + + } + + /** + * Returns the size of this object in bytes + * + * @return int + */ + public function getSize() { + + if (array_key_exists('size',$this->objectData)) { + return $this->objectData['size']; + } else { + return strlen($this->get()); + } + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->calendarInfo['principaluri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryParser.php b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryParser.php new file mode 100644 index 000000000..bd0d34338 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryParser.php @@ -0,0 +1,296 @@ +dom = $dom; + + $this->xpath = new DOMXPath($dom); + $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); + $this->xpath->registerNameSpace('dav','urn:DAV'); + + } + + /** + * Parses the request. + * + * @return void + */ + public function parse() { + + $filterNode = null; + + $filter = $this->xpath->query('/cal:calendar-query/cal:filter'); + if ($filter->length !== 1) { + throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed'); + } + + $compFilters = $this->parseCompFilters($filter->item(0)); + if (count($compFilters)!==1) { + throw new Sabre_DAV_Exception_BadRequest('There must be exactly 1 top-level comp-filter.'); + } + + $this->filters = $compFilters[0]; + $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild)); + + $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand'); + if ($expand->length>0) { + $this->expand = $this->parseExpand($expand->item(0)); + } + + + } + + /** + * Parses all the 'comp-filter' elements from a node + * + * @param DOMElement $parentNode + * @return array + */ + protected function parseCompFilters(DOMElement $parentNode) { + + $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode); + $result = array(); + + for($ii=0; $ii < $compFilterNodes->length; $ii++) { + + $compFilterNode = $compFilterNodes->item($ii); + + $compFilter = array(); + $compFilter['name'] = $compFilterNode->getAttribute('name'); + $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0; + $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode); + $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode); + $compFilter['time-range'] = $this->parseTimeRange($compFilterNode); + + if ($compFilter['time-range'] && !in_array($compFilter['name'],array( + 'VEVENT', + 'VTODO', + 'VJOURNAL', + 'VFREEBUSY', + 'VALARM', + ))) { + throw new Sabre_DAV_Exception_BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); + }; + + $result[] = $compFilter; + + } + + return $result; + + } + + /** + * Parses all the prop-filter elements from a node + * + * @param DOMElement $parentNode + * @return array + */ + protected function parsePropFilters(DOMElement $parentNode) { + + $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode); + $result = array(); + + for ($ii=0; $ii < $propFilterNodes->length; $ii++) { + + $propFilterNode = $propFilterNodes->item($ii); + $propFilter = array(); + $propFilter['name'] = $propFilterNode->getAttribute('name'); + $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0; + $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode); + $propFilter['text-match'] = $this->parseTextMatch($propFilterNode); + $propFilter['time-range'] = $this->parseTimeRange($propFilterNode); + + $result[] = $propFilter; + + } + + return $result; + + } + + /** + * Parses the param-filter element + * + * @param DOMElement $parentNode + * @return array + */ + protected function parseParamFilters(DOMElement $parentNode) { + + $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode); + $result = array(); + + for($ii=0;$ii<$paramFilterNodes->length;$ii++) { + + $paramFilterNode = $paramFilterNodes->item($ii); + $paramFilter = array(); + $paramFilter['name'] = $paramFilterNode->getAttribute('name'); + $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0; + $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode); + + $result[] = $paramFilter; + + } + + return $result; + + } + + /** + * Parses the text-match element + * + * @param DOMElement $parentNode + * @return array|null + */ + protected function parseTextMatch(DOMElement $parentNode) { + + $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode); + + if ($textMatchNodes->length === 0) + return null; + + $textMatchNode = $textMatchNodes->item(0); + $negateCondition = $textMatchNode->getAttribute('negate-condition'); + $negateCondition = $negateCondition==='yes'; + $collation = $textMatchNode->getAttribute('collation'); + if (!$collation) $collation = 'i;ascii-casemap'; + + return array( + 'negate-condition' => $negateCondition, + 'collation' => $collation, + 'value' => $textMatchNode->nodeValue + ); + + } + + /** + * Parses the time-range element + * + * @param DOMElement $parentNode + * @return array|null + */ + protected function parseTimeRange(DOMElement $parentNode) { + + $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode); + if ($timeRangeNodes->length === 0) { + return null; + } + + $timeRangeNode = $timeRangeNodes->item(0); + + if ($start = $timeRangeNode->getAttribute('start')) { + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + } else { + $start = null; + } + if ($end = $timeRangeNode->getAttribute('end')) { + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + } else { + $end = null; + } + + if (!is_null($start) && !is_null($end) && $end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter'); + } + + return array( + 'start' => $start, + 'end' => $end, + ); + + } + + /** + * Parses the CALDAV:expand element + * + * @param DOMElement $parentNode + * @return void + */ + protected function parseExpand(DOMElement $parentNode) { + + $start = $parentNode->getAttribute('start'); + if(!$start) { + throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element'); + } + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + + $end = $parentNode->getAttribute('end'); + if(!$end) { + throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element'); + } + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + + if ($end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); + } + + return array( + 'start' => $start, + 'end' => $end, + ); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryValidator.php b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryValidator.php new file mode 100644 index 000000000..4bcd32cdf --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryValidator.php @@ -0,0 +1,369 @@ +name !== $filters['name']) { + return false; + } + + return + $this->validateCompFilters($vObject, $filters['comp-filters']) && + $this->validatePropFilters($vObject, $filters['prop-filters']); + + + } + + /** + * This method checks the validity of comp-filters. + * + * A list of comp-filters needs to be specified. Also the parent of the + * component we're checking should be specified, not the component to check + * itself. + * + * @param Sabre_VObject_Component $parent + * @param array $filters + * @return bool + */ + protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent->$filter['name']); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach($parent->$filter['name'] as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['comp-filters'] && !$filter['prop-filters']) { + continue; + } + + // If there are sub-filters, we need to find at least one component + // for which the subfilters hold true. + foreach($parent->$filter['name'] as $subComponent) { + + if ( + $this->validateCompFilters($subComponent, $filter['comp-filters']) && + $this->validatePropFilters($subComponent, $filter['prop-filters'])) { + // We had a match, so this comp-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-comp-filters or + // sub-prop-filters and there was no match. This means this filter + // needs to return false. + return false; + + } + + // If we got here it means we got through all comp-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of prop-filters. + * + * A list of prop-filters needs to be specified. Also the parent of the + * property we're checking should be specified, not the property to check + * itself. + * + * @param Sabre_VObject_Component $parent + * @param array $filters + * @return bool + */ + protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent->$filter['name']); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach($parent->$filter['name'] as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['param-filters'] && !$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one property + // for which the subfilters hold true. + foreach($parent->$filter['name'] as $subComponent) { + + if( + $this->validateParamFilters($subComponent, $filter['param-filters']) && + (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match'])) + ) { + // We had a match, so this prop-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-param-filters or + // text-match filters and there was no match. This means the + // filter needs to return false. + return false; + + } + + // If we got here it means we got through all prop-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of param-filters. + * + * A list of param-filters needs to be specified. Also the parent of the + * parameter we're checking should be specified, not the parameter to check + * itself. + * + * @param Sabre_VObject_Property $parent + * @param array $filters + * @return bool + */ + protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent[$filter['name']]); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if (!$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one parameter + // for which the subfilters hold true. + foreach($parent[$filter['name']] as $subParam) { + + if($this->validateTextMatch($subParam,$filter['text-match'])) { + // We had a match, so this param-filter succeeds + continue 2; + } + + } + + // If we got here it means there was a text-match filter and there + // were no matches. This means the filter needs to return false. + return false; + + } + + // If we got here it means we got through all param-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of a text-match. + * + * A single text-match should be specified as well as the specific property + * or parameter we need to validate. + * + * @param Sabre_VObject_Node $parent + * @param array $textMatch + * @return bool + */ + protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) { + + $value = (string)$parent; + + $isMatching = Sabre_DAV_StringUtil::textMatch($value, $textMatch['value'], $textMatch['collation']); + + return ($textMatch['negate-condition'] xor $isMatching); + + } + + /** + * Validates if a component matches the given time range. + * + * This is all based on the rules specified in rfc4791, which are quite + * complex. + * + * @param Sabre_VObject_Node $component + * @param DateTime $start + * @param DateTime $end + * @return bool + */ + protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) { + + if (is_null($start)) { + $start = new DateTime('1900-01-01'); + } + if (is_null($end)) { + $end = new DateTime('3000-01-01'); + } + + switch($component->name) { + + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + + return $component->isInTimeRange($start, $end); + + case 'VALARM' : + + // If the valarm is wrapped in a recurring event, we need to + // expand the recursions, and validate each. + // + // Our datamodel doesn't easily allow us to do this straight + // in the VALARM component code, so this is a hack, and an + // expensive one too. + if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { + + // Fire up the iterator! + $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); + while($it->valid()) { + $expandedEvent = $it->getEventObject(); + + // We need to check from these expanded alarms, which + // one is the first to trigger. Based on this, we can + // determine if we can 'give up' expanding events. + $firstAlarm = null; + foreach($expandedEvent->VALARM as $expandedAlarm) { + + $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); + if ($expandedAlarm->isInTimeRange($start, $end)) { + return true; + } + + if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') { + // This is an alarm with a non-relative trigger + // time, likely created by a buggy client. The + // implication is that every alarm in this + // recurring event trigger at the exact same + // time. It doesn't make sense to traverse + // further. + } else { + // We store the first alarm as a means to + // figure out when we can stop traversing. + if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { + $firstAlarm = $effectiveTrigger; + } + } + + } + if (is_null($firstAlarm)) { + // No alarm was found. + // + // Or technically: No alarm that will change for + // every instance of the recurrence was found, + // which means we can assume there was no match. + return false; + } + if ($firstAlarm > $end) { + return false; + } + $it->next(); + } + return false; + } else { + return $component->isInTimeRange($start, $end); + } + + case 'VFREEBUSY' : + throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); + + case 'COMPLETED' : + case 'CREATED' : + case 'DTEND' : + case 'DTSTAMP' : + case 'DTSTART' : + case 'DUE' : + case 'LAST-MODIFIED' : + return ($start <= $component->getDateTime() && $end >= $component->getDateTime()); + + + + default : + throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); + + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/CalendarRootNode.php b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarRootNode.php new file mode 100644 index 000000000..5c4265c51 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/CalendarRootNode.php @@ -0,0 +1,76 @@ +caldavBackend = $caldavBackend; + + } + + /** + * Returns the nodename + * + * We're overriding this, because the default will be the 'principalPrefix', + * and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT + * + * @return string + */ + public function getName() { + + return Sabre_CalDAV_Plugin::CALENDAR_ROOT; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return Sabre_DAV_INode + */ + public function getChildForPrincipal(array $principal) { + + return new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/ICSExportPlugin.php b/dav/SabreDAV/lib/Sabre/CalDAV/ICSExportPlugin.php new file mode 100644 index 000000000..ec42b406b --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/ICSExportPlugin.php @@ -0,0 +1,139 @@ +server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); + + } + + /** + * 'beforeMethod' event handles. This event handles intercepts GET requests ending + * with ?export + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + if ($method!='GET') return; + if ($this->server->httpRequest->getQueryString()!='export') return; + + // splitting uri + list($uri) = explode('?',$uri,2); + + $node = $this->server->tree->getNodeForPath($uri); + + if (!($node instanceof Sabre_CalDAV_Calendar)) return; + + // Checking ACL, if available. + if ($aclPlugin = $this->server->getPlugin('acl')) { + $aclPlugin->checkPrivileges($uri, '{DAV:}read'); + } + + $this->server->httpResponse->setHeader('Content-Type','text/calendar'); + $this->server->httpResponse->sendStatus(200); + + $nodes = $this->server->getPropertiesForPath($uri, array( + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data', + ),1); + + $this->server->httpResponse->sendBody($this->generateICS($nodes)); + + // Returning false to break the event chain + return false; + + } + + /** + * Merges all calendar objects, and builds one big ics export + * + * @param array $nodes + * @return string + */ + public function generateICS(array $nodes) { + + $calendar = new Sabre_VObject_Component('vcalendar'); + $calendar->version = '2.0'; + if (Sabre_DAV_Server::$exposeVersion) { + $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; + } else { + $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; + } + $calendar->calscale = 'GREGORIAN'; + + $collectedTimezones = array(); + + $timezones = array(); + $objects = array(); + + foreach($nodes as $node) { + + if (!isset($node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'])) { + continue; + } + $nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data']; + + $nodeComp = Sabre_VObject_Reader::read($nodeData); + + foreach($nodeComp->children() as $child) { + + switch($child->name) { + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + $objects[] = $child; + break; + + // VTIMEZONE is special, because we need to filter out the duplicates + case 'VTIMEZONE' : + // Naively just checking tzid. + if (in_array((string)$child->TZID, $collectedTimezones)) continue; + + $timezones[] = $child; + $collectedTimezones[] = $child->TZID; + break; + + } + + } + + } + + foreach($timezones as $tz) $calendar->add($tz); + foreach($objects as $obj) $calendar->add($obj); + + return $calendar->serialize(); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/ICalendar.php b/dav/SabreDAV/lib/Sabre/CalDAV/ICalendar.php new file mode 100644 index 000000000..40aa9f957 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/ICalendar.php @@ -0,0 +1,35 @@ +imipHandler = $imipHandler; + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + // The MKCALENDAR is only available on unmapped uri's, whose + // parents extend IExtendedCollection + list($parent, $name) = Sabre_DAV_URLUtil::splitPath($uri); + + $node = $this->server->tree->getNodeForPath($parent); + + if ($node instanceof Sabre_DAV_IExtendedCollection) { + try { + $node->getChild($name); + } catch (Sabre_DAV_Exception_NotFound $e) { + return array('MKCALENDAR'); + } + } + return array(); + + } + + /** + * Returns a list of features for the DAV: HTTP header. + * + * @return array + */ + public function getFeatures() { + + return array('calendar-access', 'calendar-proxy'); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre_DAV_Server::getPlugin + * + * @return string + */ + public function getPluginName() { + + return 'caldav'; + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + public function getSupportedReportSet($uri) { + + $node = $this->server->tree->getNodeForPath($uri); + + $reports = array(); + if ($node instanceof Sabre_CalDAV_ICalendar || $node instanceof Sabre_CalDAV_ICalendarObject) { + $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget'; + $reports[] = '{' . self::NS_CALDAV . '}calendar-query'; + } + if ($node instanceof Sabre_CalDAV_ICalendar) { + $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; + } + return $reports; + + } + + /** + * Initializes the plugin + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000); + $server->subscribeEvent('report',array($this,'report')); + $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); + $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); + $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); + $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); + $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); + + $server->xmlNamespaces[self::NS_CALDAV] = 'cal'; + $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; + + $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet'; + + $server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; + $server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; + $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; + $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; + + array_push($server->protectedProperties, + + '{' . self::NS_CALDAV . '}supported-calendar-component-set', + '{' . self::NS_CALDAV . '}supported-calendar-data', + '{' . self::NS_CALDAV . '}max-resource-size', + '{' . self::NS_CALDAV . '}min-date-time', + '{' . self::NS_CALDAV . '}max-date-time', + '{' . self::NS_CALDAV . '}max-instances', + '{' . self::NS_CALDAV . '}max-attendees-per-instance', + '{' . self::NS_CALDAV . '}calendar-home-set', + '{' . self::NS_CALDAV . '}supported-collation-set', + '{' . self::NS_CALDAV . '}calendar-data', + + // scheduling extension + '{' . self::NS_CALDAV . '}schedule-inbox-URL', + '{' . self::NS_CALDAV . '}schedule-outbox-URL', + '{' . self::NS_CALDAV . '}calendar-user-address-set', + '{' . self::NS_CALDAV . '}calendar-user-type', + + // CalendarServer extensions + '{' . self::NS_CALENDARSERVER . '}getctag', + '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', + '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for' + + ); + } + + /** + * This function handles support for the MKCALENDAR method + * + * @param string $method + * @param string $uri + * @return bool + */ + public function unknownMethod($method, $uri) { + + switch ($method) { + case 'MKCALENDAR' : + $this->httpMkCalendar($uri); + // false is returned to stop the propagation of the + // unknownMethod event. + return false; + case 'POST' : + // Checking if we're talking to an outbox + try { + $node = $this->server->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + return; + } + if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox) + return; + + $this->outboxRequest($node); + return false; + + } + + } + + /** + * This functions handles REPORT requests specific to CalDAV + * + * @param string $reportName + * @param DOMNode $dom + * @return bool + */ + public function report($reportName,$dom) { + + switch($reportName) { + case '{'.self::NS_CALDAV.'}calendar-multiget' : + $this->calendarMultiGetReport($dom); + return false; + case '{'.self::NS_CALDAV.'}calendar-query' : + $this->calendarQueryReport($dom); + return false; + case '{'.self::NS_CALDAV.'}free-busy-query' : + $this->freeBusyQueryReport($dom); + return false; + + } + + + } + + /** + * This function handles the MKCALENDAR HTTP method, which creates + * a new calendar. + * + * @param string $uri + * @return void + */ + public function httpMkCalendar($uri) { + + // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support + // for clients matching iCal in the user agent + //$ua = $this->server->httpRequest->getHeader('User-Agent'); + //if (strpos($ua,'iCal/')!==false) { + // throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); + //} + + $body = $this->server->httpRequest->getBody(true); + $properties = array(); + + if ($body) { + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + foreach($dom->firstChild->childNodes as $child) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue; + foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { + $properties[$k] = $prop; + } + + } + } + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); + + $this->server->createCollection($uri,$resourceType,$properties); + + $this->server->httpResponse->sendStatus(201); + $this->server->httpResponse->setHeader('Content-Length',0); + } + + /** + * beforeGetProperties + * + * This method handler is invoked before any after properties for a + * resource are fetched. This allows us to add in any CalDAV specific + * properties. + * + * @param string $path + * @param Sabre_DAV_INode $node + * @param array $requestedProperties + * @param array $returnedProperties + * @return void + */ + public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) { + + if ($node instanceof Sabre_DAVACL_IPrincipal) { + + // calendar-home-set property + $calHome = '{' . self::NS_CALDAV . '}calendar-home-set'; + if (in_array($calHome,$requestedProperties)) { + $principalId = $node->getName(); + $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/'; + unset($requestedProperties[$calHome]); + $returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath); + } + + // schedule-outbox-URL property + $scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL'; + if (in_array($scheduleProp,$requestedProperties)) { + $principalId = $node->getName(); + $outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox'; + unset($requestedProperties[$scheduleProp]); + $returnedProperties[200][$scheduleProp] = new Sabre_DAV_Property_Href($outboxPath); + } + + // calendar-user-address-set property + $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set'; + if (in_array($calProp,$requestedProperties)) { + + $addresses = $node->getAlternateUriSet(); + $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl(); + unset($requestedProperties[$calProp]); + $returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false); + + } + + // These two properties are shortcuts for ical to easily find + // other principals this principal has access to. + $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; + $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; + if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) { + + $membership = $node->getGroupMembership(); + $readList = array(); + $writeList = array(); + + foreach($membership as $group) { + + $groupNode = $this->server->tree->getNodeForPath($group); + + // If the node is either ap proxy-read or proxy-write + // group, we grab the parent principal and add it to the + // list. + if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) { + list($readList[]) = Sabre_DAV_URLUtil::splitPath($group); + } + if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) { + list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group); + } + + } + if (in_array($propRead,$requestedProperties)) { + unset($requestedProperties[$propRead]); + $returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList); + } + if (in_array($propWrite,$requestedProperties)) { + unset($requestedProperties[$propWrite]); + $returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList); + } + + } + + } // instanceof IPrincipal + + + if ($node instanceof Sabre_CalDAV_ICalendarObject) { + // The calendar-data property is not supposed to be a 'real' + // property, but in large chunks of the spec it does act as such. + // Therefore we simply expose it as a property. + $calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'; + if (in_array($calDataProp, $requestedProperties)) { + unset($requestedProperties[$calDataProp]); + $val = $node->get(); + if (is_resource($val)) + $val = stream_get_contents($val); + + // Taking out \r to not screw up the xml output + $returnedProperties[200][$calDataProp] = str_replace("\r","", $val); + + } + } + + } + + /** + * This function handles the calendar-multiget REPORT. + * + * This report is used by the client to fetch the content of a series + * of urls. Effectively avoiding a lot of redundant requests. + * + * @param DOMNode $dom + * @return void + */ + public function calendarMultiGetReport($dom) { + + $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); + $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + + $xpath = new DOMXPath($dom); + $xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); + $xpath->registerNameSpace('dav','urn:DAV'); + + $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); + if ($expand->length>0) { + $expandElem = $expand->item(0); + $start = $expandElem->getAttribute('start'); + $end = $expandElem->getAttribute('end'); + if(!$start || !$end) { + throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); + } + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + + if ($end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); + } + + $expand = true; + + } else { + + $expand = false; + + } + + foreach($hrefElems as $elem) { + $uri = $this->server->calculateUri($elem->nodeValue); + list($objProps) = $this->server->getPropertiesForPath($uri,$properties); + + if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { + $vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); + $vObject->expand($start, $end); + $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + + $propertyList[]=$objProps; + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList)); + + } + + /** + * This function handles the calendar-query REPORT + * + * This report is used by clients to request calendar objects based on + * complex conditions. + * + * @param DOMNode $dom + * @return void + */ + public function calendarQueryReport($dom) { + + $parser = new Sabre_CalDAV_CalendarQueryParser($dom); + $parser->parse(); + + $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); + $depth = $this->server->getHTTPDepth(0); + + // The default result is an empty array + $result = array(); + + // The calendarobject was requested directly. In this case we handle + // this locally. + if ($depth == 0 && $node instanceof Sabre_CalDAV_ICalendarObject) { + + $requestedCalendarData = true; + $requestedProperties = $parser->requestedProperties; + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + + // We always retrieve calendar-data, as we need it for filtering. + $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; + + // If calendar-data wasn't explicitly requested, we need to remove + // it after processing. + $requestedCalendarData = false; + } + + $properties = $this->server->getPropertiesForPath( + $this->server->getRequestUri(), + $requestedProperties, + 0 + ); + + // This array should have only 1 element, the first calendar + // object. + $properties = current($properties); + + // If there wasn't any calendar-data returned somehow, we ignore + // this. + if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + $vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + if ($validator->validate($vObject,$parser->filters)) { + + // If the client didn't require the calendar-data property, + // we won't give it back. + if (!$requestedCalendarData) { + unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + } else { + if ($parser->expand) { + $vObject->expand($parser->expand['start'], $parser->expand['end']); + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + } + + $result = array($properties); + + } + + } + + } + // If we're dealing with a calendar, the calendar itself is responsible + // for the calendar-query. + if ($node instanceof Sabre_CalDAV_ICalendar && $depth = 1) { + + $nodePaths = $node->calendarQuery($parser->filters); + + foreach($nodePaths as $path) { + + list($properties) = + $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties); + + if ($parser->expand) { + // We need to do some post-processing + $vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + $vObject->expand($parser->expand['start'], $parser->expand['end']); + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + + $result[] = $properties; + + } + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result)); + + } + + /** + * This method is responsible for parsing the request and generating the + * response for the CALDAV:free-busy-query REPORT. + * + * @param DOMNode $dom + * @return void + */ + protected function freeBusyQueryReport(DOMNode $dom) { + + $start = null; + $end = null; + + foreach($dom->firstChild->childNodes as $childNode) { + + $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode); + if ($clark == '{' . self::NS_CALDAV . '}time-range') { + $start = $childNode->getAttribute('start'); + $end = $childNode->getAttribute('end'); + break; + } + + } + if ($start) { + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + } + if ($end) { + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + } + + if (!$start && !$end) { + throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter'); + } + $acl = $this->server->getPlugin('acl'); + + if (!$acl) { + throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work'); + } + $uri = $this->server->getRequestUri(); + $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy'); + + $calendar = $this->server->tree->getNodeForPath($uri); + if (!$calendar instanceof Sabre_CalDAV_ICalendar) { + throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars'); + } + + $objects = array_map(function($child) { + $obj = $child->get(); + if (is_resource($obj)) { + $obj = stream_get_contents($obj); + } + return $obj; + }, $calendar->getChildren()); + + $generator = new Sabre_VObject_FreeBusyGenerator(); + $generator->setObjects($objects); + $generator->setTimeRange($start, $end); + $result = $generator->getResult(); + $result = $result->serialize(); + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); + $this->server->httpResponse->setHeader('Content-Length', strlen($result)); + $this->server->httpResponse->sendBody($result); + + } + + /** + * This method is triggered before a file gets updated with new content. + * + * This plugin uses this method to ensure that CalDAV objects receive + * valid calendar data. + * + * @param string $path + * @param Sabre_DAV_IFile $node + * @param resource $data + * @return void + */ + public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) { + + if (!$node instanceof Sabre_CalDAV_ICalendarObject) + return; + + $this->validateICalendar($data); + + } + + /** + * This method is triggered before a new file is created. + * + * This plugin uses this method to ensure that newly created calendar + * objects contain valid calendar data. + * + * @param string $path + * @param resource $data + * @param Sabre_DAV_ICollection $parentNode + * @return void + */ + public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) { + + if (!$parentNode instanceof Sabre_CalDAV_Calendar) + return; + + $this->validateICalendar($data); + + } + + /** + * Checks if the submitted iCalendar data is in fact, valid. + * + * An exception is thrown if it's not. + * + * @param resource|string $data + * @return void + */ + protected function validateICalendar(&$data) { + + // If it's a stream, we convert it to a string first. + if (is_resource($data)) { + $data = stream_get_contents($data); + } + + // Converting the data to unicode, if needed. + $data = Sabre_DAV_StringUtil::ensureUTF8($data); + + try { + + $vobj = Sabre_VObject_Reader::read($data); + + } catch (Sabre_VObject_ParseException $e) { + + throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); + + } + + if ($vobj->name !== 'VCALENDAR') { + throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.'); + } + + $foundType = null; + $foundUID = null; + foreach($vobj->getComponents() as $component) { + switch($component->name) { + case 'VTIMEZONE' : + continue 2; + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + if (is_null($foundType)) { + $foundType = $component->name; + if (!isset($component->UID)) { + throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID'); + } + $foundUID = (string)$component->UID; + } else { + if ($foundType !== $component->name) { + throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType); + } + if ($foundUID !== (string)$component->UID) { + throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); + } + } + break; + default : + throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); + + } + } + if (!$foundType) + throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); + + } + + /** + * This method handles POST requests to the schedule-outbox + * + * @param Sabre_CalDAV_Schedule_IOutbox $outboxNode + * @return void + */ + public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) { + + $originator = $this->server->httpRequest->getHeader('Originator'); + $recipients = $this->server->httpRequest->getHeader('Recipient'); + + if (!$originator) { + throw new Sabre_DAV_Exception_BadRequest('The Originator: header must be specified when making POST requests'); + } + if (!$recipients) { + throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests'); + } + + if (!preg_match('/^mailto:(.*)@(.*)$/', $originator)) { + throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address'); + } + $originator = substr($originator,7); + + $recipients = explode(',',$recipients); + foreach($recipients as $k=>$recipient) { + + $recipient = trim($recipient); + if (!preg_match('/^mailto:(.*)@(.*)$/', $recipient)) { + throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address'); + } + $recipient = substr($recipient, 7); + $recipients[$k] = $recipient; + } + + // We need to make sure that 'originator' matches one of the email + // addresses of the selected principal. + $principal = $outboxNode->getOwner(); + $props = $this->server->getProperties($principal,array( + '{' . self::NS_CALDAV . '}calendar-user-address-set', + )); + + $addresses = array(); + if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) { + $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs(); + } + + if (!in_array('mailto:' . $originator, $addresses)) { + throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); + } + + try { + $vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true)); + } catch (Sabre_VObject_ParseException $e) { + throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); + } + + // Checking for the object type + $componentType = null; + foreach($vObject->getComponents() as $component) { + if ($component->name !== 'VTIMEZONE') { + $componentType = $component->name; + break; + } + } + if (is_null($componentType)) { + throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); + } + + // Validating the METHOD + $method = strtoupper((string)$vObject->METHOD); + if (!$method) { + throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages'); + } + + if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') { + $this->iMIPMessage($originator, $recipients, $vObject, $principal); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody('Messages sent'); + } else { + throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented'); + } + + } + + /** + * Sends an iMIP message by email. + * + * @param string $originator + * @param array $recipients + * @param Sabre_VObject_Component $vObject + * @param string $principal Principal url + * @return void + */ + protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject, $principal) { + + if (!$this->imipHandler) { + throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.'); + } + $this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal); + + } + + /** + * This method is used to generate HTML output for the + * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users + * can use to create new calendars. + * + * @param Sabre_DAV_INode $node + * @param string $output + * @return bool + */ + public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { + + if (!$node instanceof Sabre_CalDAV_UserCalendars) + return; + + $output.= '
+

Create new calendar

+ +
+
+ +
+ '; + + return false; + + } + + /** + * This method allows us to intercept the 'mkcalendar' sabreAction. This + * action enables the user to create new calendars from the browser plugin. + * + * @param string $uri + * @param string $action + * @param array $postVars + * @return bool + */ + public function browserPostAction($uri, $action, array $postVars) { + + if ($action!=='mkcalendar') + return; + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); + $properties = array(); + if (isset($postVars['{DAV:}displayname'])) { + $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; + } + $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); + return false; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Principal/Collection.php b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/Collection.php new file mode 100644 index 000000000..abbefa556 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/Collection.php @@ -0,0 +1,31 @@ +principalBackend, $principalInfo); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyRead.php b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyRead.php new file mode 100644 index 000000000..4b3f03563 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyRead.php @@ -0,0 +1,178 @@ +principalInfo = $principalInfo; + $this->principalBackend = $principalBackend; + + } + + /** + * Returns this principals name. + * + * @return string + */ + public function getName() { + + return 'calendar-proxy-read'; + + } + + /** + * Returns the last modification time + * + * @return null + */ + public function getLastModified() { + + return null; + + } + + /** + * Deletes the current node + * + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node'); + + } + + /** + * Renames the node + * + * @throws Sabre_DAV_Exception_Forbidden + * @param string $name The new name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file'); + + } + + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + public function getAlternateUriSet() { + + return array(); + + } + + /** + * Returns the full principal url + * + * @return string + */ + public function getPrincipalUrl() { + + return $this->principalInfo['uri'] . '/' . $this->getName(); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + public function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + public function getGroupMembership() { + + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + + } + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void + */ + public function setGroupMemberSet(array $principals) { + + $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); + + } + + /** + * Returns the displayname + * + * This should be a human readable name for the principal. + * If none is available, return the nodename. + * + * @return string + */ + public function getDisplayName() { + + return $this->getName(); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyWrite.php b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyWrite.php new file mode 100644 index 000000000..dd0c2e86e --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyWrite.php @@ -0,0 +1,178 @@ +principalInfo = $principalInfo; + $this->principalBackend = $principalBackend; + + } + + /** + * Returns this principals name. + * + * @return string + */ + public function getName() { + + return 'calendar-proxy-write'; + + } + + /** + * Returns the last modification time + * + * @return null + */ + public function getLastModified() { + + return null; + + } + + /** + * Deletes the current node + * + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node'); + + } + + /** + * Renames the node + * + * @throws Sabre_DAV_Exception_Forbidden + * @param string $name The new name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file'); + + } + + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + public function getAlternateUriSet() { + + return array(); + + } + + /** + * Returns the full principal url + * + * @return string + */ + public function getPrincipalUrl() { + + return $this->principalInfo['uri'] . '/' . $this->getName(); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + public function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + public function getGroupMembership() { + + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + + } + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void + */ + public function setGroupMemberSet(array $principals) { + + $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); + + } + + /** + * Returns the displayname + * + * This should be a human readable name for the principal. + * If none is available, return the nodename. + * + * @return string + */ + public function getDisplayName() { + + return $this->getName(); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Principal/User.php b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/User.php new file mode 100644 index 000000000..8453b877a --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Principal/User.php @@ -0,0 +1,132 @@ +principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); + if (!$principal) { + throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found'); + } + if ($name === 'calendar-proxy-read') + return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties); + + if ($name === 'calendar-proxy-write') + return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties); + + throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found'); + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $r = array(); + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { + $r[] = new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties); + } + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) { + $r[] = new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties); + } + + return $r; + + } + + /** + * Returns whether or not the child node exists + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + try { + $this->getChild($name); + return true; + } catch (Sabre_DAV_Exception_NotFound $e) { + return false; + } + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $acl = parent::getACL(); + $acl[] = array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', + 'protected' => true, + ); + $acl[] = array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', + 'protected' => true, + ); + return $acl; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php new file mode 100644 index 000000000..2ea078d7d --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php @@ -0,0 +1,85 @@ +components = $components; + + } + + /** + * Returns the list of supported components + * + * @return array + */ + public function getValue() { + + return $this->components; + + } + + /** + * Serializes the property in a DOMDocument + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + foreach($this->components as $component) { + + $xcomp = $doc->createElement('cal:comp'); + $xcomp->setAttribute('name',$component); + $node->appendChild($xcomp); + + } + + } + + /** + * Unserializes the DOMElement back into a Property class. + * + * @param DOMElement $node + * @return Sabre_CalDAV_Property_SupportedCalendarComponentSet + */ + static function unserialize(DOMElement $node) { + + $components = array(); + foreach($node->childNodes as $childNode) { + if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') { + $components[] = $childNode->getAttribute('name'); + } + } + return new self($components); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCalendarData.php b/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCalendarData.php new file mode 100644 index 000000000..1d848dd5c --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCalendarData.php @@ -0,0 +1,38 @@ +ownerDocument; + + $prefix = isset($server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal'; + + $caldata = $doc->createElement($prefix . ':calendar-data'); + $caldata->setAttribute('content-type','text/calendar'); + $caldata->setAttribute('version','2.0'); + + $node->appendChild($caldata); + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCollationSet.php b/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCollationSet.php new file mode 100644 index 000000000..24e84d4c1 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Property/SupportedCollationSet.php @@ -0,0 +1,44 @@ +ownerDocument; + + $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav'); + if (!$prefix) $prefix = 'cal'; + + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;ascii-casemap') + ); + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;octet') + ); + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;unicode-casemap') + ); + + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IMip.php b/dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IMip.php new file mode 100644 index 000000000..1be63a06b --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IMip.php @@ -0,0 +1,103 @@ +senderEmail = $senderEmail; + + } + + /** + * Sends one or more iTip messages through email. + * + * @param string $originator Originator Email + * @param array $recipients Array of email addresses + * @param Sabre_VObject_Component $vObject + * @param string $principal Principal Url of the originator + * @return void + */ + public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject, $principal) { + + foreach($recipients as $recipient) { + + $to = $recipient; + $replyTo = $originator; + $subject = 'SabreDAV iTIP message'; + + switch(strtoupper($vObject->METHOD)) { + case 'REPLY' : + $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY; + break; + case 'REQUEST' : + $subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY; + break; + case 'CANCEL' : + $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY; + break; + } + + $headers = array(); + $headers[] = 'Reply-To: ' . $replyTo; + $headers[] = 'From: ' . $this->senderEmail; + $headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8'; + if (Sabre_DAV_Server::$exposeVersion) { + $headers[] = 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY; + } + + $vcalBody = $vObject->serialize(); + + $this->mail($to, $subject, $vcalBody, $headers); + + } + + } + + /** + * This function is reponsible for sending the actual email. + * + * @param string $to Recipient email address + * @param string $subject Subject of the email + * @param string $body iCalendar body + * @param array $headers List of headers + * @return void + */ + protected function mail($to, $subject, $body, array $headers) { + + mail($to, $subject, $body, implode("\r\n", $headers)); + + } + + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IOutbox.php b/dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IOutbox.php new file mode 100644 index 000000000..46d77514b --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IOutbox.php @@ -0,0 +1,16 @@ +principalUri = $principalUri; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + public function getName() { + + return 'outbox'; + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + return array(); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalUri; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', + 'principal' => $this->getOwner(), + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner(), + 'protected' => true, + ), + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('You\'re not allowed to update the ACL'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet(); + $default['aggregates'][] = array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', + ); + + return $default; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Server.php b/dav/SabreDAV/lib/Sabre/CalDAV/Server.php new file mode 100644 index 000000000..325e3d80a --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Server.php @@ -0,0 +1,68 @@ +authRealm); + $this->addPlugin($authPlugin); + + $aclPlugin = new Sabre_DAVACL_Plugin(); + $this->addPlugin($aclPlugin); + + $caldavPlugin = new Sabre_CalDAV_Plugin(); + $this->addPlugin($caldavPlugin); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/UserCalendars.php b/dav/SabreDAV/lib/Sabre/CalDAV/UserCalendars.php new file mode 100644 index 000000000..b8d3f0573 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/UserCalendars.php @@ -0,0 +1,298 @@ +principalBackend = $principalBackend; + $this->caldavBackend = $caldavBackend; + $this->principalInfo = $principalBackend->getPrincipalByPath($userUri); + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Deletes this object + * + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + public function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @return void + */ + public function createFile($filename, $data=null) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @return void + */ + public function createDirectory($filename) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @todo needs optimizing + * @return Sabre_CalDAV_Calendar + */ + public function getChild($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return $child; + + } + throw new Sabre_DAV_Exception_NotFound('Calendar with name \'' . $name . '\' could not be found'); + + } + + /** + * Checks if a calendar exists. + * + * @param string $name + * @todo needs optimizing + * @return bool + */ + public function childExists($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return true; + + } + return false; + + } + + /** + * Returns a list of calendars + * + * @return array + */ + public function getChildren() { + + $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); + $objs = array(); + foreach($calendars as $calendar) { + $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); + } + $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']); + return $objs; + + } + + /** + * Creates a new calendar + * + * @param string $name + * @param array $resourceType + * @param array $properties + * @return void + */ + public function createExtendedCollection($name, array $resourceType, array $properties) { + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) { + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection'); + } + $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalInfo['uri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CalDAV/Version.php b/dav/SabreDAV/lib/Sabre/CalDAV/Version.php new file mode 100644 index 000000000..d862da810 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CalDAV/Version.php @@ -0,0 +1,24 @@ +carddavBackend = $carddavBackend; + $this->addressBookInfo = $addressBookInfo; + + } + + /** + * Returns the name of the addressbook + * + * @return string + */ + public function getName() { + + return $this->addressBookInfo['uri']; + + } + + /** + * Returns a card + * + * @param string $name + * @return Sabre_CardDAV_ICard + */ + public function getChild($name) { + + $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Card not found'); + return new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj); + + } + + /** + * Returns the full list of cards + * + * @return array + */ + public function getChildren() { + + $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj); + } + return $children; + + } + + /** + * Creates a new directory + * + * We actually block this, as subdirectories are not allowed in addressbooks. + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in addressbooks is not allowed'); + + } + + /** + * Creates a new file + * + * The contents of the new file must be a valid VCARD. + * + * This method may return an ETag. + * + * @param string $name + * @param resource $vcardData + * @return string|null + */ + public function createFile($name,$vcardData = null) { + + if (is_resource($vcardData)) { + $vcardData = stream_get_contents($vcardData); + } + // Converting to UTF-8, if needed + $vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData); + + return $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData); + + } + + /** + * Deletes the entire addressbook. + * + * @return void + */ + public function delete() { + + $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']); + + } + + /** + * Renames the addressbook + * + * @param string $newName + * @return void + */ + public function setName($newName) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming addressbooks is not yet supported'); + + } + + /** + * Returns the last modification date as a unix timestamp. + * + * @return void + */ + public function getLastModified() { + + return null; + + } + + /** + * Updates properties on this node, + * + * The properties array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existent property is always successful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param array $mutations + * @return bool|array + */ + public function updateProperties($mutations) { + + return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations); + + } + + /** + * Returns a list of properties for this nodes. + * + * The properties list is a list of propertynames the client requested, + * encoded in clark-notation {xmlnamespace}tagname + * + * If the array is empty, it means 'all properties' were requested. + * + * @param array $properties + * @return array + */ + public function getProperties($properties) { + + $response = array(); + foreach($properties as $propertyName) { + + if (isset($this->addressBookInfo[$propertyName])) { + + $response[$propertyName] = $this->addressBookInfo[$propertyName]; + + } + + } + + return $response; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->addressBookInfo['principaluri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->addressBookInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->addressBookInfo['principaluri'], + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/AddressBookQueryParser.php b/dav/SabreDAV/lib/Sabre/CardDAV/AddressBookQueryParser.php new file mode 100644 index 000000000..46bb8ff18 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/AddressBookQueryParser.php @@ -0,0 +1,219 @@ +dom = $dom; + + $this->xpath = new DOMXPath($dom); + $this->xpath->registerNameSpace('card',Sabre_CardDAV_Plugin::NS_CARDDAV); + + } + + /** + * Parses the request. + * + * @return void + */ + public function parse() { + + $filterNode = null; + + $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)'); + if (is_nan($limit)) $limit = null; + + $filter = $this->xpath->query('/card:addressbook-query/card:filter'); + + // According to the CardDAV spec there needs to be exactly 1 filter + // element. However, KDE 4.8.2 contains a bug that will encode 0 filter + // elements, so this is a workaround for that. + // + // See: https://bugs.kde.org/show_bug.cgi?id=300047 + if ($filter->length === 0) { + $test = null; + $filter = null; + } elseif ($filter->length === 1) { + $filter = $filter->item(0); + $test = $this->xpath->evaluate('string(@test)', $filter); + } else { + throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed'); + } + + if (!$test) $test = self::TEST_ANYOF; + if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) { + throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"'); + } + + $propFilters = array(); + + $propFilterNodes = $this->xpath->query('card:prop-filter', $filter); + for($ii=0; $ii < $propFilterNodes->length; $ii++) { + + $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii)); + + + } + + $this->filters = $propFilters; + $this->limit = $limit; + $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild)); + $this->test = $test; + + } + + /** + * Parses the prop-filter xml element + * + * @param DOMElement $propFilterNode + * @return array + */ + protected function parsePropFilterNode(DOMElement $propFilterNode) { + + $propFilter = array(); + $propFilter['name'] = $propFilterNode->getAttribute('name'); + $propFilter['test'] = $propFilterNode->getAttribute('test'); + if (!$propFilter['test']) $propFilter['test'] = 'anyof'; + + $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0; + + $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode); + + $propFilter['param-filters'] = array(); + + + for($ii=0;$ii<$paramFilterNodes->length;$ii++) { + + $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii)); + + } + $propFilter['text-matches'] = array(); + $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode); + + for($ii=0;$ii<$textMatchNodes->length;$ii++) { + + $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii)); + + } + + return $propFilter; + + } + + /** + * Parses the param-filter element + * + * @param DOMElement $paramFilterNode + * @return array + */ + public function parseParamFilterNode(DOMElement $paramFilterNode) { + + $paramFilter = array(); + $paramFilter['name'] = $paramFilterNode->getAttribute('name'); + $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0; + $paramFilter['text-match'] = null; + + $textMatch = $this->xpath->query('card:text-match', $paramFilterNode); + if ($textMatch->length>0) { + $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0)); + } + + return $paramFilter; + + } + + /** + * Text match + * + * @param DOMElement $textMatchNode + * @return array + */ + public function parseTextMatchNode(DOMElement $textMatchNode) { + + $matchType = $textMatchNode->getAttribute('match-type'); + if (!$matchType) $matchType = 'contains'; + + if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) { + throw new Sabre_DAV_Exception_BadRequest('Unknown match-type: ' . $matchType); + } + + $negateCondition = $textMatchNode->getAttribute('negate-condition'); + $negateCondition = $negateCondition==='yes'; + $collation = $textMatchNode->getAttribute('collation'); + if (!$collation) $collation = 'i;unicode-casemap'; + + return array( + 'negate-condition' => $negateCondition, + 'collation' => $collation, + 'match-type' => $matchType, + 'value' => $textMatchNode->nodeValue + ); + + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/AddressBookRoot.php b/dav/SabreDAV/lib/Sabre/CardDAV/AddressBookRoot.php new file mode 100644 index 000000000..9d37b15f0 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/AddressBookRoot.php @@ -0,0 +1,78 @@ +carddavBackend = $carddavBackend; + parent::__construct($principalBackend, $principalPrefix); + + } + + /** + * Returns the name of the node + * + * @return string + */ + public function getName() { + + return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return Sabre_DAV_INode + */ + public function getChildForPrincipal(array $principal) { + + return new Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/Backend/Abstract.php b/dav/SabreDAV/lib/Sabre/CardDAV/Backend/Abstract.php new file mode 100644 index 000000000..e4806b716 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/Backend/Abstract.php @@ -0,0 +1,166 @@ +pdo = $pdo; + $this->addressBooksTableName = $addressBooksTableName; + $this->cardsTableName = $cardsTableName; + + } + + /** + * Returns the list of addressbooks for a specific user. + * + * @param string $principalUri + * @return array + */ + public function getAddressBooksForUser($principalUri) { + + $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM '.$this->addressBooksTableName.' WHERE principaluri = ?'); + $stmt->execute(array($principalUri)); + + $addressBooks = array(); + + foreach($stmt->fetchAll() as $row) { + + $addressBooks[] = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{DAV:}displayname' => $row['displayname'], + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], + '{http://calendarserver.org/ns/}getctag' => $row['ctag'], + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => + new Sabre_CardDAV_Property_SupportedAddressData(), + ); + + } + + return $addressBooks; + + } + + + /** + * Updates an addressbook's properties + * + * See Sabre_DAV_IProperties for a description of the mutations array, as + * well as the return value. + * + * @param mixed $addressBookId + * @param array $mutations + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateAddressBook($addressBookId, array $mutations) { + + $updates = array(); + + foreach($mutations as $property=>$newValue) { + + switch($property) { + case '{DAV:}displayname' : + $updates['displayname'] = $newValue; + break; + case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' : + $updates['description'] = $newValue; + break; + default : + // If any unsupported values were being updated, we must + // let the entire request fail. + return false; + } + + } + + // No values are being updated? + if (!$updates) { + return false; + } + + $query = 'UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 '; + foreach($updates as $key=>$value) { + $query.=', `' . $key . '` = :' . $key . ' '; + } + $query.=' WHERE id = :addressbookid'; + + $stmt = $this->pdo->prepare($query); + $updates['addressbookid'] = $addressBookId; + + $stmt->execute($updates); + + return true; + + } + + /** + * Creates a new address book + * + * @param string $principalUri + * @param string $url Just the 'basename' of the url. + * @param array $properties + * @return void + */ + public function createAddressBook($principalUri, $url, array $properties) { + + $values = array( + 'displayname' => null, + 'description' => null, + 'principaluri' => $principalUri, + 'uri' => $url, + ); + + foreach($properties as $property=>$newValue) { + + switch($property) { + case '{DAV:}displayname' : + $values['displayname'] = $newValue; + break; + case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' : + $values['description'] = $newValue; + break; + default : + throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property); + } + + } + + $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)'; + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + } + + /** + * Deletes an entire addressbook and all its contents + * + * @param int $addressBookId + * @return void + */ + public function deleteAddressBook($addressBookId) { + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); + $stmt->execute(array($addressBookId)); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); + $stmt->execute(array($addressBookId)); + + } + + /** + * Returns all cards for a specific addressbook id. + * + * This method should return the following properties for each card: + * * carddata - raw vcard data + * * uri - Some unique url + * * lastmodified - A unix timestamp + * + * It's recommended to also return the following properties: + * * etag - A unique etag. This must change every time the card changes. + * * size - The size of the card in bytes. + * + * If these last two properties are provided, less time will be spent + * calculating them. If they are specified, you can also ommit carddata. + * This may speed up certain requests, especially with large cards. + * + * @param mixed $addressbookId + * @return array + */ + public function getCards($addressbookId) { + + $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); + $stmt->execute(array($addressbookId)); + + return $stmt->fetchAll(PDO::FETCH_ASSOC); + + + } + + /** + * Returns a specfic card. + * + * The same set of properties must be returned as with getCards. The only + * exception is that 'carddata' is absolutely required. + * + * @param mixed $addressBookId + * @param string $cardUri + * @return array + */ + public function getCard($addressBookId, $cardUri) { + + $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1'); + $stmt->execute(array($addressBookId, $cardUri)); + + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + return (count($result)>0?$result[0]:false); + + } + + /** + * Creates a new card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getCards method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag is for the + * newly created resource, and must be enclosed with double quotes (that + * is, the string itself must contain the double quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null + */ + public function createCard($addressBookId, $cardUri, $cardData) { + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)'); + + $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId)); + + $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); + $stmt2->execute(array($addressBookId)); + + return '"' . md5($cardData) . '"'; + + } + + /** + * Updates a card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressbooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag should + * match that of the updated resource, and must be enclosed with double + * quotes (that is: the string itself must contain the actual quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null + */ + public function updateCard($addressBookId, $cardUri, $cardData) { + + $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?'); + $stmt->execute(array($cardData, time(), $cardUri, $addressBookId)); + + $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); + $stmt2->execute(array($addressBookId)); + + return '"' . md5($cardData) . '"'; + + } + + /** + * Deletes a card + * + * @param mixed $addressBookId + * @param string $cardUri + * @return bool + */ + public function deleteCard($addressBookId, $cardUri) { + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); + $stmt->execute(array($addressBookId, $cardUri)); + + $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); + $stmt2->execute(array($addressBookId)); + + return $stmt->rowCount()===1; + + } +} diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/Card.php b/dav/SabreDAV/lib/Sabre/CardDAV/Card.php new file mode 100644 index 000000000..36ae24db4 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/Card.php @@ -0,0 +1,250 @@ +carddavBackend = $carddavBackend; + $this->addressBookInfo = $addressBookInfo; + $this->cardData = $cardData; + + } + + /** + * Returns the uri for this object + * + * @return string + */ + public function getName() { + + return $this->cardData['uri']; + + } + + /** + * Returns the VCard-formatted object + * + * @return string + */ + public function get() { + + // Pre-populating 'carddata' is optional. If we don't yet have it + // already, we fetch it from the backend. + if (!isset($this->cardData['carddata'])) { + $this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']); + } + return $this->cardData['carddata']; + + } + + /** + * Updates the VCard-formatted object + * + * @param string $cardData + * @return string|null + */ + public function put($cardData) { + + if (is_resource($cardData)) + $cardData = stream_get_contents($cardData); + + // Converting to UTF-8, if needed + $cardData = Sabre_DAV_StringUtil::ensureUTF8($cardData); + + $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData); + $this->cardData['carddata'] = $cardData; + $this->cardData['etag'] = $etag; + + return $etag; + + } + + /** + * Deletes the card + * + * @return void + */ + public function delete() { + + $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']); + + } + + /** + * Returns the mime content-type + * + * @return string + */ + public function getContentType() { + + return 'text/x-vcard; charset=utf-8'; + + } + + /** + * Returns an ETag for this object + * + * @return string + */ + public function getETag() { + + if (isset($this->cardData['etag'])) { + return $this->cardData['etag']; + } else { + return '"' . md5($this->get()) . '"'; + } + + } + + /** + * Returns the last modification date as a unix timestamp + * + * @return int + */ + public function getLastModified() { + + return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null; + + } + + /** + * Returns the size of this object in bytes + * + * @return int + */ + public function getSize() { + + if (array_key_exists('size', $this->cardData)) { + return $this->cardData['size']; + } else { + return strlen($this->get()); + } + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->addressBookInfo['principaluri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->addressBookInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->addressBookInfo['principaluri'], + 'protected' => true, + ), + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/IAddressBook.php b/dav/SabreDAV/lib/Sabre/CardDAV/IAddressBook.php new file mode 100644 index 000000000..2bc275bcf --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/IAddressBook.php @@ -0,0 +1,18 @@ +subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); + $server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties')); + $server->subscribeEvent('updateProperties', array($this, 'updateProperties')); + $server->subscribeEvent('report', array($this,'report')); + $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); + $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); + $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); + $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); + + /* Namespaces */ + $server->xmlNamespaces[self::NS_CARDDAV] = 'card'; + + /* Mapping Interfaces to {DAV:}resourcetype values */ + $server->resourceTypeMapping['Sabre_CardDAV_IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; + $server->resourceTypeMapping['Sabre_CardDAV_IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; + + /* Adding properties that may never be changed */ + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set'; + + $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre_DAV_Property_Href'; + + $this->server = $server; + + } + + /** + * Returns a list of supported features. + * + * This is used in the DAV: header in the OPTIONS and PROPFIND requests. + * + * @return array + */ + public function getFeatures() { + + return array('addressbook'); + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + public function getSupportedReportSet($uri) { + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_CardDAV_IAddressBook || $node instanceof Sabre_CardDAV_ICard) { + return array( + '{' . self::NS_CARDDAV . '}addressbook-multiget', + '{' . self::NS_CARDDAV . '}addressbook-query', + ); + } + return array(); + + } + + + /** + * Adds all CardDAV-specific properties + * + * @param string $path + * @param Sabre_DAV_INode $node + * @param array $requestedProperties + * @param array $returnedProperties + * @return void + */ + public function beforeGetProperties($path, Sabre_DAV_INode $node, array &$requestedProperties, array &$returnedProperties) { + + if ($node instanceof Sabre_DAVACL_IPrincipal) { + + // calendar-home-set property + $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set'; + if (in_array($addHome,$requestedProperties)) { + $principalId = $node->getName(); + $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/'; + unset($requestedProperties[array_search($addHome, $requestedProperties)]); + $returnedProperties[200][$addHome] = new Sabre_DAV_Property_Href($addressbookHomePath); + } + + $directories = '{' . self::NS_CARDDAV . '}directory-gateway'; + if ($this->directories && in_array($directories, $requestedProperties)) { + unset($requestedProperties[array_search($directories, $requestedProperties)]); + $returnedProperties[200][$directories] = new Sabre_DAV_Property_HrefList($this->directories); + } + + } + + if ($node instanceof Sabre_CardDAV_ICard) { + + // The address-data property is not supposed to be a 'real' + // property, but in large chunks of the spec it does act as such. + // Therefore we simply expose it as a property. + $addressDataProp = '{' . self::NS_CARDDAV . '}address-data'; + if (in_array($addressDataProp, $requestedProperties)) { + unset($requestedProperties[$addressDataProp]); + $val = $node->get(); + if (is_resource($val)) + $val = stream_get_contents($val); + + // Taking out \r to not screw up the xml output + $returnedProperties[200][$addressDataProp] = str_replace("\r","", $val); + + } + } + + if ($node instanceof Sabre_CardDAV_UserAddressBooks) { + + $meCardProp = '{http://calendarserver.org/ns/}me-card'; + if (in_array($meCardProp, $requestedProperties)) { + + $props = $this->server->getProperties($node->getOwner(), array('{http://sabredav.org/ns}vcard-url')); + if (isset($props['{http://sabredav.org/ns}vcard-url'])) { + + $returnedProperties[200][$meCardProp] = new Sabre_DAV_Property_Href( + $props['{http://sabredav.org/ns}vcard-url'] + ); + $pos = array_search($meCardProp, $requestedProperties); + unset($requestedProperties[$pos]); + + } + + } + + } + + } + + /** + * This event is triggered when a PROPPATCH method is executed + * + * @param array $mutations + * @param array $result + * @param Sabre_DAV_INode $node + * @return bool + */ + public function updateProperties(&$mutations, &$result, $node) { + + if (!$node instanceof Sabre_CardDAV_UserAddressBooks) { + return true; + } + + $meCard = '{http://calendarserver.org/ns/}me-card'; + + // The only property we care about + if (!isset($mutations[$meCard])) + return true; + + $value = $mutations[$meCard]; + unset($mutations[$meCard]); + + if ($value instanceof Sabre_DAV_Property_IHref) { + $value = $value->getHref(); + $value = $this->server->calculateUri($value); + } elseif (!is_null($value)) { + $result[400][$meCard] = null; + return false; + } + + $innerResult = $this->server->updateProperties( + $node->getOwner(), + array( + '{http://sabredav.org/ns}vcard-url' => $value, + ) + ); + + $closureResult = false; + foreach($innerResult as $status => $props) { + if (is_array($props) && array_key_exists('{http://sabredav.org/ns}vcard-url', $props)) { + $result[$status][$meCard] = null; + $closureResult = ($status>=200 && $status<300); + } + + } + + return $result; + + } + + /** + * This functions handles REPORT requests specific to CardDAV + * + * @param string $reportName + * @param DOMNode $dom + * @return bool + */ + public function report($reportName,$dom) { + + switch($reportName) { + case '{'.self::NS_CARDDAV.'}addressbook-multiget' : + $this->addressbookMultiGetReport($dom); + return false; + case '{'.self::NS_CARDDAV.'}addressbook-query' : + $this->addressBookQueryReport($dom); + return false; + default : + return; + + } + + + } + + /** + * This function handles the addressbook-multiget REPORT. + * + * This report is used by the client to fetch the content of a series + * of urls. Effectively avoiding a lot of redundant requests. + * + * @param DOMNode $dom + * @return void + */ + public function addressbookMultiGetReport($dom) { + + $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); + + $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + $propertyList = array(); + + foreach($hrefElems as $elem) { + + $uri = $this->server->calculateUri($elem->nodeValue); + list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties); + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList)); + + } + + /** + * This method is triggered before a file gets updated with new content. + * + * This plugin uses this method to ensure that Card nodes receive valid + * vcard data. + * + * @param string $path + * @param Sabre_DAV_IFile $node + * @param resource $data + * @return void + */ + public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) { + + if (!$node instanceof Sabre_CardDAV_ICard) + return; + + $this->validateVCard($data); + + } + + /** + * This method is triggered before a new file is created. + * + * This plugin uses this method to ensure that Card nodes receive valid + * vcard data. + * + * @param string $path + * @param resource $data + * @param Sabre_DAV_ICollection $parentNode + * @return void + */ + public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) { + + if (!$parentNode instanceof Sabre_CardDAV_IAddressBook) + return; + + $this->validateVCard($data); + + } + + /** + * Checks if the submitted iCalendar data is in fact, valid. + * + * An exception is thrown if it's not. + * + * @param resource|string $data + * @return void + */ + protected function validateVCard(&$data) { + + // If it's a stream, we convert it to a string first. + if (is_resource($data)) { + $data = stream_get_contents($data); + } + + // Converting the data to unicode, if needed. + $data = Sabre_DAV_StringUtil::ensureUTF8($data); + + try { + + $vobj = Sabre_VObject_Reader::read($data); + + } catch (Sabre_VObject_ParseException $e) { + + throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage()); + + } + + if ($vobj->name !== 'VCARD') { + throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.'); + } + + } + + + /** + * This function handles the addressbook-query REPORT + * + * This report is used by the client to filter an addressbook based on a + * complex query. + * + * @param DOMNode $dom + * @return void + */ + protected function addressbookQueryReport($dom) { + + $query = new Sabre_CardDAV_AddressBookQueryParser($dom); + $query->parse(); + + $depth = $this->server->getHTTPDepth(0); + + if ($depth==0) { + $candidateNodes = array( + $this->server->tree->getNodeForPath($this->server->getRequestUri()) + ); + } else { + $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri()); + } + + $validNodes = array(); + foreach($candidateNodes as $node) { + + if (!$node instanceof Sabre_CardDAV_ICard) + continue; + + $blob = $node->get(); + if (is_resource($blob)) { + $blob = stream_get_contents($blob); + } + + if (!$this->validateFilters($blob, $query->filters, $query->test)) { + continue; + } + + $validNodes[] = $node; + + if ($query->limit && $query->limit <= count($validNodes)) { + // We hit the maximum number of items, we can stop now. + break; + } + + } + + $result = array(); + foreach($validNodes as $validNode) { + + if ($depth==0) { + $href = $this->server->getRequestUri(); + } else { + $href = $this->server->getRequestUri() . '/' . $validNode->getName(); + } + + list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0); + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result)); + + } + + /** + * Validates if a vcard makes it throught a list of filters. + * + * @param string $vcardData + * @param array $filters + * @param string $test anyof or allof (which means OR or AND) + * @return bool + */ + public function validateFilters($vcardData, array $filters, $test) { + + $vcard = Sabre_VObject_Reader::read($vcardData); + + foreach($filters as $filter) { + + $isDefined = isset($vcard->{$filter['name']}); + if ($filter['is-not-defined']) { + if ($isDefined) { + $success = false; + } else { + $success = true; + } + } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) { + + // We only need to check for existence + $success = $isDefined; + + } else { + + $vProperties = $vcard->select($filter['name']); + + $results = array(); + if ($filter['param-filters']) { + $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']); + } + if ($filter['text-matches']) { + $texts = array(); + foreach($vProperties as $vProperty) + $texts[] = $vProperty->value; + + $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']); + } + + if (count($results)===1) { + $success = $results[0]; + } else { + if ($filter['test'] === 'anyof') { + $success = $results[0] || $results[1]; + } else { + $success = $results[0] && $results[1]; + } + } + + } // else + + // There are two conditions where we can already determine whether + // or not this filter succeeds. + if ($test==='anyof' && $success) { + return true; + } + if ($test==='allof' && !$success) { + return false; + } + + } // foreach + + // If we got all the way here, it means we haven't been able to + // determine early if the test failed or not. + // + // This implies for 'anyof' that the test failed, and for 'allof' that + // we succeeded. Sounds weird, but makes sense. + return $test==='allof'; + + } + + /** + * Validates if a param-filter can be applied to a specific property. + * + * @todo currently we're only validating the first parameter of the passed + * property. Any subsequence parameters with the same name are + * ignored. + * @param array $vProperties + * @param array $filters + * @param string $test + * @return bool + */ + protected function validateParamFilters(array $vProperties, array $filters, $test) { + + foreach($filters as $filter) { + + $isDefined = false; + foreach($vProperties as $vProperty) { + $isDefined = isset($vProperty[$filter['name']]); + if ($isDefined) break; + } + + if ($filter['is-not-defined']) { + if ($isDefined) { + $success = false; + } else { + $success = true; + } + + // If there's no text-match, we can just check for existence + } elseif (!$filter['text-match'] || !$isDefined) { + + $success = $isDefined; + + } else { + + $success = false; + foreach($vProperties as $vProperty) { + // If we got all the way here, we'll need to validate the + // text-match filter. + $success = Sabre_DAV_StringUtil::textMatch($vProperty[$filter['name']]->value, $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']); + if ($success) break; + } + if ($filter['text-match']['negate-condition']) { + $success = !$success; + } + + } // else + + // There are two conditions where we can already determine whether + // or not this filter succeeds. + if ($test==='anyof' && $success) { + return true; + } + if ($test==='allof' && !$success) { + return false; + } + + } + + // If we got all the way here, it means we haven't been able to + // determine early if the test failed or not. + // + // This implies for 'anyof' that the test failed, and for 'allof' that + // we succeeded. Sounds weird, but makes sense. + return $test==='allof'; + + } + + /** + * Validates if a text-filter can be applied to a specific property. + * + * @param array $texts + * @param array $filters + * @param string $test + * @return bool + */ + protected function validateTextMatches(array $texts, array $filters, $test) { + + foreach($filters as $filter) { + + $success = false; + foreach($texts as $haystack) { + $success = Sabre_DAV_StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']); + + // Breaking on the first match + if ($success) break; + } + if ($filter['negate-condition']) { + $success = !$success; + } + + if ($success && $test==='anyof') + return true; + + if (!$success && $test=='allof') + return false; + + + } + + // If we got all the way here, it means we haven't been able to + // determine early if the test failed or not. + // + // This implies for 'anyof' that the test failed, and for 'allof' that + // we succeeded. Sounds weird, but makes sense. + return $test==='allof'; + + } + + /** + * This event is triggered after webdav-properties have been retrieved. + * + * @return bool + */ + public function afterGetProperties($uri, &$properties) { + + // If the request was made using the SOGO connector, we must rewrite + // the content-type property. By default SabreDAV will send back + // text/x-vcard; charset=utf-8, but for SOGO we must strip that last + // part. + if (!isset($properties[200]['{DAV:}getcontenttype'])) + return; + + if (strpos($this->server->httpRequest->getHeader('User-Agent'),'Thunderbird')===false) { + return; + } + + if (strpos($properties[200]['{DAV:}getcontenttype'],'text/x-vcard')===0) { + $properties[200]['{DAV:}getcontenttype'] = 'text/x-vcard'; + } + + } + + /** + * This method is used to generate HTML output for the + * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users + * can use to create new calendars. + * + * @param Sabre_DAV_INode $node + * @param string $output + * @return bool + */ + public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { + + if (!$node instanceof Sabre_CardDAV_UserAddressBooks) + return; + + $output.= '
+

Create new address book

+ +
+
+ +
+ '; + + return false; + + } + + /** + * This method allows us to intercept the 'mkcalendar' sabreAction. This + * action enables the user to create new calendars from the browser plugin. + * + * @param string $uri + * @param string $action + * @param array $postVars + * @return bool + */ + public function browserPostAction($uri, $action, array $postVars) { + + if ($action!=='mkaddressbook') + return; + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:carddav}addressbook'); + $properties = array(); + if (isset($postVars['{DAV:}displayname'])) { + $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; + } + $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); + return false; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/Property/SupportedAddressData.php b/dav/SabreDAV/lib/Sabre/CardDAV/Property/SupportedAddressData.php new file mode 100644 index 000000000..36d9306e7 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/Property/SupportedAddressData.php @@ -0,0 +1,69 @@ + 'text/vcard', 'version' => '3.0'), + array('contentType' => 'text/vcard', 'version' => '4.0'), + ); + } + + $this->supportedData = $supportedData; + + } + + /** + * Serializes the property in a DOMDocument + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + + $prefix = + isset($server->xmlNamespaces[Sabre_CardDAV_Plugin::NS_CARDDAV]) ? + $server->xmlNamespaces[Sabre_CardDAV_Plugin::NS_CARDDAV] : + 'card'; + + foreach($this->supportedData as $supported) { + + $caldata = $doc->createElementNS(Sabre_CardDAV_Plugin::NS_CARDDAV, $prefix . ':address-data-type'); + $caldata->setAttribute('content-type',$supported['contentType']); + $caldata->setAttribute('version',$supported['version']); + $node->appendChild($caldata); + + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/UserAddressBooks.php b/dav/SabreDAV/lib/Sabre/CardDAV/UserAddressBooks.php new file mode 100644 index 000000000..3f11fb112 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/UserAddressBooks.php @@ -0,0 +1,257 @@ +carddavBackend = $carddavBackend; + $this->principalUri = $principalUri; + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_MethodNotAllowed(); + + } + + /** + * Deletes this object + * + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_MethodNotAllowed(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + public function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @return void + */ + public function createFile($filename, $data=null) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @return void + */ + public function createDirectory($filename) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @todo needs optimizing + * @return Sabre_CardDAV_AddressBook + */ + public function getChild($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return $child; + + } + throw new Sabre_DAV_Exception_NotFound('Addressbook with name \'' . $name . '\' could not be found'); + + } + + /** + * Returns a list of addressbooks + * + * @return array + */ + public function getChildren() { + + $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri); + $objs = array(); + foreach($addressbooks as $addressbook) { + $objs[] = new Sabre_CardDAV_AddressBook($this->carddavBackend, $addressbook); + } + return $objs; + + } + + /** + * Creates a new addressbook + * + * @param string $name + * @param array $resourceType + * @param array $properties + * @return void + */ + public function createExtendedCollection($name, array $resourceType, array $properties) { + + if (!in_array('{'.Sabre_CardDAV_Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) { + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection'); + } + $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalUri; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalUri, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalUri, + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/CardDAV/Version.php b/dav/SabreDAV/lib/Sabre/CardDAV/Version.php new file mode 100644 index 000000000..e45840437 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/CardDAV/Version.php @@ -0,0 +1,26 @@ +currentUser; + } + + + /** + * Authenticates the user based on the current request. + * + * If authentication is successful, true must be returned. + * If authentication fails, an exception must be thrown. + * + * @param Sabre_DAV_Server $server + * @param string $realm + * @throws Sabre_DAV_Exception_NotAuthenticated + * @return bool + */ + public function authenticate(Sabre_DAV_Server $server, $realm) { + + $auth = new Sabre_HTTP_BasicAuth(); + $auth->setHTTPRequest($server->httpRequest); + $auth->setHTTPResponse($server->httpResponse); + $auth->setRealm($realm); + $userpass = $auth->getUserPass(); + if (!$userpass) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found'); + } + + // Authenticates the user + if (!$this->validateUserPass($userpass[0],$userpass[1])) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('Username or password does not match'); + } + $this->currentUser = $userpass[0]; + return true; + } + + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php new file mode 100644 index 000000000..9833928b9 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/AbstractDigest.php @@ -0,0 +1,98 @@ +setHTTPRequest($server->httpRequest); + $digest->setHTTPResponse($server->httpResponse); + + $digest->setRealm($realm); + $digest->init(); + + $username = $digest->getUsername(); + + // No username was given + if (!$username) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('No digest authentication headers were found'); + } + + $hash = $this->getDigestHash($realm, $username); + // If this was false, the user account didn't exist + if ($hash===false || is_null($hash)) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('The supplied username was not on file'); + } + if (!is_string($hash)) { + throw new Sabre_DAV_Exception('The returned value from getDigestHash must be a string or null'); + } + + // If this was false, the password or part of the hash was incorrect. + if (!$digest->validateA1($hash)) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('Incorrect username'); + } + + $this->currentUser = $username; + return true; + + } + + /** + * Returns the currently logged in username. + * + * @return string|null + */ + public function getCurrentUser() { + + return $this->currentUser; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/Apache.php b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/Apache.php new file mode 100644 index 000000000..d4294ea4d --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/Apache.php @@ -0,0 +1,62 @@ +httpRequest->getRawServerValue('REMOTE_USER'); + if (is_null($remoteUser)) { + throw new Sabre_DAV_Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured'); + } + + $this->remoteUser = $remoteUser; + return true; + + } + + /** + * Returns information about the currently logged in user. + * + * If nobody is currently logged in, this method should return null. + * + * @return array|null + */ + public function getCurrentUser() { + + return $this->remoteUser; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/File.php b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/File.php new file mode 100644 index 000000000..de308d64a --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/File.php @@ -0,0 +1,75 @@ +loadFile($filename); + + } + + /** + * Loads an htdigest-formatted file. This method can be called multiple times if + * more than 1 file is used. + * + * @param string $filename + * @return void + */ + public function loadFile($filename) { + + foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) { + + if (substr_count($line, ":") !== 2) + throw new Sabre_DAV_Exception('Malformed htdigest file. Every line should contain 2 colons'); + + list($username,$realm,$A1) = explode(':',$line); + + if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) + throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash'); + + $this->users[$realm . ':' . $username] = $A1; + + } + + } + + /** + * Returns a users' information + * + * @param string $realm + * @param string $username + * @return string + */ + public function getDigestHash($realm, $username) { + + return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/PDO.php b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/PDO.php new file mode 100644 index 000000000..eac18a23f --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Auth/Backend/PDO.php @@ -0,0 +1,65 @@ +pdo = $pdo; + $this->tableName = $tableName; + + } + + /** + * Returns the digest hash for a user. + * + * @param string $realm + * @param string $username + * @return string|null + */ + public function getDigestHash($realm,$username) { + + $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM '.$this->tableName.' WHERE username = ?'); + $stmt->execute(array($username)); + $result = $stmt->fetchAll(); + + if (!count($result)) return; + + return $result[0]['digesta1']; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Auth/IBackend.php b/dav/SabreDAV/lib/Sabre/DAV/Auth/IBackend.php new file mode 100644 index 000000000..5be5d1bc9 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Auth/IBackend.php @@ -0,0 +1,36 @@ +authBackend = $authBackend; + $this->realm = $realm; + + } + + /** + * Initializes the plugin. This function is automatically called by the server + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre_DAV_Server::getPlugin + * + * @return string + */ + public function getPluginName() { + + return 'auth'; + + } + + /** + * Returns the current users' principal uri. + * + * If nobody is logged in, this will return null. + * + * @return string|null + */ + public function getCurrentUser() { + + $userInfo = $this->authBackend->getCurrentUser(); + if (!$userInfo) return null; + + return $userInfo; + + } + + /** + * This method is called before any HTTP method and forces users to be authenticated + * + * @param string $method + * @param string $uri + * @throws Sabre_DAV_Exception_NotAuthenticated + * @return bool + */ + public function beforeMethod($method, $uri) { + + $this->authBackend->authenticate($this->server,$this->realm); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/GuessContentType.php b/dav/SabreDAV/lib/Sabre/DAV/Browser/GuessContentType.php new file mode 100644 index 000000000..b6c00d461 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Browser/GuessContentType.php @@ -0,0 +1,97 @@ + 'image/jpeg', + 'gif' => 'image/gif', + 'png' => 'image/png', + + // groupware + 'ics' => 'text/calendar', + 'vcf' => 'text/x-vcard', + + // text + 'txt' => 'text/plain', + + ); + + /** + * Initializes the plugin + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + // Using a relatively low priority (200) to allow other extensions + // to set the content-type first. + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200); + + } + + /** + * Handler for teh afterGetProperties event + * + * @param string $path + * @param array $properties + * @return void + */ + public function afterGetProperties($path, &$properties) { + + if (array_key_exists('{DAV:}getcontenttype', $properties[404])) { + + list(, $fileName) = Sabre_DAV_URLUtil::splitPath($path); + $contentType = $this->getContentType($fileName); + + if ($contentType) { + $properties[200]['{DAV:}getcontenttype'] = $contentType; + unset($properties[404]['{DAV:}getcontenttype']); + } + + } + + } + + /** + * Simple method to return the contenttype + * + * @param string $fileName + * @return string + */ + protected function getContentType($fileName) { + + // Just grabbing the extension + $extension = strtolower(substr($fileName,strrpos($fileName,'.')+1)); + if (isset($this->extensionMap[$extension])) + return $this->extensionMap[$extension]; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/MapGetToPropFind.php b/dav/SabreDAV/lib/Sabre/DAV/Browser/MapGetToPropFind.php new file mode 100644 index 000000000..158848876 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Browser/MapGetToPropFind.php @@ -0,0 +1,55 @@ +server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); + } + + /** + * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request + * + * @param string $method + * @param string $uri + * @return bool + */ + public function httpGetInterceptor($method, $uri) { + + if ($method!='GET') return true; + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_IFile) return; + + $this->server->invokeMethod('PROPFIND',$uri); + return false; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/Plugin.php b/dav/SabreDAV/lib/Sabre/DAV/Browser/Plugin.php new file mode 100644 index 000000000..09bbdd2ae --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Browser/Plugin.php @@ -0,0 +1,489 @@ + 'icons/file', + 'Sabre_DAV_ICollection' => 'icons/collection', + 'Sabre_DAVACL_IPrincipal' => 'icons/principal', + 'Sabre_CalDAV_ICalendar' => 'icons/calendar', + 'Sabre_CardDAV_IAddressBook' => 'icons/addressbook', + 'Sabre_CardDAV_ICard' => 'icons/card', + ); + + /** + * The file extension used for all icons + * + * @var string + */ + public $iconExtension = '.png'; + + /** + * reference to server class + * + * @var Sabre_DAV_Server + */ + protected $server; + + /** + * enablePost turns on the 'actions' panel, which allows people to create + * folders and upload files straight from a browser. + * + * @var bool + */ + protected $enablePost = true; + + /** + * By default the browser plugin will generate a favicon and other images. + * To turn this off, set this property to false. + * + * @var bool + */ + protected $enableAssets = true; + + /** + * Creates the object. + * + * By default it will allow file creation and uploads. + * Specify the first argument as false to disable this + * + * @param bool $enablePost + * @param bool $enableAssets + */ + public function __construct($enablePost=true, $enableAssets = true) { + + $this->enablePost = $enablePost; + $this->enableAssets = $enableAssets; + + } + + /** + * Initializes the plugin and subscribes to events + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); + $this->server->subscribeEvent('onHTMLActionsPanel', array($this, 'htmlActionsPanel'),200); + if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler')); + } + + /** + * This method intercepts GET requests to collections and returns the html + * + * @param string $method + * @param string $uri + * @return bool + */ + public function httpGetInterceptor($method, $uri) { + + if ($method !== 'GET') return true; + + // We're not using straight-up $_GET, because we want everything to be + // unit testable. + $getVars = array(); + parse_str($this->server->httpRequest->getQueryString(), $getVars); + + if (isset($getVars['sabreAction']) && $getVars['sabreAction'] === 'asset' && isset($getVars['assetName'])) { + $this->serveAsset($getVars['assetName']); + return false; + } + + try { + $node = $this->server->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + // We're simply stopping when the file isn't found to not interfere + // with other plugins. + return; + } + if ($node instanceof Sabre_DAV_IFile) + return; + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8'); + + $this->server->httpResponse->sendBody( + $this->generateDirectoryIndex($uri) + ); + + return false; + + } + + /** + * Handles POST requests for tree operations. + * + * @param string $method + * @param string $uri + * @return bool + */ + public function httpPOSTHandler($method, $uri) { + + if ($method!='POST') return; + $contentType = $this->server->httpRequest->getHeader('Content-Type'); + list($contentType) = explode(';', $contentType); + if ($contentType !== 'application/x-www-form-urlencoded' && + $contentType !== 'multipart/form-data') { + return; + } + $postVars = $this->server->httpRequest->getPostVars(); + + if (!isset($postVars['sabreAction'])) + return; + + if ($this->server->broadcastEvent('onBrowserPostAction', array($uri, $postVars['sabreAction'], $postVars))) { + + switch($postVars['sabreAction']) { + + case 'mkcol' : + if (isset($postVars['name']) && trim($postVars['name'])) { + // Using basename() because we won't allow slashes + list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($postVars['name'])); + $this->server->createDirectory($uri . '/' . $folderName); + } + break; + case 'put' : + if ($_FILES) $file = current($_FILES); + else break; + + list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name'])); + if (isset($postVars['name']) && trim($postVars['name'])) + $newName = trim($postVars['name']); + + // Making sure we only have a 'basename' component + list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName); + + if (is_uploaded_file($file['tmp_name'])) { + $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r')); + } + break; + + } + + } + $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri()); + $this->server->httpResponse->sendStatus(302); + return false; + + } + + /** + * Escapes a string for html. + * + * @param string $value + * @return string + */ + public function escapeHTML($value) { + + return htmlspecialchars($value,ENT_QUOTES,'UTF-8'); + + } + + /** + * Generates the html directory index for a given url + * + * @param string $path + * @return string + */ + public function generateDirectoryIndex($path) { + + $version = ''; + if (Sabre_DAV_Server::$exposeVersion) { + $version = Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY; + } + + $html = " + + Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . " + + "; + + if ($this->enableAssets) { + $html.=''; + } + + $html .= " + +

Index for " . $this->escapeHTML($path) . "/

+ + + "; + + $files = $this->server->getPropertiesForPath($path,array( + '{DAV:}displayname', + '{DAV:}resourcetype', + '{DAV:}getcontenttype', + '{DAV:}getcontentlength', + '{DAV:}getlastmodified', + ),1); + + $parent = $this->server->tree->getNodeForPath($path); + + + if ($path) { + + list($parentUri) = Sabre_DAV_URLUtil::splitPath($path); + $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri); + + $icon = $this->enableAssets?'Parent':''; + $html.= " + + + + + + "; + + } + + foreach($files as $file) { + + // This is the current directory, we can skip it + if (rtrim($file['href'],'/')==$path) continue; + + list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']); + + $type = null; + + + if (isset($file[200]['{DAV:}resourcetype'])) { + $type = $file[200]['{DAV:}resourcetype']->getValue(); + + // resourcetype can have multiple values + if (!is_array($type)) $type = array($type); + + foreach($type as $k=>$v) { + + // Some name mapping is preferred + switch($v) { + case '{DAV:}collection' : + $type[$k] = 'Collection'; + break; + case '{DAV:}principal' : + $type[$k] = 'Principal'; + break; + case '{urn:ietf:params:xml:ns:carddav}addressbook' : + $type[$k] = 'Addressbook'; + break; + case '{urn:ietf:params:xml:ns:caldav}calendar' : + $type[$k] = 'Calendar'; + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : + $type[$k] = 'Schedule Inbox'; + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : + $type[$k] = 'Schedule Outbox'; + break; + case '{http://calendarserver.org/ns/}calendar-proxy-read' : + $type[$k] = 'Proxy-Read'; + break; + case '{http://calendarserver.org/ns/}calendar-proxy-write' : + $type[$k] = 'Proxy-Write'; + break; + } + + } + $type = implode(', ', $type); + } + + // If no resourcetype was found, we attempt to use + // the contenttype property + if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { + $type = $file[200]['{DAV:}getcontenttype']; + } + if (!$type) $type = 'Unknown'; + + $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:''; + $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):''; + + $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/')); + + $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name; + + $displayName = $this->escapeHTML($displayName); + $type = $this->escapeHTML($type); + + $icon = ''; + + if ($this->enableAssets) { + $node = $parent->getChild($name); + foreach(array_reverse($this->iconMap) as $class=>$iconName) { + + if ($node instanceof $class) { + $icon = ''; + break; + } + + + } + + } + + $html.= " + + + + + + "; + + } + + $html.= ""; + + $output = ''; + + if ($this->enablePost) { + $this->server->broadcastEvent('onHTMLActionsPanel',array($parent, &$output)); + } + + $html.=$output; + + $html.= "
NameTypeSizeLast modified

$icon..[parent]
$icon{$displayName}{$type}{$size}{$lastmodified}

+
Generated by SabreDAV " . $version . " (c)2007-2012 http://code.google.com/p/sabredav/
+ + "; + + return $html; + + } + + /** + * This method is used to generate the 'actions panel' output for + * collections. + * + * This specifically generates the interfaces for creating new files, and + * creating new directories. + * + * @param Sabre_DAV_INode $node + * @param mixed $output + * @return void + */ + public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { + + if (!$node instanceof Sabre_DAV_ICollection) + return; + + // We also know fairly certain that if an object is a non-extended + // SimpleCollection, we won't need to show the panel either. + if (get_class($node)==='Sabre_DAV_SimpleCollection') + return; + + $output.= '
+

Create new folder

+ + Name:
+ +
+
+

Upload file

+ + Name (optional):
+ File:
+ +
+ '; + + } + + /** + * This method takes a path/name of an asset and turns it into url + * suiteable for http access. + * + * @param string $assetName + * @return string + */ + protected function getAssetUrl($assetName) { + + return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); + + } + + /** + * This method returns a local pathname to an asset. + * + * @param string $assetName + * @return string + */ + protected function getLocalAssetPath($assetName) { + + // Making sure people aren't trying to escape from the base path. + $assetSplit = explode('/', $assetName); + if (in_array('..',$assetSplit)) { + throw new Sabre_DAV_Exception('Incorrect asset path'); + } + $path = __DIR__ . '/assets/' . $assetName; + return $path; + + } + + /** + * This method reads an asset from disk and generates a full http response. + * + * @param string $assetName + * @return void + */ + protected function serveAsset($assetName) { + + $assetPath = $this->getLocalAssetPath($assetName); + if (!file_exists($assetPath)) { + throw new Sabre_DAV_Exception_NotFound('Could not find an asset with this name'); + } + // Rudimentary mime type detection + switch(strtolower(substr($assetPath,strpos($assetPath,'.')+1))) { + + case 'ico' : + $mime = 'image/vnd.microsoft.icon'; + break; + + case 'png' : + $mime = 'image/png'; + break; + + default: + $mime = 'application/octet-stream'; + break; + + } + + $this->server->httpResponse->setHeader('Content-Type', $mime); + $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); + $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody(fopen($assetPath,'r')); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/favicon.ico b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/favicon.ico new file mode 100644 index 000000000..2b2c10a22 Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/favicon.ico differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/addressbook.png b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/addressbook.png new file mode 100644 index 000000000..c9acc8417 Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/addressbook.png differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/calendar.png b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/calendar.png new file mode 100644 index 000000000..3ecd6a800 Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/calendar.png differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/card.png b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/card.png new file mode 100644 index 000000000..2ce954866 Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/card.png differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/collection.png b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/collection.png new file mode 100644 index 000000000..156fa64fd Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/collection.png differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/file.png b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/file.png new file mode 100644 index 000000000..3b98551ce Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/file.png differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/parent.png b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/parent.png new file mode 100644 index 000000000..156fa64fd Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/parent.png differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/principal.png b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/principal.png new file mode 100644 index 000000000..f8988f828 Binary files /dev/null and b/dav/SabreDAV/lib/Sabre/DAV/Browser/assets/icons/principal.png differ diff --git a/dav/SabreDAV/lib/Sabre/DAV/Client.php b/dav/SabreDAV/lib/Sabre/DAV/Client.php new file mode 100644 index 000000000..98126f8e5 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Client.php @@ -0,0 +1,524 @@ +$validSetting = $settings[$validSetting]; + } + } + + if (isset($settings['authType'])) { + $this->authType = $settings['authType']; + } else { + $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST; + } + + $this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType'; + + } + + /** + * Does a PROPFIND request + * + * The list of requested properties must be specified as an array, in clark + * notation. + * + * The returned array will contain a list of filenames as keys, and + * properties as values. + * + * The properties array will contain the list of properties. Only properties + * that are actually returned from the server (without error) will be + * returned, anything else is discarded. + * + * Depth should be either 0 or 1. A depth of 1 will cause a request to be + * made to the server to also return all child resources. + * + * @param string $url + * @param array $properties + * @param int $depth + * @return array + */ + public function propFind($url, array $properties, $depth = 0) { + + $body = '' . "\n"; + $body.= '' . "\n"; + $body.= ' ' . "\n"; + + foreach($properties as $property) { + + list( + $namespace, + $elementName + ) = Sabre_DAV_XMLUtil::parseClarkNotation($property); + + if ($namespace === 'DAV:') { + $body.=' ' . "\n"; + } else { + $body.=" \n"; + } + + } + + $body.= ' ' . "\n"; + $body.= ''; + + $response = $this->request('PROPFIND', $url, $body, array( + 'Depth' => $depth, + 'Content-Type' => 'application/xml' + )); + + $result = $this->parseMultiStatus($response['body']); + + // If depth was 0, we only return the top item + if ($depth===0) { + reset($result); + $result = current($result); + return $result[200]; + } + + $newResult = array(); + foreach($result as $href => $statusList) { + + $newResult[$href] = $statusList[200]; + + } + + return $newResult; + + } + + /** + * Updates a list of properties on the server + * + * The list of properties must have clark-notation properties for the keys, + * and the actual (string) value for the value. If the value is null, an + * attempt is made to delete the property. + * + * @todo Must be building the request using the DOM, and does not yet + * support complex properties. + * @param string $url + * @param array $properties + * @return void + */ + public function propPatch($url, array $properties) { + + $body = '' . "\n"; + $body.= '' . "\n"; + + foreach($properties as $propName => $propValue) { + + list( + $namespace, + $elementName + ) = Sabre_DAV_XMLUtil::parseClarkNotation($propName); + + if ($propValue === null) { + + $body.="\n"; + + if ($namespace === 'DAV:') { + $body.=' ' . "\n"; + } else { + $body.=" \n"; + } + + $body.="\n"; + + } else { + + $body.="\n"; + if ($namespace === 'DAV:') { + $body.=' '; + } else { + $body.=" "; + } + // Shitty.. i know + $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); + if ($namespace === 'DAV:') { + $body.='' . "\n"; + } else { + $body.="\n"; + } + $body.="\n"; + + } + + } + + $body.= ''; + + $this->request('PROPPATCH', $url, $body, array( + 'Content-Type' => 'application/xml' + )); + + } + + /** + * Performs an HTTP options request + * + * This method returns all the features from the 'DAV:' header as an array. + * If there was no DAV header, or no contents this method will return an + * empty array. + * + * @return array + */ + public function options() { + + $result = $this->request('OPTIONS'); + if (!isset($result['headers']['dav'])) { + return array(); + } + + $features = explode(',', $result['headers']['dav']); + foreach($features as &$v) { + $v = trim($v); + } + return $features; + + } + + /** + * Performs an actual HTTP request, and returns the result. + * + * If the specified url is relative, it will be expanded based on the base + * url. + * + * The returned array contains 3 keys: + * * body - the response body + * * httpCode - a HTTP code (200, 404, etc) + * * headers - a list of response http headers. The header names have + * been lowercased. + * + * @param string $method + * @param string $url + * @param string $body + * @param array $headers + * @return array + */ + public function request($method, $url = '', $body = null, $headers = array()) { + + $url = $this->getAbsoluteUrl($url); + + $curlSettings = array( + CURLOPT_RETURNTRANSFER => true, + // Return headers as part of the response + CURLOPT_HEADER => true, + CURLOPT_POSTFIELDS => $body, + // Automatically follow redirects + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + ); + + switch ($method) { + case 'HEAD' : + + // do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD + // requests when the Content-Length header is given - which in turn is perfectly valid according to HTTP + // specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with + // ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the + // response body + $curlSettings[CURLOPT_NOBODY] = true; + $curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD'; + break; + + default: + $curlSettings[CURLOPT_CUSTOMREQUEST] = $method; + break; + + } + + // Adding HTTP headers + $nHeaders = array(); + foreach($headers as $key=>$value) { + + $nHeaders[] = $key . ': ' . $value; + + } + $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders; + + if ($this->proxy) { + $curlSettings[CURLOPT_PROXY] = $this->proxy; + } + + if ($this->userName && $this->authType) { + $curlType = 0; + if ($this->authType & self::AUTH_BASIC) { + $curlType |= CURLAUTH_BASIC; + } + if ($this->authType & self::AUTH_DIGEST) { + $curlType |= CURLAUTH_DIGEST; + } + $curlSettings[CURLOPT_HTTPAUTH] = $curlType; + $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password; + } + + list( + $response, + $curlInfo, + $curlErrNo, + $curlError + ) = $this->curlRequest($url, $curlSettings); + + $headerBlob = substr($response, 0, $curlInfo['header_size']); + $response = substr($response, $curlInfo['header_size']); + + // In the case of 100 Continue, or redirects we'll have multiple lists + // of headers for each separate HTTP response. We can easily split this + // because they are separated by \r\n\r\n + $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n")); + + // We only care about the last set of headers + $headerBlob = $headerBlob[count($headerBlob)-1]; + + // Splitting headers + $headerBlob = explode("\r\n", $headerBlob); + + $headers = array(); + foreach($headerBlob as $header) { + $parts = explode(':', $header, 2); + if (count($parts)==2) { + $headers[strtolower(trim($parts[0]))] = trim($parts[1]); + } + } + + $response = array( + 'body' => $response, + 'statusCode' => $curlInfo['http_code'], + 'headers' => $headers + ); + + if ($curlErrNo) { + throw new Sabre_DAV_Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')'); + } + + if ($response['statusCode']>=400) { + switch ($response['statusCode']) { + case 400 : + throw new Sabre_DAV_Exception_BadRequest('Bad request'); + case 401 : + throw new Sabre_DAV_Exception_NotAuthenticated('Not authenticated'); + case 402 : + throw new Sabre_DAV_Exception_PaymentRequired('Payment required'); + case 403 : + throw new Sabre_DAV_Exception_Forbidden('Forbidden'); + case 404: + throw new Sabre_DAV_Exception_NotFound('Resource not found.'); + case 405 : + throw new Sabre_DAV_Exception_MethodNotAllowed('Method not allowed'); + case 409 : + throw new Sabre_DAV_Exception_Conflict('Conflict'); + case 412 : + throw new Sabre_DAV_Exception_PreconditionFailed('Precondition failed'); + case 416 : + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('Requested Range Not Satisfiable'); + case 500 : + throw new Sabre_DAV_Exception('Internal server error'); + case 501 : + throw new Sabre_DAV_Exception_NotImplemented('Not Implemeneted'); + case 507 : + throw new Sabre_DAV_Exception_InsufficientStorage('Insufficient storage'); + default: + throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')'); + } + } + + return $response; + + } + + /** + * Wrapper for all curl functions. + * + * The only reason this was split out in a separate method, is so it + * becomes easier to unittest. + * + * @param string $url + * @param array $settings + * @return array + */ + protected function curlRequest($url, $settings) { + + $curl = curl_init($url); + curl_setopt_array($curl, $settings); + + return array( + curl_exec($curl), + curl_getinfo($curl), + curl_errno($curl), + curl_error($curl) + ); + + } + + /** + * Returns the full url based on the given url (which may be relative). All + * urls are expanded based on the base url as given by the server. + * + * @param string $url + * @return string + */ + protected function getAbsoluteUrl($url) { + + // If the url starts with http:// or https://, the url is already absolute. + if (preg_match('/^http(s?):\/\//', $url)) { + return $url; + } + + // If the url starts with a slash, we must calculate the url based off + // the root of the base url. + if (strpos($url,'/') === 0) { + $parts = parse_url($this->baseUri); + return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url; + } + + // Otherwise... + return $this->baseUri . $url; + + } + + /** + * Parses a WebDAV multistatus response body + * + * This method returns an array with the following structure + * + * array( + * 'url/to/resource' => array( + * '200' => array( + * '{DAV:}property1' => 'value1', + * '{DAV:}property2' => 'value2', + * ), + * '404' => array( + * '{DAV:}property1' => null, + * '{DAV:}property2' => null, + * ), + * ) + * 'url/to/resource2' => array( + * .. etc .. + * ) + * ) + * + * + * @param string $body xml body + * @return array + */ + public function parseMultiStatus($body) { + + $body = Sabre_DAV_XMLUtil::convertDAVNamespace($body); + + $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA); + if ($responseXML===false) { + throw new InvalidArgumentException('The passed data is not valid XML'); + } + + $responseXML->registerXPathNamespace('d', 'urn:DAV'); + + $propResult = array(); + + foreach($responseXML->xpath('d:response') as $response) { + $response->registerXPathNamespace('d', 'urn:DAV'); + $href = $response->xpath('d:href'); + $href = (string)$href[0]; + + $properties = array(); + + foreach($response->xpath('d:propstat') as $propStat) { + + $propStat->registerXPathNamespace('d', 'urn:DAV'); + $status = $propStat->xpath('d:status'); + list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3); + + $properties[$statusCode] = Sabre_DAV_XMLUtil::parseProperties(dom_import_simplexml($propStat), $this->propertyMap); + + } + + $propResult[$href] = $properties; + + } + + return $propResult; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Collection.php b/dav/SabreDAV/lib/Sabre/DAV/Collection.php new file mode 100644 index 000000000..c7648a8a5 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Collection.php @@ -0,0 +1,110 @@ +getChildren() as $child) { + + if ($child->getName()==$name) return $child; + + } + throw new Sabre_DAV_Exception_NotFound('File not found: ' . $name); + + } + + /** + * Checks is a child-node exists. + * + * It is generally a good idea to try and override this. Usually it can be optimized. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + try { + + $this->getChild($name); + return true; + + } catch(Sabre_DAV_Exception_NotFound $e) { + + return false; + + } + + } + + /** + * Creates a new file in the directory + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After succesful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. + * + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string + */ + public function createFile($name, $data = null) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')'); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createDirectory($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory'); + + } + + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception.php b/dav/SabreDAV/lib/Sabre/DAV/Exception.php new file mode 100644 index 000000000..a2cd6cf58 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception.php @@ -0,0 +1,64 @@ +lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock'); + $errorNode->appendChild($error); + if (!is_object($this->lock)) var_dump($this->lock); + $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception/FileNotFound.php b/dav/SabreDAV/lib/Sabre/DAV/Exception/FileNotFound.php new file mode 100644 index 000000000..d76e400c9 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception/FileNotFound.php @@ -0,0 +1,19 @@ +ownerDocument->createElementNS('DAV:','d:valid-resourcetype'); + $errorNode->appendChild($error); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/dav/SabreDAV/lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php new file mode 100644 index 000000000..80ab7aff6 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php @@ -0,0 +1,39 @@ +message = 'The locktoken supplied does not match any locks on this entity'; + + } + + /** + * This method allows the exception to include additional information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri'); + $errorNode->appendChild($error); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception/Locked.php b/dav/SabreDAV/lib/Sabre/DAV/Exception/Locked.php new file mode 100644 index 000000000..976365ac1 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception/Locked.php @@ -0,0 +1,67 @@ +lock = $lock; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + public function getHTTPCode() { + + return 423; + + } + + /** + * This method allows the exception to include additional information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + if ($this->lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted'); + $errorNode->appendChild($error); + if (!is_object($this->lock)) var_dump($this->lock); + $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); + } + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception/MethodNotAllowed.php b/dav/SabreDAV/lib/Sabre/DAV/Exception/MethodNotAllowed.php new file mode 100644 index 000000000..318757515 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception/MethodNotAllowed.php @@ -0,0 +1,45 @@ +getAllowedMethods($server->getRequestUri()); + + return array( + 'Allow' => strtoupper(implode(', ',$methods)), + ); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception/NotAuthenticated.php b/dav/SabreDAV/lib/Sabre/DAV/Exception/NotAuthenticated.php new file mode 100644 index 000000000..87ca62442 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception/NotAuthenticated.php @@ -0,0 +1,28 @@ +header = $header; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + public function getHTTPCode() { + + return 412; + + } + + /** + * This method allows the exception to include additional information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + if ($this->header) { + $prop = $errorNode->ownerDocument->createElement('s:header'); + $prop->nodeValue = $this->header; + $errorNode->appendChild($prop); + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception/ReportNotImplemented.php b/dav/SabreDAV/lib/Sabre/DAV/Exception/ReportNotImplemented.php new file mode 100644 index 000000000..e86800f30 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception/ReportNotImplemented.php @@ -0,0 +1,30 @@ +ownerDocument->createElementNS('DAV:','d:supported-report'); + $errorNode->appendChild($error); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/dav/SabreDAV/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php new file mode 100644 index 000000000..29ee3654a --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php @@ -0,0 +1,29 @@ +path . '/' . $name; + file_put_contents($newPath,$data); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + $newPath = $this->path . '/' . $name; + mkdir($newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * + * @param string $name + * @throws Sabre_DAV_Exception_NotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); + + if (is_dir($path)) { + + return new Sabre_DAV_FS_Directory($path); + + } else { + + return new Sabre_DAV_FS_File($path); + + } + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $nodes = array(); + foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); + return $nodes; + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return void + */ + public function delete() { + + foreach($this->getChildren() as $child) $child->delete(); + rmdir($this->path); + + } + + /** + * Returns available diskspace information + * + * @return array + */ + public function getQuotaInfo() { + + return array( + disk_total_space($this->path)-disk_free_space($this->path), + disk_free_space($this->path) + ); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/FS/File.php b/dav/SabreDAV/lib/Sabre/DAV/FS/File.php new file mode 100644 index 000000000..6a8039fe3 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/FS/File.php @@ -0,0 +1,89 @@ +path,$data); + + } + + /** + * Returns the data + * + * @return string + */ + public function get() { + + return fopen($this->path,'r'); + + } + + /** + * Delete the current file + * + * @return void + */ + public function delete() { + + unlink($this->path); + + } + + /** + * Returns the size of the node, in bytes + * + * @return int + */ + public function getSize() { + + return filesize($this->path); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * + * @return mixed + */ + public function getETag() { + + return null; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * + * @return mixed + */ + public function getContentType() { + + return null; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/FS/Node.php b/dav/SabreDAV/lib/Sabre/DAV/FS/Node.php new file mode 100644 index 000000000..1283e9d0f --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/FS/Node.php @@ -0,0 +1,80 @@ +path = $path; + + } + + + + /** + * Returns the name of the node + * + * @return string + */ + public function getName() { + + list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path); + return $name; + + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + public function setName($name) { + + list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); + + $newPath = $parentPath . '/' . $newName; + rename($this->path,$newPath); + + $this->path = $newPath; + + } + + + + /** + * Returns the last modification time, as a unix timestamp + * + * @return int + */ + public function getLastModified() { + + return filemtime($this->path); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/FSExt/Directory.php b/dav/SabreDAV/lib/Sabre/DAV/FSExt/Directory.php new file mode 100644 index 000000000..70dfdc2c3 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/FSExt/Directory.php @@ -0,0 +1,157 @@ +path . '/' . $name; + file_put_contents($newPath,$data); + + return '"' . md5_file($newPath) . '"'; + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + // We're not allowing dots + if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + $newPath = $this->path . '/' . $name; + mkdir($newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * + * @param string $name + * @throws Sabre_DAV_Exception_NotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new Sabre_DAV_Exception_NotFound('File could not be located'); + if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + + if (is_dir($path)) { + + return new Sabre_DAV_FSExt_Directory($path); + + } else { + + return new Sabre_DAV_FSExt_File($path); + + } + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + if ($name=='.' || $name=='..') + throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $nodes = array(); + foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node); + return $nodes; + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return bool + */ + public function delete() { + + // Deleting all children + foreach($this->getChildren() as $child) $child->delete(); + + // Removing resource info, if its still around + if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav'); + + // Removing the directory itself + rmdir($this->path); + + return parent::delete(); + + } + + /** + * Returns available diskspace information + * + * @return array + */ + public function getQuotaInfo() { + + return array( + disk_total_space($this->path)-disk_free_space($this->path), + disk_free_space($this->path) + ); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/FSExt/File.php b/dav/SabreDAV/lib/Sabre/DAV/FSExt/File.php new file mode 100644 index 000000000..590fb808e --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/FSExt/File.php @@ -0,0 +1,117 @@ +path,$data); + return '"' . md5_file($this->path) . '"'; + + } + + /** + * Updates the data at a given offset + * + * The data argument is a readable stream resource. + * The offset argument is a 0-based offset where the data should be + * written. + * + * param resource|string $data + * @return void + */ + public function putRange($data, $offset) { + + $f = fopen($this->path, 'c'); + fseek($f,$offset-1); + if (is_string($data)) { + fwrite($f, $data); + } else { + stream_copy_to_stream($data,$f); + } + fclose($f); + return '"' . md5_file($this->path) . '"'; + + } + + /** + * Returns the data + * + * @return resource + */ + public function get() { + + return fopen($this->path,'r'); + + } + + /** + * Delete the current file + * + * @return bool + */ + public function delete() { + + unlink($this->path); + return parent::delete(); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * + * @return string|null + */ + public function getETag() { + + return '"' . md5_file($this->path). '"'; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * + * @return string|null + */ + public function getContentType() { + + return null; + + } + + /** + * Returns the size of the file, in bytes + * + * @return int + */ + public function getSize() { + + return filesize($this->path); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/FSExt/Node.php b/dav/SabreDAV/lib/Sabre/DAV/FSExt/Node.php new file mode 100644 index 000000000..68ca06beb --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/FSExt/Node.php @@ -0,0 +1,212 @@ +getResourceData(); + + foreach($properties as $propertyName=>$propertyValue) { + + // If it was null, we need to delete the property + if (is_null($propertyValue)) { + if (isset($resourceData['properties'][$propertyName])) { + unset($resourceData['properties'][$propertyName]); + } + } else { + $resourceData['properties'][$propertyName] = $propertyValue; + } + + } + + $this->putResourceData($resourceData); + return true; + } + + /** + * Returns a list of properties for this nodes.; + * + * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author + * If the array is empty, all properties should be returned + * + * @param array $properties + * @return array + */ + function getProperties($properties) { + + $resourceData = $this->getResourceData(); + + // if the array was empty, we need to return everything + if (!$properties) return $resourceData['properties']; + + $props = array(); + foreach($properties as $property) { + if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; + } + + return $props; + + } + + /** + * Returns the path to the resource file + * + * @return string + */ + protected function getResourceInfoPath() { + + list($parentDir) = Sabre_DAV_URLUtil::splitPath($this->path); + return $parentDir . '/.sabredav'; + + } + + /** + * Returns all the stored resource information + * + * @return array + */ + protected function getResourceData() { + + $path = $this->getResourceInfoPath(); + if (!file_exists($path)) return array('properties' => array()); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'r'); + flock($handle,LOCK_SH); + $data = ''; + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // We're all good + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!isset($data[$this->getName()])) { + return array('properties' => array()); + } + + $data = $data[$this->getName()]; + if (!isset($data['properties'])) $data['properties'] = array(); + return $data; + + } + + /** + * Updates the resource information + * + * @param array $newData + * @return void + */ + protected function putResourceData(array $newData) { + + $path = $this->getResourceInfoPath(); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + $data = ''; + + rewind($handle); + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + $data[$this->getName()] = $newData; + ftruncate($handle,0); + rewind($handle); + + fwrite($handle,serialize($data)); + fclose($handle); + + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + public function setName($name) { + + list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); + $newPath = $parentPath . '/' . $newName; + + // We're deleting the existing resourcedata, and recreating it + // for the new path. + $resourceData = $this->getResourceData(); + $this->deleteResourceData(); + + rename($this->path,$newPath); + $this->path = $newPath; + $this->putResourceData($resourceData); + + + } + + /** + * @return bool + */ + public function deleteResourceData() { + + // When we're deleting this node, we also need to delete any resource information + $path = $this->getResourceInfoPath(); + if (!file_exists($path)) return true; + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + $data = ''; + + rewind($handle); + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (isset($data[$this->getName()])) unset($data[$this->getName()]); + ftruncate($handle,0); + rewind($handle); + fwrite($handle,serialize($data)); + fclose($handle); + + return true; + } + + public function delete() { + + return $this->deleteResourceData(); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/File.php b/dav/SabreDAV/lib/Sabre/DAV/File.php new file mode 100644 index 000000000..3126bd8d3 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/File.php @@ -0,0 +1,85 @@ + array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param array $mutations + * @return bool|array + */ + function updateProperties($mutations); + + /** + * Returns a list of properties for this nodes. + * + * The properties list is a list of propertynames the client requested, + * encoded in clark-notation {xmlnamespace}tagname + * + * If the array is empty, it means 'all properties' were requested. + * + * @param array $properties + * @return void + */ + function getProperties($properties); + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/IQuota.php b/dav/SabreDAV/lib/Sabre/DAV/IQuota.php new file mode 100644 index 000000000..3fe4c4ece --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/IQuota.php @@ -0,0 +1,27 @@ +dataDir = $dataDir; + + } + + protected function getFileNameForUri($uri) { + + return $this->dataDir . '/sabredav_' . md5($uri) . '.locks'; + + } + + + /** + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * If returnChildLocks is set to true, this method should also look for + * any locks in the subtree of the uri for locks. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + public function getLocks($uri, $returnChildLocks) { + + $lockList = array(); + $currentPath = ''; + + foreach(explode('/',$uri) as $uriPart) { + + // weird algorithm that can probably be improved, but we're traversing the path top down + if ($currentPath) $currentPath.='/'; + $currentPath.=$uriPart; + + $uriLocks = $this->getData($currentPath); + + foreach($uriLocks as $uriLock) { + + // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0 + if($uri==$currentPath || $uriLock->depth!=0) { + $uriLock->uri = $currentPath; + $lockList[] = $uriLock; + } + + } + + } + + // Checking if we can remove any of these locks + foreach($lockList as $k=>$lock) { + if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); + } + return $lockList; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + + $locks = $this->getLocks($uri,false); + foreach($locks as $k=>$lock) { + if ($lock->token == $lockInfo->token) unset($locks[$k]); + } + $locks[] = $lockInfo; + $this->putData($uri,$locks); + return true; + + } + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + $locks = $this->getLocks($uri,false); + foreach($locks as $k=>$lock) { + + if ($lock->token == $lockInfo->token) { + + unset($locks[$k]); + $this->putData($uri,$locks); + return true; + + } + } + return false; + + } + + /** + * Returns the stored data for a uri + * + * @param string $uri + * @return array + */ + protected function getData($uri) { + + $path = $this->getFilenameForUri($uri); + if (!file_exists($path)) return array(); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'r'); + flock($handle,LOCK_SH); + $data = ''; + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // We're all good + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!$data) return array(); + return $data; + + } + + /** + * Updates the lock information + * + * @param string $uri + * @param array $newData + * @return void + */ + protected function putData($uri,array $newData) { + + $path = $this->getFileNameForUri($uri); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + ftruncate($handle,0); + rewind($handle); + + fwrite($handle,serialize($newData)); + fclose($handle); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Locks/Backend/File.php b/dav/SabreDAV/lib/Sabre/DAV/Locks/Backend/File.php new file mode 100644 index 000000000..c33f96351 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Locks/Backend/File.php @@ -0,0 +1,181 @@ +locksFile = $locksFile; + + } + + /** + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * If returnChildLocks is set to true, this method should also look for + * any locks in the subtree of the uri for locks. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + public function getLocks($uri, $returnChildLocks) { + + $newLocks = array(); + + $locks = $this->getData(); + + foreach($locks as $lock) { + + if ($lock->uri === $uri || + //deep locks on parents + ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) || + + // locks on children + ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) { + + $newLocks[] = $lock; + + } + + } + + // Checking if we can remove any of these locks + foreach($newLocks as $k=>$lock) { + if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); + } + return $newLocks; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + $lockInfo->uri = $uri; + + $locks = $this->getData(); + + foreach($locks as $k=>$lock) { + if ( + ($lock->token == $lockInfo->token) || + (time() > $lock->timeout + $lock->created) + ) { + unset($locks[$k]); + } + } + $locks[] = $lockInfo; + $this->putData($locks); + return true; + + } + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) { + + $locks = $this->getData(); + foreach($locks as $k=>$lock) { + + if ($lock->token == $lockInfo->token) { + + unset($locks[$k]); + $this->putData($locks); + return true; + + } + } + return false; + + } + + /** + * Loads the lockdata from the filesystem. + * + * @return array + */ + protected function getData() { + + if (!file_exists($this->locksFile)) return array(); + + // opening up the file, and creating a shared lock + $handle = fopen($this->locksFile,'r'); + flock($handle,LOCK_SH); + + // Reading data until the eof + $data = stream_get_contents($handle); + + // We're all good + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!$data) return array(); + return $data; + + } + + /** + * Saves the lockdata + * + * @param array $newData + * @return void + */ + protected function putData(array $newData) { + + // opening up the file, and creating an exclusive lock + $handle = fopen($this->locksFile,'a+'); + flock($handle,LOCK_EX); + + // We can only truncate and rewind once the lock is acquired. + ftruncate($handle,0); + rewind($handle); + + fwrite($handle,serialize($newData)); + fclose($handle); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Locks/Backend/PDO.php b/dav/SabreDAV/lib/Sabre/DAV/Locks/Backend/PDO.php new file mode 100644 index 000000000..acce80638 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Locks/Backend/PDO.php @@ -0,0 +1,165 @@ +pdo = $pdo; + $this->tableName = $tableName; + + } + + /** + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * If returnChildLocks is set to true, this method should also look for + * any locks in the subtree of the uri for locks. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + public function getLocks($uri, $returnChildLocks) { + + // NOTE: the following 10 lines or so could be easily replaced by + // pure sql. MySQL's non-standard string concatenation prevents us + // from doing this though. + $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; + $params = array(time(),$uri); + + // We need to check locks for every part in the uri. + $uriParts = explode('/',$uri); + + // We already covered the last part of the uri + array_pop($uriParts); + + $currentPath=''; + + foreach($uriParts as $part) { + + if ($currentPath) $currentPath.='/'; + $currentPath.=$part; + + $query.=' OR (depth!=0 AND uri = ?)'; + $params[] = $currentPath; + + } + + if ($returnChildLocks) { + + $query.=' OR (uri LIKE ?)'; + $params[] = $uri . '/%'; + + } + $query.=')'; + + $stmt = $this->pdo->prepare($query); + $stmt->execute($params); + $result = $stmt->fetchAll(); + + $lockList = array(); + foreach($result as $row) { + + $lockInfo = new Sabre_DAV_Locks_LockInfo(); + $lockInfo->owner = $row['owner']; + $lockInfo->token = $row['token']; + $lockInfo->timeout = $row['timeout']; + $lockInfo->created = $row['created']; + $lockInfo->scope = $row['scope']; + $lockInfo->depth = $row['depth']; + $lockInfo->uri = $row['uri']; + $lockList[] = $lockInfo; + + } + + return $lockList; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 30*60; + $lockInfo->created = time(); + $lockInfo->uri = $uri; + + $locks = $this->getLocks($uri,false); + $exists = false; + foreach($locks as $lock) { + if ($lock->token == $lockInfo->token) $exists = true; + } + + if ($exists) { + $stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); + $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); + } else { + $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); + $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); + } + + return true; + + } + + + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?'); + $stmt->execute(array($uri,$lockInfo->token)); + + return $stmt->rowCount()===1; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Locks/LockInfo.php b/dav/SabreDAV/lib/Sabre/DAV/Locks/LockInfo.php new file mode 100644 index 000000000..9df014a42 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Locks/LockInfo.php @@ -0,0 +1,81 @@ +addPlugin($lockPlugin); + * + * @package Sabre + * @subpackage DAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { + + /** + * locksBackend + * + * @var Sabre_DAV_Locks_Backend_Abstract + */ + private $locksBackend; + + /** + * server + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * __construct + * + * @param Sabre_DAV_Locks_Backend_Abstract $locksBackend + */ + public function __construct(Sabre_DAV_Locks_Backend_Abstract $locksBackend = null) { + + $this->locksBackend = $locksBackend; + + } + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50); + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre_DAV_Server::getPlugin + * + * @return string + */ + public function getPluginName() { + + return 'locks'; + + } + + /** + * This method is called by the Server if the user used an HTTP method + * the server didn't recognize. + * + * This plugin intercepts the LOCK and UNLOCK methods. + * + * @param string $method + * @param string $uri + * @return bool + */ + public function unknownMethod($method, $uri) { + + switch($method) { + + case 'LOCK' : $this->httpLock($uri); return false; + case 'UNLOCK' : $this->httpUnlock($uri); return false; + + } + + } + + /** + * This method is called after most properties have been found + * it allows us to add in any Lock-related properties + * + * @param string $path + * @param array $newProperties + * @return bool + */ + public function afterGetProperties($path, &$newProperties) { + + foreach($newProperties[404] as $propName=>$discard) { + + switch($propName) { + + case '{DAV:}supportedlock' : + $val = false; + if ($this->locksBackend) $val = true; + $newProperties[200][$propName] = new Sabre_DAV_Property_SupportedLock($val); + unset($newProperties[404][$propName]); + break; + + case '{DAV:}lockdiscovery' : + $newProperties[200][$propName] = new Sabre_DAV_Property_LockDiscovery($this->getLocks($path)); + unset($newProperties[404][$propName]); + break; + + } + + + } + return true; + + } + + + /** + * This method is called before the logic for any HTTP method is + * handled. + * + * This plugin uses that feature to intercept access to locked resources. + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + switch($method) { + + case 'DELETE' : + $lastLock = null; + if (!$this->validateLock($uri,$lastLock, true)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + case 'MKCOL' : + case 'PROPPATCH' : + case 'PUT' : + case 'PATCH' : + $lastLock = null; + if (!$this->validateLock($uri,$lastLock)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + case 'MOVE' : + $lastLock = null; + if (!$this->validateLock(array( + $uri, + $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), + ),$lastLock, true)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + case 'COPY' : + $lastLock = null; + if (!$this->validateLock( + $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), + $lastLock, true)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + } + + return true; + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + if ($this->locksBackend) + return array('LOCK','UNLOCK'); + + return array(); + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * In this case this is only the number 2. The 2 in the Dav: header + * indicates the server supports locks. + * + * @return array + */ + public function getFeatures() { + + return array(2); + + } + + /** + * Returns all lock information on a particular uri + * + * This function should return an array with Sabre_DAV_Locks_LockInfo objects. If there are no locks on a file, return an empty array. + * + * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree + * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object + * for any possible locks and return those as well. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + public function getLocks($uri, $returnChildLocks = false) { + + $lockList = array(); + + if ($this->locksBackend) + $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks)); + + return $lockList; + + } + + /** + * Locks an uri + * + * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock + * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type + * of lock (shared or exclusive) and the owner of the lock + * + * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock + * + * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3 + * + * @param string $uri + * @return void + */ + protected function httpLock($uri) { + + $lastLock = null; + if (!$this->validateLock($uri,$lastLock)) { + + // If the existing lock was an exclusive lock, we need to fail + if (!$lastLock || $lastLock->scope == Sabre_DAV_Locks_LockInfo::EXCLUSIVE) { + //var_dump($lastLock); + throw new Sabre_DAV_Exception_ConflictingLock($lastLock); + } + + } + + if ($body = $this->server->httpRequest->getBody(true)) { + // This is a new lock request + $lockInfo = $this->parseLockRequest($body); + $lockInfo->depth = $this->server->getHTTPDepth(); + $lockInfo->uri = $uri; + if($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) throw new Sabre_DAV_Exception_ConflictingLock($lastLock); + + } elseif ($lastLock) { + + // This must have been a lock refresh + $lockInfo = $lastLock; + + // The resource could have been locked through another uri. + if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri; + + } else { + + // There was neither a lock refresh nor a new lock request + throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests'); + + } + + if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; + + $newFile = false; + + // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first + try { + $this->server->tree->getNodeForPath($uri); + + // We need to call the beforeWriteContent event for RFC3744 + $this->server->broadcastEvent('beforeWriteContent',array($uri)); + + } catch (Sabre_DAV_Exception_NotFound $e) { + + // It didn't, lets create it + $this->server->createFile($uri,fopen('php://memory','r')); + $newFile = true; + + } + + $this->lockNode($uri,$lockInfo); + + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Lock-Token','token . '>'); + $this->server->httpResponse->sendStatus($newFile?201:200); + $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo)); + + } + + /** + * Unlocks a uri + * + * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header + * The server should return 204 (No content) on success + * + * @param string $uri + * @return void + */ + protected function httpUnlock($uri) { + + $lockToken = $this->server->httpRequest->getHeader('Lock-Token'); + + // If the locktoken header is not supplied, we need to throw a bad request exception + if (!$lockToken) throw new Sabre_DAV_Exception_BadRequest('No lock token was supplied'); + + $locks = $this->getLocks($uri); + + // Windows sometimes forgets to include < and > in the Lock-Token + // header + if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>'; + + foreach($locks as $lock) { + + if ('token . '>' == $lockToken) { + + $this->unlockNode($uri,$lock); + $this->server->httpResponse->setHeader('Content-Length','0'); + $this->server->httpResponse->sendStatus(204); + return; + + } + + } + + // If we got here, it means the locktoken was invalid + throw new Sabre_DAV_Exception_LockTokenMatchesRequestUri(); + + } + + /** + * Locks a uri + * + * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored + * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return; + + if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo); + throw new Sabre_DAV_Exception_MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.'); + + } + + /** + * Unlocks a uri + * + * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return; + if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo); + + } + + + /** + * Returns the contents of the HTTP Timeout header. + * + * The method formats the header into an integer. + * + * @return int + */ + public function getTimeoutHeader() { + + $header = $this->server->httpRequest->getHeader('Timeout'); + + if ($header) { + + if (stripos($header,'second-')===0) $header = (int)(substr($header,7)); + else if (strtolower($header)=='infinite') $header=Sabre_DAV_Locks_LockInfo::TIMEOUT_INFINITE; + else throw new Sabre_DAV_Exception_BadRequest('Invalid HTTP timeout header'); + + } else { + + $header = 0; + + } + + return $header; + + } + + /** + * Generates the response for successful LOCK requests + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return string + */ + protected function generateLockResponse(Sabre_DAV_Locks_LockInfo $lockInfo) { + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + + $prop = $dom->createElementNS('DAV:','d:prop'); + $dom->appendChild($prop); + + $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery'); + $prop->appendChild($lockDiscovery); + + $lockObj = new Sabre_DAV_Property_LockDiscovery(array($lockInfo),true); + $lockObj->serialize($this->server,$lockDiscovery); + + return $dom->saveXML(); + + } + + /** + * validateLock should be called when a write operation is about to happen + * It will check if the requested url is locked, and see if the correct lock tokens are passed + * + * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri + * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo) + * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees. + * @return bool + */ + protected function validateLock($urls = null,&$lastLock = null, $checkChildLocks = false) { + + if (is_null($urls)) { + $urls = array($this->server->getRequestUri()); + } elseif (is_string($urls)) { + $urls = array($urls); + } elseif (!is_array($urls)) { + throw new Sabre_DAV_Exception('The urls parameter should either be null, a string or an array'); + } + + $conditions = $this->getIfConditions(); + + // We're going to loop through the urls and make sure all lock conditions are satisfied + foreach($urls as $url) { + + $locks = $this->getLocks($url, $checkChildLocks); + + // If there were no conditions, but there were locks, we fail + if (!$conditions && $locks) { + reset($locks); + $lastLock = current($locks); + return false; + } + + // If there were no locks or conditions, we go to the next url + if (!$locks && !$conditions) continue; + + foreach($conditions as $condition) { + + if (!$condition['uri']) { + $conditionUri = $this->server->getRequestUri(); + } else { + $conditionUri = $this->server->calculateUri($condition['uri']); + } + + // If the condition has a url, and it isn't part of the affected url at all, check the next condition + if ($conditionUri && strpos($url,$conditionUri)!==0) continue; + + // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken + // At least 1 condition has to be satisfied + foreach($condition['tokens'] as $conditionToken) { + + $etagValid = true; + $lockValid = true; + + // key 2 can contain an etag + if ($conditionToken[2]) { + + $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); + $node = $this->server->tree->getNodeForPath($uri); + $etagValid = $node->getETag()==$conditionToken[2]; + + } + + // key 1 can contain a lock token + if ($conditionToken[1]) { + + $lockValid = false; + // Match all the locks + foreach($locks as $lockIndex=>$lock) { + + $lockToken = 'opaquelocktoken:' . $lock->token; + + // Checking NOT + if (!$conditionToken[0] && $lockToken != $conditionToken[1]) { + + // Condition valid, onto the next + $lockValid = true; + break; + } + if ($conditionToken[0] && $lockToken == $conditionToken[1]) { + + $lastLock = $lock; + // Condition valid and lock matched + unset($locks[$lockIndex]); + $lockValid = true; + break; + + } + + } + + } + + // If, after checking both etags and locks they are stil valid, + // we can continue with the next condition. + if ($etagValid && $lockValid) continue 2; + } + // No conditions matched, so we fail + throw new Sabre_DAV_Exception_PreconditionFailed('The tokens provided in the if header did not match','If'); + } + + // Conditions were met, we'll also need to check if all the locks are gone + if (count($locks)) { + + reset($locks); + + // There's still locks, we fail + $lastLock = current($locks); + return false; + + } + + + } + + // We got here, this means every condition was satisfied + return true; + + } + + /** + * This method is created to extract information from the WebDAV HTTP 'If:' header + * + * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information + * The function will return an array, containing structs with the following keys + * + * * uri - the uri the condition applies to. If this is returned as an + * empty string, this implies it's referring to the request url. + * * tokens - The lock token. another 2 dimensional array containing 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token) + * * etag - an etag, if supplied + * + * @return array + */ + public function getIfConditions() { + + $header = $this->server->httpRequest->getHeader('If'); + if (!$header) return array(); + + $matches = array(); + + $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; + preg_match_all($regex,$header,$matches,PREG_SET_ORDER); + + $conditions = array(); + + foreach($matches as $match) { + + $condition = array( + 'uri' => $match['uri'], + 'tokens' => array( + array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'') + ), + ); + + if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array( + $match['not']?0:1, + $match['token'], + isset($match['etag'])?$match['etag']:'' + ); + else { + $conditions[] = $condition; + } + + } + + return $conditions; + + } + + /** + * Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object + * + * @param string $body + * @return Sabre_DAV_Locks_LockInfo + */ + protected function parseLockRequest($body) { + + $xml = simplexml_load_string($body,null,LIBXML_NOWARNING); + $xml->registerXPathNamespace('d','DAV:'); + $lockInfo = new Sabre_DAV_Locks_LockInfo(); + + $children = $xml->children("DAV:"); + $lockInfo->owner = (string)$children->owner; + + $lockInfo->token = Sabre_DAV_UUIDUtil::getUUID(); + $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0?Sabre_DAV_Locks_LockInfo::EXCLUSIVE:Sabre_DAV_Locks_LockInfo::SHARED; + + return $lockInfo; + + } + + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Mount/Plugin.php b/dav/SabreDAV/lib/Sabre/DAV/Mount/Plugin.php new file mode 100644 index 000000000..b37a90ae9 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Mount/Plugin.php @@ -0,0 +1,80 @@ +server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); + + } + + /** + * 'beforeMethod' event handles. This event handles intercepts GET requests ending + * with ?mount + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + if ($method!='GET') return; + if ($this->server->httpRequest->getQueryString()!='mount') return; + + $currentUri = $this->server->httpRequest->getAbsoluteUri(); + + // Stripping off everything after the ? + list($currentUri) = explode('?',$currentUri); + + $this->davMount($currentUri); + + // Returning false to break the event chain + return false; + + } + + /** + * Generates the davmount response + * + * @param string $uri absolute uri + * @return void + */ + public function davMount($uri) { + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml'); + ob_start(); + echo '', "\n"; + echo "\n"; + echo " ", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; + echo ""; + $this->server->httpResponse->sendBody(ob_get_clean()); + + } + + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Node.php b/dav/SabreDAV/lib/Sabre/DAV/Node.php new file mode 100644 index 000000000..3b95dfec2 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Node.php @@ -0,0 +1,55 @@ +rootNode = $rootNode; + + } + + /** + * Returns the INode object for the requested path + * + * @param string $path + * @return Sabre_DAV_INode + */ + public function getNodeForPath($path) { + + $path = trim($path,'/'); + if (isset($this->cache[$path])) return $this->cache[$path]; + + //if (!$path || $path=='.') return $this->rootNode; + $currentNode = $this->rootNode; + + // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. + foreach(explode('/',$path) as $pathPart) { + + // If this part of the path is just a dot, it actually means we can skip it + if ($pathPart=='.' || $pathPart=='') continue; + + if (!($currentNode instanceof Sabre_DAV_ICollection)) + throw new Sabre_DAV_Exception_NotFound('Could not find node at path: ' . $path); + + $currentNode = $currentNode->getChild($pathPart); + + } + + $this->cache[$path] = $currentNode; + return $currentNode; + + } + + /** + * This function allows you to check if a node exists. + * + * @param string $path + * @return bool + */ + public function nodeExists($path) { + + try { + + // The root always exists + if ($path==='') return true; + + list($parent, $base) = Sabre_DAV_URLUtil::splitPath($path); + + $parentNode = $this->getNodeForPath($parent); + if (!$parentNode instanceof Sabre_DAV_ICollection) return false; + return $parentNode->childExists($base); + + } catch (Sabre_DAV_Exception_NotFound $e) { + + return false; + + } + + } + + /** + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array + */ + public function getChildren($path) { + + $node = $this->getNodeForPath($path); + $children = $node->getChildren(); + foreach($children as $child) { + + $this->cache[trim($path,'/') . '/' . $child->getName()] = $child; + + } + return $children; + + } + + /** + * This method is called with every tree update + * + * Examples of tree updates are: + * * node deletions + * * node creations + * * copy + * * move + * * renaming nodes + * + * If Tree classes implement a form of caching, this will allow + * them to make sure caches will be expired. + * + * If a path is passed, it is assumed that the entire subtree is dirty + * + * @param string $path + * @return void + */ + public function markDirty($path) { + + // We don't care enough about sub-paths + // flushing the entire cache + $path = trim($path,'/'); + foreach($this->cache as $nodePath=>$node) { + if ($nodePath == $path || strpos($nodePath,$path.'/')===0) + unset($this->cache[$nodePath]); + + } + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/PartialUpdate/IFile.php b/dav/SabreDAV/lib/Sabre/DAV/PartialUpdate/IFile.php new file mode 100644 index 000000000..cf5ad55c6 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/PartialUpdate/IFile.php @@ -0,0 +1,38 @@ +addPlugin($patchPlugin); + * + * @package Sabre + * @subpackage DAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_DAV_PartialUpdate_Plugin extends Sabre_DAV_ServerPlugin { + + /** + * Reference to server + * + * @var Sabre_DAV_Server + */ + protected $server; + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre_DAV_Server::getPlugin + * + * @return string + */ + public function getPluginName() { + + return 'partialupdate'; + + } + + /** + * This method is called by the Server if the user used an HTTP method + * the server didn't recognize. + * + * This plugin intercepts the PATCH methods. + * + * @param string $method + * @param string $uri + * @return bool|null + */ + public function unknownMethod($method, $uri) { + + switch($method) { + + case 'PATCH': + return $this->httpPatch($uri); + + } + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * We claim to support PATCH method (partial update) if and only if + * - the node exist + * - the node implements our partial update interface + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + $tree = $this->server->tree; + + if ($tree->nodeExists($uri) && + $tree->getNodeForPath($uri) instanceof Sabre_DAV_PartialUpdate_IFile) { + return array('PATCH'); + } + + return array(); + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * @return array + */ + public function getFeatures() { + + return array('sabredav-partialupdate'); + + } + + /** + * Patch an uri + * + * The WebDAV patch request can be used to modify only a part of an + * existing resource. If the resource does not exist yet and the first + * offset is not 0, the request fails + * + * @param string $uri + * @return void + */ + protected function httpPatch($uri) { + + // Get the node. Will throw a 404 if not found + $node = $this->server->tree->getNodeForPath($uri); + if (!($node instanceof Sabre_DAV_PartialUpdate_IFile)) { + throw new Sabre_DAV_Exception_MethodNotAllowed('The target resource does not support the PATCH method.'); + } + + $range = $this->getHTTPUpdateRange(); + + if (!$range) { + throw new Sabre_DAV_Exception_BadRequest('No valid "X-Update-Range" found in the headers'); + } + + $contentType = strtolower( + $this->server->httpRequest->getHeader('Content-Type') + ); + + if ($contentType != 'application/x-sabredav-partialupdate') { + throw new Sabre_DAV_Exception_UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); + } + + $len = $this->server->httpRequest->getHeader('Content-Length'); + + // Load the begin and end data + $start = ($range[0])?$range[0]:0; + $end = ($range[1])?$range[1]:$len-1; + + // Check consistency + if($end < $start) + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); + if($end - $start + 1 != $len) + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets'); + + // Checking If-None-Match and related headers. + if (!$this->server->checkPreconditions()) return; + + if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null))) + return; + + $body = $this->server->httpRequest->getBody(); + $etag = $node->putRange($body, $start-1); + + $this->server->broadcastEvent('afterWriteContent',array($uri, $node)); + + $this->server->httpResponse->setHeader('Content-Length','0'); + if ($etag) $this->server->httpResponse->setHeader('ETag',$etag); + $this->server->httpResponse->sendStatus(204); + + return false; + + } + + /** + * Returns the HTTP custom range update header + * + * This method returns null if there is no well-formed HTTP range request + * header or array($start, $end). + * + * The first number is the offset of the first byte in the range. + * The second number is the offset of the last byte in the range. + * + * If the second offset is null, it should be treated as the offset of the last byte of the entity + * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity + * + * @return array|null + */ + public function getHTTPUpdateRange() { + + $range = $this->server->httpRequest->getHeader('X-Update-Range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; + + if ($matches[1]==='' && $matches[2]==='') return null; + + return array( + $matches[1]!==''?$matches[1]:null, + $matches[2]!==''?$matches[2]:null, + ); + + } +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property.php b/dav/SabreDAV/lib/Sabre/DAV/Property.php new file mode 100644 index 000000000..1cfada323 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property.php @@ -0,0 +1,25 @@ +time = $time; + } elseif (is_int($time) || ctype_digit($time)) { + $this->time = new DateTime('@' . $time); + } else { + $this->time = new DateTime($time); + } + + // Setting timezone to UTC + $this->time->setTimezone(new DateTimeZone('UTC')); + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { + + $doc = $prop->ownerDocument; + $prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/'); + $prop->setAttribute('b:dt','dateTime.rfc1123'); + $prop->nodeValue = Sabre_HTTP_Util::toHTTPDate($this->time); + + } + + /** + * getTime + * + * @return DateTime + */ + public function getTime() { + + return $this->time; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/Href.php b/dav/SabreDAV/lib/Sabre/DAV/Property/Href.php new file mode 100644 index 000000000..dac564f24 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/Href.php @@ -0,0 +1,91 @@ +href = $href; + $this->autoPrefix = $autoPrefix; + + } + + /** + * Returns the uri + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Serializes this property. + * + * It will additionally prepend the href property with the server's base uri. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $dom) { + + $prefix = $server->xmlNamespaces['DAV:']; + + $elem = $dom->ownerDocument->createElement($prefix . ':href'); + $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $this->href; + $dom->appendChild($elem); + + } + + /** + * Unserializes this property from a DOM Element + * + * This method returns an instance of this class. + * It will only decode {DAV:}href values. For non-compatible elements null will be returned. + * + * @param DOMElement $dom + * @return Sabre_DAV_Property_Href + */ + static function unserialize(DOMElement $dom) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { + return new self($dom->firstChild->textContent,false); + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/HrefList.php b/dav/SabreDAV/lib/Sabre/DAV/Property/HrefList.php new file mode 100644 index 000000000..282c452ca --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/HrefList.php @@ -0,0 +1,96 @@ +hrefs = $hrefs; + $this->autoPrefix = $autoPrefix; + + } + + /** + * Returns the uris + * + * @return array + */ + public function getHrefs() { + + return $this->hrefs; + + } + + /** + * Serializes this property. + * + * It will additionally prepend the href property with the server's base uri. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + + $prefix = $server->xmlNamespaces['DAV:']; + + foreach($this->hrefs as $href) { + $elem = $dom->ownerDocument->createElement($prefix . ':href'); + $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $href; + $dom->appendChild($elem); + } + + } + + /** + * Unserializes this property from a DOM Element + * + * This method returns an instance of this class. + * It will only decode {DAV:}href values. + * + * @param DOMElement $dom + * @return Sabre_DAV_Property_HrefList + */ + static function unserialize(DOMElement $dom) { + + $hrefs = array(); + foreach($dom->childNodes as $child) { + if (Sabre_DAV_XMLUtil::toClarkNotation($child)==='{DAV:}href') { + $hrefs[] = $child->textContent; + } + } + return new self($hrefs, false); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/IHref.php b/dav/SabreDAV/lib/Sabre/DAV/Property/IHref.php new file mode 100644 index 000000000..5c0409064 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/IHref.php @@ -0,0 +1,25 @@ +locks = $locks; + $this->revealLockToken = $revealLockToken; + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { + + $doc = $prop->ownerDocument; + + foreach($this->locks as $lock) { + + $activeLock = $doc->createElementNS('DAV:','d:activelock'); + $prop->appendChild($activeLock); + + $lockScope = $doc->createElementNS('DAV:','d:lockscope'); + $activeLock->appendChild($lockScope); + + $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==Sabre_DAV_Locks_LockInfo::EXCLUSIVE?'exclusive':'shared'))); + + $lockType = $doc->createElementNS('DAV:','d:locktype'); + $activeLock->appendChild($lockType); + + $lockType->appendChild($doc->createElementNS('DAV:','d:write')); + + /* {DAV:}lockroot */ + if (!self::$hideLockRoot) { + $lockRoot = $doc->createElementNS('DAV:','d:lockroot'); + $activeLock->appendChild($lockRoot); + $href = $doc->createElementNS('DAV:','d:href'); + $href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri)); + $lockRoot->appendChild($href); + } + + $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == Sabre_DAV_Server::DEPTH_INFINITY?'infinity':$lock->depth))); + $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout)); + + if ($this->revealLockToken) { + $lockToken = $doc->createElementNS('DAV:','d:locktoken'); + $activeLock->appendChild($lockToken); + $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token)); + } + + $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner)); + + } + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/ResourceType.php b/dav/SabreDAV/lib/Sabre/DAV/Property/ResourceType.php new file mode 100644 index 000000000..f6269611e --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/ResourceType.php @@ -0,0 +1,125 @@ +resourceType = array(); + elseif ($resourceType === Sabre_DAV_Server::NODE_DIRECTORY) + $this->resourceType = array('{DAV:}collection'); + elseif (is_array($resourceType)) + $this->resourceType = $resourceType; + else + $this->resourceType = array($resourceType); + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { + + $propName = null; + $rt = $this->resourceType; + + foreach($rt as $resourceType) { + if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { + + if (isset($server->xmlNamespaces[$propName[1]])) { + $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2])); + } else { + $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2])); + } + + } + } + + } + + /** + * Returns the values in clark-notation + * + * For example array('{DAV:}collection') + * + * @return array + */ + public function getValue() { + + return $this->resourceType; + + } + + /** + * Checks if the principal contains a certain value + * + * @param string $type + * @return bool + */ + public function is($type) { + + return in_array($type, $this->resourceType); + + } + + /** + * Adds a resourcetype value to this property + * + * @param string $type + * @return void + */ + public function add($type) { + + $this->resourceType[] = $type; + $this->resourceType = array_unique($this->resourceType); + + } + + /** + * Unserializes a DOM element into a ResourceType property. + * + * @param DOMElement $dom + * @return Sabre_DAV_Property_ResourceType + */ + static public function unserialize(DOMElement $dom) { + + $value = array(); + foreach($dom->childNodes as $child) { + + $value[] = Sabre_DAV_XMLUtil::toClarkNotation($child); + + } + + return new self($value); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/Response.php b/dav/SabreDAV/lib/Sabre/DAV/Property/Response.php new file mode 100644 index 000000000..88afbcfb2 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/Response.php @@ -0,0 +1,155 @@ +href = $href; + $this->responseProperties = $responseProperties; + + } + + /** + * Returns the url + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Returns the property list + * + * @return array + */ + public function getResponseProperties() { + + return $this->responseProperties; + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $dom) { + + $document = $dom->ownerDocument; + $properties = $this->responseProperties; + + $xresponse = $document->createElement('d:response'); + $dom->appendChild($xresponse); + + $uri = Sabre_DAV_URLUtil::encodePath($this->href); + + // Adding the baseurl to the beginning of the url + $uri = $server->getBaseUri() . $uri; + + $xresponse->appendChild($document->createElement('d:href',$uri)); + + // The properties variable is an array containing properties, grouped by + // HTTP status + foreach($properties as $httpStatus=>$propertyGroup) { + + // The 'href' is also in this array, and it's special cased. + // We will ignore it + if ($httpStatus=='href') continue; + + // If there are no properties in this group, we can also just carry on + if (!count($propertyGroup)) continue; + + $xpropstat = $document->createElement('d:propstat'); + $xresponse->appendChild($xpropstat); + + $xprop = $document->createElement('d:prop'); + $xpropstat->appendChild($xprop); + + $nsList = $server->xmlNamespaces; + + foreach($propertyGroup as $propertyName=>$propertyValue) { + + $propName = null; + preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); + + // special case for empty namespaces + if ($propName[1]=='') { + + $currentProperty = $document->createElement($propName[2]); + $xprop->appendChild($currentProperty); + $currentProperty->setAttribute('xmlns',''); + + } else { + + if (!isset($nsList[$propName[1]])) { + $nsList[$propName[1]] = 'x' . count($nsList); + } + + // If the namespace was defined in the top-level xml namespaces, it means + // there was already a namespace declaration, and we don't have to worry about it. + if (isset($server->xmlNamespaces[$propName[1]])) { + $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]); + } else { + $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); + } + $xprop->appendChild($currentProperty); + + } + + if (is_scalar($propertyValue)) { + $text = $document->createTextNode($propertyValue); + $currentProperty->appendChild($text); + } elseif ($propertyValue instanceof Sabre_DAV_Property) { + $propertyValue->serialize($server,$currentProperty); + } elseif (!is_null($propertyValue)) { + throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); + } + + } + + $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus))); + + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/ResponseList.php b/dav/SabreDAV/lib/Sabre/DAV/Property/ResponseList.php new file mode 100644 index 000000000..cae923afb --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/ResponseList.php @@ -0,0 +1,57 @@ +responses = $responses; + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + + foreach($this->responses as $response) { + $response->serialize($server, $dom); + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/SupportedLock.php b/dav/SabreDAV/lib/Sabre/DAV/Property/SupportedLock.php new file mode 100644 index 000000000..4e3aaf23a --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/SupportedLock.php @@ -0,0 +1,76 @@ +supportsLocks = $supportsLocks; + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + $doc = $prop->ownerDocument; + + if (!$this->supportsLocks) return null; + + $lockEntry1 = $doc->createElementNS('DAV:','d:lockentry'); + $lockEntry2 = $doc->createElementNS('DAV:','d:lockentry'); + + $prop->appendChild($lockEntry1); + $prop->appendChild($lockEntry2); + + $lockScope1 = $doc->createElementNS('DAV:','d:lockscope'); + $lockScope2 = $doc->createElementNS('DAV:','d:lockscope'); + $lockType1 = $doc->createElementNS('DAV:','d:locktype'); + $lockType2 = $doc->createElementNS('DAV:','d:locktype'); + + $lockEntry1->appendChild($lockScope1); + $lockEntry1->appendChild($lockType1); + $lockEntry2->appendChild($lockScope2); + $lockEntry2->appendChild($lockType2); + + $lockScope1->appendChild($doc->createElementNS('DAV:','d:exclusive')); + $lockScope2->appendChild($doc->createElementNS('DAV:','d:shared')); + + $lockType1->appendChild($doc->createElementNS('DAV:','d:write')); + $lockType2->appendChild($doc->createElementNS('DAV:','d:write')); + + //$frag->appendXML(''); + //$frag->appendXML(''); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Property/SupportedReportSet.php b/dav/SabreDAV/lib/Sabre/DAV/Property/SupportedReportSet.php new file mode 100644 index 000000000..e62699f3b --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Property/SupportedReportSet.php @@ -0,0 +1,109 @@ +addReport($reports); + + } + + /** + * Adds a report to this property + * + * The report must be a string in clark-notation. + * Multiple reports can be specified as an array. + * + * @param mixed $report + * @return void + */ + public function addReport($report) { + + if (!is_array($report)) $report = array($report); + + foreach($report as $r) { + + if (!preg_match('/^{([^}]*)}(.*)$/',$r)) + throw new Sabre_DAV_Exception('Reportname must be in clark-notation'); + + $this->reports[] = $r; + + } + + } + + /** + * Returns the list of supported reports + * + * @return array + */ + public function getValue() { + + return $this->reports; + + } + + /** + * Serializes the node + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { + + foreach($this->reports as $reportName) { + + $supportedReport = $prop->ownerDocument->createElement('d:supported-report'); + $prop->appendChild($supportedReport); + + $report = $prop->ownerDocument->createElement('d:report'); + $supportedReport->appendChild($report); + + preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches); + + list(, $namespace, $element) = $matches; + + $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null; + + if ($prefix) { + $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element)); + } else { + $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element)); + } + + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Server.php b/dav/SabreDAV/lib/Sabre/DAV/Server.php new file mode 100644 index 000000000..65a88cfc1 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Server.php @@ -0,0 +1,2006 @@ + 'd', + 'http://sabredav.org/ns' => 's', + ); + + /** + * The propertymap can be used to map properties from + * requests to property classes. + * + * @var array + */ + public $propertyMap = array( + '{DAV:}resourcetype' => 'Sabre_DAV_Property_ResourceType', + ); + + public $protectedProperties = array( + // RFC4918 + '{DAV:}getcontentlength', + '{DAV:}getetag', + '{DAV:}getlastmodified', + '{DAV:}lockdiscovery', + '{DAV:}resourcetype', + '{DAV:}supportedlock', + + // RFC4331 + '{DAV:}quota-available-bytes', + '{DAV:}quota-used-bytes', + + // RFC3744 + '{DAV:}supported-privilege-set', + '{DAV:}current-user-privilege-set', + '{DAV:}acl', + '{DAV:}acl-restrictions', + '{DAV:}inherited-acl-set', + + ); + + /** + * This is a flag that allow or not showing file, line and code + * of the exception in the returned XML + * + * @var bool + */ + public $debugExceptions = false; + + /** + * This property allows you to automatically add the 'resourcetype' value + * based on a node's classname or interface. + * + * The preset ensures that {DAV:}collection is automaticlly added for nodes + * implementing Sabre_DAV_ICollection. + * + * @var array + */ + public $resourceTypeMapping = array( + 'Sabre_DAV_ICollection' => '{DAV:}collection', + ); + + /** + * If this setting is turned off, SabreDAV's version number will be hidden + * from various places. + * + * Some people feel this is a good security measure. + * + * @var bool + */ + static public $exposeVersion = true; + + /** + * Sets up the server + * + * If a Sabre_DAV_Tree object is passed as an argument, it will + * use it as the directory tree. If a Sabre_DAV_INode is passed, it + * will create a Sabre_DAV_ObjectTree and use the node as the root. + * + * If nothing is passed, a Sabre_DAV_SimpleCollection is created in + * a Sabre_DAV_ObjectTree. + * + * If an array is passed, we automatically create a root node, and use + * the nodes in the array as top-level children. + * + * @param Sabre_DAV_Tree|Sabre_DAV_INode|array|null $treeOrNode The tree object + */ + public function __construct($treeOrNode = null) { + + if ($treeOrNode instanceof Sabre_DAV_Tree) { + $this->tree = $treeOrNode; + } elseif ($treeOrNode instanceof Sabre_DAV_INode) { + $this->tree = new Sabre_DAV_ObjectTree($treeOrNode); + } elseif (is_array($treeOrNode)) { + + // If it's an array, a list of nodes was passed, and we need to + // create the root node. + foreach($treeOrNode as $node) { + if (!($node instanceof Sabre_DAV_INode)) { + throw new Sabre_DAV_Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre_DAV_INode'); + } + } + + $root = new Sabre_DAV_SimpleCollection('root', $treeOrNode); + $this->tree = new Sabre_DAV_ObjectTree($root); + + } elseif (is_null($treeOrNode)) { + $root = new Sabre_DAV_SimpleCollection('root'); + $this->tree = new Sabre_DAV_ObjectTree($root); + } else { + throw new Sabre_DAV_Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre_DAV_Tree, Sabre_DAV_INode, an array or null'); + } + $this->httpResponse = new Sabre_HTTP_Response(); + $this->httpRequest = new Sabre_HTTP_Request(); + + } + + /** + * Starts the DAV Server + * + * @return void + */ + public function exec() { + + try { + + $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri()); + + } catch (Exception $e) { + + $DOM = new DOMDocument('1.0','utf-8'); + $DOM->formatOutput = true; + + $error = $DOM->createElementNS('DAV:','d:error'); + $error->setAttribute('xmlns:s',self::NS_SABREDAV); + $DOM->appendChild($error); + + $error->appendChild($DOM->createElement('s:exception',get_class($e))); + $error->appendChild($DOM->createElement('s:message',$e->getMessage())); + if ($this->debugExceptions) { + $error->appendChild($DOM->createElement('s:file',$e->getFile())); + $error->appendChild($DOM->createElement('s:line',$e->getLine())); + $error->appendChild($DOM->createElement('s:code',$e->getCode())); + $error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString())); + + } + if (self::$exposeVersion) { + $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION)); + } + + if($e instanceof Sabre_DAV_Exception) { + + $httpCode = $e->getHTTPCode(); + $e->serialize($this,$error); + $headers = $e->getHTTPHeaders($this); + + } else { + + $httpCode = 500; + $headers = array(); + + } + $headers['Content-Type'] = 'application/xml; charset=utf-8'; + + $this->httpResponse->sendStatus($httpCode); + $this->httpResponse->setHeaders($headers); + $this->httpResponse->sendBody($DOM->saveXML()); + + } + + } + + /** + * Sets the base server uri + * + * @param string $uri + * @return void + */ + public function setBaseUri($uri) { + + // If the baseUri does not end with a slash, we must add it + if ($uri[strlen($uri)-1]!=='/') + $uri.='/'; + + $this->baseUri = $uri; + + } + + /** + * Returns the base responding uri + * + * @return string + */ + public function getBaseUri() { + + if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); + return $this->baseUri; + + } + + /** + * This method attempts to detect the base uri. + * Only the PATH_INFO variable is considered. + * + * If this variable is not set, the root (/) is assumed. + * + * @return string + */ + public function guessBaseUri() { + + $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO'); + $uri = $this->httpRequest->getRawServerValue('REQUEST_URI'); + + // If PATH_INFO is found, we can assume it's accurate. + if (!empty($pathInfo)) { + + // We need to make sure we ignore the QUERY_STRING part + if ($pos = strpos($uri,'?')) + $uri = substr($uri,0,$pos); + + // PATH_INFO is only set for urls, such as: /example.php/path + // in that case PATH_INFO contains '/path'. + // Note that REQUEST_URI is percent encoded, while PATH_INFO is + // not, Therefore they are only comparable if we first decode + // REQUEST_INFO as well. + $decodedUri = Sabre_DAV_URLUtil::decodePath($uri); + + // A simple sanity check: + if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) { + $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo)); + return rtrim($baseUri,'/') . '/'; + } + + throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); + + } + + // The last fallback is that we're just going to assume the server root. + return '/'; + + } + + /** + * Adds a plugin to the server + * + * For more information, console the documentation of Sabre_DAV_ServerPlugin + * + * @param Sabre_DAV_ServerPlugin $plugin + * @return void + */ + public function addPlugin(Sabre_DAV_ServerPlugin $plugin) { + + $this->plugins[$plugin->getPluginName()] = $plugin; + $plugin->initialize($this); + + } + + /** + * Returns an initialized plugin by it's name. + * + * This function returns null if the plugin was not found. + * + * @param string $name + * @return Sabre_DAV_ServerPlugin + */ + public function getPlugin($name) { + + if (isset($this->plugins[$name])) + return $this->plugins[$name]; + + // This is a fallback and deprecated. + foreach($this->plugins as $plugin) { + if (get_class($plugin)===$name) return $plugin; + } + + return null; + + } + + /** + * Returns all plugins + * + * @return array + */ + public function getPlugins() { + + return $this->plugins; + + } + + + /** + * Subscribe to an event. + * + * When the event is triggered, we'll call all the specified callbacks. + * It is possible to control the order of the callbacks through the + * priority argument. + * + * This is for example used to make sure that the authentication plugin + * is triggered before anything else. If it's not needed to change this + * number, it is recommended to ommit. + * + * @param string $event + * @param callback $callback + * @param int $priority + * @return void + */ + public function subscribeEvent($event, $callback, $priority = 100) { + + if (!isset($this->eventSubscriptions[$event])) { + $this->eventSubscriptions[$event] = array(); + } + while(isset($this->eventSubscriptions[$event][$priority])) $priority++; + $this->eventSubscriptions[$event][$priority] = $callback; + ksort($this->eventSubscriptions[$event]); + + } + + /** + * Broadcasts an event + * + * This method will call all subscribers. If one of the subscribers returns false, the process stops. + * + * The arguments parameter will be sent to all subscribers + * + * @param string $eventName + * @param array $arguments + * @return bool + */ + public function broadcastEvent($eventName,$arguments = array()) { + + if (isset($this->eventSubscriptions[$eventName])) { + + foreach($this->eventSubscriptions[$eventName] as $subscriber) { + + $result = call_user_func_array($subscriber,$arguments); + if ($result===false) return false; + + } + + } + + return true; + + } + + /** + * Handles a http request, and execute a method based on its name + * + * @param string $method + * @param string $uri + * @return void + */ + public function invokeMethod($method, $uri) { + + $method = strtoupper($method); + + if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return; + + // Make sure this is a HTTP method we support + $internalMethods = array( + 'OPTIONS', + 'GET', + 'HEAD', + 'DELETE', + 'PROPFIND', + 'MKCOL', + 'PUT', + 'PROPPATCH', + 'COPY', + 'MOVE', + 'REPORT' + ); + + if (in_array($method,$internalMethods)) { + + call_user_func(array($this,'http' . $method), $uri); + + } else { + + if ($this->broadcastEvent('unknownMethod',array($method, $uri))) { + // Unsupported method + throw new Sabre_DAV_Exception_NotImplemented('There was no handler found for this "' . $method . '" method'); + } + + } + + } + + // {{{ HTTP Method implementations + + /** + * HTTP OPTIONS + * + * @param string $uri + * @return void + */ + protected function httpOptions($uri) { + + $methods = $this->getAllowedMethods($uri); + + $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods))); + $features = array('1','3', 'extended-mkcol'); + + foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); + + $this->httpResponse->setHeader('DAV',implode(', ',$features)); + $this->httpResponse->setHeader('MS-Author-Via','DAV'); + $this->httpResponse->setHeader('Accept-Ranges','bytes'); + if (self::$exposeVersion) { + $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION); + } + $this->httpResponse->setHeader('Content-Length',0); + $this->httpResponse->sendStatus(200); + + } + + /** + * HTTP GET + * + * This method simply fetches the contents of a uri, like normal + * + * @param string $uri + * @return bool + */ + protected function httpGet($uri) { + + $node = $this->tree->getNodeForPath($uri,0); + + if (!$this->checkPreconditions(true)) return false; + + if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects'); + $body = $node->get(); + + // Converting string into stream, if needed. + if (is_string($body)) { + $stream = fopen('php://temp','r+'); + fwrite($stream,$body); + rewind($stream); + $body = $stream; + } + + /* + * TODO: getetag, getlastmodified, getsize should also be used using + * this method + */ + $httpHeaders = $this->getHTTPHeaders($uri); + + /* ContentType needs to get a default, because many webservers will otherwise + * default to text/html, and we don't want this for security reasons. + */ + if (!isset($httpHeaders['Content-Type'])) { + $httpHeaders['Content-Type'] = 'application/octet-stream'; + } + + + if (isset($httpHeaders['Content-Length'])) { + + $nodeSize = $httpHeaders['Content-Length']; + + // Need to unset Content-Length, because we'll handle that during figuring out the range + unset($httpHeaders['Content-Length']); + + } else { + $nodeSize = null; + } + + $this->httpResponse->setHeaders($httpHeaders); + + $range = $this->getHTTPRange(); + $ifRange = $this->httpRequest->getHeader('If-Range'); + $ignoreRangeHeader = false; + + // If ifRange is set, and range is specified, we first need to check + // the precondition. + if ($nodeSize && $range && $ifRange) { + + // if IfRange is parsable as a date we'll treat it as a DateTime + // otherwise, we must treat it as an etag. + try { + $ifRangeDate = new DateTime($ifRange); + + // It's a date. We must check if the entity is modified since + // the specified date. + if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; + else { + $modified = new DateTime($httpHeaders['Last-Modified']); + if($modified > $ifRangeDate) $ignoreRangeHeader = true; + } + + } catch (Exception $e) { + + // It's an entity. We can do a simple comparison. + if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; + elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true; + } + } + + // We're only going to support HTTP ranges if the backend provided a filesize + if (!$ignoreRangeHeader && $nodeSize && $range) { + + // Determining the exact byte offsets + if (!is_null($range[0])) { + + $start = $range[0]; + $end = $range[1]?$range[1]:$nodeSize-1; + if($start >= $nodeSize) + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); + + if($end < $start) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); + if($end >= $nodeSize) $end = $nodeSize-1; + + } else { + + $start = $nodeSize-$range[1]; + $end = $nodeSize-1; + + if ($start<0) $start = 0; + + } + + // New read/write stream + $newStream = fopen('php://temp','r+'); + + stream_copy_to_stream($body, $newStream, $end-$start+1, $start); + rewind($newStream); + + $this->httpResponse->setHeader('Content-Length', $end-$start+1); + $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize); + $this->httpResponse->sendStatus(206); + $this->httpResponse->sendBody($newStream); + + + } else { + + if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize); + $this->httpResponse->sendStatus(200); + $this->httpResponse->sendBody($body); + + } + + } + + /** + * HTTP HEAD + * + * This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body + * This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again + * + * @param string $uri + * @return void + */ + protected function httpHead($uri) { + + $node = $this->tree->getNodeForPath($uri); + /* This information is only collection for File objects. + * Ideally we want to throw 405 Method Not Allowed for every + * non-file, but MS Office does not like this + */ + if ($node instanceof Sabre_DAV_IFile) { + $headers = $this->getHTTPHeaders($this->getRequestUri()); + if (!isset($headers['Content-Type'])) { + $headers['Content-Type'] = 'application/octet-stream'; + } + $this->httpResponse->setHeaders($headers); + } + $this->httpResponse->sendStatus(200); + + } + + /** + * HTTP Delete + * + * The HTTP delete method, deletes a given uri + * + * @param string $uri + * @return void + */ + protected function httpDelete($uri) { + + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; + $this->tree->delete($uri); + $this->broadcastEvent('afterUnbind',array($uri)); + + $this->httpResponse->sendStatus(204); + $this->httpResponse->setHeader('Content-Length','0'); + + } + + + /** + * WebDAV PROPFIND + * + * This WebDAV method requests information about an uri resource, or a list of resources + * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value + * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) + * + * The request body contains an XML data structure that has a list of properties the client understands + * The response body is also an xml document, containing information about every uri resource and the requested properties + * + * It has to return a HTTP 207 Multi-status status code + * + * @param string $uri + * @return void + */ + protected function httpPropfind($uri) { + + // $xml = new Sabre_DAV_XMLReader(file_get_contents('php://input')); + $requestedProperties = $this->parsePropfindRequest($this->httpRequest->getBody(true)); + + $depth = $this->getHTTPDepth(1); + // The only two options for the depth of a propfind is 0 or 1 + if ($depth!=0) $depth = 1; + + $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); + + // This is a multi-status response + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + + // Normally this header is only needed for OPTIONS responses, however.. + // iCal seems to also depend on these being set for PROPFIND. Since + // this is not harmful, we'll add it. + $features = array('1','3', 'extended-mkcol'); + foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); + $this->httpResponse->setHeader('DAV',implode(', ',$features)); + + $data = $this->generateMultiStatus($newProperties); + $this->httpResponse->sendBody($data); + + } + + /** + * WebDAV PROPPATCH + * + * This method is called to update properties on a Node. The request is an XML body with all the mutations. + * In this XML body it is specified which properties should be set/updated and/or deleted + * + * @param string $uri + * @return void + */ + protected function httpPropPatch($uri) { + + $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true)); + + $result = $this->updateProperties($uri, $newProperties); + + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + + $this->httpResponse->sendBody( + $this->generateMultiStatus(array($result)) + ); + + } + + /** + * HTTP PUT method + * + * This HTTP method updates a file, or creates a new one. + * + * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content + * + * @param string $uri + * @return bool + */ + protected function httpPut($uri) { + + $body = $this->httpRequest->getBody(); + + // Intercepting Content-Range + if ($this->httpRequest->getHeader('Content-Range')) { + /** + Content-Range is dangerous for PUT requests: PUT per definition + stores a full resource. draft-ietf-httpbis-p2-semantics-15 says + in section 7.6: + An origin server SHOULD reject any PUT request that contains a + Content-Range header field, since it might be misinterpreted as + partial content (or might be partial content that is being mistakenly + PUT as a full representation). Partial content updates are possible + by targeting a separately identified resource with state that + overlaps a portion of the larger resource, or by using a different + method that has been specifically defined for partial updates (for + example, the PATCH method defined in [RFC5789]). + This clarifies RFC2616 section 9.6: + The recipient of the entity MUST NOT ignore any Content-* + (e.g. Content-Range) headers that it does not understand or implement + and MUST return a 501 (Not Implemented) response in such cases. + OTOH is a PUT request with a Content-Range currently the only way to + continue an aborted upload request and is supported by curl, mod_dav, + Tomcat and others. Since some clients do use this feature which results + in unexpected behaviour (cf PEAR::HTTP_WebDAV_Client 1.0.1), we reject + all PUT requests with a Content-Range for now. + */ + + throw new Sabre_DAV_Exception_NotImplemented('PUT with Content-Range is not allowed.'); + } + + // Intercepting the Finder problem + if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) { + + /** + Many webservers will not cooperate well with Finder PUT requests, + because it uses 'Chunked' transfer encoding for the request body. + + The symptom of this problem is that Finder sends files to the + server, but they arrive as 0-length files in PHP. + + If we don't do anything, the user might think they are uploading + files successfully, but they end up empty on the server. Instead, + we throw back an error if we detect this. + + The reason Finder uses Chunked, is because it thinks the files + might change as it's being uploaded, and therefore the + Content-Length can vary. + + Instead it sends the X-Expected-Entity-Length header with the size + of the file at the very start of the request. If this header is set, + but we don't get a request body we will fail the request to + protect the end-user. + */ + + // Only reading first byte + $firstByte = fread($body,1); + if (strlen($firstByte)!==1) { + throw new Sabre_DAV_Exception_Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); + } + + // The body needs to stay intact, so we copy everything to a + // temporary stream. + + $newBody = fopen('php://temp','r+'); + fwrite($newBody,$firstByte); + stream_copy_to_stream($body, $newBody); + rewind($newBody); + + $body = $newBody; + + } + + if ($this->tree->nodeExists($uri)) { + + $node = $this->tree->getNodeForPath($uri); + + // Checking If-None-Match and related headers. + if (!$this->checkPreconditions()) return; + + // If the node is a collection, we'll deny it + if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.'); + if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false; + + $etag = $node->put($body); + + $this->broadcastEvent('afterWriteContent',array($uri, $node)); + + $this->httpResponse->setHeader('Content-Length','0'); + if ($etag) $this->httpResponse->setHeader('ETag',$etag); + $this->httpResponse->sendStatus(204); + + } else { + + $etag = null; + // If we got here, the resource didn't exist yet. + if (!$this->createFile($this->getRequestUri(),$body,$etag)) { + // For one reason or another the file was not created. + return; + } + + $this->httpResponse->setHeader('Content-Length','0'); + if ($etag) $this->httpResponse->setHeader('ETag', $etag); + $this->httpResponse->sendStatus(201); + + } + + } + + + /** + * WebDAV MKCOL + * + * The MKCOL method is used to create a new collection (directory) on the server + * + * @param string $uri + * @return void + */ + protected function httpMkcol($uri) { + + $requestBody = $this->httpRequest->getBody(true); + + if ($requestBody) { + + $contentType = $this->httpRequest->getHeader('Content-Type'); + if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) { + + // We must throw 415 for unsupported mkcol bodies + throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); + + } + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($requestBody); + if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') { + + // We must throw 415 for unsupported mkcol bodies + throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.'); + + } + + $properties = array(); + foreach($dom->firstChild->childNodes as $childNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue; + $properties = array_merge($properties, Sabre_DAV_XMLUtil::parseProperties($childNode, $this->propertyMap)); + + } + if (!isset($properties['{DAV:}resourcetype'])) + throw new Sabre_DAV_Exception_BadRequest('The mkcol request must include a {DAV:}resourcetype property'); + + $resourceType = $properties['{DAV:}resourcetype']->getValue(); + unset($properties['{DAV:}resourcetype']); + + } else { + + $properties = array(); + $resourceType = array('{DAV:}collection'); + + } + + $result = $this->createCollection($uri, $resourceType, $properties); + + if (is_array($result)) { + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + + $this->httpResponse->sendBody( + $this->generateMultiStatus(array($result)) + ); + + } else { + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus(201); + } + + } + + /** + * WebDAV HTTP MOVE method + * + * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo + * + * @param string $uri + * @return bool + */ + protected function httpMove($uri) { + + $moveInfo = $this->getCopyAndMoveInfo(); + + // If the destination is part of the source tree, we must fail + if ($moveInfo['destination']==$uri) + throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.'); + + if ($moveInfo['destinationExists']) { + + if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false; + $this->tree->delete($moveInfo['destination']); + $this->broadcastEvent('afterUnbind',array($moveInfo['destination'])); + + } + + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false; + if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false; + $this->tree->move($uri,$moveInfo['destination']); + $this->broadcastEvent('afterUnbind',array($uri)); + $this->broadcastEvent('afterBind',array($moveInfo['destination'])); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201); + + } + + /** + * WebDAV HTTP COPY method + * + * This method copies one uri to a different uri, and works much like the MOVE request + * A lot of the actual request processing is done in getCopyMoveInfo + * + * @param string $uri + * @return bool + */ + protected function httpCopy($uri) { + + $copyInfo = $this->getCopyAndMoveInfo(); + // If the destination is part of the source tree, we must fail + if ($copyInfo['destination']==$uri) + throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.'); + + if ($copyInfo['destinationExists']) { + if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false; + $this->tree->delete($copyInfo['destination']); + + } + if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false; + $this->tree->copy($uri,$copyInfo['destination']); + $this->broadcastEvent('afterBind',array($copyInfo['destination'])); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201); + + } + + + + /** + * HTTP REPORT method implementation + * + * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) + * It's used in a lot of extensions, so it made sense to implement it into the core. + * + * @param string $uri + * @return void + */ + protected function httpReport($uri) { + + $body = $this->httpRequest->getBody(true); + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $reportName = Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild); + + if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) { + + // If broadcastEvent returned true, it means the report was not supported + throw new Sabre_DAV_Exception_ReportNotImplemented(); + + } + + } + + // }}} + // {{{ HTTP/WebDAV protocol helpers + + /** + * Returns an array with all the supported HTTP methods for a specific uri. + * + * @param string $uri + * @return array + */ + public function getAllowedMethods($uri) { + + $methods = array( + 'OPTIONS', + 'GET', + 'HEAD', + 'DELETE', + 'PROPFIND', + 'PUT', + 'PROPPATCH', + 'COPY', + 'MOVE', + 'REPORT' + ); + + // The MKCOL is only allowed on an unmapped uri + try { + $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + $methods[] = 'MKCOL'; + } + + // We're also checking if any of the plugins register any new methods + foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($uri)); + array_unique($methods); + + return $methods; + + } + + /** + * Gets the uri for the request, keeping the base uri into consideration + * + * @return string + */ + public function getRequestUri() { + + return $this->calculateUri($this->httpRequest->getUri()); + + } + + /** + * Calculates the uri for a request, making sure that the base uri is stripped out + * + * @param string $uri + * @throws Sabre_DAV_Exception_Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri + * @return string + */ + public function calculateUri($uri) { + + if ($uri[0]!='/' && strpos($uri,'://')) { + + $uri = parse_url($uri,PHP_URL_PATH); + + } + + $uri = str_replace('//','/',$uri); + + if (strpos($uri,$this->getBaseUri())===0) { + + return trim(Sabre_DAV_URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/'); + + // A special case, if the baseUri was accessed without a trailing + // slash, we'll accept it as well. + } elseif ($uri.'/' === $this->getBaseUri()) { + + return ''; + + } else { + + throw new Sabre_DAV_Exception_Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); + + } + + } + + /** + * Returns the HTTP depth header + * + * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre_DAV_Server::DEPTH_INFINITY object + * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent + * + * @param mixed $default + * @return int + */ + public function getHTTPDepth($default = self::DEPTH_INFINITY) { + + // If its not set, we'll grab the default + $depth = $this->httpRequest->getHeader('Depth'); + + if (is_null($depth)) return $default; + + if ($depth == 'infinity') return self::DEPTH_INFINITY; + + + // If its an unknown value. we'll grab the default + if (!ctype_digit($depth)) return $default; + + return (int)$depth; + + } + + /** + * Returns the HTTP range header + * + * This method returns null if there is no well-formed HTTP range request + * header or array($start, $end). + * + * The first number is the offset of the first byte in the range. + * The second number is the offset of the last byte in the range. + * + * If the second offset is null, it should be treated as the offset of the last byte of the entity + * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity + * + * @return array|null + */ + public function getHTTPRange() { + + $range = $this->httpRequest->getHeader('range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; + + if ($matches[1]==='' && $matches[2]==='') return null; + + return array( + $matches[1]!==''?$matches[1]:null, + $matches[2]!==''?$matches[2]:null, + ); + + } + + + /** + * Returns information about Copy and Move requests + * + * This function is created to help getting information about the source and the destination for the + * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions + * + * The returned value is an array with the following keys: + * * destination - Destination path + * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten) + * + * @return array + */ + public function getCopyAndMoveInfo() { + + // Collecting the relevant HTTP headers + if (!$this->httpRequest->getHeader('Destination')) throw new Sabre_DAV_Exception_BadRequest('The destination header was not supplied'); + $destination = $this->calculateUri($this->httpRequest->getHeader('Destination')); + $overwrite = $this->httpRequest->getHeader('Overwrite'); + if (!$overwrite) $overwrite = 'T'; + if (strtoupper($overwrite)=='T') $overwrite = true; + elseif (strtoupper($overwrite)=='F') $overwrite = false; + // We need to throw a bad request exception, if the header was invalid + else throw new Sabre_DAV_Exception_BadRequest('The HTTP Overwrite header should be either T or F'); + + list($destinationDir) = Sabre_DAV_URLUtil::splitPath($destination); + + try { + $destinationParent = $this->tree->getNodeForPath($destinationDir); + if (!($destinationParent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_UnsupportedMediaType('The destination node is not a collection'); + } catch (Sabre_DAV_Exception_NotFound $e) { + + // If the destination parent node is not found, we throw a 409 + throw new Sabre_DAV_Exception_Conflict('The destination node is not found'); + } + + try { + + $destinationNode = $this->tree->getNodeForPath($destination); + + // If this succeeded, it means the destination already exists + // we'll need to throw precondition failed in case overwrite is false + if (!$overwrite) throw new Sabre_DAV_Exception_PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite'); + + } catch (Sabre_DAV_Exception_NotFound $e) { + + // Destination didn't exist, we're all good + $destinationNode = false; + + + + } + + // These are the three relevant properties we need to return + return array( + 'destination' => $destination, + 'destinationExists' => $destinationNode==true, + 'destinationNode' => $destinationNode, + ); + + } + + /** + * Returns a list of properties for a path + * + * This is a simplified version getPropertiesForPath. + * if you aren't interested in status codes, but you just + * want to have a flat list of properties. Use this method. + * + * @param string $path + * @param array $propertyNames + */ + public function getProperties($path, $propertyNames) { + + $result = $this->getPropertiesForPath($path,$propertyNames,0); + return $result[0][200]; + + } + + /** + * A kid-friendly way to fetch properties for a node's children. + * + * The returned array will be indexed by the path of the of child node. + * Only properties that are actually found will be returned. + * + * The parent node will not be returned. + * + * @param string $path + * @param array $propertyNames + * @return array + */ + public function getPropertiesForChildren($path, $propertyNames) { + + $result = array(); + foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) { + + // Skipping the parent path + if ($k === 0) continue; + + $result[$row['href']] = $row[200]; + + } + return $result; + + } + + /** + * Returns a list of HTTP headers for a particular resource + * + * The generated http headers are based on properties provided by the + * resource. The method basically provides a simple mapping between + * DAV property and HTTP header. + * + * The headers are intended to be used for HEAD and GET requests. + * + * @param string $path + * @return array + */ + public function getHTTPHeaders($path) { + + $propertyMap = array( + '{DAV:}getcontenttype' => 'Content-Type', + '{DAV:}getcontentlength' => 'Content-Length', + '{DAV:}getlastmodified' => 'Last-Modified', + '{DAV:}getetag' => 'ETag', + ); + + $properties = $this->getProperties($path,array_keys($propertyMap)); + + $headers = array(); + foreach($propertyMap as $property=>$header) { + if (!isset($properties[$property])) continue; + + if (is_scalar($properties[$property])) { + $headers[$header] = $properties[$property]; + + // GetLastModified gets special cased + } elseif ($properties[$property] instanceof Sabre_DAV_Property_GetLastModified) { + $headers[$header] = Sabre_HTTP_Util::toHTTPDate($properties[$property]->getTime()); + } + + } + + return $headers; + + } + + /** + * Returns a list of properties for a given path + * + * The path that should be supplied should have the baseUrl stripped out + * The list of properties should be supplied in Clark notation. If the list is empty + * 'allprops' is assumed. + * + * If a depth of 1 is requested child elements will also be returned. + * + * @param string $path + * @param array $propertyNames + * @param int $depth + * @return array + */ + public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) { + + if ($depth!=0) $depth = 1; + + $returnPropertyList = array(); + + $parentNode = $this->tree->getNodeForPath($path); + $nodes = array( + $path => $parentNode + ); + if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) { + foreach($this->tree->getChildren($path) as $childNode) + $nodes[$path . '/' . $childNode->getName()] = $childNode; + } + + // If the propertyNames array is empty, it means all properties are requested. + // We shouldn't actually return everything we know though, and only return a + // sensible list. + $allProperties = count($propertyNames)==0; + + foreach($nodes as $myPath=>$node) { + + $currentPropertyNames = $propertyNames; + + $newProperties = array( + '200' => array(), + '404' => array(), + ); + + if ($allProperties) { + // Default list of propertyNames, when all properties were requested. + $currentPropertyNames = array( + '{DAV:}getlastmodified', + '{DAV:}getcontentlength', + '{DAV:}resourcetype', + '{DAV:}quota-used-bytes', + '{DAV:}quota-available-bytes', + '{DAV:}getetag', + '{DAV:}getcontenttype', + ); + } + + // If the resourceType was not part of the list, we manually add it + // and mark it for removal. We need to know the resourcetype in order + // to make certain decisions about the entry. + // WebDAV dictates we should add a / and the end of href's for collections + $removeRT = false; + if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) { + $currentPropertyNames[] = '{DAV:}resourcetype'; + $removeRT = true; + } + + $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties)); + // If this method explicitly returned false, we must ignore this + // node as it is inaccessible. + if ($result===false) continue; + + if (count($currentPropertyNames) > 0) { + + if ($node instanceof Sabre_DAV_IProperties) + $newProperties['200'] = $newProperties[200] + $node->getProperties($currentPropertyNames); + + } + + + foreach($currentPropertyNames as $prop) { + + if (isset($newProperties[200][$prop])) continue; + + switch($prop) { + case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break; + case '{DAV:}getcontentlength' : + if ($node instanceof Sabre_DAV_IFile) { + $size = $node->getSize(); + if (!is_null($size)) { + $newProperties[200][$prop] = (int)$node->getSize(); + } + } + break; + case '{DAV:}quota-used-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[0]; + } + break; + case '{DAV:}quota-available-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[1]; + } + break; + case '{DAV:}getetag' : if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break; + case '{DAV:}getcontenttype' : if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break; + case '{DAV:}supported-report-set' : + $reports = array(); + foreach($this->plugins as $plugin) { + $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath)); + } + $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports); + break; + case '{DAV:}resourcetype' : + $newProperties[200]['{DAV:}resourcetype'] = new Sabre_DAV_Property_ResourceType(); + foreach($this->resourceTypeMapping as $className => $resourceType) { + if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType); + } + break; + + } + + // If we were unable to find the property, we will list it as 404. + if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null; + + } + + $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties)); + + $newProperties['href'] = trim($myPath,'/'); + + // Its is a WebDAV recommendation to add a trailing slash to collectionnames. + // Apple's iCal also requires a trailing slash for principals (rfc 3744). + // Therefore we add a trailing / for any non-file. This might need adjustments + // if we find there are other edge cases. + if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && count($newProperties[200]['{DAV:}resourcetype']->getValue())>0) $newProperties['href'] .='/'; + + // If the resourcetype property was manually added to the requested property list, + // we will remove it again. + if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']); + + $returnPropertyList[] = $newProperties; + + } + + return $returnPropertyList; + + } + + /** + * This method is invoked by sub-systems creating a new file. + * + * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). + * It was important to get this done through a centralized function, + * allowing plugins to intercept this using the beforeCreateFile event. + * + * This method will return true if the file was actually created + * + * @param string $uri + * @param resource $data + * @param string $etag + * @return bool + */ + public function createFile($uri,$data, &$etag = null) { + + list($dir,$name) = Sabre_DAV_URLUtil::splitPath($uri); + + if (!$this->broadcastEvent('beforeBind',array($uri))) return false; + + $parent = $this->tree->getNodeForPath($dir); + + if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false; + + $etag = $parent->createFile($name,$data); + $this->tree->markDirty($dir); + + $this->broadcastEvent('afterBind',array($uri)); + $this->broadcastEvent('afterCreateFile',array($uri, $parent)); + + return true; + } + + /** + * This method is invoked by sub-systems creating a new directory. + * + * @param string $uri + * @return void + */ + public function createDirectory($uri) { + + $this->createCollection($uri,array('{DAV:}collection'),array()); + + } + + /** + * Use this method to create a new collection + * + * The {DAV:}resourcetype is specified using the resourceType array. + * At the very least it must contain {DAV:}collection. + * + * The properties array can contain a list of additional properties. + * + * @param string $uri The new uri + * @param array $resourceType The resourceType(s) + * @param array $properties A list of properties + * @return array|null + */ + public function createCollection($uri, array $resourceType, array $properties) { + + list($parentUri,$newName) = Sabre_DAV_URLUtil::splitPath($uri); + + // Making sure {DAV:}collection was specified as resourceType + if (!in_array('{DAV:}collection', $resourceType)) { + throw new Sabre_DAV_Exception_InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection'); + } + + + // Making sure the parent exists + try { + + $parent = $this->tree->getNodeForPath($parentUri); + + } catch (Sabre_DAV_Exception_NotFound $e) { + + throw new Sabre_DAV_Exception_Conflict('Parent node does not exist'); + + } + + // Making sure the parent is a collection + if (!$parent instanceof Sabre_DAV_ICollection) { + throw new Sabre_DAV_Exception_Conflict('Parent node is not a collection'); + } + + + + // Making sure the child does not already exist + try { + $parent->getChild($newName); + + // If we got here.. it means there's already a node on that url, and we need to throw a 405 + throw new Sabre_DAV_Exception_MethodNotAllowed('The resource you tried to create already exists'); + + } catch (Sabre_DAV_Exception_NotFound $e) { + // This is correct + } + + + if (!$this->broadcastEvent('beforeBind',array($uri))) return; + + // There are 2 modes of operation. The standard collection + // creates the directory, and then updates properties + // the extended collection can create it directly. + if ($parent instanceof Sabre_DAV_IExtendedCollection) { + + $parent->createExtendedCollection($newName, $resourceType, $properties); + + } else { + + // No special resourcetypes are supported + if (count($resourceType)>1) { + throw new Sabre_DAV_Exception_InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.'); + } + + $parent->createDirectory($newName); + $rollBack = false; + $exception = null; + $errorResult = null; + + if (count($properties)>0) { + + try { + + $errorResult = $this->updateProperties($uri, $properties); + if (!isset($errorResult[200])) { + $rollBack = true; + } + + } catch (Sabre_DAV_Exception $e) { + + $rollBack = true; + $exception = $e; + + } + + } + + if ($rollBack) { + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; + $this->tree->delete($uri); + + // Re-throwing exception + if ($exception) throw $exception; + + return $errorResult; + } + + } + $this->tree->markDirty($parentUri); + $this->broadcastEvent('afterBind',array($uri)); + + } + + /** + * This method updates a resource's properties + * + * The properties array must be a list of properties. Array-keys are + * property names in clarknotation, array-values are it's values. + * If a property must be deleted, the value should be null. + * + * Note that this request should either completely succeed, or + * completely fail. + * + * The response is an array with statuscodes for keys, which in turn + * contain arrays with propertynames. This response can be used + * to generate a multistatus body. + * + * @param string $uri + * @param array $properties + * @return array + */ + public function updateProperties($uri, array $properties) { + + // we'll start by grabbing the node, this will throw the appropriate + // exceptions if it doesn't. + $node = $this->tree->getNodeForPath($uri); + + $result = array( + 200 => array(), + 403 => array(), + 424 => array(), + ); + $remainingProperties = $properties; + $hasError = false; + + // Running through all properties to make sure none of them are protected + if (!$hasError) foreach($properties as $propertyName => $value) { + if(in_array($propertyName, $this->protectedProperties)) { + $result[403][$propertyName] = null; + unset($remainingProperties[$propertyName]); + $hasError = true; + } + } + + if (!$hasError) { + // Allowing plugins to take care of property updating + $hasError = !$this->broadcastEvent('updateProperties',array( + &$remainingProperties, + &$result, + $node + )); + } + + // If the node is not an instance of Sabre_DAV_IProperties, every + // property is 403 Forbidden + if (!$hasError && count($remainingProperties) && !($node instanceof Sabre_DAV_IProperties)) { + $hasError = true; + foreach($properties as $propertyName=> $value) { + $result[403][$propertyName] = null; + } + $remainingProperties = array(); + } + + // Only if there were no errors we may attempt to update the resource + if (!$hasError) { + + if (count($remainingProperties)>0) { + + $updateResult = $node->updateProperties($remainingProperties); + + if ($updateResult===true) { + // success + foreach($remainingProperties as $propertyName=>$value) { + $result[200][$propertyName] = null; + } + + } elseif ($updateResult===false) { + // The node failed to update the properties for an + // unknown reason + foreach($remainingProperties as $propertyName=>$value) { + $result[403][$propertyName] = null; + } + + } elseif (is_array($updateResult)) { + + // The node has detailed update information + // We need to merge the results with the earlier results. + foreach($updateResult as $status => $props) { + if (is_array($props)) { + if (!isset($result[$status])) + $result[$status] = array(); + + $result[$status] = array_merge($result[$status], $updateResult[$status]); + } + } + + } else { + throw new Sabre_DAV_Exception('Invalid result from updateProperties'); + } + $remainingProperties = array(); + } + + } + + foreach($remainingProperties as $propertyName=>$value) { + // if there are remaining properties, it must mean + // there's a dependency failure + $result[424][$propertyName] = null; + } + + // Removing empty array values + foreach($result as $status=>$props) { + + if (count($props)===0) unset($result[$status]); + + } + $result['href'] = $uri; + return $result; + + } + + /** + * This method checks the main HTTP preconditions. + * + * Currently these are: + * * If-Match + * * If-None-Match + * * If-Modified-Since + * * If-Unmodified-Since + * + * The method will return true if all preconditions are met + * The method will return false, or throw an exception if preconditions + * failed. If false is returned the operation should be aborted, and + * the appropriate HTTP response headers are already set. + * + * Normally this method will throw 412 Precondition Failed for failures + * related to If-None-Match, If-Match and If-Unmodified Since. It will + * set the status to 304 Not Modified for If-Modified_since. + * + * If the $handleAsGET argument is set to true, it will also return 304 + * Not Modified for failure of the If-None-Match precondition. This is the + * desired behaviour for HTTP GET and HTTP HEAD requests. + * + * @param bool $handleAsGET + * @return bool + */ + public function checkPreconditions($handleAsGET = false) { + + $uri = $this->getRequestUri(); + $node = null; + $lastMod = null; + $etag = null; + + if ($ifMatch = $this->httpRequest->getHeader('If-Match')) { + + // If-Match contains an entity tag. Only if the entity-tag + // matches we are allowed to make the request succeed. + // If the entity-tag is '*' we are only allowed to make the + // request succeed if a resource exists at that url. + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match'); + } + + // Only need to check entity tags if they are not * + if ($ifMatch!=='*') { + + // There can be multiple etags + $ifMatch = explode(',',$ifMatch); + $haveMatch = false; + foreach($ifMatch as $ifMatchItem) { + + // Stripping any extra spaces + $ifMatchItem = trim($ifMatchItem,' '); + + $etag = $node->getETag(); + if ($etag===$ifMatchItem) { + $haveMatch = true; + } else { + // Evolution has a bug where it sometimes prepends the " + // with a \. This is our workaround. + if (str_replace('\\"','"', $ifMatchItem) === $etag) { + $haveMatch = true; + } + } + + } + if (!$haveMatch) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match'); + } + } + } + + if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) { + + // The If-None-Match header contains an etag. + // Only if the ETag does not match the current ETag, the request will succeed + // The header can also contain *, in which case the request + // will only succeed if the entity does not exist at all. + $nodeExists = true; + if (!$node) { + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + $nodeExists = false; + } + } + if ($nodeExists) { + $haveMatch = false; + if ($ifNoneMatch==='*') $haveMatch = true; + else { + + // There might be multiple etags + $ifNoneMatch = explode(',', $ifNoneMatch); + $etag = $node->getETag(); + + foreach($ifNoneMatch as $ifNoneMatchItem) { + + // Stripping any extra spaces + $ifNoneMatchItem = trim($ifNoneMatchItem,' '); + + if ($etag===$ifNoneMatchItem) $haveMatch = true; + + } + + } + + if ($haveMatch) { + if ($handleAsGET) { + $this->httpResponse->sendStatus(304); + return false; + } else { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match'); + } + } + } + + } + + if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) { + + // The If-Modified-Since header contains a date. We + // will only return the entity if it has been changed since + // that date. If it hasn't been changed, we return a 304 + // header + // Note that this header only has to be checked if there was no If-None-Match header + // as per the HTTP spec. + $date = Sabre_HTTP_Util::parseHTTPDate($ifModifiedSince); + + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($uri); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new DateTime('@' . $lastMod); + if ($lastMod <= $date) { + $this->httpResponse->sendStatus(304); + $this->httpResponse->setHeader('Last-Modified', Sabre_HTTP_Util::toHTTPDate($lastMod)); + return false; + } + } + } + } + + if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) { + + // The If-Unmodified-Since will allow allow the request if the + // entity has not changed since the specified date. + $date = Sabre_HTTP_Util::parseHTTPDate($ifUnmodifiedSince); + + // We must only check the date if it's valid + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($uri); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new DateTime('@' . $lastMod); + if ($lastMod > $date) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since'); + } + } + } + + } + return true; + + } + + // }}} + // {{{ XML Readers & Writers + + + /** + * Generates a WebDAV propfind response body based on a list of nodes + * + * @param array $fileProperties The list with nodes + * @return string + */ + public function generateMultiStatus(array $fileProperties) { + + $dom = new DOMDocument('1.0','utf-8'); + //$dom->formatOutput = true; + $multiStatus = $dom->createElement('d:multistatus'); + $dom->appendChild($multiStatus); + + // Adding in default namespaces + foreach($this->xmlNamespaces as $namespace=>$prefix) { + + $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); + + } + + foreach($fileProperties as $entry) { + + $href = $entry['href']; + unset($entry['href']); + + $response = new Sabre_DAV_Property_Response($href,$entry); + $response->serialize($this,$multiStatus); + + } + + return $dom->saveXML(); + + } + + /** + * This method parses a PropPatch request + * + * PropPatch changes the properties for a resource. This method + * returns a list of properties. + * + * The keys in the returned array contain the property name (e.g.: {DAV:}displayname, + * and the value contains the property value. If a property is to be removed the value + * will be null. + * + * @param string $body xml body + * @return array list of properties in need of updating or deletion + */ + public function parsePropPatchRequest($body) { + + //We'll need to change the DAV namespace declaration to something else in order to make it parsable + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $newProperties = array(); + + foreach($dom->firstChild->childNodes as $child) { + + if ($child->nodeType !== XML_ELEMENT_NODE) continue; + + $operation = Sabre_DAV_XMLUtil::toClarkNotation($child); + + if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue; + + $innerProperties = Sabre_DAV_XMLUtil::parseProperties($child, $this->propertyMap); + + foreach($innerProperties as $propertyName=>$propertyValue) { + + if ($operation==='{DAV:}remove') { + $propertyValue = null; + } + + $newProperties[$propertyName] = $propertyValue; + + } + + } + + return $newProperties; + + } + + /** + * This method parses the PROPFIND request and returns its information + * + * This will either be a list of properties, or an empty array; in which case + * an {DAV:}allprop was requested. + * + * @param string $body + * @return array + */ + public function parsePropFindRequest($body) { + + // If the propfind body was empty, it means IE is requesting 'all' properties + if (!$body) return array(); + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); + return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); + + } + + // }}} + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/ServerPlugin.php b/dav/SabreDAV/lib/Sabre/DAV/ServerPlugin.php new file mode 100644 index 000000000..131863d13 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/ServerPlugin.php @@ -0,0 +1,90 @@ +name = $name; + foreach($children as $child) { + + if (!($child instanceof Sabre_DAV_INode)) throw new Sabre_DAV_Exception('Only instances of Sabre_DAV_INode are allowed to be passed in the children argument'); + $this->addChild($child); + + } + + } + + /** + * Adds a new childnode to this collection + * + * @param Sabre_DAV_INode $child + * @return void + */ + public function addChild(Sabre_DAV_INode $child) { + + $this->children[$child->getName()] = $child; + + } + + /** + * Returns the name of the collection + * + * @return string + */ + public function getName() { + + return $this->name; + + } + + /** + * Returns a child object, by its name. + * + * This method makes use of the getChildren method to grab all the child nodes, and compares the name. + * Generally its wise to override this, as this can usually be optimized + * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * + * @param string $name + * @throws Sabre_DAV_Exception_NotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + if (isset($this->children[$name])) return $this->children[$name]; + throw new Sabre_DAV_Exception_NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\''); + + } + + /** + * Returns a list of children for this collection + * + * @return array + */ + public function getChildren() { + + return array_values($this->children); + + } + + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/SimpleFile.php b/dav/SabreDAV/lib/Sabre/DAV/SimpleFile.php new file mode 100644 index 000000000..58330d686 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/SimpleFile.php @@ -0,0 +1,121 @@ +name = $name; + $this->contents = $contents; + $this->mimeType = $mimeType; + + } + + /** + * Returns the node name for this file. + * + * This name is used to construct the url. + * + * @return string + */ + public function getName() { + + return $this->name; + + } + + /** + * Returns the data + * + * This method may either return a string or a readable stream resource + * + * @return mixed + */ + public function get() { + + return $this->contents; + + } + + /** + * Returns the size of the file, in bytes. + * + * @return int + */ + public function getSize() { + + return strlen($this->contents); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * @return string + */ + public function getETag() { + + return '"' . md5($this->contents) . '"'; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * @return string + */ + public function getContentType() { + + return $this->mimeType; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/StringUtil.php b/dav/SabreDAV/lib/Sabre/DAV/StringUtil.php new file mode 100644 index 000000000..b126a94c8 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/StringUtil.php @@ -0,0 +1,91 @@ +dataDir = $dataDir; + + } + + /** + * Initialize the plugin + * + * This is called automatically be the Server class after this plugin is + * added with Sabre_DAV_Server::addPlugin() + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('beforeMethod',array($this,'beforeMethod')); + $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile')); + + } + + /** + * This method is called before any HTTP method handler + * + * This method intercepts any GET, DELETE, PUT and PROPFIND calls to + * filenames that are known to match the 'temporary file' regex. + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + if (!$tempLocation = $this->isTempFile($uri)) + return true; + + switch($method) { + case 'GET' : + return $this->httpGet($tempLocation); + case 'PUT' : + return $this->httpPut($tempLocation); + case 'PROPFIND' : + return $this->httpPropfind($tempLocation, $uri); + case 'DELETE' : + return $this->httpDelete($tempLocation); + } + return true; + + } + + /** + * This method is invoked if some subsystem creates a new file. + * + * This is used to deal with HTTP LOCK requests which create a new + * file. + * + * @param string $uri + * @param resource $data + * @return bool + */ + public function beforeCreateFile($uri,$data) { + + if ($tempPath = $this->isTempFile($uri)) { + + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + file_put_contents($tempPath,$data); + return false; + } + return true; + + } + + /** + * This method will check if the url matches the temporary file pattern + * if it does, it will return an path based on $this->dataDir for the + * temporary file storage. + * + * @param string $path + * @return boolean|string + */ + protected function isTempFile($path) { + + // We're only interested in the basename. + list(, $tempPath) = Sabre_DAV_URLUtil::splitPath($path); + + foreach($this->temporaryFilePatterns as $tempFile) { + + if (preg_match($tempFile,$tempPath)) { + return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile'; + } + + } + + return false; + + } + + + /** + * This method handles the GET method for temporary files. + * If the file doesn't exist, it will return false which will kick in + * the regular system for the GET method. + * + * @param string $tempLocation + * @return bool + */ + public function httpGet($tempLocation) { + + if (!file_exists($tempLocation)) return true; + + $hR = $this->server->httpResponse; + $hR->setHeader('Content-Type','application/octet-stream'); + $hR->setHeader('Content-Length',filesize($tempLocation)); + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(200); + $hR->sendBody(fopen($tempLocation,'r')); + return false; + + } + + /** + * This method handles the PUT method. + * + * @param string $tempLocation + * @return bool + */ + public function httpPut($tempLocation) { + + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + + $newFile = !file_exists($tempLocation); + + if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { + throw new Sabre_DAV_Exception_PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); + } + + file_put_contents($tempLocation,$this->server->httpRequest->getBody()); + $hR->sendStatus($newFile?201:200); + return false; + + } + + /** + * This method handles the DELETE method. + * + * If the file didn't exist, it will return false, which will make the + * standard HTTP DELETE handler kick in. + * + * @param string $tempLocation + * @return bool + */ + public function httpDelete($tempLocation) { + + if (!file_exists($tempLocation)) return true; + + unlink($tempLocation); + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(204); + return false; + + } + + /** + * This method handles the PROPFIND method. + * + * It's a very lazy method, it won't bother checking the request body + * for which properties were requested, and just sends back a default + * set of properties. + * + * @param string $tempLocation + * @param string $uri + * @return bool + */ + public function httpPropfind($tempLocation, $uri) { + + if (!file_exists($tempLocation)) return true; + + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(207); + $hR->setHeader('Content-Type','application/xml; charset=utf-8'); + + $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); + + $properties = array( + 'href' => $uri, + 200 => array( + '{DAV:}getlastmodified' => new Sabre_DAV_Property_GetLastModified(filemtime($tempLocation)), + '{DAV:}getcontentlength' => filesize($tempLocation), + '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType(null), + '{'.Sabre_DAV_Server::NS_SABREDAV.'}tempFile' => true, + + ), + ); + + $data = $this->server->generateMultiStatus(array($properties)); + $hR->sendBody($data); + return false; + + } + + + /** + * This method returns the directory where the temporary files should be stored. + * + * @return string + */ + protected function getDataDir() + { + return $this->dataDir; + } +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/Tree.php b/dav/SabreDAV/lib/Sabre/DAV/Tree.php new file mode 100644 index 000000000..502163941 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Tree.php @@ -0,0 +1,193 @@ +getNodeForPath($path); + return true; + + } catch (Sabre_DAV_Exception_NotFound $e) { + + return false; + + } + + } + + /** + * Copies a file from path to another + * + * @param string $sourcePath The source location + * @param string $destinationPath The full destination path + * @return void + */ + public function copy($sourcePath, $destinationPath) { + + $sourceNode = $this->getNodeForPath($sourcePath); + + // grab the dirname and basename components + list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath); + + $destinationParent = $this->getNodeForPath($destinationDir); + $this->copyNode($sourceNode,$destinationParent,$destinationName); + + $this->markDirty($destinationDir); + + } + + /** + * Moves a file from one location to another + * + * @param string $sourcePath The path to the file which should be moved + * @param string $destinationPath The full destination path, so not just the destination parent node + * @return int + */ + public function move($sourcePath, $destinationPath) { + + list($sourceDir, $sourceName) = Sabre_DAV_URLUtil::splitPath($sourcePath); + list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath); + + if ($sourceDir===$destinationDir) { + $renameable = $this->getNodeForPath($sourcePath); + $renameable->setName($destinationName); + } else { + $this->copy($sourcePath,$destinationPath); + $this->getNodeForPath($sourcePath)->delete(); + } + $this->markDirty($sourceDir); + $this->markDirty($destinationDir); + + } + + /** + * Deletes a node from the tree + * + * @param string $path + * @return void + */ + public function delete($path) { + + $node = $this->getNodeForPath($path); + $node->delete(); + + list($parent) = Sabre_DAV_URLUtil::splitPath($path); + $this->markDirty($parent); + + } + + /** + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array + */ + public function getChildren($path) { + + $node = $this->getNodeForPath($path); + return $node->getChildren(); + + } + + /** + * This method is called with every tree update + * + * Examples of tree updates are: + * * node deletions + * * node creations + * * copy + * * move + * * renaming nodes + * + * If Tree classes implement a form of caching, this will allow + * them to make sure caches will be expired. + * + * If a path is passed, it is assumed that the entire subtree is dirty + * + * @param string $path + * @return void + */ + public function markDirty($path) { + + + } + + /** + * copyNode + * + * @param Sabre_DAV_INode $source + * @param Sabre_DAV_ICollection $destinationParent + * @param string $destinationName + * @return void + */ + protected function copyNode(Sabre_DAV_INode $source,Sabre_DAV_ICollection $destinationParent,$destinationName = null) { + + if (!$destinationName) $destinationName = $source->getName(); + + if ($source instanceof Sabre_DAV_IFile) { + + $data = $source->get(); + + // If the body was a string, we need to convert it to a stream + if (is_string($data)) { + $stream = fopen('php://temp','r+'); + fwrite($stream,$data); + rewind($stream); + $data = $stream; + } + $destinationParent->createFile($destinationName,$data); + $destination = $destinationParent->getChild($destinationName); + + } elseif ($source instanceof Sabre_DAV_ICollection) { + + $destinationParent->createDirectory($destinationName); + + $destination = $destinationParent->getChild($destinationName); + foreach($source->getChildren() as $child) { + + $this->copyNode($child,$destination); + + } + + } + if ($source instanceof Sabre_DAV_IProperties && $destination instanceof Sabre_DAV_IProperties) { + + $props = $source->getProperties(array()); + $destination->updateProperties($props); + + } + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/Tree/Filesystem.php b/dav/SabreDAV/lib/Sabre/DAV/Tree/Filesystem.php new file mode 100644 index 000000000..85a9ee317 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/Tree/Filesystem.php @@ -0,0 +1,123 @@ +basePath = $basePath; + + } + + /** + * Returns a new node for the given path + * + * @param string $path + * @return Sabre_DAV_FS_Node + */ + public function getNodeForPath($path) { + + $realPath = $this->getRealPath($path); + if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found'); + if (is_dir($realPath)) { + return new Sabre_DAV_FS_Directory($path); + } else { + return new Sabre_DAV_FS_File($path); + } + + } + + /** + * Returns the real filesystem path for a webdav url. + * + * @param string $publicPath + * @return string + */ + protected function getRealPath($publicPath) { + + return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/'); + + } + + /** + * Copies a file or directory. + * + * This method must work recursively and delete the destination + * if it exists + * + * @param string $source + * @param string $destination + * @return void + */ + public function copy($source,$destination) { + + $source = $this->getRealPath($source); + $destination = $this->getRealPath($destination); + $this->realCopy($source,$destination); + + } + + /** + * Used by self::copy + * + * @param string $source + * @param string $destination + * @return void + */ + protected function realCopy($source,$destination) { + + if (is_file($source)) { + copy($source,$destination); + } else { + mkdir($destination); + foreach(scandir($source) as $subnode) { + + if ($subnode=='.' || $subnode=='..') continue; + $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode); + + } + } + + } + + /** + * Moves a file or directory recursively. + * + * If the destination exists, delete it first. + * + * @param string $source + * @param string $destination + * @return void + */ + public function move($source,$destination) { + + $source = $this->getRealPath($source); + $destination = $this->getRealPath($destination); + rename($source,$destination); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAV/URLUtil.php b/dav/SabreDAV/lib/Sabre/DAV/URLUtil.php new file mode 100644 index 000000000..794665a44 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/URLUtil.php @@ -0,0 +1,121 @@ + + * will be returned as: + * {http://www.example.org}myelem + * + * This format is used throughout the SabreDAV sourcecode. + * Elements encoded with the urn:DAV namespace will + * be returned as if they were in the DAV: namespace. This is to avoid + * compatibility problems. + * + * This function will return null if a nodetype other than an Element is passed. + * + * @param DOMNode $dom + * @return string + */ + static function toClarkNotation(DOMNode $dom) { + + if ($dom->nodeType !== XML_ELEMENT_NODE) return null; + + // Mapping back to the real namespace, in case it was dav + if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; + + // Mapping to clark notation + return '{' . $ns . '}' . $dom->localName; + + } + + /** + * Parses a clark-notation string, and returns the namespace and element + * name components. + * + * If the string was invalid, it will throw an InvalidArgumentException. + * + * @param string $str + * @throws InvalidArgumentException + * @return array + */ + static function parseClarkNotation($str) { + + if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) { + throw new InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); + } + + return array( + $matches[1], + $matches[2] + ); + + } + + /** + * This method takes an XML document (as string) and converts all instances of the + * DAV: namespace to urn:DAV + * + * This is unfortunately needed, because the DAV: namespace violates the xml namespaces + * spec, and causes the DOM to throw errors + * + * @param string $xmlDocument + * @return array|string|null + */ + static function convertDAVNamespace($xmlDocument) { + + // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: + // namespace is actually a violation of the XML namespaces specification, and will cause errors + return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument); + + } + + /** + * This method provides a generic way to load a DOMDocument for WebDAV use. + * + * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors. + * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. + * + * @param string $xml + * @throws Sabre_DAV_Exception_BadRequest + * @return DOMDocument + */ + static function loadDOMDocument($xml) { + + if (empty($xml)) + throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent'); + + // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower) + // does not support this, so we must intercept this and convert to UTF-8. + if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") { + + // Note: the preceeding byte sequence is "]*)encoding="UTF-16"([^>]*)>|u','',$xml); + + } + + // Retaining old error setting + $oldErrorSetting = libxml_use_internal_errors(true); + + // Clearing any previous errors + libxml_clear_errors(); + + $dom = new DOMDocument(); + $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR); + + // We don't generally care about any whitespace + $dom->preserveWhiteSpace = false; + + if ($error = libxml_get_last_error()) { + libxml_clear_errors(); + throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')'); + } + + // Restoring old mechanism for error handling + if ($oldErrorSetting===false) libxml_use_internal_errors(false); + + return $dom; + + } + + /** + * Parses all WebDAV properties out of a DOM Element + * + * Generally WebDAV properties are enclosed in {DAV:}prop elements. This + * method helps by going through all these and pulling out the actual + * propertynames, making them array keys and making the property values, + * well.. the array values. + * + * If no value was given (self-closing element) null will be used as the + * value. This is used in for example PROPFIND requests. + * + * Complex values are supported through the propertyMap argument. The + * propertyMap should have the clark-notation properties as it's keys, and + * classnames as values. + * + * When any of these properties are found, the unserialize() method will be + * (statically) called. The result of this method is used as the value. + * + * @param DOMElement $parentNode + * @param array $propertyMap + * @return array + */ + static function parseProperties(DOMElement $parentNode, array $propertyMap = array()) { + + $propList = array(); + foreach($parentNode->childNodes as $propNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($propNode)!=='{DAV:}prop') continue; + + foreach($propNode->childNodes as $propNodeData) { + + /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */ + if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue; + + $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData); + if (isset($propertyMap[$propertyName])) { + $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); + } else { + $propList[$propertyName] = $propNodeData->textContent; + } + } + + + } + return $propList; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAV/includes.php b/dav/SabreDAV/lib/Sabre/DAV/includes.php new file mode 100644 index 000000000..07ecb16f2 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAV/includes.php @@ -0,0 +1,95 @@ +principalPrefix = $principalPrefix; + $this->principalBackend = $principalBackend; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principalInfo + * @return Sabre_DAVACL_IPrincipal + */ + abstract function getChildForPrincipal(array $principalInfo); + + /** + * Returns the name of this collection. + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalPrefix); + return $name; + + } + + /** + * Return the list of users + * + * @return array + */ + public function getChildren() { + + if ($this->disableListing) + throw new Sabre_DAV_Exception_MethodNotAllowed('Listing members of this collection is disabled'); + + $children = array(); + foreach($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) { + + $children[] = $this->getChildForPrincipal($principalInfo); + + + } + return $children; + + } + + /** + * Returns a child object, by its name. + * + * @param string $name + * @throws Sabre_DAV_Exception_NotFound + * @return Sabre_DAVACL_IPrincipal + */ + public function getChild($name) { + + $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name); + if (!$principalInfo) throw new Sabre_DAV_Exception_NotFound('Principal with name ' . $name . ' not found'); + return $this->getChildForPrincipal($principalInfo); + + } + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * If multiple properties are being searched on, the search should be + * AND'ed. + * + * This method should simply return a list of 'child names', which may be + * used to call $this->getChild in the future. + * + * @param array $searchProperties + * @return array + */ + public function searchPrincipals(array $searchProperties) { + + $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties); + $r = array(); + + foreach($result as $row) { + list(, $r[]) = Sabre_DAV_URLUtil::splitPath($row); + } + + return $r; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Exception/AceConflict.php b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/AceConflict.php new file mode 100644 index 000000000..4b9f93b00 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/AceConflict.php @@ -0,0 +1,32 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:','d:no-ace-conflict'); + $errorNode->appendChild($np); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NeedPrivileges.php b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NeedPrivileges.php new file mode 100644 index 000000000..9b055dd97 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NeedPrivileges.php @@ -0,0 +1,81 @@ +uri = $uri; + $this->privileges = $privileges; + + parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"'); + + } + + /** + * Adds in extra information in the xml response. + * + * This method adds the {DAV:}need-privileges element as defined in rfc3744 + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + $doc = $errorNode->ownerDocument; + + $np = $doc->createElementNS('DAV:','d:need-privileges'); + $errorNode->appendChild($np); + + foreach($this->privileges as $privilege) { + + $resource = $doc->createElementNS('DAV:','d:resource'); + $np->appendChild($resource); + + $resource->appendChild($doc->createElementNS('DAV:','d:href',$server->getBaseUri() . $this->uri)); + + $priv = $doc->createElementNS('DAV:','d:privilege'); + $resource->appendChild($priv); + + preg_match('/^{([^}]*)}(.*)$/',$privilege,$privilegeParts); + $priv->appendChild($doc->createElementNS($privilegeParts[1],'d:' . $privilegeParts[2])); + + + } + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NoAbstract.php b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NoAbstract.php new file mode 100644 index 000000000..f44e3e322 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NoAbstract.php @@ -0,0 +1,32 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:','d:no-abstract'); + $errorNode->appendChild($np); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php new file mode 100644 index 000000000..8d1e38ca1 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php @@ -0,0 +1,32 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:','d:recognized-principal'); + $errorNode->appendChild($np); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php new file mode 100644 index 000000000..3b5d012d7 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php @@ -0,0 +1,32 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:','d:not-supported-privilege'); + $errorNode->appendChild($np); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/IACL.php b/dav/SabreDAV/lib/Sabre/DAVACL/IACL.php new file mode 100644 index 000000000..356bb481d --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/IACL.php @@ -0,0 +1,73 @@ + array( + * '{DAV:}prop1' => null, + * ), + * 201 => array( + * '{DAV:}prop2' => null, + * ), + * 403 => array( + * '{DAV:}prop3' => null, + * ), + * 424 => array( + * '{DAV:}prop4' => null, + * ), + * ); + * + * In this previous example prop1 was successfully updated or deleted, and + * prop2 was succesfully created. + * + * prop3 failed to update due to '403 Forbidden' and because of this prop4 + * also could not be updated with '424 Failed dependency'. + * + * This last example was actually incorrect. While 200 and 201 could appear + * in 1 response, if there's any error (403) the other properties should + * always fail with 423 (failed dependency). + * + * But anyway, if you don't want to scratch your head over this, just + * return true or false. + * + * @param string $path + * @param array $mutations + * @return array|bool + */ + function updatePrincipal($path, $mutations); + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * If multiple properties are being searched on, the search should be + * AND'ed. + * + * This method should simply return an array with full principal uri's. + * + * If somebody attempted to search on a property the backend does not + * support, you should simply return 0 results. + * + * You can also just return 0 results if you choose to not support + * searching at all, but keep in mind that this may stop certain features + * from working. + * + * @param string $prefixPath + * @param array $searchProperties + * @return array + */ + function searchPrincipals($prefixPath, array $searchProperties); + + /** + * Returns the list of members for a group-principal + * + * @param string $principal + * @return array + */ + function getGroupMemberSet($principal); + + /** + * Returns the list of groups a principal is a member of + * + * @param string $principal + * @return array + */ + function getGroupMembership($principal); + + /** + * Updates the list of group members for a group principal. + * + * The principals should be passed as a list of uri's. + * + * @param string $principal + * @param array $members + * @return void + */ + function setGroupMemberSet($principal, array $members); + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Plugin.php b/dav/SabreDAV/lib/Sabre/DAVACL/Plugin.php new file mode 100644 index 000000000..1dd00c875 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Plugin.php @@ -0,0 +1,1349 @@ + 'Display name', + '{http://sabredav.org/ns}email-address' => 'Email address', + ); + + /** + * Any principal uri's added here, will automatically be added to the list + * of ACL's. They will effectively receive {DAV:}all privileges, as a + * protected privilege. + * + * @var array + */ + public $adminPrincipals = array(); + + /** + * Returns a list of features added by this plugin. + * + * This list is used in the response of a HTTP OPTIONS request. + * + * @return array + */ + public function getFeatures() { + + return array('access-control'); + + } + + /** + * Returns a list of available methods for a given url + * + * @param string $uri + * @return array + */ + public function getMethods($uri) { + + return array('ACL'); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre_DAV_Server::getPlugin + * + * @return string + */ + public function getPluginName() { + + return 'acl'; + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + public function getSupportedReportSet($uri) { + + return array( + '{DAV:}expand-property', + '{DAV:}principal-property-search', + '{DAV:}principal-search-property-set', + ); + + } + + + /** + * Checks if the current user has the specified privilege(s). + * + * You can specify a single privilege, or a list of privileges. + * This method will throw an exception if the privilege is not available + * and return true otherwise. + * + * @param string $uri + * @param array|string $privileges + * @param int $recursion + * @param bool $throwExceptions if set to false, this method won't through exceptions. + * @throws Sabre_DAVACL_Exception_NeedPrivileges + * @return bool + */ + public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { + + if (!is_array($privileges)) $privileges = array($privileges); + + $acl = $this->getCurrentUserPrivilegeSet($uri); + + if (is_null($acl)) { + if ($this->allowAccessToNodesWithoutACL) { + return true; + } else { + if ($throwExceptions) + throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$privileges); + else + return false; + + } + } + + $failed = array(); + foreach($privileges as $priv) { + + if (!in_array($priv, $acl)) { + $failed[] = $priv; + } + + } + + if ($failed) { + if ($throwExceptions) + throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$failed); + else + return false; + } + return true; + + } + + /** + * Returns the standard users' principal. + * + * This is one authorative principal url for the current user. + * This method will return null if the user wasn't logged in. + * + * @return string|null + */ + public function getCurrentUserPrincipal() { + + $authPlugin = $this->server->getPlugin('auth'); + if (is_null($authPlugin)) return null; + /** @var $authPlugin Sabre_DAV_Auth_Plugin */ + + $userName = $authPlugin->getCurrentUser(); + if (!$userName) return null; + + return $this->defaultUsernamePath . '/' . $userName; + + } + + /** + * Returns a list of principals that's associated to the current + * user, either directly or through group membership. + * + * @return array + */ + public function getCurrentUserPrincipals() { + + $currentUser = $this->getCurrentUserPrincipal(); + + if (is_null($currentUser)) return array(); + + $check = array($currentUser); + $principals = array($currentUser); + + while(count($check)) { + + $principal = array_shift($check); + + $node = $this->server->tree->getNodeForPath($principal); + if ($node instanceof Sabre_DAVACL_IPrincipal) { + foreach($node->getGroupMembership() as $groupMember) { + + if (!in_array($groupMember, $principals)) { + + $check[] = $groupMember; + $principals[] = $groupMember; + + } + + } + + } + + } + + return $principals; + + } + + /** + * Returns the supported privilege structure for this ACL plugin. + * + * See RFC3744 for more details. Currently we default on a simple, + * standard structure. + * + * You can either get the list of privileges by a uri (path) or by + * specifying a Node. + * + * @param string|Sabre_DAV_INode $node + * @return array + */ + public function getSupportedPrivilegeSet($node) { + + if (is_string($node)) { + $node = $this->server->tree->getNodeForPath($node); + } + + if ($node instanceof Sabre_DAVACL_IACL) { + $result = $node->getSupportedPrivilegeSet(); + + if ($result) + return $result; + } + + return self::getDefaultSupportedPrivilegeSet(); + + } + + /** + * Returns a fairly standard set of privileges, which may be useful for + * other systems to use as a basis. + * + * @return array + */ + static function getDefaultSupportedPrivilegeSet() { + + return array( + 'privilege' => '{DAV:}all', + 'abstract' => true, + 'aggregates' => array( + array( + 'privilege' => '{DAV:}read', + 'aggregates' => array( + array( + 'privilege' => '{DAV:}read-acl', + 'abstract' => true, + ), + array( + 'privilege' => '{DAV:}read-current-user-privilege-set', + 'abstract' => true, + ), + ), + ), // {DAV:}read + array( + 'privilege' => '{DAV:}write', + 'aggregates' => array( + array( + 'privilege' => '{DAV:}write-acl', + 'abstract' => true, + ), + array( + 'privilege' => '{DAV:}write-properties', + 'abstract' => true, + ), + array( + 'privilege' => '{DAV:}write-content', + 'abstract' => true, + ), + array( + 'privilege' => '{DAV:}bind', + 'abstract' => true, + ), + array( + 'privilege' => '{DAV:}unbind', + 'abstract' => true, + ), + array( + 'privilege' => '{DAV:}unlock', + 'abstract' => true, + ), + ), + ), // {DAV:}write + ), + ); // {DAV:}all + + } + + /** + * Returns the supported privilege set as a flat list + * + * This is much easier to parse. + * + * The returned list will be index by privilege name. + * The value is a struct containing the following properties: + * - aggregates + * - abstract + * - concrete + * + * @param string|Sabre_DAV_INode $node + * @return array + */ + final public function getFlatPrivilegeSet($node) { + + $privs = $this->getSupportedPrivilegeSet($node); + + $flat = array(); + $this->getFPSTraverse($privs, null, $flat); + + return $flat; + + } + + /** + * Traverses the privilege set tree for reordering + * + * This function is solely used by getFlatPrivilegeSet, and would have been + * a closure if it wasn't for the fact I need to support PHP 5.2. + * + * @param array $priv + * @param $concrete + * @param array $flat + * @return void + */ + final private function getFPSTraverse($priv, $concrete, &$flat) { + + $myPriv = array( + 'privilege' => $priv['privilege'], + 'abstract' => isset($priv['abstract']) && $priv['abstract'], + 'aggregates' => array(), + 'concrete' => isset($priv['abstract']) && $priv['abstract']?$concrete:$priv['privilege'], + ); + + if (isset($priv['aggregates'])) + foreach($priv['aggregates'] as $subPriv) $myPriv['aggregates'][] = $subPriv['privilege']; + + $flat[$priv['privilege']] = $myPriv; + + if (isset($priv['aggregates'])) { + + foreach($priv['aggregates'] as $subPriv) { + + $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat); + + } + + } + + } + + /** + * Returns the full ACL list. + * + * Either a uri or a Sabre_DAV_INode may be passed. + * + * null will be returned if the node doesn't support ACLs. + * + * @param string|Sabre_DAV_INode $node + * @return array + */ + public function getACL($node) { + + if (is_string($node)) { + $node = $this->server->tree->getNodeForPath($node); + } + if (!$node instanceof Sabre_DAVACL_IACL) { + return null; + } + $acl = $node->getACL(); + foreach($this->adminPrincipals as $adminPrincipal) { + $acl[] = array( + 'principal' => $adminPrincipal, + 'privilege' => '{DAV:}all', + 'protected' => true, + ); + } + return $acl; + + } + + /** + * Returns a list of privileges the current user has + * on a particular node. + * + * Either a uri or a Sabre_DAV_INode may be passed. + * + * null will be returned if the node doesn't support ACLs. + * + * @param string|Sabre_DAV_INode $node + * @return array + */ + public function getCurrentUserPrivilegeSet($node) { + + if (is_string($node)) { + $node = $this->server->tree->getNodeForPath($node); + } + + $acl = $this->getACL($node); + + if (is_null($acl)) return null; + + $principals = $this->getCurrentUserPrincipals(); + + $collected = array(); + + foreach($acl as $ace) { + + $principal = $ace['principal']; + + switch($principal) { + + case '{DAV:}owner' : + $owner = $node->getOwner(); + if ($owner && in_array($owner, $principals)) { + $collected[] = $ace; + } + break; + + + // 'all' matches for every user + case '{DAV:}all' : + + // 'authenticated' matched for every user that's logged in. + // Since it's not possible to use ACL while not being logged + // in, this is also always true. + case '{DAV:}authenticated' : + $collected[] = $ace; + break; + + // 'unauthenticated' can never occur either, so we simply + // ignore these. + case '{DAV:}unauthenticated' : + break; + + default : + if (in_array($ace['principal'], $principals)) { + $collected[] = $ace; + } + break; + + } + + + + } + + // Now we deduct all aggregated privileges. + $flat = $this->getFlatPrivilegeSet($node); + + $collected2 = array(); + while(count($collected)) { + + $current = array_pop($collected); + $collected2[] = $current['privilege']; + + foreach($flat[$current['privilege']]['aggregates'] as $subPriv) { + $collected2[] = $subPriv; + $collected[] = $flat[$subPriv]; + } + + } + + return array_values(array_unique($collected2)); + + } + + /** + * Principal property search + * + * This method can search for principals matching certain values in + * properties. + * + * This method will return a list of properties for the matched properties. + * + * @param array $searchProperties The properties to search on. This is a + * key-value list. The keys are property + * names, and the values the strings to + * match them on. + * @param array $requestedProperties This is the list of properties to + * return for every match. + * @param string $collectionUri The principal collection to search on. + * If this is ommitted, the standard + * principal collection-set will be used. + * @return array This method returns an array structure similar to + * Sabre_DAV_Server::getPropertiesForPath. Returned + * properties are index by a HTTP status code. + * + */ + public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null) { + + if (!is_null($collectionUri)) { + $uris = array($collectionUri); + } else { + $uris = $this->principalCollectionSet; + } + + $lookupResults = array(); + foreach($uris as $uri) { + + $principalCollection = $this->server->tree->getNodeForPath($uri); + if (!$principalCollection instanceof Sabre_DAVACL_AbstractPrincipalCollection) { + // Not a principal collection, we're simply going to ignore + // this. + continue; + } + + $results = $principalCollection->searchPrincipals($searchProperties); + foreach($results as $result) { + $lookupResults[] = rtrim($uri,'/') . '/' . $result; + } + + } + + $matches = array(); + + foreach($lookupResults as $lookupResult) { + + list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0); + + } + + return $matches; + + } + + /** + * Sets up the plugin + * + * This method is automatically called by the server class. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); + + $server->subscribeEvent('beforeMethod', array($this,'beforeMethod'),20); + $server->subscribeEvent('beforeBind', array($this,'beforeBind'),20); + $server->subscribeEvent('beforeUnbind', array($this,'beforeUnbind'),20); + $server->subscribeEvent('updateProperties',array($this,'updateProperties')); + $server->subscribeEvent('beforeUnlock', array($this,'beforeUnlock'),20); + $server->subscribeEvent('report',array($this,'report')); + $server->subscribeEvent('unknownMethod', array($this, 'unknownMethod')); + + array_push($server->protectedProperties, + '{DAV:}alternate-URI-set', + '{DAV:}principal-URL', + '{DAV:}group-membership', + '{DAV:}principal-collection-set', + '{DAV:}current-user-principal', + '{DAV:}supported-privilege-set', + '{DAV:}current-user-privilege-set', + '{DAV:}acl', + '{DAV:}acl-restrictions', + '{DAV:}inherited-acl-set', + '{DAV:}owner', + '{DAV:}group' + ); + + // Automatically mapping nodes implementing IPrincipal to the + // {DAV:}principal resourcetype. + $server->resourceTypeMapping['Sabre_DAVACL_IPrincipal'] = '{DAV:}principal'; + + // Mapping the group-member-set property to the HrefList property + // class. + $server->propertyMap['{DAV:}group-member-set'] = 'Sabre_DAV_Property_HrefList'; + + } + + + /* {{{ Event handlers */ + + /** + * Triggered before any method is handled + * + * @param string $method + * @param string $uri + * @return void + */ + public function beforeMethod($method, $uri) { + + $exists = $this->server->tree->nodeExists($uri); + + // If the node doesn't exists, none of these checks apply + if (!$exists) return; + + switch($method) { + + case 'GET' : + case 'HEAD' : + case 'OPTIONS' : + // For these 3 we only need to know if the node is readable. + $this->checkPrivileges($uri,'{DAV:}read'); + break; + + case 'PUT' : + case 'LOCK' : + case 'UNLOCK' : + // This method requires the write-content priv if the node + // already exists, and bind on the parent if the node is being + // created. + // The bind privilege is handled in the beforeBind event. + $this->checkPrivileges($uri,'{DAV:}write-content'); + break; + + + case 'PROPPATCH' : + $this->checkPrivileges($uri,'{DAV:}write-properties'); + break; + + case 'ACL' : + $this->checkPrivileges($uri,'{DAV:}write-acl'); + break; + + case 'COPY' : + case 'MOVE' : + // Copy requires read privileges on the entire source tree. + // If the target exists write-content normally needs to be + // checked, however, we're deleting the node beforehand and + // creating a new one after, so this is handled by the + // beforeUnbind event. + // + // The creation of the new node is handled by the beforeBind + // event. + // + // If MOVE is used beforeUnbind will also be used to check if + // the sourcenode can be deleted. + $this->checkPrivileges($uri,'{DAV:}read',self::R_RECURSIVE); + + break; + + } + + } + + /** + * Triggered before a new node is created. + * + * This allows us to check permissions for any operation that creates a + * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE. + * + * @param string $uri + * @return void + */ + public function beforeBind($uri) { + + list($parentUri,$nodeName) = Sabre_DAV_URLUtil::splitPath($uri); + $this->checkPrivileges($parentUri,'{DAV:}bind'); + + } + + /** + * Triggered before a node is deleted + * + * This allows us to check permissions for any operation that will delete + * an existing node. + * + * @param string $uri + * @return void + */ + public function beforeUnbind($uri) { + + list($parentUri,$nodeName) = Sabre_DAV_URLUtil::splitPath($uri); + $this->checkPrivileges($parentUri,'{DAV:}unbind',self::R_RECURSIVEPARENTS); + + } + + /** + * Triggered before a node is unlocked. + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lock + * @TODO: not yet implemented + * @return void + */ + public function beforeUnlock($uri, Sabre_DAV_Locks_LockInfo $lock) { + + + } + + /** + * Triggered before properties are looked up in specific nodes. + * + * @param string $uri + * @param Sabre_DAV_INode $node + * @param array $requestedProperties + * @param array $returnedProperties + * @TODO really should be broken into multiple methods, or even a class. + * @return bool + */ + public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) { + + // Checking the read permission + if (!$this->checkPrivileges($uri,'{DAV:}read',self::R_PARENT,false)) { + + // User is not allowed to read properties + if ($this->hideNodesFromListings) { + return false; + } + + // Marking all requested properties as '403'. + foreach($requestedProperties as $key=>$requestedProperty) { + unset($requestedProperties[$key]); + $returnedProperties[403][$requestedProperty] = null; + } + return; + + } + + /* Adding principal properties */ + if ($node instanceof Sabre_DAVACL_IPrincipal) { + + if (false !== ($index = array_search('{DAV:}alternate-URI-set', $requestedProperties))) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}alternate-URI-set'] = new Sabre_DAV_Property_HrefList($node->getAlternateUriSet()); + + } + if (false !== ($index = array_search('{DAV:}principal-URL', $requestedProperties))) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}principal-URL'] = new Sabre_DAV_Property_Href($node->getPrincipalUrl() . '/'); + + } + if (false !== ($index = array_search('{DAV:}group-member-set', $requestedProperties))) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}group-member-set'] = new Sabre_DAV_Property_HrefList($node->getGroupMemberSet()); + + } + if (false !== ($index = array_search('{DAV:}group-membership', $requestedProperties))) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}group-membership'] = new Sabre_DAV_Property_HrefList($node->getGroupMembership()); + + } + + if (false !== ($index = array_search('{DAV:}displayname', $requestedProperties))) { + + $returnedProperties[200]['{DAV:}displayname'] = $node->getDisplayName(); + + } + + } + if (false !== ($index = array_search('{DAV:}principal-collection-set', $requestedProperties))) { + + unset($requestedProperties[$index]); + $val = $this->principalCollectionSet; + // Ensuring all collections end with a slash + foreach($val as $k=>$v) $val[$k] = $v . '/'; + $returnedProperties[200]['{DAV:}principal-collection-set'] = new Sabre_DAV_Property_HrefList($val); + + } + if (false !== ($index = array_search('{DAV:}current-user-principal', $requestedProperties))) { + + unset($requestedProperties[$index]); + if ($url = $this->getCurrentUserPrincipal()) { + $returnedProperties[200]['{DAV:}current-user-principal'] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF, $url . '/'); + } else { + $returnedProperties[200]['{DAV:}current-user-principal'] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::UNAUTHENTICATED); + } + + } + if (false !== ($index = array_search('{DAV:}supported-privilege-set', $requestedProperties))) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Sabre_DAVACL_Property_SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); + + } + if (false !== ($index = array_search('{DAV:}current-user-privilege-set', $requestedProperties))) { + + if (!$this->checkPrivileges($uri, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) { + $returnedProperties[403]['{DAV:}current-user-privilege-set'] = null; + unset($requestedProperties[$index]); + } else { + $val = $this->getCurrentUserPrivilegeSet($node); + if (!is_null($val)) { + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}current-user-privilege-set'] = new Sabre_DAVACL_Property_CurrentUserPrivilegeSet($val); + } + } + + } + + /* The ACL property contains all the permissions */ + if (false !== ($index = array_search('{DAV:}acl', $requestedProperties))) { + + if (!$this->checkPrivileges($uri, '{DAV:}read-acl', self::R_PARENT, false)) { + + unset($requestedProperties[$index]); + $returnedProperties[403]['{DAV:}acl'] = null; + + } else { + + $acl = $this->getACL($node); + if (!is_null($acl)) { + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}acl'] = new Sabre_DAVACL_Property_Acl($this->getACL($node)); + } + + } + + } + + /* The acl-restrictions property contains information on how privileges + * must behave. + */ + if (false !== ($index = array_search('{DAV:}acl-restrictions', $requestedProperties))) { + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}acl-restrictions'] = new Sabre_DAVACL_Property_AclRestrictions(); + } + + } + + /** + * This method intercepts PROPPATCH methods and make sure the + * group-member-set is updated correctly. + * + * @param array $propertyDelta + * @param array $result + * @param Sabre_DAV_INode $node + * @return bool + */ + public function updateProperties(&$propertyDelta, &$result, Sabre_DAV_INode $node) { + + if (!array_key_exists('{DAV:}group-member-set', $propertyDelta)) + return; + + if (is_null($propertyDelta['{DAV:}group-member-set'])) { + $memberSet = array(); + } elseif ($propertyDelta['{DAV:}group-member-set'] instanceof Sabre_DAV_Property_HrefList) { + $memberSet = $propertyDelta['{DAV:}group-member-set']->getHrefs(); + } else { + throw new Sabre_DAV_Exception('The group-member-set property MUST be an instance of Sabre_DAV_Property_HrefList or null'); + } + + if (!($node instanceof Sabre_DAVACL_IPrincipal)) { + $result[403]['{DAV:}group-member-set'] = null; + unset($propertyDelta['{DAV:}group-member-set']); + + // Returning false will stop the updateProperties process + return false; + } + + $node->setGroupMemberSet($memberSet); + + $result[200]['{DAV:}group-member-set'] = null; + unset($propertyDelta['{DAV:}group-member-set']); + + } + + /** + * This method handles HTTP REPORT requests + * + * @param string $reportName + * @param DOMNode $dom + * @return bool + */ + public function report($reportName, $dom) { + + switch($reportName) { + + case '{DAV:}principal-property-search' : + $this->principalPropertySearchReport($dom); + return false; + case '{DAV:}principal-search-property-set' : + $this->principalSearchPropertySetReport($dom); + return false; + case '{DAV:}expand-property' : + $this->expandPropertyReport($dom); + return false; + + } + + } + + /** + * This event is triggered for any HTTP method that is not known by the + * webserver. + * + * @param string $method + * @param string $uri + * @return bool + */ + public function unknownMethod($method, $uri) { + + if ($method!=='ACL') return; + + $this->httpACL($uri); + return false; + + } + + /** + * This method is responsible for handling the 'ACL' event. + * + * @param string $uri + * @return void + */ + public function httpACL($uri) { + + $body = $this->server->httpRequest->getBody(true); + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $newAcl = + Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild) + ->getPrivileges(); + + // Normalizing urls + foreach($newAcl as $k=>$newAce) { + $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']); + } + + $node = $this->server->tree->getNodeForPath($uri); + + if (!($node instanceof Sabre_DAVACL_IACL)) { + throw new Sabre_DAV_Exception_MethodNotAllowed('This node does not support the ACL method'); + } + + $oldAcl = $this->getACL($node); + + $supportedPrivileges = $this->getFlatPrivilegeSet($node); + + /* Checking if protected principals from the existing principal set are + not overwritten. */ + foreach($oldAcl as $oldAce) { + + if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; + + $found = false; + foreach($newAcl as $newAce) { + if ( + $newAce['privilege'] === $oldAce['privilege'] && + $newAce['principal'] === $oldAce['principal'] && + $newAce['protected'] + ) + $found = true; + } + + if (!$found) + throw new Sabre_DAVACL_Exception_AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request'); + + } + + foreach($newAcl as $newAce) { + + // Do we recognize the privilege + if (!isset($supportedPrivileges[$newAce['privilege']])) { + throw new Sabre_DAVACL_Exception_NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server'); + } + + if ($supportedPrivileges[$newAce['privilege']]['abstract']) { + throw new Sabre_DAVACL_Exception_NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege'); + } + + // Looking up the principal + try { + $principal = $this->server->tree->getNodeForPath($newAce['principal']); + } catch (Sabre_DAV_Exception_NotFound $e) { + throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); + } + if (!($principal instanceof Sabre_DAVACL_IPrincipal)) { + throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal'); + } + + } + $node->setACL($newAcl); + + } + + /* }}} */ + + /* Reports {{{ */ + + /** + * The expand-property report is defined in RFC3253 section 3-8. + * + * This report is very similar to a standard PROPFIND. The difference is + * that it has the additional ability to look at properties containing a + * {DAV:}href element, follow that property and grab additional elements + * there. + * + * Other rfc's, such as ACL rely on this report, so it made sense to put + * it in this plugin. + * + * @param DOMElement $dom + * @return void + */ + protected function expandPropertyReport($dom) { + + $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild); + $depth = $this->server->getHTTPDepth(0); + $requestUri = $this->server->getRequestUri(); + + $result = $this->expandProperties($requestUri,$requestedProperties,$depth); + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $multiStatus = $dom->createElement('d:multistatus'); + $dom->appendChild($multiStatus); + + // Adding in default namespaces + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); + + } + + foreach($result as $response) { + $response->serialize($this->server, $multiStatus); + } + + $xml = $dom->saveXML(); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->sendBody($xml); + + } + + /** + * This method is used by expandPropertyReport to parse + * out the entire HTTP request. + * + * @param DOMElement $node + * @return array + */ + protected function parseExpandPropertyReportRequest($node) { + + $requestedProperties = array(); + do { + + if (Sabre_DAV_XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue; + + if ($node->firstChild) { + + $children = $this->parseExpandPropertyReportRequest($node->firstChild); + + } else { + + $children = array(); + + } + + $namespace = $node->getAttribute('namespace'); + if (!$namespace) $namespace = 'DAV:'; + + $propName = '{'.$namespace.'}' . $node->getAttribute('name'); + $requestedProperties[$propName] = $children; + + } while ($node = $node->nextSibling); + + return $requestedProperties; + + } + + /** + * This method expands all the properties and returns + * a list with property values + * + * @param array $path + * @param array $requestedProperties the list of required properties + * @param int $depth + * @return array + */ + protected function expandProperties($path, array $requestedProperties, $depth) { + + $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth); + + $result = array(); + + foreach($foundProperties as $node) { + + foreach($requestedProperties as $propertyName=>$childRequestedProperties) { + + // We're only traversing if sub-properties were requested + if(count($childRequestedProperties)===0) continue; + + // We only have to do the expansion if the property was found + // and it contains an href element. + if (!array_key_exists($propertyName,$node[200])) continue; + + if ($node[200][$propertyName] instanceof Sabre_DAV_Property_IHref) { + $hrefs = array($node[200][$propertyName]->getHref()); + } elseif ($node[200][$propertyName] instanceof Sabre_DAV_Property_HrefList) { + $hrefs = $node[200][$propertyName]->getHrefs(); + } + + $childProps = array(); + foreach($hrefs as $href) { + $childProps = array_merge($childProps, $this->expandProperties($href, $childRequestedProperties, 0)); + } + $node[200][$propertyName] = new Sabre_DAV_Property_ResponseList($childProps); + + } + $result[] = new Sabre_DAV_Property_Response($path, $node); + + } + + return $result; + + } + + /** + * principalSearchPropertySetReport + * + * This method responsible for handing the + * {DAV:}principal-search-property-set report. This report returns a list + * of properties the client may search on, using the + * {DAV:}principal-property-search report. + * + * @param DOMDocument $dom + * @return void + */ + protected function principalSearchPropertySetReport(DOMDocument $dom) { + + $httpDepth = $this->server->getHTTPDepth(0); + if ($httpDepth!==0) { + throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); + } + + if ($dom->firstChild->hasChildNodes()) + throw new Sabre_DAV_Exception_BadRequest('The principal-search-property-set report element is not allowed to have child elements'); + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $root = $dom->createElement('d:principal-search-property-set'); + $dom->appendChild($root); + // Adding in default namespaces + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $root->setAttribute('xmlns:' . $prefix,$namespace); + + } + + $nsList = $this->server->xmlNamespaces; + + foreach($this->principalSearchPropertySet as $propertyName=>$description) { + + $psp = $dom->createElement('d:principal-search-property'); + $root->appendChild($psp); + + $prop = $dom->createElement('d:prop'); + $psp->appendChild($prop); + + $propName = null; + preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); + + $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]); + $prop->appendChild($currentProperty); + + $descriptionElem = $dom->createElement('d:description'); + $descriptionElem->setAttribute('xml:lang','en'); + $descriptionElem->appendChild($dom->createTextNode($description)); + $psp->appendChild($descriptionElem); + + + } + + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody($dom->saveXML()); + + } + + /** + * principalPropertySearchReport + * + * This method is responsible for handing the + * {DAV:}principal-property-search report. This report can be used for + * clients to search for groups of principals, based on the value of one + * or more properties. + * + * @param DOMDocument $dom + * @return void + */ + protected function principalPropertySearchReport(DOMDocument $dom) { + + list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom); + + $uri = null; + if (!$applyToPrincipalCollectionSet) { + $uri = $this->server->getRequestUri(); + } + $result = $this->principalSearch($searchProperties, $requestedProperties, $uri); + + $xml = $this->server->generateMultiStatus($result); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->sendBody($xml); + + } + + /** + * parsePrincipalPropertySearchReportRequest + * + * This method parses the request body from a + * {DAV:}principal-property-search report. + * + * This method returns an array with two elements: + * 1. an array with properties to search on, and their values + * 2. a list of propertyvalues that should be returned for the request. + * + * @param DOMDocument $dom + * @return array + */ + protected function parsePrincipalPropertySearchReportRequest($dom) { + + $httpDepth = $this->server->getHTTPDepth(0); + if ($httpDepth!==0) { + throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); + } + + $searchProperties = array(); + + $applyToPrincipalCollectionSet = false; + + // Parsing the search request + foreach($dom->firstChild->childNodes as $searchNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') { + $applyToPrincipalCollectionSet = true; + } + + if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search') + continue; + + $propertyName = null; + $propertyValue = null; + + foreach($searchNode->childNodes as $childNode) { + + switch(Sabre_DAV_XMLUtil::toClarkNotation($childNode)) { + + case '{DAV:}prop' : + $property = Sabre_DAV_XMLUtil::parseProperties($searchNode); + reset($property); + $propertyName = key($property); + break; + + case '{DAV:}match' : + $propertyValue = $childNode->textContent; + break; + + } + + + } + + if (is_null($propertyName) || is_null($propertyValue)) + throw new Sabre_DAV_Exception_BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue); + + $searchProperties[$propertyName] = $propertyValue; + + } + + return array($searchProperties, array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet); + + } + + + /* }}} */ + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Principal.php b/dav/SabreDAV/lib/Sabre/DAVACL/Principal.php new file mode 100644 index 000000000..51c6658af --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Principal.php @@ -0,0 +1,279 @@ +principalBackend = $principalBackend; + $this->principalProperties = $principalProperties; + + } + + /** + * Returns the full principal url + * + * @return string + */ + public function getPrincipalUrl() { + + return $this->principalProperties['uri']; + + } + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + public function getAlternateUriSet() { + + $uris = array(); + if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) { + + $uris = $this->principalProperties['{DAV:}alternate-URI-set']; + + } + + if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) { + $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; + } + + return array_unique($uris); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + public function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + public function getGroupMembership() { + + return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']); + + } + + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $groupMembers + * @return void + */ + public function setGroupMemberSet(array $groupMembers) { + + $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers); + + } + + + /** + * Returns this principals name. + * + * @return string + */ + public function getName() { + + $uri = $this->principalProperties['uri']; + list(, $name) = Sabre_DAV_URLUtil::splitPath($uri); + return $name; + + } + + /** + * Returns the name of the user + * + * @return string + */ + public function getDisplayName() { + + if (isset($this->principalProperties['{DAV:}displayname'])) { + return $this->principalProperties['{DAV:}displayname']; + } else { + return $this->getName(); + } + + } + + /** + * Returns a list of properties + * + * @param array $requestedProperties + * @return array + */ + public function getProperties($requestedProperties) { + + $newProperties = array(); + foreach($requestedProperties as $propName) { + + if (isset($this->principalProperties[$propName])) { + $newProperties[$propName] = $this->principalProperties[$propName]; + } + + } + + return $newProperties; + + } + + /** + * Updates this principals properties. + * + * @param array $mutations + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateProperties($mutations) { + + return $this->principalBackend->updatePrincipal($this->principalProperties['uri'], $mutations); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalProperties['uri']; + + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->getPrincipalUrl(), + 'protected' => true, + ), + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Updating ACLs is not allowed here'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/PrincipalBackend/PDO.php b/dav/SabreDAV/lib/Sabre/DAVACL/PrincipalBackend/PDO.php new file mode 100644 index 000000000..a76b4a9d7 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/PrincipalBackend/PDO.php @@ -0,0 +1,427 @@ + array( + 'dbField' => 'displayname', + ), + + /** + * This property is actually used by the CardDAV plugin, where it gets + * mapped to {http://calendarserver.orgi/ns/}me-card. + * + * The reason we don't straight-up use that property, is because + * me-card is defined as a property on the users' addressbook + * collection. + */ + '{http://sabredav.org/ns}vcard-url' => array( + 'dbField' => 'vcardurl', + ), + /** + * This is the users' primary email-address. + */ + '{http://sabredav.org/ns}email-address' => array( + 'dbField' => 'email', + ), + ); + + /** + * Sets up the backend. + * + * @param PDO $pdo + * @param string $tableName + * @param string $groupMembersTableName + */ + public function __construct(PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') { + + $this->pdo = $pdo; + $this->tableName = $tableName; + $this->groupMembersTableName = $groupMembersTableName; + + } + + + /** + * Returns a list of principals based on a prefix. + * + * This prefix will often contain something like 'principals'. You are only + * expected to return principals that are in this base path. + * + * You are expected to return at least a 'uri' for every user, you can + * return any additional properties if you wish so. Common properties are: + * {DAV:}displayname + * {http://sabredav.org/ns}email-address - This is a custom SabreDAV + * field that's actualy injected in a number of other properties. If + * you have an email address, use this property. + * + * @param string $prefixPath + * @return array + */ + public function getPrincipalsByPrefix($prefixPath) { + + $fields = array( + 'uri', + ); + + foreach($this->fieldMap as $key=>$value) { + $fields[] = $value['dbField']; + } + $result = $this->pdo->query('SELECT '.implode(',', $fields).' FROM '. $this->tableName); + + $principals = array(); + + while($row = $result->fetch(PDO::FETCH_ASSOC)) { + + // Checking if the principal is in the prefix + list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']); + if ($rowPrefix !== $prefixPath) continue; + + $principal = array( + 'uri' => $row['uri'], + ); + foreach($this->fieldMap as $key=>$value) { + if ($row[$value['dbField']]) { + $principal[$key] = $row[$value['dbField']]; + } + } + $principals[] = $principal; + + } + + return $principals; + + } + + /** + * Returns a specific principal, specified by it's path. + * The returned structure should be the exact same as from + * getPrincipalsByPrefix. + * + * @param string $path + * @return array + */ + public function getPrincipalByPath($path) { + + $fields = array( + 'id', + 'uri', + ); + + foreach($this->fieldMap as $key=>$value) { + $fields[] = $value['dbField']; + } + $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?'); + $stmt->execute(array($path)); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$row) return; + + $principal = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + ); + foreach($this->fieldMap as $key=>$value) { + if ($row[$value['dbField']]) { + $principal[$key] = $row[$value['dbField']]; + } + } + return $principal; + + } + + /** + * Updates one ore more webdav properties on a principal. + * + * The list of mutations is supplied as an array. Each key in the array is + * a propertyname, such as {DAV:}displayname. + * + * Each value is the actual value to be updated. If a value is null, it + * must be deleted. + * + * This method should be atomic. It must either completely succeed, or + * completely fail. Success and failure can simply be returned as 'true' or + * 'false'. + * + * It is also possible to return detailed failure information. In that case + * an array such as this should be returned: + * + * array( + * 200 => array( + * '{DAV:}prop1' => null, + * ), + * 201 => array( + * '{DAV:}prop2' => null, + * ), + * 403 => array( + * '{DAV:}prop3' => null, + * ), + * 424 => array( + * '{DAV:}prop4' => null, + * ), + * ); + * + * In this previous example prop1 was successfully updated or deleted, and + * prop2 was succesfully created. + * + * prop3 failed to update due to '403 Forbidden' and because of this prop4 + * also could not be updated with '424 Failed dependency'. + * + * This last example was actually incorrect. While 200 and 201 could appear + * in 1 response, if there's any error (403) the other properties should + * always fail with 423 (failed dependency). + * + * But anyway, if you don't want to scratch your head over this, just + * return true or false. + * + * @param string $path + * @param array $mutations + * @return array|bool + */ + public function updatePrincipal($path, $mutations) { + + $updateAble = array(); + foreach($mutations as $key=>$value) { + + // We are not aware of this field, we must fail. + if (!isset($this->fieldMap[$key])) { + + $response = array( + 403 => array( + $key => null, + ), + 424 => array(), + ); + + // Adding the rest to the response as a 424 + foreach($mutations as $subKey=>$subValue) { + if ($subKey !== $key) { + $response[424][$subKey] = null; + } + } + return $response; + } + + $updateAble[$this->fieldMap[$key]['dbField']] = $value; + + } + + // No fields to update + $query = "UPDATE " . $this->tableName . " SET "; + + $first = true; + foreach($updateAble as $key => $value) { + if (!$first) { + $query.= ', '; + } + $first = false; + $query.= "$key = :$key "; + } + $query.='WHERE uri = :uri'; + $stmt = $this->pdo->prepare($query); + $updateAble['uri'] = $path; + $stmt->execute($updateAble); + + return true; + + } + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * If multiple properties are being searched on, the search should be + * AND'ed. + * + * This method should simply return an array with full principal uri's. + * + * If somebody attempted to search on a property the backend does not + * support, you should simply return 0 results. + * + * You can also just return 0 results if you choose to not support + * searching at all, but keep in mind that this may stop certain features + * from working. + * + * @param string $prefixPath + * @param array $searchProperties + * @return array + */ + public function searchPrincipals($prefixPath, array $searchProperties) { + + $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 '; + $values = array(); + foreach($searchProperties as $property => $value) { + + switch($property) { + + case '{DAV:}displayname' : + $query.=' AND displayname LIKE ?'; + $values[] = '%' . $value . '%'; + break; + case '{http://sabredav.org/ns}email-address' : + $query.=' AND email LIKE ?'; + $values[] = '%' . $value . '%'; + break; + default : + // Unsupported property + return array(); + + } + + } + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + $principals = array(); + while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + + // Checking if the principal is in the prefix + list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']); + if ($rowPrefix !== $prefixPath) continue; + + $principals[] = $row['uri']; + + } + + return $principals; + + } + + /** + * Returns the list of members for a group-principal + * + * @param string $principal + * @return array + */ + public function getGroupMemberSet($principal) { + + $principal = $this->getPrincipalByPath($principal); + if (!$principal) throw new Sabre_DAV_Exception('Principal not found'); + + $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?'); + $stmt->execute(array($principal['id'])); + + $result = array(); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row['uri']; + } + return $result; + + } + + /** + * Returns the list of groups a principal is a member of + * + * @param string $principal + * @return array + */ + public function getGroupMembership($principal) { + + $principal = $this->getPrincipalByPath($principal); + if (!$principal) throw new Sabre_DAV_Exception('Principal not found'); + + $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?'); + $stmt->execute(array($principal['id'])); + + $result = array(); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row['uri']; + } + return $result; + + } + + /** + * Updates the list of group members for a group principal. + * + * The principals should be passed as a list of uri's. + * + * @param string $principal + * @param array $members + * @return void + */ + public function setGroupMemberSet($principal, array $members) { + + // Grabbing the list of principal id's. + $stmt = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');'); + $stmt->execute(array_merge(array($principal), $members)); + + $memberIds = array(); + $principalId = null; + + while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + if ($row['uri'] == $principal) { + $principalId = $row['id']; + } else { + $memberIds[] = $row['id']; + } + } + if (!$principalId) throw new Sabre_DAV_Exception('Principal not found'); + + // Wiping out old members + $stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;'); + $stmt->execute(array($principalId)); + + foreach($memberIds as $memberId) { + + $stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);'); + $stmt->execute(array($principalId, $memberId)); + + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/PrincipalCollection.php b/dav/SabreDAV/lib/Sabre/DAVACL/PrincipalCollection.php new file mode 100644 index 000000000..c3e4cb83f --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/PrincipalCollection.php @@ -0,0 +1,35 @@ +principalBackend, $principal); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Property/Acl.php b/dav/SabreDAV/lib/Sabre/DAVACL/Property/Acl.php new file mode 100644 index 000000000..05e1a690b --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Property/Acl.php @@ -0,0 +1,209 @@ +privileges = $privileges; + $this->prefixBaseUrl = $prefixBaseUrl; + + } + + /** + * Returns the list of privileges for this property + * + * @return array + */ + public function getPrivileges() { + + return $this->privileges; + + } + + /** + * Serializes the property into a DOMElement + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + foreach($this->privileges as $ace) { + + $this->serializeAce($doc, $node, $ace, $server); + + } + + } + + /** + * Unserializes the {DAV:}acl xml element. + * + * @param DOMElement $dom + * @return Sabre_DAVACL_Property_Acl + */ + static public function unserialize(DOMElement $dom) { + + $privileges = array(); + $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace'); + for($ii=0; $ii < $xaces->length; $ii++) { + + $xace = $xaces->item($ii); + $principal = $xace->getElementsByTagNameNS('urn:DAV','principal'); + if ($principal->length !== 1) { + throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); + } + $principal = Sabre_DAVACL_Property_Principal::unserialize($principal->item(0)); + + switch($principal->getType()) { + case Sabre_DAVACL_Property_Principal::HREF : + $principal = $principal->getHref(); + break; + case Sabre_DAVACL_Property_Principal::AUTHENTICATED : + $principal = '{DAV:}authenticated'; + break; + case Sabre_DAVACL_Property_Principal::UNAUTHENTICATED : + $principal = '{DAV:}unauthenticated'; + break; + case Sabre_DAVACL_Property_Principal::ALL : + $principal = '{DAV:}all'; + break; + + } + + $protected = false; + + if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) { + $protected = true; + } + + $grants = $xace->getElementsByTagNameNS('urn:DAV','grant'); + if ($grants->length < 1) { + throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); + } + $grant = $grants->item(0); + + $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege'); + for($jj=0; $jj<$xprivs->length; $jj++) { + + $xpriv = $xprivs->item($jj); + + $privilegeName = null; + + for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) { + + $childNode = $xpriv->childNodes->item($kk); + if ($t = Sabre_DAV_XMLUtil::toClarkNotation($childNode)) { + $privilegeName = $t; + break; + } + } + if (is_null($privilegeName)) { + throw new Sabre_DAV_Exception_BadRequest('{DAV:}privilege elements must have a privilege element contained within them.'); + } + + $privileges[] = array( + 'principal' => $principal, + 'protected' => $protected, + 'privilege' => $privilegeName, + ); + + } + + } + + return new self($privileges); + + } + + /** + * Serializes a single access control entry. + * + * @param DOMDocument $doc + * @param DOMElement $node + * @param array $ace + * @param Sabre_DAV_Server $server + * @return void + */ + private function serializeAce($doc,$node,$ace, $server) { + + $xace = $doc->createElementNS('DAV:','d:ace'); + $node->appendChild($xace); + + $principal = $doc->createElementNS('DAV:','d:principal'); + $xace->appendChild($principal); + switch($ace['principal']) { + case '{DAV:}authenticated' : + $principal->appendChild($doc->createElementNS('DAV:','d:authenticated')); + break; + case '{DAV:}unauthenticated' : + $principal->appendChild($doc->createElementNS('DAV:','d:unauthenticated')); + break; + case '{DAV:}all' : + $principal->appendChild($doc->createElementNS('DAV:','d:all')); + break; + default: + $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/')); + } + + $grant = $doc->createElementNS('DAV:','d:grant'); + $xace->appendChild($grant); + + $privParts = null; + + preg_match('/^{([^}]*)}(.*)$/',$ace['privilege'],$privParts); + + $xprivilege = $doc->createElementNS('DAV:','d:privilege'); + $grant->appendChild($xprivilege); + + $xprivilege->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); + + if (isset($ace['protected']) && $ace['protected']) + $xace->appendChild($doc->createElement('d:protected')); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Property/AclRestrictions.php b/dav/SabreDAV/lib/Sabre/DAVACL/Property/AclRestrictions.php new file mode 100644 index 000000000..a8b054956 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Property/AclRestrictions.php @@ -0,0 +1,32 @@ +ownerDocument; + + $elem->appendChild($doc->createElementNS('DAV:','d:grant-only')); + $elem->appendChild($doc->createElementNS('DAV:','d:no-invert')); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/dav/SabreDAV/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php new file mode 100644 index 000000000..94a296406 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php @@ -0,0 +1,75 @@ +privileges = $privileges; + + } + + /** + * Serializes the property in the DOM + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + foreach($this->privileges as $privName) { + + $this->serializePriv($doc,$node,$privName); + + } + + } + + /** + * Serializes one privilege + * + * @param DOMDocument $doc + * @param DOMElement $node + * @param string $privName + * @return void + */ + protected function serializePriv($doc,$node,$privName) { + + $xp = $doc->createElementNS('DAV:','d:privilege'); + $node->appendChild($xp); + + $privParts = null; + preg_match('/^{([^}]*)}(.*)$/',$privName,$privParts); + + $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Property/Principal.php b/dav/SabreDAV/lib/Sabre/DAVACL/Property/Principal.php new file mode 100644 index 000000000..3f6817422 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Property/Principal.php @@ -0,0 +1,160 @@ +type = $type; + + if ($type===self::HREF && is_null($href)) { + throw new Sabre_DAV_Exception('The href argument must be specified for the HREF principal type.'); + } + $this->href = $href; + + } + + /** + * Returns the principal type + * + * @return int + */ + public function getType() { + + return $this->type; + + } + + /** + * Returns the principal uri. + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Serializes the property into a DOMElement. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $node) { + + $prefix = $server->xmlNamespaces['DAV:']; + switch($this->type) { + + case self::UNAUTHENTICATED : + $node->appendChild( + $node->ownerDocument->createElement($prefix . ':unauthenticated') + ); + break; + case self::AUTHENTICATED : + $node->appendChild( + $node->ownerDocument->createElement($prefix . ':authenticated') + ); + break; + case self::HREF : + $href = $node->ownerDocument->createElement($prefix . ':href'); + $href->nodeValue = $server->getBaseUri() . $this->href; + $node->appendChild($href); + break; + + } + + } + + /** + * Deserializes a DOM element into a property object. + * + * @param DOMElement $dom + * @return Sabre_DAVACL_Property_Principal + */ + static public function unserialize(DOMElement $dom) { + + $parent = $dom->firstChild; + while(!Sabre_DAV_XMLUtil::toClarkNotation($parent)) { + $parent = $parent->nextSibling; + } + + switch(Sabre_DAV_XMLUtil::toClarkNotation($parent)) { + + case '{DAV:}unauthenticated' : + return new self(self::UNAUTHENTICATED); + case '{DAV:}authenticated' : + return new self(self::AUTHENTICATED); + case '{DAV:}href': + return new self(self::HREF, $parent->textContent); + case '{DAV:}all': + return new self(self::ALL); + default : + throw new Sabre_DAV_Exception_BadRequest('Unexpected element (' . Sabre_DAV_XMLUtil::toClarkNotation($parent) . '). Could not deserialize'); + + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/dav/SabreDAV/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php new file mode 100644 index 000000000..276d57ae0 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php @@ -0,0 +1,92 @@ +privileges = $privileges; + + } + + /** + * Serializes the property into a domdocument. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + $this->serializePriv($doc, $node, $this->privileges); + + } + + /** + * Serializes a property + * + * This is a recursive function. + * + * @param DOMDocument $doc + * @param DOMElement $node + * @param array $privilege + * @return void + */ + private function serializePriv($doc,$node,$privilege) { + + $xsp = $doc->createElementNS('DAV:','d:supported-privilege'); + $node->appendChild($xsp); + + $xp = $doc->createElementNS('DAV:','d:privilege'); + $xsp->appendChild($xp); + + $privParts = null; + preg_match('/^{([^}]*)}(.*)$/',$privilege['privilege'],$privParts); + + $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); + + if (isset($privilege['abstract']) && $privilege['abstract']) { + $xsp->appendChild($doc->createElementNS('DAV:','d:abstract')); + } + + if (isset($privilege['description'])) { + $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description'])); + } + + if (isset($privilege['aggregates'])) { + foreach($privilege['aggregates'] as $subPrivilege) { + $this->serializePriv($doc,$xsp,$subPrivilege); + } + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/DAVACL/Version.php b/dav/SabreDAV/lib/Sabre/DAVACL/Version.php new file mode 100644 index 000000000..9950f7487 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/DAVACL/Version.php @@ -0,0 +1,24 @@ +httpRequest->getHeader('Authorization'); + $authHeader = explode(' ',$authHeader); + + if ($authHeader[0]!='AWS' || !isset($authHeader[1])) { + $this->errorCode = self::ERR_NOAWSHEADER; + return false; + } + + list($this->accessKey,$this->signature) = explode(':',$authHeader[1]); + + return true; + + } + + /** + * Returns the username for the request + * + * @return string + */ + public function getAccessKey() { + + return $this->accessKey; + + } + + /** + * Validates the signature based on the secretKey + * + * @param string $secretKey + * @return bool + */ + public function validate($secretKey) { + + $contentMD5 = $this->httpRequest->getHeader('Content-MD5'); + + if ($contentMD5) { + // We need to validate the integrity of the request + $body = $this->httpRequest->getBody(true); + $this->httpRequest->setBody($body,true); + + if ($contentMD5!=base64_encode(md5($body,true))) { + // content-md5 header did not match md5 signature of body + $this->errorCode = self::ERR_MD5CHECKSUMWRONG; + return false; + } + + } + + if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) + $requestDate = $this->httpRequest->getHeader('Date'); + + if (!$this->validateRFC2616Date($requestDate)) + return false; + + $amzHeaders = $this->getAmzHeaders(); + + $signature = base64_encode( + $this->hmacsha1($secretKey, + $this->httpRequest->getMethod() . "\n" . + $contentMD5 . "\n" . + $this->httpRequest->getHeader('Content-type') . "\n" . + $requestDate . "\n" . + $amzHeaders . + $this->httpRequest->getURI() + ) + ); + + if ($this->signature != $signature) { + + $this->errorCode = self::ERR_INVALIDSIGNATURE; + return false; + + } + + return true; + + } + + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','AWS'); + $this->httpResponse->sendStatus(401); + + } + + /** + * Makes sure the supplied value is a valid RFC2616 date. + * + * If we would just use strtotime to get a valid timestamp, we have no way of checking if a + * user just supplied the word 'now' for the date header. + * + * This function also makes sure the Date header is within 15 minutes of the operating + * system date, to prevent replay attacks. + * + * @param string $dateHeader + * @return bool + */ + protected function validateRFC2616Date($dateHeader) { + + $date = Sabre_HTTP_Util::parseHTTPDate($dateHeader); + + // Unknown format + if (!$date) { + $this->errorCode = self::ERR_INVALIDDATEFORMAT; + return false; + } + + $min = new DateTime('-15 minutes'); + $max = new DateTime('+15 minutes'); + + // We allow 15 minutes around the current date/time + if ($date > $max || $date < $min) { + $this->errorCode = self::ERR_REQUESTTIMESKEWED; + return false; + } + + return $date; + + } + + /** + * Returns a list of AMZ headers + * + * @return string + */ + protected function getAmzHeaders() { + + $amzHeaders = array(); + $headers = $this->httpRequest->getHeaders(); + foreach($headers as $headerName => $headerValue) { + if (strpos(strtolower($headerName),'x-amz-')===0) { + $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n"; + } + } + ksort($amzHeaders); + + $headerStr = ''; + foreach($amzHeaders as $h=>$v) { + $headerStr.=$h.':'.$v; + } + + return $headerStr; + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + $blocksize=64; + if (strlen($key)>$blocksize) + $key=pack('H*', sha1($key)); + $key=str_pad($key,$blocksize,chr(0x00)); + $ipad=str_repeat(chr(0x36),$blocksize); + $opad=str_repeat(chr(0x5c),$blocksize); + $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message)))); + return $hmac; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/HTTP/AbstractAuth.php b/dav/SabreDAV/lib/Sabre/HTTP/AbstractAuth.php new file mode 100644 index 000000000..3bccabcd1 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/HTTP/AbstractAuth.php @@ -0,0 +1,111 @@ +httpResponse = new Sabre_HTTP_Response(); + $this->httpRequest = new Sabre_HTTP_Request(); + + } + + /** + * Sets an alternative HTTP response object + * + * @param Sabre_HTTP_Response $response + * @return void + */ + public function setHTTPResponse(Sabre_HTTP_Response $response) { + + $this->httpResponse = $response; + + } + + /** + * Sets an alternative HTTP request object + * + * @param Sabre_HTTP_Request $request + * @return void + */ + public function setHTTPRequest(Sabre_HTTP_Request $request) { + + $this->httpRequest = $request; + + } + + + /** + * Sets the realm + * + * The realm is often displayed in authentication dialog boxes + * Commonly an application name displayed here + * + * @param string $realm + * @return void + */ + public function setRealm($realm) { + + $this->realm = $realm; + + } + + /** + * Returns the realm + * + * @return string + */ + public function getRealm() { + + return $this->realm; + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + abstract public function requireLogin(); + +} diff --git a/dav/SabreDAV/lib/Sabre/HTTP/BasicAuth.php b/dav/SabreDAV/lib/Sabre/HTTP/BasicAuth.php new file mode 100644 index 000000000..a747cc6a3 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/HTTP/BasicAuth.php @@ -0,0 +1,67 @@ +httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) { + + return array($user,$pass); + + } + + // Most other webservers + $auth = $this->httpRequest->getHeader('Authorization'); + + // Apache could prefix environment variables with REDIRECT_ when urls + // are passed through mod_rewrite + if (!$auth) { + $auth = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); + } + + if (!$auth) return false; + + if (strpos(strtolower($auth),'basic')!==0) return false; + + return explode(':', base64_decode(substr($auth, 6))); + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"'); + $this->httpResponse->sendStatus(401); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/HTTP/DigestAuth.php b/dav/SabreDAV/lib/Sabre/HTTP/DigestAuth.php new file mode 100644 index 000000000..ee7f05c08 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/HTTP/DigestAuth.php @@ -0,0 +1,240 @@ +nonce = uniqid(); + $this->opaque = md5($this->realm); + parent::__construct(); + + } + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return void + */ + public function init() { + + $digest = $this->getDigest(); + $this->digestParts = $this->parseDigest($digest); + + } + + /** + * Sets the quality of protection value. + * + * Possible values are: + * Sabre_HTTP_DigestAuth::QOP_AUTH + * Sabre_HTTP_DigestAuth::QOP_AUTHINT + * + * Multiple values can be specified using logical OR. + * + * QOP_AUTHINT ensures integrity of the request body, but this is not + * supported by most HTTP clients. QOP_AUTHINT also requires the entire + * request body to be md5'ed, which can put strains on CPU and memory. + * + * @param int $qop + * @return void + */ + public function setQOP($qop) { + + $this->qop = $qop; + + } + + /** + * Validates the user. + * + * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); + * + * @param string $A1 + * @return bool + */ + public function validateA1($A1) { + + $this->A1 = $A1; + return $this->validate(); + + } + + /** + * Validates authentication through a password. The actual password must be provided here. + * It is strongly recommended not store the password in plain-text and use validateA1 instead. + * + * @param string $password + * @return bool + */ + public function validatePassword($password) { + + $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); + return $this->validate(); + + } + + /** + * Returns the username for the request + * + * @return string + */ + public function getUsername() { + + return $this->digestParts['username']; + + } + + /** + * Validates the digest challenge + * + * @return bool + */ + protected function validate() { + + $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri']; + + if ($this->digestParts['qop']=='auth-int') { + // Making sure we support this qop value + if (!($this->qop & self::QOP_AUTHINT)) return false; + // We need to add an md5 of the entire request body to the A2 part of the hash + $body = $this->httpRequest->getBody(true); + $this->httpRequest->setBody($body,true); + $A2 .= ':' . md5($body); + } else { + + // We need to make sure we support this qop value + if (!($this->qop & self::QOP_AUTH)) return false; + } + + $A2 = md5($A2); + + $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); + + return $this->digestParts['response']==$validResponse; + + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $qop = ''; + switch($this->qop) { + case self::QOP_AUTH : $qop = 'auth'; break; + case self::QOP_AUTHINT : $qop = 'auth-int'; break; + case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break; + } + + $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); + $this->httpResponse->sendStatus(401); + + } + + + /** + * This method returns the full digest string. + * + * It should be compatibile with mod_php format and other webservers. + * + * If the header could not be found, null will be returned + * + * @return mixed + */ + public function getDigest() { + + // mod_php + $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST'); + if ($digest) return $digest; + + // most other servers + $digest = $this->httpRequest->getHeader('Authorization'); + + // Apache could prefix environment variables with REDIRECT_ when urls + // are passed through mod_rewrite + if (!$digest) { + $digest = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); + } + + if ($digest && strpos(strtolower($digest),'digest')===0) { + return substr($digest,7); + } else { + return null; + } + + } + + + /** + * Parses the different pieces of the digest string into an array. + * + * This method returns false if an incomplete digest was supplied + * + * @param string $digest + * @return mixed + */ + protected function parseDigest($digest) { + + // protect against missing data + $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); + $data = array(); + + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); + + foreach ($matches as $m) { + $data[$m[1]] = $m[2] ? $m[2] : $m[3]; + unset($needed_parts[$m[1]]); + } + + return $needed_parts ? false : $data; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/HTTP/Request.php b/dav/SabreDAV/lib/Sabre/HTTP/Request.php new file mode 100644 index 000000000..74d5ed3d7 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/HTTP/Request.php @@ -0,0 +1,268 @@ +_SERVER = $serverData; + else $this->_SERVER =& $_SERVER; + + if ($postData) $this->_POST = $postData; + else $this->_POST =& $_POST; + + } + + /** + * Returns the value for a specific http header. + * + * This method returns null if the header did not exist. + * + * @param string $name + * @return string + */ + public function getHeader($name) { + + $name = strtoupper(str_replace(array('-'),array('_'),$name)); + if (isset($this->_SERVER['HTTP_' . $name])) { + return $this->_SERVER['HTTP_' . $name]; + } + + // There's a few headers that seem to end up in the top-level + // server array. + switch($name) { + case 'CONTENT_TYPE' : + case 'CONTENT_LENGTH' : + if (isset($this->_SERVER[$name])) { + return $this->_SERVER[$name]; + } + break; + + } + return; + + } + + /** + * Returns all (known) HTTP headers. + * + * All headers are converted to lower-case, and additionally all underscores + * are automatically converted to dashes + * + * @return array + */ + public function getHeaders() { + + $hdrs = array(); + foreach($this->_SERVER as $key=>$value) { + + switch($key) { + case 'CONTENT_LENGTH' : + case 'CONTENT_TYPE' : + $hdrs[strtolower(str_replace('_','-',$key))] = $value; + break; + default : + if (strpos($key,'HTTP_')===0) { + $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value; + } + break; + } + + } + + return $hdrs; + + } + + /** + * Returns the HTTP request method + * + * This is for example POST or GET + * + * @return string + */ + public function getMethod() { + + return $this->_SERVER['REQUEST_METHOD']; + + } + + /** + * Returns the requested uri + * + * @return string + */ + public function getUri() { + + return $this->_SERVER['REQUEST_URI']; + + } + + /** + * Will return protocol + the hostname + the uri + * + * @return string + */ + public function getAbsoluteUri() { + + // Checking if the request was made through HTTPS. The last in line is for IIS + $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off'); + return ($protocol?'https':'http') . '://' . $this->getHeader('Host') . $this->getUri(); + + } + + /** + * Returns everything after the ? from the current url + * + * @return string + */ + public function getQueryString() { + + return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:''; + + } + + /** + * Returns the HTTP request body body + * + * This method returns a readable stream resource. + * If the asString parameter is set to true, a string is sent instead. + * + * @param bool $asString + * @return resource + */ + public function getBody($asString = false) { + + if (is_null($this->body)) { + if (!is_null(self::$defaultInputStream)) { + $this->body = self::$defaultInputStream; + } else { + $this->body = fopen('php://input','r'); + self::$defaultInputStream = $this->body; + } + } + if ($asString) { + $body = stream_get_contents($this->body); + return $body; + } else { + return $this->body; + } + + } + + /** + * Sets the contents of the HTTP request body + * + * This method can either accept a string, or a readable stream resource. + * + * If the setAsDefaultInputStream is set to true, it means for this run of the + * script the supplied body will be used instead of php://input. + * + * @param mixed $body + * @param bool $setAsDefaultInputStream + * @return void + */ + public function setBody($body,$setAsDefaultInputStream = false) { + + if(is_resource($body)) { + $this->body = $body; + } else { + + $stream = fopen('php://temp','r+'); + fputs($stream,$body); + rewind($stream); + // String is assumed + $this->body = $stream; + } + if ($setAsDefaultInputStream) { + self::$defaultInputStream = $this->body; + } + + } + + /** + * Returns PHP's _POST variable. + * + * The reason this is in a method is so it can be subclassed and + * overridden. + * + * @return array + */ + public function getPostVars() { + + return $this->_POST; + + } + + /** + * Returns a specific item from the _SERVER array. + * + * Do not rely on this feature, it is for internal use only. + * + * @param string $field + * @return string + */ + public function getRawServerValue($field) { + + return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/HTTP/Response.php b/dav/SabreDAV/lib/Sabre/HTTP/Response.php new file mode 100644 index 000000000..ffe9bda20 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/HTTP/Response.php @@ -0,0 +1,157 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authorative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC 4918 + 208 => 'Already Reported', // RFC 5842 + 226 => 'IM Used', // RFC 3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Reserved', + 307 => 'Temporary Redirect', + 400 => 'Bad request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC 2324 + 422 => 'Unprocessable Entity', // RFC 4918 + 423 => 'Locked', // RFC 4918 + 424 => 'Failed Dependency', // RFC 4918 + 426 => 'Upgrade required', + 428 => 'Precondition required', // draft-nottingham-http-new-status + 429 => 'Too Many Requests', // draft-nottingham-http-new-status + 431 => 'Request Header Fields Too Large', // draft-nottingham-http-new-status + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', // RFC 4918 + 508 => 'Loop Detected', // RFC 5842 + 509 => 'Bandwidth Limit Exceeded', // non-standard + 510 => 'Not extended', + 511 => 'Network Authentication Required', // draft-nottingham-http-new-status + ); + + return 'HTTP/1.1 ' . $code . ' ' . $msg[$code]; + + } + + /** + * Sends an HTTP status header to the client + * + * @param int $code HTTP status code + * @return bool + */ + public function sendStatus($code) { + + if (!headers_sent()) + return header($this->getStatusMessage($code)); + else return false; + + } + + /** + * Sets an HTTP header for the response + * + * @param string $name + * @param string $value + * @param bool $replace + * @return bool + */ + public function setHeader($name, $value, $replace = true) { + + $value = str_replace(array("\r","\n"),array('\r','\n'),$value); + if (!headers_sent()) + return header($name . ': ' . $value, $replace); + else return false; + + } + + /** + * Sets a bunch of HTTP Headers + * + * headersnames are specified as keys, value in the array value + * + * @param array $headers + * @return void + */ + public function setHeaders(array $headers) { + + foreach($headers as $key=>$value) + $this->setHeader($key, $value); + + } + + /** + * Sends the entire response body + * + * This method can accept either an open filestream, or a string. + * + * @param mixed $body + * @return void + */ + public function sendBody($body) { + + if (is_resource($body)) { + + fpassthru($body); + + } else { + + // We assume a string + echo $body; + + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/HTTP/Util.php b/dav/SabreDAV/lib/Sabre/HTTP/Util.php new file mode 100644 index 000000000..67bdd489e --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/HTTP/Util.php @@ -0,0 +1,82 @@ += 0) + return new DateTime('@' . $realDate, new DateTimeZone('UTC')); + + } + + /** + * Transforms a DateTime object to HTTP's most common date format. + * + * We're serializing it as the RFC 1123 date, which, for HTTP must be + * specified as GMT. + * + * @param DateTime $dateTime + * @return string + */ + static function toHTTPDate(DateTime $dateTime) { + + // We need to clone it, as we don't want to affect the existing + // DateTime. + $dateTime = clone $dateTime; + $dateTime->setTimeZone(new DateTimeZone('GMT')); + return $dateTime->format('D, d M Y H:i:s \G\M\T'); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/HTTP/Version.php b/dav/SabreDAV/lib/Sabre/HTTP/Version.php new file mode 100644 index 000000000..23dc7f8a7 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/HTTP/Version.php @@ -0,0 +1,24 @@ + 'Sabre_VObject_Component_VCalendar', + 'VEVENT' => 'Sabre_VObject_Component_VEvent', + 'VTODO' => 'Sabre_VObject_Component_VTodo', + 'VJOURNAL' => 'Sabre_VObject_Component_VJournal', + 'VALARM' => 'Sabre_VObject_Component_VAlarm', + ); + + /** + * Creates the new component by name, but in addition will also see if + * there's a class mapped to the property name. + * + * @param string $name + * @param string $value + * @return Sabre_VObject_Component + */ + static public function create($name, $value = null) { + + $name = strtoupper($name); + + if (isset(self::$classMap[$name])) { + return new self::$classMap[$name]($name, $value); + } else { + return new self($name, $value); + } + + } + + /** + * Creates a new component. + * + * By default this object will iterate over its own children, but this can + * be overridden with the iterator argument + * + * @param string $name + * @param Sabre_VObject_ElementList $iterator + */ + public function __construct($name, Sabre_VObject_ElementList $iterator = null) { + + $this->name = strtoupper($name); + if (!is_null($iterator)) $this->iterator = $iterator; + + } + + /** + * Turns the object back into a serialized blob. + * + * @return string + */ + public function serialize() { + + $str = "BEGIN:" . $this->name . "\r\n"; + + /** + * Gives a component a 'score' for sorting purposes. + * + * This is solely used by the childrenSort method. + * + * A higher score means the item will be higher in the list + * + * @param Sabre_VObject_Node $n + * @return int + */ + $sortScore = function($n) { + + if ($n instanceof Sabre_VObject_Component) { + // We want to encode VTIMEZONE first, this is a personal + // preference. + if ($n->name === 'VTIMEZONE') { + return 1; + } else { + return 0; + } + } else { + // VCARD version 4.0 wants the VERSION property to appear first + if ($n->name === 'VERSION') { + return 3; + } else { + return 2; + } + } + + }; + + usort($this->children, function($a, $b) use ($sortScore) { + + $sA = $sortScore($a); + $sB = $sortScore($b); + + if ($sA === $sB) return 0; + + return ($sA > $sB) ? -1 : 1; + + }); + + foreach($this->children as $child) $str.=$child->serialize(); + $str.= "END:" . $this->name . "\r\n"; + + return $str; + + } + + /** + * Adds a new component or element + * + * You can call this method with the following syntaxes: + * + * add(Sabre_VObject_Element $element) + * add(string $name, $value) + * + * The first version adds an Element + * The second adds a property as a string. + * + * @param mixed $item + * @param mixed $itemValue + * @return void + */ + public function add($item, $itemValue = null) { + + if ($item instanceof Sabre_VObject_Element) { + if (!is_null($itemValue)) { + throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject'); + } + $item->parent = $this; + $this->children[] = $item; + } elseif(is_string($item)) { + + if (!is_scalar($itemValue)) { + throw new InvalidArgumentException('The second argument must be scalar'); + } + $item = Sabre_VObject_Property::create($item,$itemValue); + $item->parent = $this; + $this->children[] = $item; + + } else { + + throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string'); + + } + + } + + /** + * Returns an iterable list of children + * + * @return Sabre_VObject_ElementList + */ + public function children() { + + return new Sabre_VObject_ElementList($this->children); + + } + + /** + * Returns an array with elements that match the specified name. + * + * This function is also aware of MIME-Directory groups (as they appear in + * vcards). This means that if a property is grouped as "HOME.EMAIL", it + * will also be returned when searching for just "EMAIL". If you want to + * search for a property in a specific group, you can select on the entire + * string ("HOME.EMAIL"). If you want to search on a specific property that + * has not been assigned a group, specify ".EMAIL". + * + * Keys are retained from the 'children' array, which may be confusing in + * certain cases. + * + * @param string $name + * @return array + */ + public function select($name) { + + $group = null; + $name = strtoupper($name); + if (strpos($name,'.')!==false) { + list($group,$name) = explode('.', $name, 2); + } + + $result = array(); + foreach($this->children as $key=>$child) { + + if ( + strtoupper($child->name) === $name && + (is_null($group) || ( $child instanceof Sabre_VObject_Property && strtoupper($child->group) === $group)) + ) { + + $result[$key] = $child; + + } + } + + reset($result); + return $result; + + } + + /** + * This method only returns a list of sub-components. Properties are + * ignored. + * + * @return array + */ + public function getComponents() { + + $result = array(); + foreach($this->children as $child) { + if ($child instanceof Sabre_VObject_Component) { + $result[] = $child; + } + } + + return $result; + + } + + /* Magic property accessors {{{ */ + + /** + * Using 'get' you will either get a property or component, + * + * If there were no child-elements found with the specified name, + * null is returned. + * + * @param string $name + * @return Sabre_VObject_Property + */ + public function __get($name) { + + $matches = $this->select($name); + if (count($matches)===0) { + return null; + } else { + $firstMatch = current($matches); + /** @var $firstMatch Sabre_VObject_Property */ + $firstMatch->setIterator(new Sabre_VObject_ElementList(array_values($matches))); + return $firstMatch; + } + + } + + /** + * This method checks if a sub-element with the specified name exists. + * + * @param string $name + * @return bool + */ + public function __isset($name) { + + $matches = $this->select($name); + return count($matches)>0; + + } + + /** + * Using the setter method you can add properties or subcomponents + * + * You can either pass a Sabre_VObject_Component, Sabre_VObject_Property + * object, or a string to automatically create a Property. + * + * If the item already exists, it will be removed. If you want to add + * a new item with the same name, always use the add() method. + * + * @param string $name + * @param mixed $value + * @return void + */ + public function __set($name, $value) { + + $matches = $this->select($name); + $overWrite = count($matches)?key($matches):null; + + if ($value instanceof Sabre_VObject_Component || $value instanceof Sabre_VObject_Property) { + $value->parent = $this; + if (!is_null($overWrite)) { + $this->children[$overWrite] = $value; + } else { + $this->children[] = $value; + } + } elseif (is_scalar($value)) { + $property = Sabre_VObject_Property::create($name,$value); + $property->parent = $this; + if (!is_null($overWrite)) { + $this->children[$overWrite] = $property; + } else { + $this->children[] = $property; + } + } else { + throw new InvalidArgumentException('You must pass a Sabre_VObject_Component, Sabre_VObject_Property or scalar type'); + } + + } + + /** + * Removes all properties and components within this component. + * + * @param string $name + * @return void + */ + public function __unset($name) { + + $matches = $this->select($name); + foreach($matches as $k=>$child) { + + unset($this->children[$k]); + $child->parent = null; + + } + + } + + /* }}} */ + + /** + * This method is automatically called when the object is cloned. + * Specifically, this will ensure all child elements are also cloned. + * + * @return void + */ + public function __clone() { + + foreach($this->children as $key=>$child) { + $this->children[$key] = clone $child; + $this->children[$key]->parent = $this; + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Component/VAlarm.php b/dav/SabreDAV/lib/Sabre/VObject/Component/VAlarm.php new file mode 100644 index 000000000..1d1dd69d5 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Component/VAlarm.php @@ -0,0 +1,100 @@ +TRIGGER; + if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') { + $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($this->TRIGGER); + $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START'; + + $parentComponent = $this->parent; + if ($related === 'START') { + $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); + $effectiveTrigger->add($triggerDuration); + } else { + if ($parentComponent->name === 'VTODO') { + $endProp = 'DUE'; + } elseif ($parentComponent->name === 'VEVENT') { + $endProp = 'DTEND'; + } else { + throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); + } + + if (isset($parentComponent->$endProp)) { + $effectiveTrigger = clone $parentComponent->$endProp->getDateTime(); + $effectiveTrigger->add($triggerDuration); + } elseif (isset($parentComponent->DURATION)) { + $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); + $duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION); + $effectiveTrigger->add($duration); + $effectiveTrigger->add($triggerDuration); + } else { + $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); + $effectiveTrigger->add($triggerDuration); + } + } + } else { + $effectiveTrigger = $trigger->getDateTime(); + } + return $effectiveTrigger; + + } + + /** + * Returns true or false depending on if the event falls in the specified + * time-range. This is used for filtering purposes. + * + * The rules used to determine if an event falls within the specified + * time-range is based on the CalDAV specification. + * + * @param DateTime $start + * @param DateTime $end + * @return bool + */ + public function isInTimeRange(DateTime $start, DateTime $end) { + + $effectiveTrigger = $this->getEffectiveTriggerTime(); + + if (isset($this->DURATION)) { + $duration = Sabre_VObject_DateTimeParser::parseDuration($this->DURATION); + $repeat = (string)$this->repeat; + if (!$repeat) { + $repeat = 1; + } + + $period = new DatePeriod($effectiveTrigger, $duration, (int)$repeat); + + foreach($period as $occurrence) { + + if ($start <= $occurrence && $end > $occurrence) { + return true; + } + } + return false; + } else { + return ($start <= $effectiveTrigger && $end > $effectiveTrigger); + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Component/VCalendar.php b/dav/SabreDAV/lib/Sabre/VObject/Component/VCalendar.php new file mode 100644 index 000000000..f3be29afd --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Component/VCalendar.php @@ -0,0 +1,133 @@ +children as $component) { + + if (!$component instanceof Sabre_VObject_Component) + continue; + + if (isset($component->{'RECURRENCE-ID'})) + continue; + + if ($componentName && $component->name !== strtoupper($componentName)) + continue; + + if ($component->name === 'VTIMEZONE') + continue; + + $components[] = $component; + + } + + return $components; + + } + + /** + * If this calendar object, has events with recurrence rules, this method + * can be used to expand the event into multiple sub-events. + * + * Each event will be stripped from it's recurrence information, and only + * the instances of the event in the specified timerange will be left + * alone. + * + * In addition, this method will cause timezone information to be stripped, + * and normalized to UTC. + * + * This method will alter the VCalendar. This cannot be reversed. + * + * This functionality is specifically used by the CalDAV standard. It is + * possible for clients to request expand events, if they are rather simple + * clients and do not have the possibility to calculate recurrences. + * + * @param DateTime $start + * @param DateTime $end + * @return void + */ + public function expand(DateTime $start, DateTime $end) { + + $newEvents = array(); + + foreach($this->select('VEVENT') as $key=>$vevent) { + + if (isset($vevent->{'RECURRENCE-ID'})) { + unset($this->children[$key]); + continue; + } + + + if (!$vevent->rrule) { + unset($this->children[$key]); + if ($vevent->isInTimeRange($start, $end)) { + $newEvents[] = $vevent; + } + continue; + } + + $uid = (string)$vevent->uid; + if (!$uid) { + throw new LogicException('Event did not have a UID!'); + } + + $it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid); + $it->fastForward($start); + + while($it->valid() && $it->getDTStart() < $end) { + + if ($it->getDTEnd() > $start) { + + $newEvents[] = $it->getEventObject(); + + } + $it->next(); + + } + unset($this->children[$key]); + + } + + foreach($newEvents as $newEvent) { + + foreach($newEvent->children as $child) { + if ($child instanceof Sabre_VObject_Property_DateTime && + $child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) { + $child->setDateTime($child->getDateTime(),Sabre_VObject_Property_DateTime::UTC); + } + } + + $this->add($newEvent); + + } + + // Removing all VTIMEZONE components + unset($this->VTIMEZONE); + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/VObject/Component/VEvent.php b/dav/SabreDAV/lib/Sabre/VObject/Component/VEvent.php new file mode 100644 index 000000000..684f5f5a7 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Component/VEvent.php @@ -0,0 +1,68 @@ +RRULE) { + $it = new Sabre_VObject_RecurrenceIterator($this); + $it->fastForward($start); + + // We fast-forwarded to a spot where the end-time of the + // recurrence instance exceeded the start of the requested + // time-range. + // + // If the starttime of the recurrence did not exceed the + // end of the time range as well, we have a match. + return ($it->getDTStart() < $end && $it->getDTEnd() > $start); + + } + + $effectiveStart = $this->DTSTART->getDateTime(); + if (isset($this->DTEND)) { + $effectiveEnd = $this->DTEND->getDateTime(); + // If this was an all-day event, we should just increase the + // end-date by 1. Otherwise the event will last until the second + // the date changed, by increasing this by 1 day the event lasts + // all of the last day as well. + if ($this->DTSTART->getDateType() == Sabre_VObject_Property_DateTime::DATE) { + $effectiveEnd->modify('+1 day'); + } + } elseif (isset($this->DURATION)) { + $effectiveEnd = clone $effectiveStart; + $effectiveEnd->add( Sabre_VObject_DateTimeParser::parseDuration($this->DURATION) ); + } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Property_DateTime::DATE) { + $effectiveEnd = clone $effectiveStart; + $effectiveEnd->modify('+1 day'); + } else { + $effectiveEnd = clone $effectiveStart; + } + return ( + ($start <= $effectiveEnd) && ($end > $effectiveStart) + ); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Component/VJournal.php b/dav/SabreDAV/lib/Sabre/VObject/Component/VJournal.php new file mode 100644 index 000000000..6d3949289 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Component/VJournal.php @@ -0,0 +1,44 @@ +DTSTART)?$this->DTSTART->getDateTime():null; + if ($dtstart) { + $effectiveEnd = clone $dtstart; + if ($this->DTSTART->getDateType() == Sabre_VObject_Property_DateTime::DATE) { + $effectiveEnd->modify('+1 day'); + } + + return ($start <= $effectiveEnd && $end > $dtstart); + + } + return false; + + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Component/VTodo.php b/dav/SabreDAV/lib/Sabre/VObject/Component/VTodo.php new file mode 100644 index 000000000..2fb6654ed --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Component/VTodo.php @@ -0,0 +1,66 @@ +DTSTART)?$this->DTSTART->getDateTime():null; + $duration = isset($this->DURATION)?Sabre_VObject_DateTimeParser::parseDuration($this->DURATION):null; + $due = isset($this->DUE)?$this->DUE->getDateTime():null; + $completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null; + $created = isset($this->CREATED)?$this->CREATED->getDateTime():null; + + if ($dtstart) { + if ($duration) { + $effectiveEnd = clone $dtstart; + $effectiveEnd->add($duration); + return $start <= $effectiveEnd && $end > $dtstart; + } elseif ($due) { + return + ($start < $due || $start <= $dtstart) && + ($end > $dtstart || $end >= $due); + } else { + return $start <= $dtstart && $end > $dtstart; + } + } + if ($due) { + return ($start < $due && $end >= $due); + } + if ($completed && $created) { + return + ($start <= $created || $start <= $completed) && + ($end >= $created || $end >= $completed); + } + if ($completed) { + return ($start <= $completed && $end >= $completed); + } + if ($created) { + return ($end > $created); + } + return true; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/DateTimeParser.php b/dav/SabreDAV/lib/Sabre/VObject/DateTimeParser.php new file mode 100644 index 000000000..23a4bb699 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/DateTimeParser.php @@ -0,0 +1,181 @@ +setTimeZone(new DateTimeZone('UTC')); + return $date; + + } + + /** + * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object + * + * @param string $date + * @return DateTime + */ + static public function parseDate($date) { + + // Format is YYYYMMDD + $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); + + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date); + } + + $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC')); + return $date; + + } + + /** + * Parses an iCalendar (RFC5545) formatted duration value. + * + * This method will either return a DateTimeInterval object, or a string + * suitable for strtotime or DateTime::modify. + * + * @param string $duration + * @param bool $asString + * @return DateInterval|string + */ + static public function parseDuration($duration, $asString = false) { + + $result = preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $duration, $matches); + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration); + } + + if (!$asString) { + $invert = false; + if ($matches['plusminus']==='-') { + $invert = true; + } + + + $parts = array( + 'week', + 'day', + 'hour', + 'minute', + 'second', + ); + foreach($parts as $part) { + $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0; + } + + + // We need to re-construct the $duration string, because weeks and + // days are not supported by DateInterval in the same string. + $duration = 'P'; + $days = $matches['day']; + if ($matches['week']) { + $days+=$matches['week']*7; + } + if ($days) + $duration.=$days . 'D'; + + if ($matches['minute'] || $matches['second'] || $matches['hour']) { + $duration.='T'; + + if ($matches['hour']) + $duration.=$matches['hour'].'H'; + + if ($matches['minute']) + $duration.=$matches['minute'].'M'; + + if ($matches['second']) + $duration.=$matches['second'].'S'; + + } + + if ($duration==='P') { + $duration = 'PT0S'; + } + $iv = new DateInterval($duration); + if ($invert) $iv->invert = true; + + return $iv; + + } + + + + $parts = array( + 'week', + 'day', + 'hour', + 'minute', + 'second', + ); + + $newDur = ''; + foreach($parts as $part) { + if (isset($matches[$part]) && $matches[$part]) { + $newDur.=' '.$matches[$part] . ' ' . $part . 's'; + } + } + + $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); + if ($newDur === '+') { $newDur = '+0 seconds'; }; + return $newDur; + + } + + /** + * Parses either a Date or DateTime, or Duration value. + * + * @param string $date + * @param DateTimeZone|string $referenceTZ + * @return DateTime|DateInterval + */ + static public function parse($date, $referenceTZ = null) { + + if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) { + return self::parseDuration($date); + } elseif (strlen($date)===8) { + return self::parseDate($date); + } else { + return self::parseDateTime($date, $referenceTZ); + } + + } + + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Element.php b/dav/SabreDAV/lib/Sabre/VObject/Element.php new file mode 100644 index 000000000..e20ff0b35 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Element.php @@ -0,0 +1,16 @@ +vevent where there's multiple VEVENT objects. + * + * @package Sabre + * @subpackage VObject + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { + + /** + * Inner elements + * + * @var array + */ + protected $elements = array(); + + /** + * Creates the element list. + * + * @param array $elements + */ + public function __construct(array $elements) { + + $this->elements = $elements; + + } + + /* {{{ Iterator interface */ + + /** + * Current position + * + * @var int + */ + private $key = 0; + + /** + * Returns current item in iteration + * + * @return Sabre_VObject_Element + */ + public function current() { + + return $this->elements[$this->key]; + + } + + /** + * To the next item in the iterator + * + * @return void + */ + public function next() { + + $this->key++; + + } + + /** + * Returns the current iterator key + * + * @return int + */ + public function key() { + + return $this->key; + + } + + /** + * Returns true if the current position in the iterator is a valid one + * + * @return bool + */ + public function valid() { + + return isset($this->elements[$this->key]); + + } + + /** + * Rewinds the iterator + * + * @return void + */ + public function rewind() { + + $this->key = 0; + + } + + /* }}} */ + + /* {{{ Countable interface */ + + /** + * Returns the number of elements + * + * @return int + */ + public function count() { + + return count($this->elements); + + } + + /* }}} */ + + /* {{{ ArrayAccess Interface */ + + + /** + * Checks if an item exists through ArrayAccess. + * + * @param int $offset + * @return bool + */ + public function offsetExists($offset) { + + return isset($this->elements[$offset]); + + } + + /** + * Gets an item through ArrayAccess. + * + * @param int $offset + * @return mixed + */ + public function offsetGet($offset) { + + return $this->elements[$offset]; + + } + + /** + * Sets an item through ArrayAccess. + * + * @param int $offset + * @param mixed $value + * @return void + */ + public function offsetSet($offset,$value) { + + throw new LogicException('You can not add new objects to an ElementList'); + + } + + /** + * Sets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * @return void + */ + public function offsetUnset($offset) { + + throw new LogicException('You can not remove objects from an ElementList'); + + } + + /* }}} */ + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/FreeBusyGenerator.php b/dav/SabreDAV/lib/Sabre/VObject/FreeBusyGenerator.php new file mode 100644 index 000000000..1c96a64a0 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/FreeBusyGenerator.php @@ -0,0 +1,297 @@ +baseObject = $vcalendar; + + } + + /** + * Sets the input objects + * + * Every object must either be a string or a Sabre_VObject_Component. + * + * @param array $objects + * @return void + */ + public function setObjects(array $objects) { + + $this->objects = array(); + foreach($objects as $object) { + + if (is_string($object)) { + $this->objects[] = Sabre_VObject_Reader::read($object); + } elseif ($object instanceof Sabre_VObject_Component) { + $this->objects[] = $object; + } else { + throw new InvalidArgumentException('You can only pass strings or Sabre_VObject_Component arguments to setObjects'); + } + + } + + } + + /** + * Sets the time range + * + * Any freebusy object falling outside of this time range will be ignored. + * + * @param DateTime $start + * @param DateTime $end + * @return void + */ + public function setTimeRange(DateTime $start = null, DateTime $end = null) { + + $this->start = $start; + $this->end = $end; + + } + + /** + * Parses the input data and returns a correct VFREEBUSY object, wrapped in + * a VCALENDAR. + * + * @return Sabre_VObject_Component + */ + public function getResult() { + + $busyTimes = array(); + + foreach($this->objects as $object) { + + foreach($object->getBaseComponents() as $component) { + + switch($component->name) { + + case 'VEVENT' : + + $FBTYPE = 'BUSY'; + if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) { + break; + } + if (isset($component->STATUS)) { + $status = strtoupper($component->STATUS); + if ($status==='CANCELLED') { + break; + } + if ($status==='TENTATIVE') { + $FBTYPE = 'BUSY-TENTATIVE'; + } + } + + $times = array(); + + if ($component->RRULE) { + + $iterator = new Sabre_VObject_RecurrenceIterator($object, (string)$component->uid); + if ($this->start) { + $iterator->fastForward($this->start); + } + + $maxRecurrences = 200; + + while($iterator->valid() && --$maxRecurrences) { + + $startTime = $iterator->getDTStart(); + if ($this->end && $startTime > $this->end) { + break; + } + $times[] = array( + $iterator->getDTStart(), + $iterator->getDTEnd(), + ); + + $iterator->next(); + + } + + } else { + + $startTime = $component->DTSTART->getDateTime(); + if ($this->end && $startTime > $this->end) { + break; + } + $endTime = null; + if (isset($component->DTEND)) { + $endTime = $component->DTEND->getDateTime(); + } elseif (isset($component->DURATION)) { + $duration = Sabre_VObject_DateTimeParser::parseDuration((string)$component->DURATION); + $endTime = clone $startTime; + $endTime->add($duration); + } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) { + $endTime = clone $startTime; + $endTime->modify('+1 day'); + } else { + // The event had no duration (0 seconds) + break; + } + + $times[] = array($startTime, $endTime); + + } + + foreach($times as $time) { + + if ($this->end && $time[0] > $this->end) break; + if ($this->start && $time[1] < $this->start) break; + + $busyTimes[] = array( + $time[0], + $time[1], + $FBTYPE, + ); + } + break; + + case 'VFREEBUSY' : + foreach($component->FREEBUSY as $freebusy) { + + $fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY'; + + // Skipping intervals marked as 'free' + if ($fbType==='FREE') + continue; + + $values = explode(',', $freebusy); + foreach($values as $value) { + list($startTime, $endTime) = explode('/', $value); + $startTime = Sabre_VObject_DateTimeParser::parseDateTime($startTime); + + if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') { + $duration = Sabre_VObject_DateTimeParser::parseDuration($endTime); + $endTime = clone $startTime; + $endTime->add($duration); + } else { + $endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime); + } + + if($this->start && $this->start > $endTime) continue; + if($this->end && $this->end < $startTime) continue; + $busyTimes[] = array( + $startTime, + $endTime, + $fbType + ); + + } + + + } + break; + + + + } + + + } + + } + + if ($this->baseObject) { + $calendar = $this->baseObject; + } else { + $calendar = new Sabre_VObject_Component('VCALENDAR'); + $calendar->version = '2.0'; + if (Sabre_DAV_Server::$exposeVersion) { + $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN'; + } else { + $calendar->prodid = '-//SabreDAV//Sabre VObject//EN'; + } + $calendar->calscale = 'GREGORIAN'; + } + + $vfreebusy = new Sabre_VObject_Component('VFREEBUSY'); + $calendar->add($vfreebusy); + + if ($this->start) { + $dtstart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtstart->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); + $vfreebusy->add($dtstart); + } + if ($this->end) { + $dtend = new Sabre_VObject_Property_DateTime('DTEND'); + $dtend->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); + $vfreebusy->add($dtend); + } + $dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP'); + $dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC); + $vfreebusy->add($dtstamp); + + foreach($busyTimes as $busyTime) { + + $busyTime[0]->setTimeZone(new DateTimeZone('UTC')); + $busyTime[1]->setTimeZone(new DateTimeZone('UTC')); + + $prop = new Sabre_VObject_Property( + 'FREEBUSY', + $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z') + ); + $prop['FBTYPE'] = $busyTime[2]; + $vfreebusy->add($prop); + + } + + return $calendar; + + } + +} + diff --git a/dav/SabreDAV/lib/Sabre/VObject/Node.php b/dav/SabreDAV/lib/Sabre/VObject/Node.php new file mode 100644 index 000000000..7701309be --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Node.php @@ -0,0 +1,149 @@ +iterator)) + return $this->iterator; + + return new Sabre_VObject_ElementList(array($this)); + + } + + /** + * Sets the overridden iterator + * + * Note that this is not actually part of the iterator interface + * + * @param Sabre_VObject_ElementList $iterator + * @return void + */ + public function setIterator(Sabre_VObject_ElementList $iterator) { + + $this->iterator = $iterator; + + } + + /* }}} */ + + /* {{{ Countable interface */ + + /** + * Returns the number of elements + * + * @return int + */ + public function count() { + + $it = $this->getIterator(); + return $it->count(); + + } + + /* }}} */ + + /* {{{ ArrayAccess Interface */ + + + /** + * Checks if an item exists through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * @return bool + */ + public function offsetExists($offset) { + + $iterator = $this->getIterator(); + return $iterator->offsetExists($offset); + + } + + /** + * Gets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * @return mixed + */ + public function offsetGet($offset) { + + $iterator = $this->getIterator(); + return $iterator->offsetGet($offset); + + } + + /** + * Sets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * @param mixed $value + * @return void + */ + public function offsetSet($offset,$value) { + + $iterator = $this->getIterator(); + $iterator->offsetSet($offset,$value); + + } + + /** + * Sets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * @return void + */ + public function offsetUnset($offset) { + + $iterator = $this->getIterator(); + $iterator->offsetUnset($offset); + + } + + /* }}} */ + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Parameter.php b/dav/SabreDAV/lib/Sabre/VObject/Parameter.php new file mode 100644 index 000000000..2e39af5f7 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Parameter.php @@ -0,0 +1,84 @@ +name = strtoupper($name); + $this->value = $value; + + } + + /** + * Turns the object back into a serialized blob. + * + * @return string + */ + public function serialize() { + + if (is_null($this->value)) { + return $this->name; + } + $src = array( + '\\', + "\n", + ';', + ',', + ); + $out = array( + '\\\\', + '\n', + '\;', + '\,', + ); + + return $this->name . '=' . str_replace($src, $out, $this->value); + + } + + /** + * Called when this object is being cast to a string + * + * @return string + */ + public function __toString() { + + return $this->value; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/ParseException.php b/dav/SabreDAV/lib/Sabre/VObject/ParseException.php new file mode 100644 index 000000000..1b5e95bf1 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/ParseException.php @@ -0,0 +1,12 @@ + 'Sabre_VObject_Property_DateTime', + 'CREATED' => 'Sabre_VObject_Property_DateTime', + 'DTEND' => 'Sabre_VObject_Property_DateTime', + 'DTSTAMP' => 'Sabre_VObject_Property_DateTime', + 'DTSTART' => 'Sabre_VObject_Property_DateTime', + 'DUE' => 'Sabre_VObject_Property_DateTime', + 'EXDATE' => 'Sabre_VObject_Property_MultiDateTime', + 'LAST-MODIFIED' => 'Sabre_VObject_Property_DateTime', + 'RECURRENCE-ID' => 'Sabre_VObject_Property_DateTime', + 'TRIGGER' => 'Sabre_VObject_Property_DateTime', + ); + + /** + * Creates the new property by name, but in addition will also see if + * there's a class mapped to the property name. + * + * @param string $name + * @param string $value + * @return Sabre_VObject_Property + */ + static public function create($name, $value = null) { + + $name = strtoupper($name); + $shortName = $name; + $group = null; + if (strpos($shortName,'.')!==false) { + list($group, $shortName) = explode('.', $shortName); + } + + if (isset(self::$classMap[$shortName])) { + return new self::$classMap[$shortName]($name, $value); + } else { + return new self($name, $value); + } + + } + + /** + * Creates a new property object + * + * By default this object will iterate over its own children, but this can + * be overridden with the iterator argument + * + * @param string $name + * @param string $value + * @param Sabre_VObject_ElementList $iterator + */ + public function __construct($name, $value = null, $iterator = null) { + + $name = strtoupper($name); + $group = null; + if (strpos($name,'.')!==false) { + list($group, $name) = explode('.', $name); + } + $this->name = $name; + $this->group = $group; + if (!is_null($iterator)) $this->iterator = $iterator; + $this->setValue($value); + + } + + + + /** + * Updates the internal value + * + * @param string $value + * @return void + */ + public function setValue($value) { + + $this->value = $value; + + } + + /** + * Turns the object back into a serialized blob. + * + * @return string + */ + public function serialize() { + + $str = $this->name; + if ($this->group) $str = $this->group . '.' . $this->name; + + if (count($this->parameters)) { + foreach($this->parameters as $param) { + + $str.=';' . $param->serialize(); + + } + } + $src = array( + '\\', + "\n", + ); + $out = array( + '\\\\', + '\n', + ); + $str.=':' . str_replace($src, $out, $this->value); + + $out = ''; + while(strlen($str)>0) { + if (strlen($str)>75) { + $out.= mb_strcut($str,0,75,'utf-8') . "\r\n"; + $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8'); + } else { + $out.=$str . "\r\n"; + $str=''; + break; + } + } + + return $out; + + } + + /** + * Adds a new componenten or element + * + * You can call this method with the following syntaxes: + * + * add(Sabre_VObject_Parameter $element) + * add(string $name, $value) + * + * The first version adds an Parameter + * The second adds a property as a string. + * + * @param mixed $item + * @param mixed $itemValue + * @return void + */ + public function add($item, $itemValue = null) { + + if ($item instanceof Sabre_VObject_Parameter) { + if (!is_null($itemValue)) { + throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject'); + } + $item->parent = $this; + $this->parameters[] = $item; + } elseif(is_string($item)) { + + if (!is_scalar($itemValue) && !is_null($itemValue)) { + throw new InvalidArgumentException('The second argument must be scalar'); + } + $parameter = new Sabre_VObject_Parameter($item,$itemValue); + $parameter->parent = $this; + $this->parameters[] = $parameter; + + } else { + + throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string'); + + } + + } + + /* ArrayAccess interface {{{ */ + + /** + * Checks if an array element exists + * + * @param mixed $name + * @return bool + */ + public function offsetExists($name) { + + if (is_int($name)) return parent::offsetExists($name); + + $name = strtoupper($name); + + foreach($this->parameters as $parameter) { + if ($parameter->name == $name) return true; + } + return false; + + } + + /** + * Returns a parameter, or parameter list. + * + * @param string $name + * @return Sabre_VObject_Element + */ + public function offsetGet($name) { + + if (is_int($name)) return parent::offsetGet($name); + $name = strtoupper($name); + + $result = array(); + foreach($this->parameters as $parameter) { + if ($parameter->name == $name) + $result[] = $parameter; + } + + if (count($result)===0) { + return null; + } elseif (count($result)===1) { + return $result[0]; + } else { + $result[0]->setIterator(new Sabre_VObject_ElementList($result)); + return $result[0]; + } + + } + + /** + * Creates a new parameter + * + * @param string $name + * @param mixed $value + * @return void + */ + public function offsetSet($name, $value) { + + if (is_int($name)) parent::offsetSet($name, $value); + + if (is_scalar($value)) { + if (!is_string($name)) + throw new InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.'); + + $this->offsetUnset($name); + $parameter = new Sabre_VObject_Parameter($name, $value); + $parameter->parent = $this; + $this->parameters[] = $parameter; + + } elseif ($value instanceof Sabre_VObject_Parameter) { + if (!is_null($name)) + throw new InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a Sabre_VObject_Parameter. Add using $array[]=$parameterObject.'); + + $value->parent = $this; + $this->parameters[] = $value; + } else { + throw new InvalidArgumentException('You can only add parameters to the property object'); + } + + } + + /** + * Removes one or more parameters with the specified name + * + * @param string $name + * @return void + */ + public function offsetUnset($name) { + + if (is_int($name)) parent::offsetUnset($name); + $name = strtoupper($name); + + foreach($this->parameters as $key=>$parameter) { + if ($parameter->name == $name) { + $parameter->parent = null; + unset($this->parameters[$key]); + } + + } + + } + + /* }}} */ + + /** + * Called when this object is being cast to a string + * + * @return string + */ + public function __toString() { + + return (string)$this->value; + + } + + /** + * This method is automatically called when the object is cloned. + * Specifically, this will ensure all child elements are also cloned. + * + * @return void + */ + public function __clone() { + + foreach($this->parameters as $key=>$child) { + $this->parameters[$key] = clone $child; + $this->parameters[$key]->parent = $this; + } + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Property/DateTime.php b/dav/SabreDAV/lib/Sabre/VObject/Property/DateTime.php new file mode 100755 index 000000000..fe2372caa --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Property/DateTime.php @@ -0,0 +1,260 @@ +setValue($dt->format('Ymd\\THis')); + $this->offsetUnset('VALUE'); + $this->offsetUnset('TZID'); + $this->offsetSet('VALUE','DATE-TIME'); + break; + case self::UTC : + $dt->setTimeZone(new DateTimeZone('UTC')); + $this->setValue($dt->format('Ymd\\THis\\Z')); + $this->offsetUnset('VALUE'); + $this->offsetUnset('TZID'); + $this->offsetSet('VALUE','DATE-TIME'); + break; + case self::LOCALTZ : + $this->setValue($dt->format('Ymd\\THis')); + $this->offsetUnset('VALUE'); + $this->offsetUnset('TZID'); + $this->offsetSet('VALUE','DATE-TIME'); + $this->offsetSet('TZID', $dt->getTimeZone()->getName()); + break; + case self::DATE : + $this->setValue($dt->format('Ymd')); + $this->offsetUnset('VALUE'); + $this->offsetUnset('TZID'); + $this->offsetSet('VALUE','DATE'); + break; + default : + throw new InvalidArgumentException('You must pass a valid dateType constant'); + + } + $this->dateTime = $dt; + $this->dateType = $dateType; + + } + + /** + * Returns the current DateTime value. + * + * If no value was set, this method returns null. + * + * @return DateTime|null + */ + public function getDateTime() { + + if ($this->dateTime) + return $this->dateTime; + + list( + $this->dateType, + $this->dateTime + ) = self::parseData($this->value, $this); + return $this->dateTime; + + } + + /** + * Returns the type of Date format. + * + * This method returns one of the format constants. If no date was set, + * this method will return null. + * + * @return int|null + */ + public function getDateType() { + + if ($this->dateType) + return $this->dateType; + + list( + $this->dateType, + $this->dateTime, + ) = self::parseData($this->value, $this); + return $this->dateType; + + } + + /** + * Parses the internal data structure to figure out what the current date + * and time is. + * + * The returned array contains two elements: + * 1. A 'DateType' constant (as defined on this class), or null. + * 2. A DateTime object (or null) + * + * @param string|null $propertyValue The string to parse (yymmdd or + * ymmddThhmmss, etc..) + * @param Sabre_VObject_Property|null $property The instance of the + * property we're parsing. + * @return array + */ + static public function parseData($propertyValue, Sabre_VObject_Property $property = null) { + + if (is_null($propertyValue)) { + return array(null, null); + } + + $date = '(?P[1-2][0-9]{3})(?P[0-1][0-9])(?P[0-3][0-9])'; + $time = '(?P[0-2][0-9])(?P[0-5][0-9])(?P[0-5][0-9])'; + $regex = "/^$date(T$time(?PZ)?)?$/"; + + if (!preg_match($regex, $propertyValue, $matches)) { + throw new InvalidArgumentException($propertyValue . ' is not a valid DateTime or Date string'); + } + + if (!isset($matches['hour'])) { + // Date-only + return array( + self::DATE, + new DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00'), + ); + } + + $dateStr = + $matches['year'] .'-' . + $matches['month'] . '-' . + $matches['date'] . ' ' . + $matches['hour'] . ':' . + $matches['minute'] . ':' . + $matches['second']; + + if (isset($matches['isutc'])) { + $dt = new DateTime($dateStr,new DateTimeZone('UTC')); + $dt->setTimeZone(new DateTimeZone('UTC')); + return array( + self::UTC, + $dt + ); + } + + // Finding the timezone. + $tzid = $property['TZID']; + if (!$tzid) { + return array( + self::LOCAL, + new DateTime($dateStr) + ); + } + + try { + // tzid an Olson identifier? + $tz = new DateTimeZone($tzid->value); + } catch (Exception $e) { + + // Not an Olson id, we're going to try to find the information + // through the time zone name map. + $newtzid = Sabre_VObject_WindowsTimezoneMap::lookup($tzid->value); + if (is_null($newtzid)) { + + // Not a well known time zone name either, we're going to try + // to find the information through the VTIMEZONE object. + + // First we find the root object + $root = $property; + while($root->parent) { + $root = $root->parent; + } + + if (isset($root->VTIMEZONE)) { + foreach($root->VTIMEZONE as $vtimezone) { + if (((string)$vtimezone->TZID) == $tzid) { + if (isset($vtimezone->{'X-LIC-LOCATION'})) { + $newtzid = (string)$vtimezone->{'X-LIC-LOCATION'}; + } else { + // No libical location specified. As a last resort we could + // try matching $vtimezone's DST rules against all known + // time zones returned by DateTimeZone::list* + + // TODO + } + } + } + } + } + + try { + $tz = new DateTimeZone($newtzid); + } catch (Exception $e) { + // If all else fails, we use the default PHP timezone + $tz = new DateTimeZone(date_default_timezone_get()); + } + } + $dt = new DateTime($dateStr, $tz); + $dt->setTimeZone($tz); + + return array( + self::LOCALTZ, + $dt + ); + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Property/MultiDateTime.php b/dav/SabreDAV/lib/Sabre/VObject/Property/MultiDateTime.php new file mode 100644 index 000000000..ae53ab6a6 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Property/MultiDateTime.php @@ -0,0 +1,166 @@ +offsetUnset('VALUE'); + $this->offsetUnset('TZID'); + switch($dateType) { + + case Sabre_VObject_Property_DateTime::LOCAL : + $val = array(); + foreach($dt as $i) { + $val[] = $i->format('Ymd\\THis'); + } + $this->setValue(implode(',',$val)); + $this->offsetSet('VALUE','DATE-TIME'); + break; + case Sabre_VObject_Property_DateTime::UTC : + $val = array(); + foreach($dt as $i) { + $i->setTimeZone(new DateTimeZone('UTC')); + $val[] = $i->format('Ymd\\THis\\Z'); + } + $this->setValue(implode(',',$val)); + $this->offsetSet('VALUE','DATE-TIME'); + break; + case Sabre_VObject_Property_DateTime::LOCALTZ : + $val = array(); + foreach($dt as $i) { + $val[] = $i->format('Ymd\\THis'); + } + $this->setValue(implode(',',$val)); + $this->offsetSet('VALUE','DATE-TIME'); + $this->offsetSet('TZID', $dt[0]->getTimeZone()->getName()); + break; + case Sabre_VObject_Property_DateTime::DATE : + $val = array(); + foreach($dt as $i) { + $val[] = $i->format('Ymd'); + } + $this->setValue(implode(',',$val)); + $this->offsetSet('VALUE','DATE'); + break; + default : + throw new InvalidArgumentException('You must pass a valid dateType constant'); + + } + $this->dateTimes = $dt; + $this->dateType = $dateType; + + } + + /** + * Returns the current DateTime value. + * + * If no value was set, this method returns null. + * + * @return array|null + */ + public function getDateTimes() { + + if ($this->dateTimes) + return $this->dateTimes; + + $dts = array(); + + if (!$this->value) { + $this->dateTimes = null; + $this->dateType = null; + return null; + } + + foreach(explode(',',$this->value) as $val) { + list( + $type, + $dt + ) = Sabre_VObject_Property_DateTime::parseData($val, $this); + $dts[] = $dt; + $this->dateType = $type; + } + $this->dateTimes = $dts; + return $this->dateTimes; + + } + + /** + * Returns the type of Date format. + * + * This method returns one of the format constants. If no date was set, + * this method will return null. + * + * @return int|null + */ + public function getDateType() { + + if ($this->dateType) + return $this->dateType; + + if (!$this->value) { + $this->dateTimes = null; + $this->dateType = null; + return null; + } + + $dts = array(); + foreach(explode(',',$this->value) as $val) { + list( + $type, + $dt + ) = Sabre_VObject_Property_DateTime::parseData($val, $this); + $dts[] = $dt; + $this->dateType = $type; + } + $this->dateTimes = $dts; + return $this->dateType; + + } + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/Reader.php b/dav/SabreDAV/lib/Sabre/VObject/Reader.php new file mode 100644 index 000000000..eea73fa3d --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Reader.php @@ -0,0 +1,183 @@ +add(self::readLine($lines)); + + $nextLine = current($lines); + + if ($nextLine===false) + throw new Sabre_VObject_ParseException('Invalid VObject. Document ended prematurely.'); + + } + + // Checking component name of the 'END:' line. + if (substr($nextLine,4)!==$obj->name) { + throw new Sabre_VObject_ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"'); + } + next($lines); + + return $obj; + + } + + // Properties + //$result = preg_match('/(?P[A-Z0-9-]+)(?:;(?P^(?([^:^\"]|\"([^\"]*)\")*))?"; + $regex = "/^(?P$token)$parameters:(?P.*)$/i"; + + $result = preg_match($regex,$line,$matches); + + if (!$result) { + throw new Sabre_VObject_ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format'); + } + + $propertyName = strtoupper($matches['name']); + $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) { + if ($matches[2]==='n' || $matches[2]==='N') { + return "\n"; + } else { + return $matches[2]; + } + }, $matches['value']); + + $obj = Sabre_VObject_Property::create($propertyName, $propertyValue); + + if ($matches['parameters']) { + + foreach(self::readParameters($matches['parameters']) as $param) { + $obj->add($param); + } + + } + + return $obj; + + + } + + /** + * Reads a parameter list from a property + * + * This method returns an array of Sabre_VObject_Parameter + * + * @param string $parameters + * @return array + */ + static private function readParameters($parameters) { + + $token = '[A-Z0-9-]+'; + + $paramValue = '(?P[^\"^;]*|"[^"]*")'; + + $regex = "/(?<=^|;)(?P$token)(=$paramValue(?=$|;))?/i"; + preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER); + + $params = array(); + foreach($matches as $match) { + + $value = isset($match['paramValue'])?$match['paramValue']:null; + + if (isset($value[0])) { + // Stripping quotes, if needed + if ($value[0] === '"') $value = substr($value,1,strlen($value)-2); + } else { + $value = ''; + } + + $value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) { + if ($matches[2]==='n' || $matches[2]==='N') { + return "\n"; + } else { + return $matches[2]; + } + }, $value); + + $params[] = new Sabre_VObject_Parameter($match['paramName'], $value); + + } + + return $params; + + } + + +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/RecurrenceIterator.php b/dav/SabreDAV/lib/Sabre/VObject/RecurrenceIterator.php new file mode 100644 index 000000000..39d1b6990 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/RecurrenceIterator.php @@ -0,0 +1,1024 @@ + 0, + 'MO' => 1, + 'TU' => 2, + 'WE' => 3, + 'TH' => 4, + 'FR' => 5, + 'SA' => 6, + ); + + /** + * Mappings between the day number and english day name. + * + * @var array + */ + private $dayNames = array( + 0 => 'Sunday', + 1 => 'Monday', + 2 => 'Tuesday', + 3 => 'Wednesday', + 4 => 'Thursday', + 5 => 'Friday', + 6 => 'Saturday', + ); + + /** + * If the current iteration of the event is an overriden event, this + * property will hold the VObject + * + * @var Sabre_VObject_Component + */ + private $currentOverriddenEvent; + + /** + * This property may contain the date of the next not-overridden event. + * This date is calculated sometimes a bit early, before overridden events + * are evaluated. + * + * @var DateTime + */ + private $nextDate; + + /** + * Creates the iterator + * + * You should pass a VCALENDAR component, as well as the UID of the event + * we're going to traverse. + * + * @param Sabre_VObject_Component $vcal + * @param string|null $uid + */ + public function __construct(Sabre_VObject_Component $vcal, $uid=null) { + + if (is_null($uid)) { + if ($vcal->name === 'VCALENDAR') { + throw new InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well'); + } + $components = array($vcal); + $uid = (string)$vcal->uid; + } else { + $components = $vcal->select('VEVENT'); + } + foreach($components as $component) { + if ((string)$component->uid == $uid) { + if (isset($component->{'RECURRENCE-ID'})) { + $this->overriddenEvents[$component->DTSTART->getDateTime()->getTimeStamp()] = $component; + $this->overriddenDates[] = $component->{'RECURRENCE-ID'}->getDateTime(); + } else { + $this->baseEvent = $component; + } + } + } + if (!$this->baseEvent) { + throw new InvalidArgumentException('Could not find a base event with uid: ' . $uid); + } + + $this->startDate = clone $this->baseEvent->DTSTART->getDateTime(); + + $this->endDate = null; + if (isset($this->baseEvent->DTEND)) { + $this->endDate = clone $this->baseEvent->DTEND->getDateTime(); + } else { + $this->endDate = clone $this->startDate; + if (isset($this->baseEvent->DURATION)) { + $this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value)); + } + } + $this->currentDate = clone $this->startDate; + + $rrule = (string)$this->baseEvent->RRULE; + + $parts = explode(';', $rrule); + + // If no rrule was specified, we create a default setting + if (!$rrule) { + $this->frequency = 'daily'; + $this->count = 1; + } else foreach($parts as $part) { + + list($key, $value) = explode('=', $part, 2); + + switch(strtoupper($key)) { + + case 'FREQ' : + if (!in_array( + strtolower($value), + array('secondly','minutely','hourly','daily','weekly','monthly','yearly') + )) { + throw new InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value)); + + } + $this->frequency = strtolower($value); + break; + + case 'UNTIL' : + $this->until = Sabre_VObject_DateTimeParser::parse($value); + break; + + case 'COUNT' : + $this->count = (int)$value; + break; + + case 'INTERVAL' : + $this->interval = (int)$value; + break; + + case 'BYSECOND' : + $this->bySecond = explode(',', $value); + break; + + case 'BYMINUTE' : + $this->byMinute = explode(',', $value); + break; + + case 'BYHOUR' : + $this->byHour = explode(',', $value); + break; + + case 'BYDAY' : + $this->byDay = explode(',', strtoupper($value)); + break; + + case 'BYMONTHDAY' : + $this->byMonthDay = explode(',', $value); + break; + + case 'BYYEARDAY' : + $this->byYearDay = explode(',', $value); + break; + + case 'BYWEEKNO' : + $this->byWeekNo = explode(',', $value); + break; + + case 'BYMONTH' : + $this->byMonth = explode(',', $value); + break; + + case 'BYSETPOS' : + $this->bySetPos = explode(',', $value); + break; + + case 'WKST' : + $this->weekStart = strtoupper($value); + break; + + } + + } + + // Parsing exception dates + if (isset($this->baseEvent->EXDATE)) { + foreach($this->baseEvent->EXDATE as $exDate) { + + foreach(explode(',', (string)$exDate) as $exceptionDate) { + + $this->exceptionDates[] = + Sabre_VObject_DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone()); + + } + + } + + } + + } + + /** + * Returns the current item in the list + * + * @return DateTime + */ + public function current() { + + if (!$this->valid()) return null; + return clone $this->currentDate; + + } + + /** + * This method returns the startdate for the current iteration of the + * event. + * + * @return DateTime + */ + public function getDtStart() { + + if (!$this->valid()) return null; + return clone $this->currentDate; + + } + + /** + * This method returns the enddate for the current iteration of the + * event. + * + * @return DateTime + */ + public function getDtEnd() { + + if (!$this->valid()) return null; + $dtEnd = clone $this->currentDate; + $dtEnd->add( $this->startDate->diff( $this->endDate ) ); + return clone $dtEnd; + + } + + /** + * Returns a VEVENT object with the updated start and end date. + * + * Any recurrence information is removed, and this function may return an + * 'overridden' event instead. + * + * This method always returns a cloned instance. + * + * @return Sabre_VObject_Component_VEvent + */ + public function getEventObject() { + + if ($this->currentOverriddenEvent) { + return clone $this->currentOverriddenEvent; + } + $event = clone $this->baseEvent; + unset($event->RRULE); + unset($event->EXDATE); + unset($event->RDATE); + unset($event->EXRULE); + + $event->DTSTART->setDateTime($this->getDTStart(), $event->DTSTART->getDateType()); + if (isset($event->DTEND)) { + $event->DTEND->setDateTime($this->getDtEnd(), $event->DTSTART->getDateType()); + } + if ($this->counter > 0) { + $event->{'RECURRENCE-ID'} = (string)$event->DTSTART; + } + + return $event; + + } + + /** + * Returns the current item number + * + * @return int + */ + public function key() { + + return $this->counter; + + } + + /** + * Whether or not there is a 'next item' + * + * @return bool + */ + public function valid() { + + if (!is_null($this->count)) { + return $this->counter < $this->count; + } + if (!is_null($this->until)) { + return $this->currentDate <= $this->until; + } + return true; + + } + + /** + * Resets the iterator + * + * @return void + */ + public function rewind() { + + $this->currentDate = clone $this->startDate; + $this->counter = 0; + + } + + /** + * This method allows you to quickly go to the next occurrence after the + * specified date. + * + * Note that this checks the current 'endDate', not the 'stardDate'. This + * means that if you forward to January 1st, the iterator will stop at the + * first event that ends *after* January 1st. + * + * @param DateTime $dt + * @return void + */ + public function fastForward(DateTime $dt) { + + while($this->valid() && $this->getDTEnd() < $dt) { + $this->next(); + } + + } + + /** + * Returns true if this recurring event never ends. + * + * @return bool + */ + public function isInfinite() { + + return !$this->count && !$this->until; + + } + + /** + * Goes on to the next iteration + * + * @return void + */ + public function next() { + + /* + if (!is_null($this->count) && $this->counter >= $this->count) { + $this->currentDate = null; + }*/ + + + $previousStamp = $this->currentDate->getTimeStamp(); + + while(true) { + + $this->currentOverriddenEvent = null; + + // If we have a next date 'stored', we use that + if ($this->nextDate) { + $this->currentDate = $this->nextDate; + $currentStamp = $this->currentDate->getTimeStamp(); + $this->nextDate = null; + } else { + + // Otherwise, we calculate it + switch($this->frequency) { + + case 'daily' : + $this->nextDaily(); + break; + + case 'weekly' : + $this->nextWeekly(); + break; + + case 'monthly' : + $this->nextMonthly(); + break; + + case 'yearly' : + $this->nextYearly(); + break; + + } + $currentStamp = $this->currentDate->getTimeStamp(); + + // Checking exception dates + foreach($this->exceptionDates as $exceptionDate) { + if ($this->currentDate == $exceptionDate) { + $this->counter++; + continue 2; + } + } + foreach($this->overriddenDates as $overriddenDate) { + if ($this->currentDate == $overriddenDate) { + continue 2; + } + } + + } + + // Checking overridden events + foreach($this->overriddenEvents as $index=>$event) { + if ($index > $previousStamp && $index <= $currentStamp) { + + // We're moving the 'next date' aside, for later use. + $this->nextDate = clone $this->currentDate; + + $this->currentDate = $event->DTSTART->getDateTime(); + $this->currentOverriddenEvent = $event; + + break; + } + } + + break; + + } + + /* + if (!is_null($this->until)) { + if($this->currentDate > $this->until) { + $this->currentDate = null; + } + }*/ + + $this->counter++; + + } + + /** + * Does the processing for advancing the iterator for daily frequency. + * + * @return void + */ + protected function nextDaily() { + + if (!$this->byDay) { + $this->currentDate->modify('+' . $this->interval . ' days'); + return; + } + + $recurrenceDays = array(); + foreach($this->byDay as $byDay) { + + // The day may be preceeded with a positive (+n) or + // negative (-n) integer. However, this does not make + // sense in 'weekly' so we ignore it here. + $recurrenceDays[] = $this->dayMap[substr($byDay,-2)]; + + } + + do { + + $this->currentDate->modify('+' . $this->interval . ' days'); + + // Current day of the week + $currentDay = $this->currentDate->format('w'); + + } while (!in_array($currentDay, $recurrenceDays)); + + } + + /** + * Does the processing for advancing the iterator for weekly frequency. + * + * @return void + */ + protected function nextWeekly() { + + if (!$this->byDay) { + $this->currentDate->modify('+' . $this->interval . ' weeks'); + return; + } + + $recurrenceDays = array(); + foreach($this->byDay as $byDay) { + + // The day may be preceeded with a positive (+n) or + // negative (-n) integer. However, this does not make + // sense in 'weekly' so we ignore it here. + $recurrenceDays[] = $this->dayMap[substr($byDay,-2)]; + + } + + // Current day of the week + $currentDay = $this->currentDate->format('w'); + + // First day of the week: + $firstDay = $this->dayMap[$this->weekStart]; + + $time = array( + $this->currentDate->format('H'), + $this->currentDate->format('i'), + $this->currentDate->format('s') + ); + + // Increasing the 'current day' until we find our next + // occurrence. + while(true) { + + $currentDay++; + + if ($currentDay>6) { + $currentDay = 0; + } + + // We need to roll over to the next week + if ($currentDay === $firstDay) { + $this->currentDate->modify('+' . $this->interval . ' weeks'); + + // We need to go to the first day of this week, but only if we + // are not already on this first day of this week. + if($this->currentDate->format('w') != $firstDay) { + $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]); + $this->currentDate->setTime($time[0],$time[1],$time[2]); + } + } + + // We have a match + if (in_array($currentDay ,$recurrenceDays)) { + $this->currentDate->modify($this->dayNames[$currentDay]); + $this->currentDate->setTime($time[0],$time[1],$time[2]); + break; + } + + } + + } + + /** + * Does the processing for advancing the iterator for monthly frequency. + * + * @return void + */ + protected function nextMonthly() { + + $currentDayOfMonth = $this->currentDate->format('j'); + if (!$this->byMonthDay && !$this->byDay) { + + // If the current day is higher than the 28th, rollover can + // occur to the next month. We Must skip these invalid + // entries. + if ($currentDayOfMonth < 29) { + $this->currentDate->modify('+' . $this->interval . ' months'); + } else { + $increase = 0; + do { + $increase++; + $tempDate = clone $this->currentDate; + $tempDate->modify('+ ' . ($this->interval*$increase) . ' months'); + } while ($tempDate->format('j') != $currentDayOfMonth); + $this->currentDate = $tempDate; + } + return; + } + + while(true) { + + $occurrences = $this->getMonthlyOccurrences(); + + foreach($occurrences as $occurrence) { + + // The first occurrence thats higher than the current + // day of the month wins. + if ($occurrence > $currentDayOfMonth) { + break 2; + } + + } + + // If we made it all the way here, it means there were no + // valid occurrences, and we need to advance to the next + // month. + $this->currentDate->modify('first day of this month'); + $this->currentDate->modify('+ ' . $this->interval . ' months'); + + // This goes to 0 because we need to start counting at hte + // beginning. + $currentDayOfMonth = 0; + + } + + $this->currentDate->setDate($this->currentDate->format('Y'), $this->currentDate->format('n'), $occurrence); + + } + + /** + * Does the processing for advancing the iterator for yearly frequency. + * + * @return void + */ + protected function nextYearly() { + + if (!$this->byMonth) { + $this->currentDate->modify('+' . $this->interval . ' years'); + return; + } + + $currentMonth = $this->currentDate->format('n'); + $currentYear = $this->currentDate->format('Y'); + $currentDayOfMonth = $this->currentDate->format('j'); + + $advancedToNewMonth = false; + + // If we got a byDay or getMonthDay filter, we must first expand + // further. + if ($this->byDay || $this->byMonthDay) { + + while(true) { + + $occurrences = $this->getMonthlyOccurrences(); + + foreach($occurrences as $occurrence) { + + // The first occurrence that's higher than the current + // day of the month wins. + // If we advanced to the next month or year, the first + // occurrence is always correct. + if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { + break 2; + } + + } + + // If we made it here, it means we need to advance to + // the next month or year. + $currentDayOfMonth = 1; + $advancedToNewMonth = true; + do { + + $currentMonth++; + if ($currentMonth>12) { + $currentYear+=$this->interval; + $currentMonth = 1; + } + } while (!in_array($currentMonth, $this->byMonth)); + + $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); + + } + + // If we made it here, it means we got a valid occurrence + $this->currentDate->setDate($currentYear, $currentMonth, $occurrence); + return; + + } else { + + // no byDay or byMonthDay, so we can just loop through the + // months. + do { + + $currentMonth++; + if ($currentMonth>12) { + $currentYear+=$this->interval; + $currentMonth = 1; + } + } while (!in_array($currentMonth, $this->byMonth)); + $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); + return; + + } + + } + + /** + * Returns all the occurrences for a monthly frequency with a 'byDay' or + * 'byMonthDay' expansion for the current month. + * + * The returned list is an array of integers with the day of month (1-31). + * + * @return array + */ + protected function getMonthlyOccurrences() { + + $startDate = clone $this->currentDate; + + $byDayResults = array(); + + // Our strategy is to simply go through the byDays, advance the date to + // that point and add it to the results. + if ($this->byDay) foreach($this->byDay as $day) { + + $dayName = $this->dayNames[$this->dayMap[substr($day,-2)]]; + + // Dayname will be something like 'wednesday'. Now we need to find + // all wednesdays in this month. + $dayHits = array(); + + $checkDate = clone $startDate; + $checkDate->modify('first day of this month'); + $checkDate->modify($dayName); + + do { + $dayHits[] = $checkDate->format('j'); + $checkDate->modify('next ' . $dayName); + } while ($checkDate->format('n') === $startDate->format('n')); + + // So now we have 'all wednesdays' for month. It is however + // possible that the user only really wanted the 1st, 2nd or last + // wednesday. + if (strlen($day)>2) { + $offset = (int)substr($day,0,-2); + + if ($offset>0) { + // It is possible that the day does not exist, such as a + // 5th or 6th wednesday of the month. + if (isset($dayHits[$offset-1])) { + $byDayResults[] = $dayHits[$offset-1]; + } + } else { + + // if it was negative we count from the end of the array + $byDayResults[] = $dayHits[count($dayHits) + $offset]; + } + } else { + // There was no counter (first, second, last wednesdays), so we + // just need to add the all to the list). + $byDayResults = array_merge($byDayResults, $dayHits); + + } + + } + + $byMonthDayResults = array(); + if ($this->byMonthDay) foreach($this->byMonthDay as $monthDay) { + + // Removing values that are out of range for this month + if ($monthDay > $startDate->format('t') || + $monthDay < 0-$startDate->format('t')) { + continue; + } + if ($monthDay>0) { + $byMonthDayResults[] = $monthDay; + } else { + // Negative values + $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay; + } + } + + // If there was just byDay or just byMonthDay, they just specify our + // (almost) final list. If both were provided, then byDay limits the + // list. + if ($this->byMonthDay && $this->byDay) { + $result = array_intersect($byMonthDayResults, $byDayResults); + } elseif ($this->byMonthDay) { + $result = $byMonthDayResults; + } else { + $result = $byDayResults; + } + $result = array_unique($result); + sort($result, SORT_NUMERIC); + + // The last thing that needs checking is the BYSETPOS. If it's set, it + // means only certain items in the set survive the filter. + if (!$this->bySetPos) { + return $result; + } + + $filteredResult = array(); + foreach($this->bySetPos as $setPos) { + + if ($setPos<0) { + $setPos = count($result)-($setPos+1); + } + if (isset($result[$setPos-1])) { + $filteredResult[] = $result[$setPos-1]; + } + } + + sort($filteredResult, SORT_NUMERIC); + return $filteredResult; + + } + + +} + diff --git a/dav/SabreDAV/lib/Sabre/VObject/Version.php b/dav/SabreDAV/lib/Sabre/VObject/Version.php new file mode 100644 index 000000000..2617c7b12 --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/Version.php @@ -0,0 +1,24 @@ +'Australia/Darwin', + 'AUS Eastern Standard Time'=>'Australia/Sydney', + 'Afghanistan Standard Time'=>'Asia/Kabul', + 'Alaskan Standard Time'=>'America/Anchorage', + 'Arab Standard Time'=>'Asia/Riyadh', + 'Arabian Standard Time'=>'Asia/Dubai', + 'Arabic Standard Time'=>'Asia/Baghdad', + 'Argentina Standard Time'=>'America/Buenos_Aires', + 'Armenian Standard Time'=>'Asia/Yerevan', + 'Atlantic Standard Time'=>'America/Halifax', + 'Azerbaijan Standard Time'=>'Asia/Baku', + 'Azores Standard Time'=>'Atlantic/Azores', + 'Bangladesh Standard Time'=>'Asia/Dhaka', + 'Canada Central Standard Time'=>'America/Regina', + 'Cape Verde Standard Time'=>'Atlantic/Cape_Verde', + 'Caucasus Standard Time'=>'Asia/Yerevan', + 'Cen. Australia Standard Time'=>'Australia/Adelaide', + 'Central America Standard Time'=>'America/Guatemala', + 'Central Asia Standard Time'=>'Asia/Almaty', + 'Central Brazilian Standard Time'=>'America/Cuiaba', + 'Central Europe Standard Time'=>'Europe/Budapest', + 'Central European Standard Time'=>'Europe/Warsaw', + 'Central Pacific Standard Time'=>'Pacific/Guadalcanal', + 'Central Standard Time'=>'America/Chicago', + 'Central Standard Time (Mexico)'=>'America/Mexico_City', + 'China Standard Time'=>'Asia/Shanghai', + 'Dateline Standard Time'=>'Etc/GMT+12', + 'E. Africa Standard Time'=>'Africa/Nairobi', + 'E. Australia Standard Time'=>'Australia/Brisbane', + 'E. Europe Standard Time'=>'Europe/Minsk', + 'E. South America Standard Time'=>'America/Sao_Paulo', + 'Eastern Standard Time'=>'America/New_York', + 'Egypt Standard Time'=>'Africa/Cairo', + 'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg', + 'FLE Standard Time'=>'Europe/Kiev', + 'Fiji Standard Time'=>'Pacific/Fiji', + 'GMT Standard Time'=>'Europe/London', + 'GTB Standard Time'=>'Europe/Istanbul', + 'Georgian Standard Time'=>'Asia/Tbilisi', + 'Greenland Standard Time'=>'America/Godthab', + 'Greenwich Standard Time'=>'Atlantic/Reykjavik', + 'Hawaiian Standard Time'=>'Pacific/Honolulu', + 'India Standard Time'=>'Asia/Calcutta', + 'Iran Standard Time'=>'Asia/Tehran', + 'Israel Standard Time'=>'Asia/Jerusalem', + 'Jordan Standard Time'=>'Asia/Amman', + 'Kamchatka Standard Time'=>'Asia/Kamchatka', + 'Korea Standard Time'=>'Asia/Seoul', + 'Magadan Standard Time'=>'Asia/Magadan', + 'Mauritius Standard Time'=>'Indian/Mauritius', + 'Mexico Standard Time'=>'America/Mexico_City', + 'Mexico Standard Time 2'=>'America/Chihuahua', + 'Mid-Atlantic Standard Time'=>'Etc/GMT+2', + 'Middle East Standard Time'=>'Asia/Beirut', + 'Montevideo Standard Time'=>'America/Montevideo', + 'Morocco Standard Time'=>'Africa/Casablanca', + 'Mountain Standard Time'=>'America/Denver', + 'Mountain Standard Time (Mexico)'=>'America/Chihuahua', + 'Myanmar Standard Time'=>'Asia/Rangoon', + 'N. Central Asia Standard Time'=>'Asia/Novosibirsk', + 'Namibia Standard Time'=>'Africa/Windhoek', + 'Nepal Standard Time'=>'Asia/Katmandu', + 'New Zealand Standard Time'=>'Pacific/Auckland', + 'Newfoundland Standard Time'=>'America/St_Johns', + 'North Asia East Standard Time'=>'Asia/Irkutsk', + 'North Asia Standard Time'=>'Asia/Krasnoyarsk', + 'Pacific SA Standard Time'=>'America/Santiago', + 'Pacific Standard Time'=>'America/Los_Angeles', + 'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel', + 'Pakistan Standard Time'=>'Asia/Karachi', + 'Paraguay Standard Time'=>'America/Asuncion', + 'Romance Standard Time'=>'Europe/Paris', + 'Russian Standard Time'=>'Europe/Moscow', + 'SA Eastern Standard Time'=>'America/Cayenne', + 'SA Pacific Standard Time'=>'America/Bogota', + 'SA Western Standard Time'=>'America/La_Paz', + 'SE Asia Standard Time'=>'Asia/Bangkok', + 'Samoa Standard Time'=>'Pacific/Apia', + 'Singapore Standard Time'=>'Asia/Singapore', + 'South Africa Standard Time'=>'Africa/Johannesburg', + 'Sri Lanka Standard Time'=>'Asia/Colombo', + 'Syria Standard Time'=>'Asia/Damascus', + 'Taipei Standard Time'=>'Asia/Taipei', + 'Tasmania Standard Time'=>'Australia/Hobart', + 'Tokyo Standard Time'=>'Asia/Tokyo', + 'Tonga Standard Time'=>'Pacific/Tongatapu', + 'US Eastern Standard Time'=>'America/Indianapolis', + 'US Mountain Standard Time'=>'America/Phoenix', + 'UTC'=>'Etc/GMT', + 'UTC+12'=>'Etc/GMT-12', + 'UTC-02'=>'Etc/GMT+2', + 'UTC-11'=>'Etc/GMT+11', + 'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar', + 'Venezuela Standard Time'=>'America/Caracas', + 'Vladivostok Standard Time'=>'Asia/Vladivostok', + 'W. Australia Standard Time'=>'Australia/Perth', + 'W. Central Africa Standard Time'=>'Africa/Lagos', + 'W. Europe Standard Time'=>'Europe/Berlin', + 'West Asia Standard Time'=>'Asia/Tashkent', + 'West Pacific Standard Time'=>'Pacific/Port_Moresby', + 'Yakutsk Standard Time'=>'Asia/Yakutsk', + ); + + static public function lookup($tzid) { + return isset(self::$map[$tzid]) ? self::$map[$tzid] : null; + } +} diff --git a/dav/SabreDAV/lib/Sabre/VObject/includes.php b/dav/SabreDAV/lib/Sabre/VObject/includes.php new file mode 100644 index 000000000..e7f57a06f --- /dev/null +++ b/dav/SabreDAV/lib/Sabre/VObject/includes.php @@ -0,0 +1,39 @@ +pdo); + $this->assertTrue($backend instanceof Sabre_CalDAV_Backend_PDO); + + } + + /** + * @depends testConstruct + */ + function testGetCalendarsForUserNoCalendars() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $calendars = $backend->getCalendarsForUser('principals/user2'); + $this->assertEquals(array(),$calendars); + + } + + /** + * @depends testConstruct + */ + function testCreateCalendarAndFetch() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array( + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet(array('VEVENT')), + '{DAV:}displayname' => 'Hello!', + )); + $calendars = $backend->getCalendarsForUser('principals/user2'); + + $elementCheck = array( + 'id' => $returnedId, + 'uri' => 'somerandomid', + '{DAV:}displayname' => 'Hello!', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => '', + ); + + $this->assertInternalType('array',$calendars); + $this->assertEquals(1,count($calendars)); + + foreach($elementCheck as $name=>$value) { + + $this->assertArrayHasKey($name, $calendars[0]); + $this->assertEquals($value,$calendars[0][$name]); + + } + + } + + /** + * @depends testConstruct + */ + function testUpdateCalendarAndFetch() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2','somerandomid',array()); + + // Updating the calendar + $result = $backend->updateCalendar($newId,array( + '{DAV:}displayname' => 'myCalendar', + )); + + // Verifying the result of the update + $this->assertEquals(true, $result); + + // Fetching all calendars from this user + $calendars = $backend->getCalendarsForUser('principals/user2'); + + // Checking if all the information is still correct + $elementCheck = array( + 'id' => $newId, + 'uri' => 'somerandomid', + '{DAV:}displayname' => 'myCalendar', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => '', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => '', + '{http://calendarserver.org/ns/}getctag' => '2', + ); + + $this->assertInternalType('array',$calendars); + $this->assertEquals(1,count($calendars)); + + foreach($elementCheck as $name=>$value) { + + $this->assertArrayHasKey($name, $calendars[0]); + $this->assertEquals($value,$calendars[0][$name]); + + } + + } + + /** + * @depends testUpdateCalendarAndFetch + */ + function testUpdateCalendarUnknownProperty() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2','somerandomid',array()); + + // Updating the calendar + $result = $backend->updateCalendar($newId,array( + '{DAV:}displayname' => 'myCalendar', + '{DAV:}yourmom' => 'wittycomment', + )); + + // Verifying the result of the update + $this->assertEquals(array( + '403' => array('{DAV:}yourmom' => null), + '424' => array('{DAV:}displayname' => null), + ), $result); + + } + + /** + * @depends testCreateCalendarAndFetch + */ + function testDeleteCalendar() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array( + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet(array('VEVENT')), + '{DAV:}displayname' => 'Hello!', + )); + + $backend->deleteCalendar($returnedId); + + $calendars = $backend->getCalendarsForUser('principals/user2'); + $this->assertEquals(array(),$calendars); + + } + + /** + * @depends testCreateCalendarAndFetch + * @expectedException Sabre_DAV_Exception + */ + function testCreateCalendarIncorrectComponentSet() {; + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2','somerandomid',array( + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => 'blabla', + )); + + } + + function testCreateCalendarObject() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); + $this->assertEquals(array( + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('20120101'), + 'lastoccurence' => strtotime('20120101')+(3600*24), + 'componenttype' => 'VEVENT', + ), $result->fetch(PDO::FETCH_ASSOC)); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectNoComponent() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectDuration() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nDURATION:P2D\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); + $this->assertEquals(array( + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('20120101'), + 'lastoccurence' => strtotime('20120101')+(3600*48), + 'componenttype' => 'VEVENT', + ), $result->fetch(PDO::FETCH_ASSOC)); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectNoDTEND() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); + $this->assertEquals(array( + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('2012-01-01 10:00:00'), + 'lastoccurence' => strtotime('2012-01-01 10:00:00'), + 'componenttype' => 'VEVENT', + ), $result->fetch(PDO::FETCH_ASSOC)); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectInfiniteReccurence() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nRRULE:FREQ=DAILY\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); + $this->assertEquals(array( + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('2012-01-01 10:00:00'), + 'lastoccurence' => strtotime(Sabre_CalDAV_Backend_PDO::MAX_DATE), + 'componenttype' => 'VEVENT', + ), $result->fetch(PDO::FETCH_ASSOC)); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectEndingReccurence() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND;VALUE=DATE-TIME:20120101T110000Z\r\nRRULE:FREQ=DAILY;COUNT=1000\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); + $this->assertEquals(array( + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('2012-01-01 10:00:00'), + 'lastoccurence' => strtotime('2012-01-01 11:00:00') + (3600 * 24 * 999), + 'componenttype' => 'VEVENT', + ), $result->fetch(PDO::FETCH_ASSOC)); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectTask() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nDUE;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); + $this->assertEquals(array( + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => null, + 'lastoccurence' => null, + 'componenttype' => 'VTODO', + ), $result->fetch(PDO::FETCH_ASSOC)); + + } + + /** + * @depends testCreateCalendarObject + */ + function testGetCalendarObjects() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $data = $backend->getCalendarObjects($returnedId,'random-id'); + + $this->assertEquals(1, count($data)); + $data = $data[0]; + + $this->assertEquals($returnedId, $data['calendarid']); + $this->assertEquals('random-id', $data['uri']); + $this->assertEquals(strlen($object),$data['size']); + + + } + + /** + * @depends testCreateCalendarObject + */ + function testUpdateCalendarObject() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $object2 = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20130101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + $backend->updateCalendarObject($returnedId, 'random-id', $object2); + + $data = $backend->getCalendarObject($returnedId,'random-id'); + + $this->assertEquals($object2, $data['calendardata']); + $this->assertEquals($returnedId, $data['calendarid']); + $this->assertEquals('random-id', $data['uri']); + + + } + + /** + * @depends testCreateCalendarObject + */ + function testDeleteCalendarObject() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + $backend->deleteCalendarObject($returnedId, 'random-id'); + + $data = $backend->getCalendarObject($returnedId,'random-id'); + $this->assertNull($data); + + } + + function testCalendarQueryNoResult() { + + $abstract = new Sabre_CalDAV_Backend_PDO($this->pdo); + $filters = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VJOURNAL', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + + $this->assertEquals(array( + ), $abstract->calendarQuery(1, $filters)); + + } + + function testCalendarQueryTodo() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VTODO', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + + $this->assertEquals(array( + "todo", + ), $backend->calendarQuery(1, $filters)); + + } + function testCalendarQueryTodoNotMatch() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VTODO', + 'comp-filters' => array(), + 'prop-filters' => array( + array( + 'name' => 'summary', + 'text-match' => null, + 'time-range' => null, + 'param-filters' => array(), + 'is-not-defined' => false, + ), + ), + 'is-not-defined' => false, + 'time-range' => null, + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + + $this->assertEquals(array( + ), $backend->calendarQuery(1, $filters)); + + } + + function testCalendarQueryNoFilter() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + + $result = $backend->calendarQuery(1, $filters); + $this->assertTrue(in_array('todo', $result)); + $this->assertTrue(in_array('event', $result)); + + } + + function testCalendarQueryTimeRange() { + + $backend = new Sabre_CalDAV_Backend_PDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('20120103'), + 'end' => new DateTime('20120104'), + ), + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + + $this->assertEquals(array( + "event2", + ), $backend->calendarQuery(1, $filters)); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Backend/AbstractTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/AbstractTest.php new file mode 100644 index 000000000..5e393f5f2 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/AbstractTest.php @@ -0,0 +1,86 @@ +assertEquals(false, $abstract->updateCalendar('randomid', array('{DAV:}displayname' => 'anything'))); + + } + + function testCalendarQuery() { + + $abstract = new Sabre_CalDAV_Backend_AbstractMock(); + $filters = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + + $this->assertEquals(array( + 'event1.ics', + ), $abstract->calendarQuery(1, $filters)); + + } + +} + +class Sabre_CalDAV_Backend_AbstractMock extends Sabre_CalDAV_Backend_Abstract { + + function getCalendarsForUser($principalUri) { } + function createCalendar($principalUri,$calendarUri,array $properties) { } + function deleteCalendar($calendarId) { } + function getCalendarObjects($calendarId) { + + return array( + array( + 'id' => 1, + 'calendarid' => 1, + 'uri' => 'event1.ics', + ), + array( + 'id' => 2, + 'calendarid' => 1, + 'uri' => 'task1.ics', + ), + ); + + } + function getCalendarObject($calendarId,$objectUri) { + + switch($objectUri) { + + case 'event1.ics' : + return array( + 'id' => 1, + 'calendarid' => 1, + 'uri' => 'event1.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + ); + case 'task1.ics' : + return array( + 'id' => 1, + 'calendarid' => 1, + 'uri' => 'event1.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", + ); + + } + + } + function createCalendarObject($calendarId,$objectUri,$calendarData) { } + function updateCalendarObject($calendarId,$objectUri,$calendarData) { } + function deleteCalendarObject($calendarId,$objectUri) { } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Backend/Mock.php b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/Mock.php new file mode 100644 index 000000000..511dd9fd2 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/Mock.php @@ -0,0 +1,230 @@ +calendars = $calendars; + $this->calendarData = $calendarData; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principalUri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + function getCalendarsForUser($principalUri) { + + $r = array(); + foreach($this->calendars as $row) { + if ($row['principaluri'] == $principalUri) { + $r[] = $row; + } + } + + return $r; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar. + * + * This function must return a server-wide unique id that can be used + * later to reference the calendar. + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return string|int + */ + function createCalendar($principalUri,$calendarUri,array $properties) { + + throw new Exception('Not implemented'); + + } + + /** + * Updates properties on this node, + * + * The properties array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existent property is always successful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $properties + * @return bool|array + */ + public function updateCalendar($calendarId, array $properties) { + + return false; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + public function deleteCalendar($calendarId) { + + throw new Exception('Not implemented'); + + } + + /** + * Returns all calendar objects within a calendar object. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * calendarid - The calendarid as it was passed to this function. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * @param string $calendarId + * @return array + */ + public function getCalendarObjects($calendarId) { + + if (!isset($this->calendarData[$calendarId])) + return array(); + + $objects = $this->calendarData[$calendarId]; + foreach($objects as $uri => &$object) { + $object['calendarid'] = $calendarId; + $object['uri'] = $uri; + + } + return $objects; + + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + function getCalendarObject($calendarId,$objectUri) { + + if (!isset($this->calendarData[$calendarId][$objectUri])) { + throw new Sabre_DAV_Exception_NotFound('Object could not be found'); + } + $object = $this->calendarData[$calendarId][$objectUri]; + $object['calendarid'] = $calendarId; + $object['uri'] = $objectUri; + return $object; + + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + function createCalendarObject($calendarId,$objectUri,$calendarData) { + + $this->calendarData[$calendarId][$objectUri] = array( + 'calendardata' => $calendarData, + 'calendarid' => $calendarId, + 'uri' => $objectUri, + ); + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + function updateCalendarObject($calendarId,$objectUri,$calendarData) { + + $this->calendarData[$calendarId][$objectUri] = array( + 'calendardata' => $calendarData, + 'calendarid' => $calendarId, + 'uri' => $objectUri, + ); + + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + function deleteCalendarObject($calendarId,$objectUri) { + + throw new Exception('Not implemented'); + + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php new file mode 100644 index 000000000..8898cdec8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php @@ -0,0 +1,37 @@ +markTestSkipped('MySQL driver is not available, or not properly configured'); + $pdo = Sabre_TestUtil::getMySQLDB(); + if (!$pdo) $this->markTestSkipped('Could not connect to mysql database'); + + $pdo->query('DROP TABLE IF EXISTS calendarobjects, calendars'); + + $queries = explode( + ';', + file_get_contents(__DIR__ . '/../../../../examples/sql/mysql.calendars.sql') + ); + + foreach($queries as $query) { + $query = trim($query," \r\n\t"); + if ($query) + $pdo->exec($query); + } + $this->pdo = $pdo; + + } + + function teardown() { + + $this->pdo = null; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php new file mode 100644 index 000000000..8b7045df3 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php @@ -0,0 +1,21 @@ +markTestSkipped('SQLite driver is not available'); + $this->pdo = Sabre_CalDAV_TestUtil::getSQLiteDB(); + + } + + function teardown() { + + $this->pdo = null; + unlink(SABRE_TEMPDIR . '/testdb.sqlite'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/CalendarObjectTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarObjectTest.php new file mode 100644 index 000000000..3a10a4a9e --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarObjectTest.php @@ -0,0 +1,360 @@ +markTestSkipped('SQLite driver is not available'); + $this->backend = Sabre_CalDAV_TestUtil::getBackend(); + $this->principalBackend = new Sabre_DAVACL_MockPrincipalBackend; + + $calendars = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(2,count($calendars)); + $this->calendar = new Sabre_CalDAV_Calendar($this->principalBackend,$this->backend, $calendars[0]); + + } + + function teardown() { + + unset($this->calendar); + unset($this->backend); + + } + + function testSetup() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $this->assertInternalType('string',$children[0]->getName()); + $this->assertInternalType('string',$children[0]->get()); + $this->assertInternalType('string',$children[0]->getETag()); + $this->assertEquals('text/calendar; charset=utf-8', $children[0]->getContentType()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg1() { + + $obj = new Sabre_CalDAV_CalendarObject( + new Sabre_CalDAV_Backend_Mock(array(),array()), + array(), + array() + ); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg2() { + + $obj = new Sabre_CalDAV_CalendarObject( + new Sabre_CalDAV_Backend_Mock(array(),array()), + array(), + array('calendarid' => '1') + ); + + } + + /** + * @depends testSetup + */ + function testPut() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + $newData = Sabre_CalDAV_TestUtil::getTestCalendarData(); + + $children[0]->put($newData); + $this->assertEquals($newData, $children[0]->get()); + + } + + /** + * @depends testSetup + */ + function testPutStream() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + $newData = Sabre_CalDAV_TestUtil::getTestCalendarData(); + + $stream = fopen('php://temp','r+'); + fwrite($stream, $newData); + rewind($stream); + $children[0]->put($stream); + $this->assertEquals($newData, $children[0]->get()); + + } + + + /** + * @depends testSetup + */ + function testDelete() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + $obj->delete(); + + $children2 = $this->calendar->getChildren(); + $this->assertEquals(count($children)-1, count($children2)); + + } + + /** + * @depends testSetup + */ + function testGetLastModified() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + + $lastMod = $obj->getLastModified(); + $this->assertTrue(is_int($lastMod) || ctype_digit($lastMod)); + + } + + /** + * @depends testSetup + */ + function testGetSize() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + + $size = $obj->getSize(); + $this->assertInternalType('int', $size); + + } + + function testGetOwner() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + $this->assertEquals('principals/user1', $obj->getOwner()); + + } + + function testGetGroup() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + $this->assertNull($obj->getGroup()); + + } + + function testGetACL() { + + $expected = array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ), + ); + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + $this->assertEquals($expected, $obj->getACL()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetACL() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + $obj->setACL(array()); + + } + + function testGet() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + $obj = $children[0]; + + $expected = "BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//iCal 4.0.1//EN +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Asia/Seoul +BEGIN:DAYLIGHT +TZOFFSETFROM:+0900 +RRULE:FREQ=YEARLY;UNTIL=19880507T150000Z;BYMONTH=5;BYDAY=2SU +DTSTART:19870510T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+1000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1000 +DTSTART:19881009T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+0900 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20100225T154229Z +UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 +TRANSP:TRANSPARENT +SUMMARY:Something here +DTSTAMP:20100228T130202Z +DTSTART;TZID=Asia/Seoul:20100223T060000 +DTEND;TZID=Asia/Seoul:20100223T070000 +ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com +SEQUENCE:2 +END:VEVENT +END:VCALENDAR"; + + + + $this->assertEquals($expected, $obj->get()); + + } + + function testGetRefetch() { + + $backend = new Sabre_CalDAV_Backend_Mock(array(), array( + 1 => array( + 'foo' => array( + 'calendardata' => 'foo', + 'uri' => 'foo' + ), + ) + )); + $obj = new Sabre_CalDAV_CalendarObject($backend, array(), array('calendarid' => 1, 'uri' => 'foo')); + + $this->assertEquals('foo', $obj->get()); + + } + + function testGetEtag1() { + + $objectInfo = array( + 'calendardata' => 'foo', + 'uri' => 'foo', + 'etag' => 'bar', + 'calendarid' => 1 + ); + + $backend = new Sabre_CalDAV_Backend_Mock(array(), array()); + $obj = new Sabre_CalDAV_CalendarObject($backend, array(), $objectInfo); + + $this->assertEquals('bar', $obj->getETag()); + + } + + function testGetEtag2() { + + $objectInfo = array( + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ); + + $backend = new Sabre_CalDAV_Backend_Mock(array(), array()); + $obj = new Sabre_CalDAV_CalendarObject($backend, array(), $objectInfo); + + $this->assertEquals('"' . md5('foo') . '"', $obj->getETag()); + + } + + function testGetSupportedPrivilegesSet() { + + $objectInfo = array( + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ); + + $backend = new Sabre_CalDAV_Backend_Mock(array(), array()); + $obj = new Sabre_CalDAV_CalendarObject($backend, array(), $objectInfo); + $this->assertNull($obj->getSupportedPrivilegeSet()); + + } + + function testGetSize1() { + + $objectInfo = array( + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ); + + $backend = new Sabre_CalDAV_Backend_Mock(array(), array()); + $obj = new Sabre_CalDAV_CalendarObject($backend, array(), $objectInfo); + $this->assertEquals(3, $obj->getSize()); + + } + + function testGetSize2() { + + $objectInfo = array( + 'uri' => 'foo', + 'calendarid' => 1, + 'size' => 4, + ); + + $backend = new Sabre_CalDAV_Backend_Mock(array(), array()); + $obj = new Sabre_CalDAV_CalendarObject($backend, array(), $objectInfo); + $this->assertEquals(4, $obj->getSize()); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryParserTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryParserTest.php new file mode 100644 index 000000000..fa3734695 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryParserTest.php @@ -0,0 +1,537 @@ + + +' . implode("\n", $xml) . ' +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + + $q = new Sabre_CalDAV_CalendarQueryParser($dom); + $q->parse(); + return $q->filters; + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testNoFilter() { + + $xml = array(); + $this->parse($xml); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testTwoCompFilter() { + + $xml = array( + '', + ' ', + ' ', + '' + ); + $this->parse($xml); + + } + + function testBasicFilter() { + + $xml = array( + '', + ' ', + '' + ); + $result = $this->parse($xml); + + $expected = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => false + ); + + $this->assertEquals( + $expected, + $result + ); + + } + + function testCompIsNotDefined() { + + $xml = array( + '', + ' ', + ' ', + ' ', + ' ', + ' ', + '' + ); + $result = $this->parse($xml); + + $expected = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => true, + 'time-range' => false + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => false + ); + + $this->assertEquals( + $expected, + $result + ); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testCompTimeRangeOnVCALENDAR() { + + $xml = array( + '', + ' ', + ' ', + ' ', + '' + ); + $result = $this->parse($xml); + + } + + function testCompTimeRange() { + + $xml = array( + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '' + ); + $result = $this->parse($xml); + + $expected = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2011-01-01 00:00:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-12-31 23:59:59', new DateTimeZone('GMT')), + ), + ), + array( + 'name' => 'VTODO', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2011-01-01 00:00:00', new DateTimeZone('GMT')), + 'end' => null, + ), + ), + array( + 'name' => 'VJOURNAL', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => null, + 'end' => new DateTime('2011-12-31 23:59:59', new DateTimeZone('GMT')), + ), + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => false + ); + + $this->assertEquals( + $expected, + $result + ); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testCompTimeRangeBadRange() { + + $xml = array( + '', + ' ', + ' ', + ' ', + ' ', + ' ', + '' + ); + $this->parse($xml); + + } + + function testProp() { + + $xml = array( + '', + ' ', + ' ', + ' ', + ' vacation', + ' ', + ' ', + ' ', + '' + ); + $result = $this->parse($xml); + + $expected = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'comp-filters' => array(), + 'prop-filters' => array( + array( + 'name' => 'SUMMARY', + 'is-not-defined' => false, + 'param-filters' => array(), + 'text-match' => array( + 'negate-condition' => false, + 'collation' => 'i;ascii-casemap', + 'value' => 'vacation', + ), + 'time-range' => null, + ), + ), + 'time-range' => null, + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => false + ); + + $this->assertEquals( + $expected, + $result + ); + + } + + function testComplex() { + + $xml = array( + '', + ' ', + ' ', + ' ', + ' vacation', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' DATE', + ' ', + ' ', + ' ', + ' ', + '' + ); + $result = $this->parse($xml); + + $expected = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'comp-filters' => array(), + 'prop-filters' => array( + array( + 'name' => 'SUMMARY', + 'is-not-defined' => false, + 'param-filters' => array(), + 'text-match' => array( + 'negate-condition' => false, + 'collation' => 'i;unicode-casemap', + 'value' => 'vacation', + ), + 'time-range' => null, + ), + array( + 'name' => 'DTSTAMP', + 'is-not-defined' => false, + 'param-filters' => array(), + 'text-match' => null, + 'time-range' => array( + 'start' => new DateTime('2011-07-04 00:00:00', new DateTimeZone('GMT')), + 'end' => null, + ), + ), + array( + 'name' => 'ORGANIZER', + 'is-not-defined' => true, + 'param-filters' => array(), + 'text-match' => null, + 'time-range' => null, + ), + array( + 'name' => 'DTSTART', + 'is-not-defined' => false, + 'param-filters' => array( + array( + 'name' => 'VALUE', + 'is-not-defined' => false, + 'text-match' => array( + 'negate-condition' => true, + 'value' => 'DATE', + 'collation' => 'i;ascii-casemap', + ), + ), + ), + 'text-match' => null, + 'time-range' => null, + ), + ), + 'time-range' => null, + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => false + ); + + $this->assertEquals( + $expected, + $result + ); + + } + + function testOther1() { + + // This body was exactly sent to us from the sabredav mailing list. Checking if this parses correctly. + + $body = << + + + + + + + + + + + + + +BLA; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $q = new Sabre_CalDAV_CalendarQueryParser($dom); + $q->parse(); + + $this->assertEquals(array( + '{urn:ietf:params:xml:ns:caldav}calendar-data', + '{DAV:}getetag', + ), $q->requestedProperties); + + $expectedFilters = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'time-range' => array( + 'start' => new DateTime('2009-01-01 00:00:00', new DateTimeZone('UTC')), + 'end' => new DateTime('2012-12-02 00:00:00', new DateTimeZone('UTC')), + ), + 'is-not-defined' => false, + ), + ), + 'prop-filters' => array(), + 'time-range' => null, + 'is-not-defined' => false, + ); + + $this->assertEquals($expectedFilters, $q->filters); + + } + + function testExpand() { + + $xml = array( + '', + ' ', + ' ', + ' ', + '', + '', + ' ', + '' + ); + + $xml = +' + +' . implode("\n", $xml) . ' +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $q = new Sabre_CalDAV_CalendarQueryParser($dom); + $q->parse(); + + + $expected = array( + 'name' => 'VCALENDAR', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => false + ); + + $this->assertEquals( + $expected, + $q->filters + ); + + $this->assertEquals(array( + '{urn:ietf:params:xml:ns:caldav}calendar-data', + ), $q->requestedProperties); + + $this->assertEquals( + array( + 'start' => new DateTime('2011-01-01 00:00:00', new DateTimeZone('UTC')), + 'end' => new DateTime('2012-01-01 00:00:00', new DateTimeZone('UTC')), + ), + $q->expand + ); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testExpandNoStart() { + + $xml = array( + '', + ' ', + ' ', + ' ', + '', + '', + ' ', + '' + ); + + $xml = +' + +' . implode("\n", $xml) . ' +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $q = new Sabre_CalDAV_CalendarQueryParser($dom); + $q->parse(); + + } + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testExpandNoEnd() { + + $xml = array( + '', + ' ', + ' ', + ' ', + '', + '', + ' ', + '' + ); + + $xml = +' + +' . implode("\n", $xml) . ' +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $q = new Sabre_CalDAV_CalendarQueryParser($dom); + $q->parse(); + + } + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testExpandBadTimes() { + + $xml = array( + '', + ' ', + ' ', + ' ', + '', + '', + ' ', + '' + ); + + $xml = +' + +' . implode("\n", $xml) . ' +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $q = new Sabre_CalDAV_CalendarQueryParser($dom); + $q->parse(); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryVAlarmTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryVAlarmTest.php new file mode 100644 index 000000000..3677198c6 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryVAlarmTest.php @@ -0,0 +1,115 @@ +RRULE = 'FREQ=MONTHLY'; + $vevent->DTSTART = '20120101T120000Z'; + $vevent->UID = 'bla'; + + $valarm = Sabre_VObject_Component::create('VALARM'); + $valarm->TRIGGER = '-P15D'; + $vevent->add($valarm); + + $vcalendar = Sabre_VObject_Component::create('VCALENDAR'); + $vcalendar->add($vevent); + + $filter = array( + 'name' => 'VCALENDAR', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => array(), + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => array(), + 'comp-filters' => array( + array( + 'name' => 'VALARM', + 'is-not-defined' => false, + 'prop-filters' => array(), + 'comp-filters' => array(), + 'time-range' => array( + 'start' => new DateTime('2012-05-10'), + 'end' => new DateTime('2012-05-20'), + ), + ), + ), + ), + ), + ); + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + $this->assertTrue($validator->validate($vcalendar, $filter)); + + + // A limited recurrence rule, should return false + $vevent = Sabre_VObject_Component::create('VEVENT'); + $vevent->RRULE = 'FREQ=MONTHLY;COUNT=1'; + $vevent->DTSTART = '20120101T120000Z'; + $vevent->UID = 'bla'; + + $valarm = Sabre_VObject_Component::create('VALARM'); + $valarm->TRIGGER = '-P15D'; + $vevent->add($valarm); + + $vcalendar = Sabre_VObject_Component::create('VCALENDAR'); + $vcalendar->add($vevent); + + $this->assertFalse($validator->validate($vcalendar, $filter)); + } + + function testAlarmWayBefore() { + + $vevent = Sabre_VObject_Component::create('VEVENT'); + $vevent->DTSTART = '20120101T120000Z'; + $vevent->UID = 'bla'; + + $valarm = Sabre_VObject_Component::create('VALARM'); + $valarm->TRIGGER = '-P2W1D'; + $vevent->add($valarm); + + $vcalendar = Sabre_VObject_Component::create('VCALENDAR'); + $vcalendar->add($vevent); + + $filter = array( + 'name' => 'VCALENDAR', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => array(), + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => array(), + 'comp-filters' => array( + array( + 'name' => 'VALARM', + 'is-not-defined' => false, + 'prop-filters' => array(), + 'comp-filters' => array(), + 'time-range' => array( + 'start' => new DateTime('2011-12-10'), + 'end' => new DateTime('2011-12-20'), + ), + ), + ), + ), + ), + ); + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + $this->assertTrue($validator->validate($vcalendar, $filter)); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php new file mode 100644 index 000000000..b75c398ab --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php @@ -0,0 +1,748 @@ + 'VCALENDAR', + 'comp-filters' => array($filters), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + + $vObject = Sabre_VObject_Reader::read($icalObject); + + switch($outcome) { + case 0 : + $this->assertFalse($validator->validate($vObject, $filters)); + break; + case 1 : + $this->assertTrue($validator->validate($vObject, $filters)); + break; + case -1 : + try { + $validator->validate($vObject, $filters); + } catch (Sabre_DAV_Exception $e) { + // Success + } + break; + + } + + } + + function provider() { + + $blob1 = << 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + $filter2 = $filter1; + $filter2['name'] = 'VTODO'; + + $filter3 = $filter1; + $filter3['is-not-defined'] = true; + + $filter4 = $filter1; + $filter4['name'] = 'VTODO'; + $filter4['is-not-defined'] = true; + + $filter5 = $filter1; + $filter5['comp-filters'] = array( + array( + 'name' => 'VALARM', + 'is-not-defined' => false, + 'comp-filters' => array(), + 'prop-filters' => array(), + 'time-range' => null, + ), + ); + $filter6 = $filter1; + $filter6['prop-filters'] = array( + array( + 'name' => 'SUMMARY', + 'is-not-defined' => false, + 'param-filters' => array(), + 'time-range' => null, + 'text-match' => null, + ), + ); + $filter7 = $filter6; + $filter7['prop-filters'][0]['name'] = 'DESCRIPTION'; + + $filter8 = $filter6; + $filter8['prop-filters'][0]['is-not-defined'] = true; + + $filter9 = $filter7; + $filter9['prop-filters'][0]['is-not-defined'] = true; + + $filter10 = $filter5; + $filter10['prop-filters'] = $filter6['prop-filters']; + + // Param filters + $filter11 = $filter1; + $filter11['prop-filters'] = array( + array( + 'name' => 'DTSTART', + 'is-not-defined' => false, + 'param-filters' => array( + array( + 'name' => 'VALUE', + 'is-not-defined' => false, + 'text-match' => null, + ), + ), + 'time-range' => null, + 'text-match' => null, + ), + ); + + $filter12 = $filter11; + $filter12['prop-filters'][0]['param-filters'][0]['name'] = 'TZID'; + + $filter13 = $filter11; + $filter13['prop-filters'][0]['param-filters'][0]['is-not-defined'] = true; + + $filter14 = $filter12; + $filter14['prop-filters'][0]['param-filters'][0]['is-not-defined'] = true; + + // Param text filter + $filter15 = $filter11; + $filter15['prop-filters'][0]['param-filters'][0]['text-match'] = array( + 'collation' => 'i;ascii-casemap', + 'value' => 'dAtE', + 'negate-condition' => false, + ); + $filter16 = $filter15; + $filter16['prop-filters'][0]['param-filters'][0]['text-match']['collation'] = 'i;octet'; + + $filter17 = $filter15; + $filter17['prop-filters'][0]['param-filters'][0]['text-match']['negate-condition'] = true; + + $filter18 = $filter15; + $filter18['prop-filters'][0]['param-filters'][0]['text-match']['negate-condition'] = true; + $filter18['prop-filters'][0]['param-filters'][0]['text-match']['collation'] = 'i;octet'; + + // prop + text + $filter19 = $filter5; + $filter19['comp-filters'][0]['prop-filters'] = array( + array( + 'name' => 'action', + 'is-not-defined' => false, + 'time-range' => null, + 'param-filters' => array(), + 'text-match' => array( + 'collation' => 'i;ascii-casemap', + 'value' => 'display', + 'negate-condition' => false, + ), + ), + ); + + // Time range + $filter20 = array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2011-01-01 10:00:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 13:00:00', new DateTimeZone('GMT')), + ), + ); + // Time range, no end date + $filter21 = $filter20; + $filter21['time-range']['end'] = null; + + // Time range, no start date + $filter22 = $filter20; + $filter22['time-range']['start'] = null; + + // Time range, other dates + $filter23 = $filter20; + $filter23['time-range'] = array( + 'start' => new DateTime('2011-02-01 10:00:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-02-01 13:00:00', new DateTimeZone('GMT')), + ); + // Time range + $filter24 = array( + 'name' => 'VTODO', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2011-01-01 12:45:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 13:15:00', new DateTimeZone('GMT')), + ), + ); + // Time range, other dates (1 month in the future) + $filter25 = $filter24; + $filter25['time-range'] = array( + 'start' => new DateTime('2011-02-01 10:00:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-02-01 13:00:00', new DateTimeZone('GMT')), + ); + $filter26 = $filter24; + $filter26['time-range'] = array( + 'start' => new DateTime('2011-01-01 11:45:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 12:15:00', new DateTimeZone('GMT')), + ); + + // Time range for VJOURNAL + $filter27 = array( + 'name' => 'VJOURNAL', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2011-01-01 12:45:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 13:15:00', new DateTimeZone('GMT')), + ), + ); + $filter28 = $filter27; + $filter28['time-range'] = array( + 'start' => new DateTime('2011-01-01 11:45:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 12:15:00', new DateTimeZone('GMT')), + ); + // Time range for VFREEBUSY + $filter29 = array( + 'name' => 'VFREEBUSY', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2011-01-01 12:45:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 13:15:00', new DateTimeZone('GMT')), + ), + ); + // Time range filter on property + $filter30 = array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array( + array( + 'name' => 'DTSTART', + 'is-not-defined' => false, + 'param-filters' => array(), + 'time-range' => array( + 'start' => new DateTime('2011-01-01 10:00:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 13:00:00', new DateTimeZone('GMT')), + ), + 'text-match' => null, + ), + ), + 'is-not-defined' => false, + 'time-range' => null, + ); + + // Time range for alarm + $filter31 = array( + 'name' => 'VEVENT', + 'prop-filters' => array(), + 'comp-filters' => array( + array( + 'name' => 'VALARM', + 'is-not-defined' => false, + 'comp-filters' => array(), + 'prop-filters' => array(), + 'time-range' => array( + 'start' => new DateTime('2011-01-01 10:45:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 11:15:00', new DateTimeZone('GMT')), + ), + 'text-match' => null, + ), + ), + 'is-not-defined' => false, + 'time-range' => null, + ); + $filter32 = $filter31; + $filter32['comp-filters'][0]['time-range'] = array( + 'start' => new DateTime('2011-01-01 11:45:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 12:15:00', new DateTimeZone('GMT')), + ); + + $filter33 = $filter31; + $filter33['name'] = 'VTODO'; + $filter34 = $filter32; + $filter34['name'] = 'VTODO'; + $filter35 = $filter31; + $filter35['name'] = 'VJOURNAL'; + $filter36 = $filter32; + $filter36['name'] = 'VJOURNAL'; + + // Time range filter on non-datetime property + $filter37 = array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array( + array( + 'name' => 'SUMMARY', + 'is-not-defined' => false, + 'param-filters' => array(), + 'time-range' => array( + 'start' => new DateTime('2011-01-01 10:00:00', new DateTimeZone('GMT')), + 'end' => new DateTime('2011-01-01 13:00:00', new DateTimeZone('GMT')), + ), + 'text-match' => null, + ), + ), + 'is-not-defined' => false, + 'time-range' => null, + ); + + // Time-range with RRULE + + + return array( + // Component check + + array($blob1, $filter1, 1), + array($blob1, $filter2, 0), + array($blob1, $filter3, 0), + array($blob1, $filter4, 1), + + // Subcomponent check + array($blob1, $filter5, 0), + array($blob2, $filter5, 1), + + // Property check + array($blob1, $filter6, 1), + array($blob1, $filter7, 0), + array($blob1, $filter8, 0), + array($blob1, $filter9, 1), + + // Subcomponent + property + array($blob2, $filter10, 1), + + // Param filter + array($blob3, $filter11, 1), + array($blob3, $filter12, 0), + array($blob3, $filter13, 0), + array($blob3, $filter14, 1), + + // Param + text + array($blob3, $filter15, 1), + array($blob3, $filter16, 0), + array($blob3, $filter17, 0), + array($blob3, $filter18, 1), + + // Prop + text + array($blob2, $filter19, 1), + + // Incorrect object (vcard) + array($blob4, $filter1, -1), + + // Time-range for event + array($blob5, $filter20, 1), + array($blob6, $filter20, 1), + array($blob7, $filter20, 1), + array($blob8, $filter20, 1), + + array($blob5, $filter21, 1), + array($blob5, $filter22, 1), + + array($blob5, $filter23, 0), + array($blob6, $filter23, 0), + array($blob7, $filter23, 0), + array($blob8, $filter23, 0), + + // Time-range for todo + array($blob9, $filter24, 1), + array($blob9, $filter25, 0), + array($blob9, $filter26, 1), + array($blob10, $filter24, 1), + array($blob10, $filter25, 0), + array($blob10, $filter26, 1), + + array($blob11, $filter24, 0), + array($blob11, $filter25, 0), + array($blob11, $filter26, 1), + + array($blob12, $filter24, 1), + array($blob12, $filter25, 0), + array($blob12, $filter26, 0), + + array($blob13, $filter24, 1), + array($blob13, $filter25, 0), + array($blob13, $filter26, 1), + + array($blob14, $filter24, 1), + array($blob14, $filter25, 0), + array($blob14, $filter26, 0), + + array($blob15, $filter24, 1), + array($blob15, $filter25, 1), + array($blob15, $filter26, 1), + + array($blob16, $filter24, 1), + array($blob16, $filter25, 1), + array($blob16, $filter26, 1), + + // Time-range for journals + array($blob17, $filter27, 0), + array($blob17, $filter28, 0), + array($blob18, $filter27, 0), + array($blob18, $filter28, 1), + array($blob19, $filter27, 1), + array($blob19, $filter28, 1), + + // Time-range for free-busy + array($blob20, $filter29, -1), + + // Time-range on property + array($blob5, $filter30, 1), + array($blob3, $filter37, -1), + array($blob3, $filter30, 0), + + // Time-range on alarm in vevent + array($blob21, $filter31, 1), + array($blob21, $filter32, 0), + array($blob22, $filter31, 1), + array($blob22, $filter32, 0), + array($blob23, $filter31, 1), + array($blob23, $filter32, 0), + array($blob24, $filter31, 1), + array($blob24, $filter32, 0), + array($blob25, $filter31, 1), + array($blob25, $filter32, 0), + array($blob26, $filter31, 1), + array($blob26, $filter32, 0), + + // Time-range on alarm for vtodo + array($blob27, $filter33, 1), + array($blob27, $filter34, 0), + + // Time-range on alarm for vjournal + array($blob28, $filter35, -1), + array($blob28, $filter36, -1), + + // Time-range on alarm with duration + array($blob29, $filter31, 1), + array($blob29, $filter32, 0), + array($blob30, $filter31, 0), + array($blob30, $filter32, 0), + + + // Time-range with RRULE + array($blob31, $filter20, 1), + array($blob32, $filter20, 0), + + ); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/CalendarTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarTest.php new file mode 100644 index 000000000..a27870754 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/CalendarTest.php @@ -0,0 +1,253 @@ +markTestSkipped('SQLite driver is not available'); + $this->backend = Sabre_CalDAV_TestUtil::getBackend(); + $this->principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $this->calendars = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(2, count($this->calendars)); + $this->calendar = new Sabre_CalDAV_Calendar($this->principalBackend, $this->backend, $this->calendars[0]); + + + } + + function teardown() { + + unset($this->backend); + + } + + function testSimple() { + + $this->assertEquals($this->calendars[0]['uri'], $this->calendar->getName()); + + } + + /** + * @depends testSimple + */ + function testUpdateProperties() { + + $result = $this->calendar->updateProperties(array( + '{DAV:}displayname' => 'NewName', + )); + + $this->assertEquals(true, $result); + + $calendars2 = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals('NewName',$calendars2[0]['{DAV:}displayname']); + + } + + /** + * @depends testSimple + */ + function testGetProperties() { + + $question = array( + '{DAV:}owner', + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set', + '{urn:ietf:params:xml:ns:caldav}supported-calendar-data', + '{urn:ietf:params:xml:ns:caldav}supported-collation-set', + ); + + $result = $this->calendar->getProperties($question); + + foreach($question as $q) $this->assertArrayHasKey($q,$result); + + $this->assertEquals(array('VEVENT','VTODO'), $result['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue()); + + $this->assertTrue($result['{urn:ietf:params:xml:ns:caldav}supported-collation-set'] instanceof Sabre_CalDAV_Property_SupportedCollationSet); + + $this->assertTrue($result['{DAV:}owner'] instanceof Sabre_DAVACL_Property_Principal); + $this->assertEquals('principals/user1', $result['{DAV:}owner']->getHref()); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotFound + * @depends testSimple + */ + function testGetChildNotFound() { + + $this->calendar->getChild('randomname'); + + } + + /** + * @depends testSimple + */ + function testGetChildren() { + + $children = $this->calendar->getChildren(); + $this->assertEquals(1,count($children)); + + $this->assertTrue($children[0] instanceof Sabre_CalDAV_CalendarObject); + + } + + /** + * @depends testGetChildren + */ + function testChildExists() { + + $this->assertFalse($this->calendar->childExists('foo')); + + $children = $this->calendar->getChildren(); + $this->assertTrue($this->calendar->childExists($children[0]->getName())); + } + + + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testCreateDirectory() { + + $this->calendar->createDirectory('hello'); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetName() { + + $this->calendar->setName('hello'); + + } + + function testGetLastModified() { + + $this->assertNull($this->calendar->getLastModified()); + + } + + function testCreateFile() { + + $file = fopen('php://memory','r+'); + fwrite($file,Sabre_CalDAV_TestUtil::getTestCalendarData()); + rewind($file); + + $this->calendar->createFile('hello',$file); + + $file = $this->calendar->getChild('hello'); + $this->assertTrue($file instanceof Sabre_CalDAV_CalendarObject); + + } + + function testCreateFileNoSupportedComponents() { + + $file = fopen('php://memory','r+'); + fwrite($file,Sabre_CalDAV_TestUtil::getTestCalendarData()); + rewind($file); + + $calendar = new Sabre_CalDAV_Calendar($this->principalBackend, $this->backend, $this->calendars[1]); + $calendar->createFile('hello',$file); + + $file = $calendar->getChild('hello'); + $this->assertTrue($file instanceof Sabre_CalDAV_CalendarObject); + + } + + function testDelete() { + + $this->calendar->delete(); + + $calendars = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(1, count($calendars)); + } + + function testGetOwner() { + + $this->assertEquals('principals/user1',$this->calendar->getOwner()); + + } + + function testGetGroup() { + + $this->assertNull($this->calendar->getGroup()); + + } + + function testGetACL() { + + $expected = array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ), + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ), + ); + $this->assertEquals($expected, $this->calendar->getACL()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetACL() { + + $this->calendar->setACL(array()); + + } + + function testGetSupportedPrivilegesSet() { + + $result = $this->calendar->getSupportedPrivilegeSet(); + + $this->assertEquals( + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + $result['aggregates'][0]['aggregates'][2]['privilege'] + ); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php new file mode 100644 index 000000000..984212aa2 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php @@ -0,0 +1,106 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ) + ); + + protected $caldavCalendarObjects = array( + 1 => array( + 'event.ics' => array( + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTEND;TZID=Europe/Berlin:20120207T191500 +RRULE:FREQ=DAILY;INTERVAL=1;COUNT=3 +SUMMARY:RecurringEvents 3 times +DTSTART;TZID=Europe/Berlin:20120207T181500 +END:VEVENT +BEGIN:VEVENT +CREATED:20120207T111900Z +UID:foobar +DTEND;TZID=Europe/Berlin:20120208T191500 +SUMMARY:RecurringEvents 3 times OVERWRITTEN +DTSTART;TZID=Europe/Berlin:20120208T181500 +RECURRENCE-ID;TZID=Europe/Berlin:20120208T181500 +END:VEVENT +END:VCALENDAR +', + ), + ), + ); + + function testExpand() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ','',$body); + + $vObject = Sabre_VObject_Reader::read($body); + + // check if DTSTARTs and DTENDs are correct + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre_VObject_Component_VEvent */ + foreach ($vevent->children as $child) { + /** @var $child Sabre_VObject_Property */ + + if ($child->name == 'DTSTART') { + // DTSTART has to be one of three valid values + $this->assertContains($child->value, array('20120207T171500Z', '20120208T171500Z', '20120209T171500Z'), 'DTSTART is not a valid value: '.$child->value); + } elseif ($child->name == 'DTEND') { + // DTEND has to be one of three valid values + $this->assertContains($child->value, array('20120207T181500Z', '20120208T181500Z', '20120209T181500Z'), 'DTEND is not a valid value: '.$child->value); + } + } + } + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php new file mode 100644 index 000000000..a2c2fc275 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php @@ -0,0 +1,100 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ) + ); + + protected $caldavCalendarObjects = array( + 1 => array( + 'event.ics' => array( + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTEND;TZID=Europe/Berlin:20120207T191500 +RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,TH +SUMMARY:RecurringEvents on tuesday and thursday +DTSTART;TZID=Europe/Berlin:20120207T181500 +END:VEVENT +END:VCALENDAR +', + ), + ), + ); + + function testExpandRecurringByDayEvent() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ','',$body); + + $vObject = Sabre_VObject_Reader::read($body); + + $this->assertEquals(2, count($vObject->VEVENT)); + + // check if DTSTARTs and DTENDs are correct + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre_VObject_Component_VEvent */ + foreach ($vevent->children as $child) { + /** @var $child Sabre_VObject_Property */ + + if ($child->name == 'DTSTART') { + // DTSTART has to be one of two valid values + $this->assertContains($child->value, array('20120214T171500Z', '20120216T171500Z'), 'DTSTART is not a valid value: '.$child->value); + } elseif ($child->name == 'DTEND') { + // DTEND has to be one of two valid values + $this->assertContains($child->value, array('20120214T181500Z', '20120216T181500Z'), 'DTEND is not a valid value: '.$child->value); + } + } + } + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php new file mode 100644 index 000000000..55d06b231 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php @@ -0,0 +1,99 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ) + ); + + protected $caldavCalendarObjects = array( + 1 => array( + 'event.ics' => array( + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTEND;TZID=Europe/Berlin:20120207T191500 +RRULE:FREQ=DAILY;INTERVAL=1;COUNT=3 +SUMMARY:RecurringEvents 3 times +DTSTART;TZID=Europe/Berlin:20120207T181500 +END:VEVENT +BEGIN:VEVENT +CREATED:20120207T111900Z +UID:foobar +DTEND;TZID=Europe/Berlin:20120208T191500 +SUMMARY:RecurringEvents 3 times OVERWRITTEN +DTSTART;TZID=Europe/Berlin:20120208T181500 +RECURRENCE-ID;TZID=Europe/Berlin:20120208T181500 +END:VEVENT +END:VCALENDAR +', + ), + ), + ); + + function testExpand() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ','',$body); + + $vObject = Sabre_VObject_Reader::read($body); + + // We only expect 3 events + $this->assertEquals(3, count($vObject->VEVENT),'We got 6 events instead of 3. Output: ' . $body); + + // TZID should be gone + $this->assertFalse(isset($vObject->VEVENT->DTSTART['TZID'])); + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/FreeBusyReportTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/FreeBusyReportTest.php new file mode 100644 index 000000000..3ccefdf4a --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/FreeBusyReportTest.php @@ -0,0 +1,155 @@ + array( + 'obj1' => array( + 'calendarid' => 1, + 'uri' => 'event1.ics', + 'calendardata' => $obj1, + ), + 'obj2' => array( + 'calendarid' => 1, + 'uri' => 'event2.ics', + 'calendardata' => $obj2 + ) + ), + ); + + + $caldavBackend = new Sabre_CalDAV_Backend_Mock(array(), $calendarData); + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $calendar = new Sabre_CalDAV_Calendar($principalBackend,$caldavBackend, array( + 'id' => 1, + 'uri' => 'calendar', + 'principaluri' => 'principals/user1', + )); + + $this->server = new Sabre_DAV_Server(array($calendar)); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_URI' => '/calendar', + )); + $this->server->httpRequest = $request; + $this->server->httpResponse = new Sabre_HTTP_ResponseMock(); + + $this->plugin = new Sabre_CalDAV_Plugin(); + $this->server->addPlugin($this->plugin); + $this->server->addPlugin(new Sabre_DAVACL_Plugin()); + + } + + function testFreeBusyReport() { + + $reportXML = << + + + +XML; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($reportXML); + $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); + + $this->assertEquals('HTTP/1.1 200 OK', $this->server->httpResponse->status); + $this->assertEquals('text/calendar', $this->server->httpResponse->headers['Content-Type']); + $this->assertTrue(strpos($this->server->httpResponse->body,'BEGIN:VFREEBUSY')!==false); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testFreeBusyReportNoTimeRange() { + + $reportXML = << + + +XML; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($reportXML); + $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotImplemented + */ + function testFreeBusyReportWrongNode() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_URI' => '/', + )); + $this->server->httpRequest = $request; + + $reportXML = << + + + +XML; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($reportXML); + $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); + + } + + /** + * @expectedException Sabre_DAV_Exception + */ + function testFreeBusyReportNoACLPlugin() { + + $this->server = new Sabre_DAV_Server(); + $this->plugin = new Sabre_CalDAV_Plugin(); + $this->server->addPlugin($this->plugin); + + $reportXML = << + + + +XML; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($reportXML); + $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php new file mode 100644 index 000000000..b8a97ca1d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php @@ -0,0 +1,93 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ) + ); + + protected $caldavCalendarObjects = array( + 1 => array( + 'event.ics' => array( + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +CREATED:20120313T142342Z +UID:171EBEFC-C951-499D-B234-7BA7D677B45D +DTEND;TZID=Europe/Berlin:20120227T000000 +TRANSP:OPAQUE +SUMMARY:Monday 0h +DTSTART;TZID=Europe/Berlin:20120227T000000 +DTSTAMP:20120313T142416Z +SEQUENCE:4 +END:VEVENT +END:VCALENDAR +', + ), + ), + ); + + function testQueryTimerange() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + if (strpos($response->body, 'BEGIN:VCALENDAR') === false) { + $this->fail('Got no events instead of 1. Output: '.$response->body); + } + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ','',$body); + + $vObject = Sabre_VObject_Reader::read($body); + + // We expect 1 event + $this->assertEquals(1, count($vObject->VEVENT), 'We got 0 events instead of 1. Output: ' . $body); + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/ICSExportPluginTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/ICSExportPluginTest.php new file mode 100644 index 000000000..137b7d3ca --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/ICSExportPluginTest.php @@ -0,0 +1,174 @@ +addPlugin($p); + + } + + function testBeforeMethod() { + + if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available'); + $cbackend = Sabre_CalDAV_TestUtil::getBackend(); + $pbackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $props = array( + 'uri'=>'UUID-123467', + 'principaluri' => 'admin', + 'id' => 1, + ); + $tree = array( + new Sabre_CalDAV_Calendar($pbackend,$cbackend,$props), + ); + + $p = new Sabre_CalDAV_ICSExportPlugin(); + + $s = new Sabre_DAV_Server($tree); + $s->addPlugin($p); + $s->addPlugin(new Sabre_CalDAV_Plugin()); + + $h = new Sabre_HTTP_Request(array( + 'QUERY_STRING' => 'export', + )); + + $s->httpRequest = $h; + $s->httpResponse = new Sabre_HTTP_ResponseMock(); + + $this->assertFalse($p->beforeMethod('GET','UUID-123467?export')); + + $this->assertEquals('HTTP/1.1 200 OK',$s->httpResponse->status); + $this->assertEquals(array( + 'Content-Type' => 'text/calendar', + ), $s->httpResponse->headers); + + $obj = Sabre_VObject_Reader::read($s->httpResponse->body); + + $this->assertEquals(5,count($obj->children())); + $this->assertEquals(1,count($obj->VERSION)); + $this->assertEquals(1,count($obj->CALSCALE)); + $this->assertEquals(1,count($obj->PRODID)); + $this->assertEquals(1,count($obj->VTIMEZONE)); + $this->assertEquals(1,count($obj->VEVENT)); + + } + + function testBeforeMethodNoGET() { + + $p = new Sabre_CalDAV_ICSExportPlugin(); + + $s = new Sabre_DAV_Server(); + $s->addPlugin($p); + + $this->assertNull($p->beforeMethod('POST','UUID-123467?export')); + + } + + function testBeforeMethodNoExport() { + + $p = new Sabre_CalDAV_ICSExportPlugin(); + + $s = new Sabre_DAV_Server(); + $s->addPlugin($p); + + $this->assertNull($p->beforeMethod('GET','UUID-123467')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testACLIntegrationBlocked() { + + if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available'); + $cbackend = Sabre_CalDAV_TestUtil::getBackend(); + $pbackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $props = array( + 'uri'=>'UUID-123467', + 'principaluri' => 'admin', + 'id' => 1, + ); + $tree = array( + new Sabre_CalDAV_Calendar($pbackend,$cbackend,$props), + ); + + $p = new Sabre_CalDAV_ICSExportPlugin(); + + $s = new Sabre_DAV_Server($tree); + $s->addPlugin($p); + $s->addPlugin(new Sabre_CalDAV_Plugin()); + $s->addPlugin(new Sabre_DAVACL_Plugin()); + + $h = new Sabre_HTTP_Request(array( + 'QUERY_STRING' => 'export', + )); + + $s->httpRequest = $h; + $s->httpResponse = new Sabre_HTTP_ResponseMock(); + + $p->beforeMethod('GET','UUID-123467?export'); + + } + + function testACLIntegrationNotBlocked() { + + if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available'); + $cbackend = Sabre_CalDAV_TestUtil::getBackend(); + $pbackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $props = array( + 'uri'=>'UUID-123467', + 'principaluri' => 'admin', + 'id' => 1, + ); + $tree = array( + new Sabre_CalDAV_Calendar($pbackend,$cbackend,$props), + new Sabre_DAVACL_PrincipalCollection($pbackend), + ); + + $p = new Sabre_CalDAV_ICSExportPlugin(); + + $s = new Sabre_DAV_Server($tree); + $s->addPlugin($p); + $s->addPlugin(new Sabre_CalDAV_Plugin()); + $s->addPlugin(new Sabre_DAVACL_Plugin()); + $s->addPlugin(new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'SabreDAV')); + + // Forcing login + $s->getPlugin('acl')->adminPrincipals = array('principals/admin'); + + $h = new Sabre_HTTP_Request(array( + 'QUERY_STRING' => 'export', + 'REQUEST_URI' => '/UUID-123467', + 'REQUEST_METHOD' => 'GET', + )); + + $s->httpRequest = $h; + $s->httpResponse = new Sabre_HTTP_ResponseMock(); + + $s->exec(); + + $this->assertEquals('HTTP/1.1 200 OK',$s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); + $this->assertEquals(array( + 'Content-Type' => 'text/calendar', + ), $s->httpResponse->headers); + + $obj = Sabre_VObject_Reader::read($s->httpResponse->body); + + $this->assertEquals(5,count($obj->children())); + $this->assertEquals(1,count($obj->VERSION)); + $this->assertEquals(1,count($obj->CALSCALE)); + $this->assertEquals(1,count($obj->PRODID)); + $this->assertEquals(1,count($obj->VTIMEZONE)); + $this->assertEquals(1,count($obj->VEVENT)); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Issue166Test.php b/dav/SabreDAV/tests/Sabre/CalDAV/Issue166Test.php new file mode 100644 index 000000000..3a61663ec --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Issue166Test.php @@ -0,0 +1,59 @@ + 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2011-12-01'), + 'end' => new DateTime('2012-02-01'), + ), + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + ); + $input = Sabre_VObject_Reader::read($input); + $this->assertTrue($validator->validate($input,$filters)); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Issue172Test.php b/dav/SabreDAV/tests/Sabre/CalDAV/Issue172Test.php new file mode 100644 index 000000000..024b25579 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Issue172Test.php @@ -0,0 +1,131 @@ + 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2012-01-18 21:00:00 GMT-08:00'), + 'end' => new DateTime('2012-01-18 21:00:00 GMT-08:00'), + ), + ), + ), + 'prop-filters' => array(), + ); + $input = Sabre_VObject_Reader::read($input); + $this->assertTrue($validator->validate($input,$filters)); + } + + // Pacific Standard Time, translates to America/Los_Angeles (GMT-8 in January) + function testOutlookTimezoneName() { + $input = << 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2012-01-13 10:30:00 GMT-08:00'), + 'end' => new DateTime('2012-01-13 10:30:00 GMT-08:00'), + ), + ), + ), + 'prop-filters' => array(), + ); + $input = Sabre_VObject_Reader::read($input); + $this->assertTrue($validator->validate($input,$filters)); + } + + // X-LIC-LOCATION, translates to America/Los_Angeles (GMT-8 in January) + function testLibICalLocationName() { + $input = << 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => new DateTime('2012-01-13 10:30:00 GMT-08:00'), + 'end' => new DateTime('2012-01-13 10:30:00 GMT-08:00'), + ), + ), + ), + 'prop-filters' => array(), + ); + $input = Sabre_VObject_Reader::read($input); + $this->assertTrue($validator->validate($input,$filters)); + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Issue203Test.php b/dav/SabreDAV/tests/Sabre/CalDAV/Issue203Test.php new file mode 100644 index 000000000..e9eaecab8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Issue203Test.php @@ -0,0 +1,135 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ) + ); + + protected $caldavCalendarObjects = array( + 1 => array( + 'event.ics' => array( + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:20120330T155305CEST-6585fBUVgV +DTSTAMP:20120330T135305Z +DTSTART;TZID=Europe/Berlin:20120326T155200 +DTEND;TZID=Europe/Berlin:20120326T165200 +RRULE:FREQ=DAILY;COUNT=2;INTERVAL=1 +SUMMARY:original summary +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +UID:20120330T155305CEST-6585fBUVgV +DTSTAMP:20120330T135352Z +DESCRIPTION: +DTSTART;TZID=Europe/Berlin:20120328T155200 +DTEND;TZID=Europe/Berlin:20120328T165200 +RECURRENCE-ID;TZID=Europe/Berlin:20120327T155200 +SEQUENCE:1 +SUMMARY:overwritten summary +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR +', + ), + ), + ); + + function testIssue203() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ','',$body); + + $vObject = Sabre_VObject_Reader::read($body); + + $this->assertEquals(2, count($vObject->VEVENT)); + + + $expectedEvents = array( + array( + 'DTSTART' => '20120326T135200Z', + 'DTEND' => '20120326T145200Z', + 'SUMMARY' => 'original summary', + ), + array( + 'DTSTART' => '20120328T135200Z', + 'DTEND' => '20120328T145200Z', + 'SUMMARY' => 'overwritten summary', + 'RECURRENCE-ID' => '20120327T135200Z', + ) + ); + + // try to match agains $expectedEvents array + foreach ($expectedEvents as $expectedEvent) { + + $matching = false; + + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre_VObject_Component_VEvent */ + + foreach ($vevent->children as $child) { + /** @var $child Sabre_VObject_Property */ + + if (isset($expectedEvent[$child->name])) { + if ($expectedEvent[$child->name] != $child->value) { + continue 2; + } + } + } + + $matching = true; + break; + } + + $this->assertTrue($matching, 'Did not find the following event in the response: '.var_export($expectedEvent, true)); + } + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Issue205Test.php b/dav/SabreDAV/tests/Sabre/CalDAV/Issue205Test.php new file mode 100644 index 000000000..20098252e --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Issue205Test.php @@ -0,0 +1,94 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ) + ); + + protected $caldavCalendarObjects = array( + 1 => array( + 'event.ics' => array( + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:20120330T155305CEST-6585fBUVgV +DTSTAMP:20120330T135305Z +DTSTART;TZID=Europe/Berlin:20120326T155200 +DTEND;TZID=Europe/Berlin:20120326T165200 +SUMMARY:original summary +TRANSP:OPAQUE +BEGIN:VALARM +ACTION:AUDIO +ATTACH;VALUE=URI:Basso +TRIGGER:PT0S +END:VALARM +END:VEVENT +END:VCALENDAR +', + ), + ), + ); + + function testIssue205() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody(' + + + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + $this->assertFalse(strpos($response->body, 'Exception'), 'Exception occurred: ' . $response->body); + $this->assertFalse(strpos($response->body, 'Unknown or bad format'), 'DateTime unknown format Exception: ' . $response->body); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ','',$body); + + $vObject = Sabre_VObject_Reader::read($body); + + $this->assertEquals(1, count($vObject->VEVENT)); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Issue211Test.php b/dav/SabreDAV/tests/Sabre/CalDAV/Issue211Test.php new file mode 100644 index 000000000..b48ad604d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Issue211Test.php @@ -0,0 +1,86 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ) + ); + + protected $caldavCalendarObjects = array( + 1 => array( + 'event.ics' => array( + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:20120418T172519CEST-3510gh1hVw +DTSTAMP:20120418T152519Z +DTSTART;VALUE=DATE:20120330 +DTEND;VALUE=DATE:20120531 +EXDATE;TZID=Europe/Berlin:20120330T000000 +RRULE:FREQ=YEARLY;INTERVAL=1 +SEQUENCE:1 +SUMMARY:Birthday +TRANSP:TRANSPARENT +BEGIN:VALARM +ACTION:EMAIL +ATTENDEE:MAILTO:xxx@domain.de +DESCRIPTION:Dies ist eine Kalender Erinnerung +SUMMARY:Kalender Alarm Erinnerung +TRIGGER;VALUE=DATE-TIME:20120329T060000Z +END:VALARM +END:VEVENT +END:VCALENDAR +', + ), + ), + ); + + function testIssue211() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // if this assert is reached, the endless loop is gone + // There should be no matching events + $this->assertFalse(strpos('BEGIN:VEVENT', $response->body)); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/OutboxPostTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/OutboxPostTest.php new file mode 100644 index 000000000..4d45c8ae0 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/OutboxPostTest.php @@ -0,0 +1,321 @@ + 'POST', + 'REQUEST_URI' => '/notfound', + )); + + $this->assertHTTPStatus(501, $req); + + } + + function testPostPassThruNoOutBox() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars', + )); + + $this->assertHTTPStatus(501, $req); + + } + + function testNoOriginator() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/admin/outbox', + )); + + $this->assertHTTPStatus(400, $req); + + } + + function testNoRecipient() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/admin/outbox', + 'HTTP_ORIGINATOR' => 'mailto:orig@example.org', + )); + + $this->assertHTTPStatus(400, $req); + + } + + function testBadOriginator() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/admin/outbox', + 'HTTP_ORIGINATOR' => 'nomailto:orig@example.org', + 'HTTP_RECIPIENT' => 'mailto:user1@example.org', + )); + + $this->assertHTTPStatus(400, $req); + + } + + function testBadRecipient() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/admin/outbox', + 'HTTP_ORIGINATOR' => 'mailto:orig@example.org', + 'HTTP_RECIPIENT' => 'http://user1@example.org, mailto:user2@example.org', + )); + + $this->assertHTTPStatus(400, $req); + + } + + function testIncorrectOriginator() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/admin/outbox', + 'HTTP_ORIGINATOR' => 'mailto:orig@example.org', + 'HTTP_RECIPIENT' => 'mailto:user1@example.org, mailto:user2@example.org', + )); + + $this->assertHTTPStatus(403, $req); + + } + + function testInvalidIcalBody() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + $req->setBody('foo'); + + $this->assertHTTPStatus(400, $req); + + } + + function testNoVEVENT() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + + $body = array( + 'BEGIN:VCALENDAR', + 'BEGIN:VTIMEZONE', + 'END:VTIMEZONE', + 'END:VCALENDAR', + ); + + $req->setBody(implode("\r\n",$body)); + + $this->assertHTTPStatus(400, $req); + + } + + function testNoMETHOD() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + + $body = array( + 'BEGIN:VCALENDAR', + 'BEGIN:VEVENT', + 'END:VEVENT', + 'END:VCALENDAR', + ); + + $req->setBody(implode("\r\n",$body)); + + $this->assertHTTPStatus(400, $req); + + } + + function testUnsupportedMethod() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + + $body = array( + 'BEGIN:VCALENDAR', + 'METHOD:PUBLISH', + 'BEGIN:VEVENT', + 'END:VEVENT', + 'END:VCALENDAR', + ); + + $req->setBody(implode("\r\n",$body)); + + $this->assertHTTPStatus(501, $req); + + } + + function testNoIMIPHandler() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + + $body = array( + 'BEGIN:VCALENDAR', + 'METHOD:REQUEST', + 'BEGIN:VEVENT', + 'END:VEVENT', + 'END:VCALENDAR', + ); + + $req->setBody(implode("\r\n",$body)); + + $this->assertHTTPStatus(501, $req); + + } + + function testSuccessRequest() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + + $body = array( + 'BEGIN:VCALENDAR', + 'METHOD:REQUEST', + 'BEGIN:VEVENT', + 'SUMMARY:An invitation', + 'END:VEVENT', + 'END:VCALENDAR', + ); + + $req->setBody(implode("\r\n",$body)); + + $handler = new Sabre_CalDAV_Schedule_IMip_Mock('server@example.org'); + + $this->caldavPlugin->setIMIPhandler($handler); + $this->assertHTTPStatus(200, $req); + + $this->assertEquals(array( + array( + 'to' => 'user2@example.org', + 'subject' => 'Invitation for: An invitation', + 'body' => implode("\r\n", $body) . "\r\n", + 'headers' => array( + 'Reply-To: user1.sabredav@sabredav.org', + 'From: server@example.org', + 'Content-Type: text/calendar; method=REQUEST; charset=utf-8', + 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY, + ), + ) + ), $handler->getSentEmails()); + + } + + function testSuccessReply() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + + $body = array( + 'BEGIN:VCALENDAR', + 'METHOD:REPLY', + 'BEGIN:VEVENT', + 'SUMMARY:An invitation', + 'END:VEVENT', + 'END:VCALENDAR', + ); + + $req->setBody(implode("\r\n",$body)); + + $handler = new Sabre_CalDAV_Schedule_IMip_Mock('server@example.org'); + + $this->caldavPlugin->setIMIPhandler($handler); + $this->assertHTTPStatus(200, $req); + + $this->assertEquals(array( + array( + 'to' => 'user2@example.org', + 'subject' => 'Response for: An invitation', + 'body' => implode("\r\n", $body) . "\r\n", + 'headers' => array( + 'Reply-To: user1.sabredav@sabredav.org', + 'From: server@example.org', + 'Content-Type: text/calendar; method=REPLY; charset=utf-8', + 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY, + ), + ) + ), $handler->getSentEmails()); + + } + + function testSuccessCancel() { + + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + )); + + $body = array( + 'BEGIN:VCALENDAR', + 'METHOD:CANCEL', + 'BEGIN:VEVENT', + 'SUMMARY:An invitation', + 'END:VEVENT', + 'END:VCALENDAR', + ); + + $req->setBody(implode("\r\n",$body)); + + $handler = new Sabre_CalDAV_Schedule_IMip_Mock('server@example.org'); + + $this->caldavPlugin->setIMIPhandler($handler); + $this->assertHTTPStatus(200, $req); + + $this->assertEquals(array( + array( + 'to' => 'user2@example.org', + 'subject' => 'Cancelled event: An invitation', + 'body' => implode("\r\n", $body) . "\r\n", + 'headers' => array( + 'Reply-To: user1.sabredav@sabredav.org', + 'From: server@example.org', + 'Content-Type: text/calendar; method=CANCEL; charset=utf-8', + 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY, + ), + ) + ), $handler->getSentEmails()); + + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/PluginTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/PluginTest.php new file mode 100644 index 000000000..eb097c9e8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/PluginTest.php @@ -0,0 +1,994 @@ +markTestSkipped('No PDO SQLite support'); + $this->caldavBackend = Sabre_CalDAV_TestUtil::getBackend(); + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-read',array('principals/user1')); + $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-write',array('principals/user1')); + $principalBackend->addPrincipal(array( + 'uri' => 'principals/admin/calendar-proxy-read', + )); + $principalBackend->addPrincipal(array( + 'uri' => 'principals/admin/calendar-proxy-write', + )); + + $calendars = new Sabre_CalDAV_CalendarRootNode($principalBackend,$this->caldavBackend); + $principals = new Sabre_CalDAV_Principal_Collection($principalBackend); + + $root = new Sabre_DAV_SimpleCollection('root'); + $root->addChild($calendars); + $root->addChild($principals); + + $objectTree = new Sabre_DAV_ObjectTree($root); + $this->server = new Sabre_DAV_Server($objectTree); + $this->server->debugExceptions = true; + $this->server->setBaseUri('/'); + $this->plugin = new Sabre_CalDAV_Plugin(); + $this->server->addPlugin($this->plugin); + + $this->response = new Sabre_HTTP_ResponseMock(); + $this->server->httpResponse = $this->response; + + } + + function testSimple() { + + $this->assertEquals(array('MKCALENDAR'), $this->plugin->getHTTPMethods('calendars/user1/randomnewcalendar')); + $this->assertEquals(array('calendar-access','calendar-proxy'), $this->plugin->getFeatures()); + $this->assertArrayHasKey('urn:ietf:params:xml:ns:caldav', $this->server->xmlNamespaces); + + } + + function testUnknownMethodPassThrough() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'MKBREAKFAST', + 'REQUEST_URI' => '/', + )); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 501 Not Implemented', $this->response->status,'Incorrect status returned. Full response body:' . $this->response->body); + + } + + function testReportPassThrough() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/', + )); + $request->setBody(''); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 501 Not Implemented', $this->response->status); + + } + + function testMkCalendarBadLocation() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'MKCALENDAR', + 'REQUEST_URI' => '/blabla', + )); + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 403 Forbidden', $this->response->status); + + } + + function testMkCalendarNoParentNode() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'MKCALENDAR', + 'REQUEST_URI' => '/doesntexist/calendar', + )); + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 409 Conflict', $this->response->status); + + } + + function testMkCalendarExistingCalendar() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'MKCALENDAR', + 'REQUEST_URI' => '/calendars/user1/UUID-123467', + )); + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 405 Method Not Allowed', $this->response->status); + + } + + function testMkCalendarSucceed() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'MKCALENDAR', + 'REQUEST_URI' => '/calendars/user1/NEWCALENDAR', + )); + + $timezone = 'BEGIN:VCALENDAR +PRODID:-//Example Corp.//CalDAV Client//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:Eastern Standard Time (US & Canada) +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:Eastern Daylight Time (US & Canada) +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR'; + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created', $this->response->status,'Invalid response code received. Full response body: ' .$this->response->body); + + $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); + $this->assertEquals(3, count($calendars)); + + $newCalendar = null; + foreach($calendars as $calendar) { + if ($calendar['uri'] === 'NEWCALENDAR') { + $newCalendar = $calendar; + break; + } + } + + $this->assertInternalType('array',$newCalendar); + + $keys = array( + 'uri' => 'NEWCALENDAR', + 'id' => null, + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar restricted to events.', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => $timezone, + '{DAV:}displayname' => 'Lisa\'s Events', + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null, + ); + + foreach($keys as $key=>$value) { + + $this->assertArrayHasKey($key, $newCalendar); + + if (is_null($value)) continue; + $this->assertEquals($value, $newCalendar[$key]); + + } + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $this->assertTrue($newCalendar[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet); + $this->assertEquals(array('VEVENT'),$newCalendar[$sccs]->getValue()); + + } + + function testMkCalendarEmptyBodySucceed() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'MKCALENDAR', + 'REQUEST_URI' => '/calendars/user1/NEWCALENDAR', + )); + + $request->setBody(''); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created', $this->response->status,'Invalid response code received. Full response body: ' .$this->response->body); + + $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); + $this->assertEquals(3, count($calendars)); + + $newCalendar = null; + foreach($calendars as $calendar) { + if ($calendar['uri'] === 'NEWCALENDAR') { + $newCalendar = $calendar; + break; + } + } + + $this->assertInternalType('array',$newCalendar); + + $keys = array( + 'uri' => 'NEWCALENDAR', + 'id' => null, + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null, + ); + + foreach($keys as $key=>$value) { + + $this->assertArrayHasKey($key, $newCalendar); + + if (is_null($value)) continue; + $this->assertEquals($value, $newCalendar[$key]); + + } + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $this->assertTrue($newCalendar[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet); + $this->assertEquals(array('VEVENT','VTODO'),$newCalendar[$sccs]->getValue()); + + } + + function testPrincipalProperties() { + + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_HOST' => 'sabredav.org', + )); + $this->server->httpRequest = $httpRequest; + + $props = $this->server->getPropertiesForPath('/principals/user1',array( + '{urn:ietf:params:xml:ns:caldav}calendar-home-set', + '{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', + '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-read-for', + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}calendar-proxy-write-for', + )); + + $this->assertArrayHasKey(0,$props); + $this->assertArrayHasKey(200,$props[0]); + + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-home-set',$props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-home-set']; + $this->assertTrue($prop instanceof Sabre_DAV_Property_Href); + $this->assertEquals('calendars/user1/',$prop->getHref()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL',$props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL']; + $this->assertTrue($prop instanceof Sabre_DAV_Property_Href); + $this->assertEquals('calendars/user1/outbox',$prop->getHref()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',$props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set']; + $this->assertTrue($prop instanceof Sabre_DAV_Property_HrefList); + $this->assertEquals(array('mailto:user1.sabredav@sabredav.org','/principals/user1'),$prop->getHrefs()); + + $this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-read-for', $props[0][200]); + $prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-read-for']; + $this->assertInstanceOf('Sabre_DAV_Property_HrefList', $prop); + $this->assertEquals(array('principals/admin'), $prop->getHrefs()); + + $this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-write-for', $props[0][200]); + $prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-write-for']; + $this->assertInstanceOf('Sabre_DAV_Property_HrefList', $prop); + $this->assertEquals(array('principals/admin'), $prop->getHrefs()); + + } + + function testSupportedReportSetPropertyNonCalendar() { + + $props = $this->server->getPropertiesForPath('/calendars/user1',array( + '{DAV:}supported-report-set', + )); + + $this->assertArrayHasKey(0,$props); + $this->assertArrayHasKey(200,$props[0]); + $this->assertArrayHasKey('{DAV:}supported-report-set',$props[0][200]); + + $prop = $props[0][200]['{DAV:}supported-report-set']; + + $this->assertTrue($prop instanceof Sabre_DAV_Property_SupportedReportSet); + $value = array( + ); + $this->assertEquals($value,$prop->getValue()); + + } + + /** + * @depends testSupportedReportSetPropertyNonCalendar + */ + function testSupportedReportSetProperty() { + + $props = $this->server->getPropertiesForPath('/calendars/user1/UUID-123467',array( + '{DAV:}supported-report-set', + )); + + $this->assertArrayHasKey(0,$props); + $this->assertArrayHasKey(200,$props[0]); + $this->assertArrayHasKey('{DAV:}supported-report-set',$props[0][200]); + + $prop = $props[0][200]['{DAV:}supported-report-set']; + + $this->assertTrue($prop instanceof Sabre_DAV_Property_SupportedReportSet); + $value = array( + '{urn:ietf:params:xml:ns:caldav}calendar-multiget', + '{urn:ietf:params:xml:ns:caldav}calendar-query', + '{urn:ietf:params:xml:ns:caldav}free-busy-query', + ); + $this->assertEquals($value,$prop->getValue()); + + } + + /** + * @depends testSupportedReportSetProperty + */ + function testCalendarMultiGetReport() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1', + 'HTTP_DEPTH' => '1', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); + + $xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); + + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:href', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', + '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', + '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', + ); + + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + $this->assertEquals(1,count($result)); + + if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); + + } + + // The response object should have a reference to the Asia/Seoul + // timezone. + $this->assertTrue(strpos($this->response->body,'Asia/Seoul')!==false); + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportExpand() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1', + 'HTTP_DEPTH' => '1', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); + + $xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); + + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:href', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', + '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', + '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', + ); + + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + $this->assertEquals(1,count($result)); + + if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); + + } + // The response object should no longer hold references to timezones. + $this->assertTrue(strpos($this->response->body,'Asia/Seoul')===false); + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReport() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1/UUID-123467', + 'HTTP_DEPTH' => '1', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); + + $xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); + + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:href', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', + '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', + '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', + ); + + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); + + if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); + + } + + } + + /** + * @depends testCalendarQueryReport + */ + function testCalendarQueryReportNoCalData() { + + $body = + '' . + '' . + '' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1//UUID-123467', + 'HTTP_DEPTH' => '1', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); + + $xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); + + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:href', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', + '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', + ); + + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); + + if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); + + } + + } + + /** + * @depends testCalendarQueryReport + */ + function testCalendarQueryReportNoFilters() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + '' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1//UUID-123467', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReport1Object() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1/UUID-123467/UUID-2345', + 'HTTP_DEPTH' => '0', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); + + $xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); + + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:href', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', + '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', + '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', + ); + + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); + + if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); + + } + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReport1ObjectNoCalData() { + + $body = + '' . + '' . + '' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1/UUID-123467/UUID-2345', + 'HTTP_DEPTH' => '0', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); + + $xml = simplexml_load_string(Sabre_DAV_XMLUtil::convertDAVNamespace($this->response->body)); + + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:href', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', + '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', + ); + + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); + + if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); + + } + + } + + function testHTMLActionsPanel() { + + $output = ''; + $r = $this->server->broadcastEvent('onHTMLActionsPanel', array($this->server->tree->getNodeForPath('calendars/user1'), &$output)); + $this->assertFalse($r); + + $this->assertTrue(!!strpos($output,'Display name')); + + } + + function testBrowserPostAction() { + + $r = $this->server->broadcastEvent('onBrowserPostAction', array('calendars/user1', 'mkcalendar', array( + 'name' => 'NEWCALENDAR', + '{DAV:}displayname' => 'foo', + ))); + $this->assertFalse($r); + + $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); + $this->assertEquals(3, count($calendars)); + + $newCalendar = null; + foreach($calendars as $calendar) { + if ($calendar['uri'] === 'NEWCALENDAR') { + $newCalendar = $calendar; + break; + } + } + if (!$newCalendar) + $this->fail('Could not find newly created calendar'); + + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportNoEnd() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1', + 'HTTP_DEPTH' => '1', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportNoStart() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1', + 'HTTP_DEPTH' => '1', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportEndBeforeStart() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/calendars/user1', + 'HTTP_DEPTH' => '1', + )); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Principal/CollectionTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/CollectionTest.php new file mode 100644 index 000000000..0495df102 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/CollectionTest.php @@ -0,0 +1,18 @@ +getChildForPrincipal(array( + 'uri' => 'principals/admin', + )); + $this->assertInstanceOf('Sabre_CalDAV_Principal_User', $r); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Principal/ProxyReadTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/ProxyReadTest.php new file mode 100644 index 000000000..41ec2f260 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/ProxyReadTest.php @@ -0,0 +1,98 @@ + 'principal/user', + )); + $this->backend = $backend; + return $principal; + + } + + function testGetName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-read', $i->getName()); + + } + function testGetDisplayName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-read', $i->getDisplayName()); + + } + + function testGetLastModified() { + + $i = $this->getInstance(); + $this->assertNull($i->getLastModified()); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + function testDelete() { + + $i = $this->getInstance(); + $i->delete(); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + function testSetName() { + + $i = $this->getInstance(); + $i->setName('foo'); + + } + + function testGetAlternateUriSet() { + + $i = $this->getInstance(); + $this->assertEquals(array(), $i->getAlternateUriSet()); + + } + + function testGetPrincipalUri() { + + $i = $this->getInstance(); + $this->assertEquals('principal/user/calendar-proxy-read', $i->getPrincipalUrl()); + + } + + function testGetGroupMemberSet() { + + $i = $this->getInstance(); + $this->assertEquals(array(), $i->getGroupMemberSet()); + + } + + function testGetGroupMembership() { + + $i = $this->getInstance(); + $this->assertEquals(array(), $i->getGroupMembership()); + + } + + function testSetGroupMemberSet() { + + $i = $this->getInstance(); + $i->setGroupMemberSet(array('principals/foo')); + + $expected = array( + $i->getPrincipalUrl() => array('principals/foo') + ); + + $this->assertEquals($expected, $this->backend->groupMembers); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Principal/ProxyWriteTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/ProxyWriteTest.php new file mode 100644 index 000000000..02bce59b8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/ProxyWriteTest.php @@ -0,0 +1,36 @@ + 'principal/user', + )); + $this->backend = $backend; + return $principal; + + } + + function testGetName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-write', $i->getName()); + + } + function testGetDisplayName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-write', $i->getDisplayName()); + + } + + function testGetPrincipalUri() { + + $i = $this->getInstance(); + $this->assertEquals('principal/user/calendar-proxy-write', $i->getPrincipalUrl()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Principal/UserTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/UserTest.php new file mode 100644 index 000000000..ddd20ade3 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Principal/UserTest.php @@ -0,0 +1,123 @@ +addPrincipal(array( + 'uri' => 'principals/user/calendar-proxy-read', + )); + $backend->addPrincipal(array( + 'uri' => 'principals/user/calendar-proxy-write', + )); + $backend->addPrincipal(array( + 'uri' => 'principals/user/random', + )); + return new Sabre_CalDAV_Principal_User($backend, array( + 'uri' => 'principals/user', + )); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + function testCreateFile() { + + $u = $this->getInstance(); + $u->createFile('test'); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + function testCreateDirectory() { + + $u = $this->getInstance(); + $u->createDirectory('test'); + + } + + function testGetChildProxyRead() { + + $u = $this->getInstance(); + $child = $u->getChild('calendar-proxy-read'); + $this->assertInstanceOf('Sabre_CalDAV_Principal_ProxyRead', $child); + + } + + function testGetChildProxyWrite() { + + $u = $this->getInstance(); + $child = $u->getChild('calendar-proxy-write'); + $this->assertInstanceOf('Sabre_CalDAV_Principal_ProxyWrite', $child); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotFound + */ + function testGetChildNotFound() { + + $u = $this->getInstance(); + $child = $u->getChild('foo'); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotFound + */ + function testGetChildNotFound2() { + + $u = $this->getInstance(); + $child = $u->getChild('random'); + + } + + function testGetChildren() { + + $u = $this->getInstance(); + $children = $u->getChildren(); + $this->assertEquals(2, count($children)); + $this->assertInstanceOf('Sabre_CalDAV_Principal_ProxyRead', $children[0]); + $this->assertInstanceOf('Sabre_CalDAV_Principal_ProxyWrite', $children[1]); + + } + + function testChildExist() { + + $u = $this->getInstance(); + $this->assertTrue($u->childExists('calendar-proxy-read')); + $this->assertTrue($u->childExists('calendar-proxy-write')); + $this->assertFalse($u->childExists('foo')); + + } + + function testGetACL() { + + $expected = array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user/calendar-proxy-read', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user/calendar-proxy-write', + 'protected' => true, + ), + ); + + $u = $this->getInstance(); + $this->assertEquals($expected, $u->getACL()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php new file mode 100644 index 000000000..52e127c22 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php @@ -0,0 +1,66 @@ +assertEquals(array('VEVENT'), $sccs->getValue()); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $property = new Sabre_CalDAV_Property_SupportedCalendarComponentSet(array('VEVENT','VJOURNAL')); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + $root->setAttribute('xmlns:cal',Sabre_CalDAV_Plugin::NS_CALDAV); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'' . +' +', $xml); + + } + + /** + * @depends testSimple + */ + function testUnserializer() { + + $xml = ' +' . +'' . +'' . +''; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + + $property = Sabre_CalDAV_Property_SupportedCalendarComponentSet::unserialize($dom->firstChild); + + $this->assertTrue($property instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet); + $this->assertEquals(array( + 'VEVENT', + 'VJOURNAL', + ), + $property->getValue()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php new file mode 100644 index 000000000..3b3c84c90 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php @@ -0,0 +1,40 @@ +createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + $root->setAttribute('xmlns:cal',Sabre_CalDAV_Plugin::NS_CALDAV); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +' +', $xml); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php new file mode 100644 index 000000000..75ce2413a --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php @@ -0,0 +1,42 @@ +createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + $root->setAttribute('xmlns:cal',Sabre_CalDAV_Plugin::NS_CALDAV); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'i;ascii-casemap' . +'i;octet' . +'i;unicode-casemap' . +' +', $xml); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Schedule/IMip/Mock.php b/dav/SabreDAV/tests/Sabre/CalDAV/Schedule/IMip/Mock.php new file mode 100644 index 000000000..3dc83fff6 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Schedule/IMip/Mock.php @@ -0,0 +1,50 @@ +emails[] = array( + 'to' => $to, + 'subject' => $subject, + 'body' => $body, + 'headers' => $headers, + ); + + } + + public function getSentEmails() { + + return $this->emails; + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/Schedule/OutboxTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/Schedule/OutboxTest.php new file mode 100644 index 000000000..b6b7afdc4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/Schedule/OutboxTest.php @@ -0,0 +1,58 @@ +assertEquals('outbox', $outbox->getName()); + $this->assertEquals(array(), $outbox->getChildren()); + $this->assertEquals('principals/user1', $outbox->getOwner()); + $this->assertEquals(null, $outbox->getGroup()); + + $this->assertEquals(array( + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ), + ), $outbox->getACL()); + + $ok = false; + try { + $outbox->setACL(array()); + } catch (Sabre_DAV_Exception_MethodNotAllowed $e) { + $ok = true; + } + if (!$ok) { + $this->fail('Exception was not emitted'); + } + + } + + function testGetSupportedPrivilegeSet() { + + $outbox = new Sabre_CalDAV_Schedule_Outbox('principals/user1'); + $r = $outbox->getSupportedPrivilegeSet(); + + $ok = false; + foreach($r['aggregates'] as $priv) { + + if ($priv['privilege'] == '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy') { + $ok = true; + } + } + + if (!$ok) { + $this->fail('{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy was not found as a supported privilege'); + } + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/ServerTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/ServerTest.php new file mode 100644 index 000000000..f00be7b18 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/ServerTest.php @@ -0,0 +1,31 @@ +markTestSkipped('SQLite driver is not available'); + $pdo = Sabre_CalDAV_TestUtil::getSQLiteDB(); + $server = new Sabre_CalDAV_Server($pdo); + + $authPlugin = $server->getPlugin('auth'); + $this->assertTrue($authPlugin instanceof Sabre_DAV_Auth_Plugin); + + $caldavPlugin = $server->getPlugin('caldav'); + $this->assertTrue($caldavPlugin instanceof Sabre_CalDAV_Plugin); + + $node = $server->tree->getNodeForPath(''); + $this->assertTrue($node instanceof Sabre_DAV_SimpleCollection); + + $this->assertEquals('root', $node->getName()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/TestUtil.php b/dav/SabreDAV/tests/Sabre/CalDAV/TestUtil.php new file mode 100644 index 000000000..01065feca --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/TestUtil.php @@ -0,0 +1,206 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + + // Yup this is definitely not 'fool proof', but good enough for now. + $queries = explode(';', file_get_contents(__DIR__ . '/../../../examples/sql/sqlite.calendars.sql')); + foreach($queries as $query) { + $pdo->exec($query); + } + // Inserting events through a backend class. + $backend = new Sabre_CalDAV_Backend_PDO($pdo); + $calendarId = $backend->createCalendar( + 'principals/user1', + 'UUID-123467', + array( + '{DAV:}displayname' => 'user1 calendar', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', + '{http://apple.com/ns/ical/}calendar-order' => '1', + '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', + ) + ); + $backend->createCalendar( + 'principals/user1', + 'UUID-123468', + array( + '{DAV:}displayname' => 'user1 calendar2', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', + '{http://apple.com/ns/ical/}calendar-order' => '1', + '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', + ) + ); + $backend->createCalendarObject($calendarId, 'UUID-2345', self::getTestCalendarData()); + return $pdo; + + } + + static function getTestCalendarData($type = 1) { + + $calendarData = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//iCal 4.0.1//EN +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Asia/Seoul +BEGIN:DAYLIGHT +TZOFFSETFROM:+0900 +RRULE:FREQ=YEARLY;UNTIL=19880507T150000Z;BYMONTH=5;BYDAY=2SU +DTSTART:19870510T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+1000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1000 +DTSTART:19881009T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+0900 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20100225T154229Z +UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 +TRANSP:TRANSPARENT +SUMMARY:Something here +DTSTAMP:20100228T130202Z'; + + switch($type) { + case 1 : + $calendarData.="\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDTEND;TZID=Asia/Seoul:20100223T070000\n"; + break; + case 2 : + $calendarData.="\nDTSTART:20100223T060000\nDTEND:20100223T070000\n"; + break; + case 3 : + $calendarData.="\nDTSTART;VALUE=DATE:20100223\nDTEND;VALUE=DATE:20100223\n"; + break; + case 4 : + $calendarData.="\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDURATION:PT1H\n"; + break; + case 5 : + $calendarData.="\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDURATION:-P5D\n"; + break; + case 6 : + $calendarData.="\nDTSTART;VALUE=DATE:20100223\n"; + break; + case 7 : + $calendarData.="\nDTSTART;VALUE=DATETIME:20100223T060000\n"; + break; + + // No DTSTART, so intentionally broken + case 'X' : + $calendarData.="\n"; + break; + } + + + $calendarData.='ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com +SEQUENCE:2 +END:VEVENT +END:VCALENDAR'; + + return $calendarData; + + } + + static function getTestTODO($type = 'due') { + + switch($type) { + + case 'due' : + $extra = "DUE:20100104T000000Z"; + break; + case 'due2' : + $extra = "DUE:20060104T000000Z"; + break; + case 'due_date' : + $extra = "DUE;VALUE=DATE:20060104"; + break; + case 'due_tz' : + $extra = "DUE;TZID=Asia/Seoul:20060104T000000Z"; + break; + case 'due_dtstart' : + $extra = "DTSTART:20050223T060000Z\nDUE:20060104T000000Z"; + break; + case 'due_dtstart2' : + $extra = "DTSTART:20090223T060000Z\nDUE:20100104T000000Z"; + break; + case 'dtstart' : + $extra = 'DTSTART:20100223T060000Z'; + break; + case 'dtstart2' : + $extra = 'DTSTART:20060223T060000Z'; + break; + case 'dtstart_date' : + $extra = 'DTSTART;VALUE=DATE:20100223'; + break; + case 'dtstart_tz' : + $extra = 'DTSTART;TZID=Asia/Seoul:20100223T060000Z'; + break; + case 'dtstart_duration' : + $extra = "DTSTART:20061023T060000Z\nDURATION:PT1H"; + break; + case 'dtstart_duration2' : + $extra = "DTSTART:20101023T060000Z\nDURATION:PT1H"; + break; + case 'completed' : + $extra = 'COMPLETED:20060601T000000Z'; + break; + case 'completed2' : + $extra = 'COMPLETED:20090601T000000Z'; + break; + case 'created' : + $extra = 'CREATED:20060601T000000Z'; + break; + case 'created2' : + $extra = 'CREATED:20090601T000000Z'; + break; + case 'completedcreated' : + $extra = "CREATED:20060601T000000Z\nCOMPLETED:20070101T000000Z"; + break; + case 'completedcreated2' : + $extra = "CREATED:20090601T000000Z\nCOMPLETED:20100101T000000Z"; + break; + case 'notime' : + $extra = 'X-FILLER:oh hello'; + break; + default : + throw new Exception('Unknown type: ' . $type); + + } + + $todo = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Example Corp.//CalDAV Client//EN +BEGIN:VTODO +DTSTAMP:20060205T235335Z +' . $extra . ' +STATUS:NEEDS-ACTION +SUMMARY:Task #1 +UID:DDDEEB7915FA61233B861457@example.com +BEGIN:VALARM +ACTION:AUDIO +TRIGGER;RELATED=START:-PT10M +END:VALARM +END:VTODO +END:VCALENDAR'; + + return $todo; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/UserCalendarsTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/UserCalendarsTest.php new file mode 100644 index 000000000..7dfb6121d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/UserCalendarsTest.php @@ -0,0 +1,185 @@ +markTestSkipped('SQLite driver is not available'); + $this->backend = Sabre_CalDAV_TestUtil::getBackend(); + $this->principalBackend = new Sabre_DAVACL_MockPrincipalBackend('realm'); + $this->usercalendars = new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->backend, 'principals/user1'); + + } + + function testSimple() { + + $this->assertEquals('user1',$this->usercalendars->getName()); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotFound + * @depends testSimple + */ + function testGetChildNotFound() { + + $this->usercalendars->getChild('randomname'); + + } + + function testChildExists() { + + $this->assertFalse($this->usercalendars->childExists('foo')); + $this->assertTrue($this->usercalendars->childExists('UUID-123467')); + + } + + function testGetOwner() { + + $this->assertEquals('principals/user1', $this->usercalendars->getOwner()); + + } + + function testGetGroup() { + + $this->assertNull($this->usercalendars->getGroup()); + + } + + function testGetACL() { + + $expected = array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ), + ); + $this->assertEquals($expected, $this->usercalendars->getACL()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetACL() { + + $this->usercalendars->setACL(array()); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + * @depends testSimple + */ + function testSetName() { + + $this->usercalendars->setName('bla'); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + * @depends testSimple + */ + function testDelete() { + + $this->usercalendars->delete(); + + } + + /** + * @depends testSimple + */ + function testGetLastModified() { + + $this->assertNull($this->usercalendars->getLastModified()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + * @depends testSimple + */ + function testCreateFile() { + + $this->usercalendars->createFile('bla'); + + } + + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + * @depends testSimple + */ + function testCreateDirectory() { + + $this->usercalendars->createDirectory('bla'); + + } + + /** + * @depends testSimple + */ + function testCreateExtendedCollection() { + + $result = $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection', '{urn:ietf:params:xml:ns:caldav}calendar'), array()); + $this->assertNull($result); + $cals = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(3,count($cals)); + + } + + /** + * @expectedException Sabre_DAV_Exception_InvalidResourceType + * @depends testSimple + */ + function testCreateExtendedCollectionBadResourceType() { + + $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection','{DAV:}blabla'), array()); + + } + + function testGetSupportedPrivilegesSet() { + + $this->assertNull($this->usercalendars->getSupportedPrivilegeSet()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/ValidateICalTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/ValidateICalTest.php new file mode 100644 index 000000000..f2a5e5563 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/ValidateICalTest.php @@ -0,0 +1,210 @@ + 'calendar1', + 'principaluri' => 'principals/admin', + 'uri' => 'calendar1', + ) + ); + + $this->calBackend = new Sabre_CalDAV_Backend_Mock($calendars,array()); + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $tree = array( + new Sabre_CalDAV_CalendarRootNode($principalBackend, $this->calBackend), + ); + + $this->server = new Sabre_DAV_Server($tree); + $this->server->debugExceptions = true; + + $plugin = new Sabre_CalDAV_Plugin(); + $this->server->addPlugin($plugin); + + $response = new Sabre_HTTP_ResponseMock(); + $this->server->httpResponse = $response; + + } + + function request(Sabre_HTTP_Request $request) { + + $this->server->httpRequest = $request; + $this->server->exec(); + + return $this->server->httpResponse; + + } + + function testCreateFile() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status); + + } + + function testCreateFileValid() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + $expected = array( + 'uri' => 'blabla.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + 'calendarid' => 'calendar1', + ); + + $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics')); + + } + + function testCreateFileNoComponents() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $request->setBody("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFileNoUID() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFileVCard() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFile2Components() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VJOURNAL\r\nUID:foo\r\nEND:VJOURNAL\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFile2UIDS() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nUID:bar\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFileWrongComponent() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VFREEBUSY\r\nUID:foo\r\nEND:VFREEBUSY\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testUpdateFile() { + + $this->calBackend->createCalendarObject('calendar1','blabla.ics','foo'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status); + + } + + function testUpdateFileParsableBody() { + + $this->calBackend->createCalendarObject('calendar1','blabla.ics','foo'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + )); + $body = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $request->setBody($body); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 204 No Content', $response->status); + + $expected = array( + 'uri' => 'blabla.ics', + 'calendardata' => $body, + 'calendarid' => 'calendar1', + ); + + $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics')); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CalDAV/VersionTest.php b/dav/SabreDAV/tests/Sabre/CalDAV/VersionTest.php new file mode 100644 index 000000000..717edd8a4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CalDAV/VersionTest.php @@ -0,0 +1,15 @@ +assertEquals(-1, version_compare('1.0.0',$v)); + + $s = Sabre_CalDAV_Version::STABILITY; + $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/AbstractPluginTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/AbstractPluginTest.php new file mode 100644 index 000000000..3b40d45ef --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/AbstractPluginTest.php @@ -0,0 +1,39 @@ +backend = new Sabre_CardDAV_Backend_Mock(); + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $tree = array( + new Sabre_CardDAV_AddressBookRoot($principalBackend, $this->backend), + new Sabre_DAVACL_PrincipalCollection($principalBackend) + ); + + $this->plugin = new Sabre_CardDAV_Plugin(); + $this->plugin->directories = array('directory'); + $this->server = new Sabre_DAV_Server($tree); + $this->server->addPlugin($this->plugin); + $this->server->debugExceptions = true; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookQueryParserTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookQueryParserTest.php new file mode 100644 index 000000000..54fa53449 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookQueryParserTest.php @@ -0,0 +1,325 @@ +parse(); + return $q; + + } + + function testFilterBasic() { + + $xml = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '' + ); + + $q = $this->parse($xml); + + $this->assertEquals( + array('{DAV:}foo'), + $q->requestedProperties + ); + + $this->assertEquals( + array( + array( + 'name' => 'NICKNAME', + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => array(), + 'text-matches' => array(), + ), + ), + $q->filters + ); + + $this->assertNull($q->limit); + $this->assertEquals('anyof', $q->test); + + } + + function testNoFilter() { + + // This is non-standard, but helps working around a KDE bug + $xml = array( + '', + '', + ' ', + ' ', + ' ', + '' + ); + + $q = $this->parse($xml); + + $this->assertEquals( + array('{DAV:}foo'), + $q->requestedProperties + ); + + $this->assertEquals( + array(), + $q->filters + ); + + $this->assertNull($q->limit); + $this->assertEquals('anyof', $q->test); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testFilterDoubleFilter() { + + $xml = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '' + ); + + $q = $this->parse($xml); + + } + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testFilterCorruptTest() { + + $xml = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '' + ); + + $q = $this->parse($xml); + + } + + function testPropFilter() { + + $xml = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' 4', + '' + ); + + $q = $this->parse($xml); + + $this->assertEquals( + array( + array( + 'name' => 'NICKNAME', + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => array(), + 'text-matches' => array(), + ), + array( + 'name' => 'EMAIL', + 'test' => 'allof', + 'is-not-defined' => false, + 'param-filters' => array(), + 'text-matches' => array(), + ), + array( + 'name' => 'FN', + 'test' => 'anyof', + 'is-not-defined' => true, + 'param-filters' => array(), + 'text-matches' => array(), + ), + ), + $q->filters + ); + + $this->assertEquals(4,$q->limit); + $this->assertEquals('allof', $q->test); + + } + + function testParamFilter() { + + $xml = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '' + ); + + $q = $this->parse($xml); + + $this->assertEquals( + array( + array( + 'name' => 'NICKNAME', + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => array( + array( + 'name' => 'BLA', + 'is-not-defined' => false, + 'text-match' => null + ), + array( + 'name' => 'BLA2', + 'is-not-defined' => true, + 'text-match' => null + ), + ), + 'text-matches' => array(), + ), + ), + $q->filters + ); + + } + + function testTextMatch() { + + $xml = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' evert', + ' evert', + ' rene', + ' e', + ' ', + ' foo', + ' ', + ' ', + ' ', + '' + ); + + $q = $this->parse($xml); + + $this->assertEquals( + array( + array( + 'name' => 'NICKNAME', + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => array( + array( + 'name' => 'BLA', + 'is-not-defined' => false, + 'text-match' => array( + 'negate-condition' => false, + 'collation' => 'i;unicode-casemap', + 'match-type' => 'contains', + 'value' => 'foo', + ), + ), + ), + 'text-matches' => array( + array( + 'negate-condition' => false, + 'collation' => 'i;unicode-casemap', + 'match-type' => 'contains', + 'value' => 'evert', + ), + array( + 'negate-condition' => false, + 'collation' => 'i;octet', + 'match-type' => 'contains', + 'value' => 'evert', + ), + array( + 'negate-condition' => true, + 'collation' => 'i;unicode-casemap', + 'match-type' => 'contains', + 'value' => 'rene', + ), + array( + 'negate-condition' => false, + 'collation' => 'i;unicode-casemap', + 'match-type' => 'starts-with', + 'value' => 'e', + ), + ), + ), + ), + $q->filters + ); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testBadTextMatch() { + + $xml = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' evert', + ' ', + ' ', + '' + ); + + $q = $this->parse($xml); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookQueryTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookQueryTest.php new file mode 100644 index 000000000..598f653bc --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookQueryTest.php @@ -0,0 +1,187 @@ + 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody( +' + + + + + + + +' + ); + + $response = new Sabre_HTTP_ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new Sabre_DAV_Client(array('baseUri'=>'/')); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals(array( + '/addressbooks/user1/book1/card1' => array( + 200 => array( + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + ), + ), + '/addressbooks/user1/book1/card2' => array( + 200 => array( + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:45678\nEND:VCARD") . '"', + ), + ) + ), $result); + + + } + + function testQueryDepth0() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1/card1', + 'HTTP_DEPTH' => '0', + )); + + $request->setBody( +' + + + + + + + +' + ); + + $response = new Sabre_HTTP_ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new Sabre_DAV_Client(array('baseUri'=>'/')); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals(array( + '/addressbooks/user1/book1/card1' => array( + 200 => array( + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + ), + ), + ), $result); + + + } + + function testQueryNoMatch() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody( +' + + + + + + + +' + ); + + $response = new Sabre_HTTP_ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new Sabre_DAV_Client(array('baseUri'=>'/')); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals(array(), $result); + + } + + function testQueryLimit() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1', + 'HTTP_DEPTH' => '1', + )); + + $request->setBody( +' + + + + + + + + 1 +' + ); + + $response = new Sabre_HTTP_ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new Sabre_DAV_Client(array('baseUri'=>'/')); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals(array( + '/addressbooks/user1/book1/card1' => array( + 200 => array( + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD"). '"', + ), + ), + ), $result); + + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookRootTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookRootTest.php new file mode 100644 index 000000000..aac3ea1df --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookRootTest.php @@ -0,0 +1,27 @@ +assertEquals('addressbooks', $root->getName()); + + } + + function testGetChildForPrincipal() { + + $pBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $cBackend = new Sabre_CardDAV_Backend_Mock(); + $root = new Sabre_CardDAV_AddressBookRoot($pBackend, $cBackend); + + $children = $root->getChildren(); + $this->assertEquals(3, count($children)); + + $this->assertInstanceOf('Sabre_CardDAV_UserAddressBooks', $children[0]); + $this->assertEquals('user1', $children[0]->getName()); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookTest.php new file mode 100644 index 000000000..f0271db4d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/AddressBookTest.php @@ -0,0 +1,159 @@ +backend = new Sabre_CardDAV_Backend_Mock(); + $this->ab = new Sabre_CardDAV_AddressBook( + $this->backend, + array( + 'uri' => 'book1', + 'id' => 'foo', + '{DAV:}displayname' => 'd-name', + 'principaluri' => 'principals/user1', + ) + ); + + } + + function testGetName() { + + $this->assertEquals('book1', $this->ab->getName()); + + } + + function testGetChild() { + + $card = $this->ab->getChild('card1'); + $this->assertInstanceOf('Sabre_CardDAV_Card', $card); + $this->assertEquals('card1', $card->getName()); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotFound + */ + function testGetChildNotFound() { + + $card = $this->ab->getChild('card3'); + + } + + function testGetChildren() { + + $cards = $this->ab->getChildren(); + $this->assertEquals(2, count($cards)); + + $this->assertEquals('card1', $cards[0]->getName()); + $this->assertEquals('card2', $cards[1]->getName()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testCreateDirectory() { + + $this->ab->createDirectory('name'); + + } + + function testCreateFile() { + + $file = fopen('php://memory','r+'); + fwrite($file,'foo'); + rewind($file); + $this->ab->createFile('card2',$file); + + $this->assertEquals('foo', $this->backend->cards['foo']['card2']); + + } + + function testDelete() { + + $this->ab->delete(); + $this->assertEquals(array(), $this->backend->addressBooks); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetName() { + + $this->ab->setName('foo'); + + } + + function testGetLastModified() { + + $this->assertNull($this->ab->getLastModified()); + + } + + function testUpdateProperties() { + + $this->assertTrue( + $this->ab->updateProperties(array('{DAV:}displayname' => 'barrr')) + ); + + $this->assertEquals('barrr', $this->backend->addressBooks[0]['{DAV:}displayname']); + + } + + function testGetProperties() { + + $props = $this->ab->getProperties(array('{DAV:}displayname')); + $this->assertEquals(array( + '{DAV:}displayname' => 'd-name', + ), $props); + + } + + function testACLMethods() { + + $this->assertEquals('principals/user1', $this->ab->getOwner()); + $this->assertNull($this->ab->getGroup()); + $this->assertEquals(array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ), + ), $this->ab->getACL()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetACL() { + + $this->ab->setACL(array()); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->ab->getSupportedPrivilegeSet() + ); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php new file mode 100644 index 000000000..02179ff0c --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php @@ -0,0 +1,245 @@ +backend = new Sabre_CardDAV_Backend_PDO($this->getPDO()); + + } + + public function testGetAddressBooksForUser() { + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = array( + array( + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => new Sabre_CardDAV_Property_SupportedAddressData(), + ) + ); + + $this->assertEquals($expected, $result); + + } + + public function testUpdateAddressBookInvalidProp() { + + $result = $this->backend->updateAddressBook(1, array( + '{DAV:}displayname' => 'updated', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', + '{DAV:}foo' => 'bar', + )); + + $this->assertFalse($result); + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = array( + array( + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => new Sabre_CardDAV_Property_SupportedAddressData(), + ) + ); + + $this->assertEquals($expected, $result); + + } + + public function testUpdateAddressBookNoProps() { + + $result = $this->backend->updateAddressBook(1, array()); + + $this->assertFalse($result); + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = array( + array( + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => new Sabre_CardDAV_Property_SupportedAddressData(), + ) + ); + + $this->assertEquals($expected, $result); + + + } + + public function testUpdateAddressBookSuccess() { + + $result = $this->backend->updateAddressBook(1, array( + '{DAV:}displayname' => 'updated', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', + )); + + $this->assertTrue($result); + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = array( + array( + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'updated', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', + '{http://calendarserver.org/ns/}getctag' => 2, + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => new Sabre_CardDAV_Property_SupportedAddressData(), + ) + ); + + $this->assertEquals($expected, $result); + + + } + + public function testDeleteAddressBook() { + + $this->backend->deleteAddressBook(1); + + $this->assertEquals(array(), $this->backend->getAddressBooksForUser('principals/user1')); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + public function testCreateAddressBookUnsupportedProp() { + + $this->backend->createAddressBook('principals/user1','book2', array( + '{DAV:}foo' => 'bar', + )); + + } + + public function testCreateAddressBookSuccess() { + + $this->backend->createAddressBook('principals/user1','book2', array( + '{DAV:}displayname' => 'book2', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 2', + )); + + $expected = array( + array( + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => new Sabre_CardDAV_Property_SupportedAddressData(), + ), + array( + 'id' => 2, + 'uri' => 'book2', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book2', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 2', + '{http://calendarserver.org/ns/}getctag' => 1, + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => new Sabre_CardDAV_Property_SupportedAddressData(), + ) + ); + $result = $this->backend->getAddressBooksForUser('principals/user1'); + $this->assertEquals($expected, $result); + + } + + public function testGetCards() { + + $result = $this->backend->getCards(1); + + $expected = array( + array( + 'id' => 1, + 'uri' => 'card1', + 'carddata' => 'card1', + 'lastmodified' => 0, + ) + ); + + $this->assertEquals($expected, $result); + + } + + public function testGetCard() { + + $result = $this->backend->getCard(1,'card1'); + + $expected = array( + 'id' => 1, + 'uri' => 'card1', + 'carddata' => 'card1', + 'lastmodified' => 0, + ); + + $this->assertEquals($expected, $result); + + } + + /** + * @depends testGetCard + */ + public function testCreateCard() { + + $result = $this->backend->createCard(1, 'card2', 'data2'); + $this->assertEquals('"' . md5('data2') . '"', $result); + $result = $this->backend->getCard(1,'card2'); + $this->assertEquals(2, $result['id']); + $this->assertEquals('card2', $result['uri']); + $this->assertEquals('data2', $result['carddata']); + + } + + /** + * @depends testGetCard + */ + public function testUpdateCard() { + + $result = $this->backend->updateCard(1, 'card1', 'newdata'); + $this->assertEquals('"' . md5('newdata') . '"', $result); + + $result = $this->backend->getCard(1,'card1'); + $this->assertEquals(1, $result['id']); + $this->assertEquals('newdata', $result['carddata']); + + } + + /** + * @depends testGetCard + */ + public function testDeleteCard() { + + $this->backend->deleteCard(1, 'card1'); + $result = $this->backend->getCard(1,'card1'); + $this->assertFalse($result); + + } +} + diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/Backend/Mock.php b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/Mock.php new file mode 100644 index 000000000..bd66dbb4d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/Mock.php @@ -0,0 +1,125 @@ +addressBooks = $addressBooks; + $this->cards = $cards; + + if (is_null($this->addressBooks)) { + $this->addressBooks = array( + array( + 'id' => 'foo', + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'd-name', + ), + ); + + $this->cards = array( + 'foo' => array( + 'card1' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", + 'card2' => "BEGIN:VCARD\nVERSION:3.0\nUID:45678\nEND:VCARD", + ), + ); + } + + } + + + function getAddressBooksForUser($principalUri) { + + $books = array(); + foreach($this->addressBooks as $book) { + if ($book['principaluri'] === $principalUri) { + $books[] = $book; + } + } + return $books; + + } + + function updateAddressBook($addressBookId, array $mutations) { + + foreach($this->addressBooks as &$book) { + if ($book['id'] !== $addressBookId) + continue; + + foreach($mutations as $key=>$value) { + $book[$key] = $value; + } + return true; + } + return false; + + } + + function createAddressBook($principalUri, $url, array $properties) { + + $this->addressBooks[] = array_merge($properties, array( + 'id' => $url, + 'uri' => $url, + 'principaluri' => $principalUri, + )); + + } + + function deleteAddressBook($addressBookId) { + + foreach($this->addressBooks as $key=>$value) { + if ($value['id'] === $addressBookId) + unset($this->addressBooks[$key]); + } + unset($this->cards[$addressBookId]); + + } + + function getCards($addressBookId) { + + $cards = array(); + foreach($this->cards[$addressBookId] as $uri=>$data) { + $cards[] = array( + 'uri' => $uri, + 'carddata' => $data, + ); + } + return $cards; + + } + + function getCard($addressBookId, $cardUri) { + + if (!isset($this->cards[$addressBookId][$cardUri])) { + return false; + } + + return array( + 'uri' => $cardUri, + 'carddata' => $this->cards[$addressBookId][$cardUri], + ); + + } + + function createCard($addressBookId, $cardUri, $cardData) { + + $this->cards[$addressBookId][$cardUri] = $cardData; + + } + + function updateCard($addressBookId, $cardUri, $cardData) { + + $this->cards[$addressBookId][$cardUri] = $cardData; + + } + + function deleteCard($addressBookId, $cardUri) { + + unset($this->cards[$addressBookId][$cardUri]); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php new file mode 100644 index 000000000..da3ffd7c1 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php @@ -0,0 +1,58 @@ +markTestSkipped('MySQL driver is not available, or not properly configured'); + + $pdo = Sabre_TestUtil::getMySQLDB(); + if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); + + $pdo->query("DROP TABLE IF EXISTS addressbooks"); + $pdo->query("DROP TABLE IF EXISTS cards"); + $pdo->query(" +CREATE TABLE addressbooks ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(255), + displayname VARCHAR(255), + uri VARCHAR(100), + description TEXT, + ctag INT(11) UNSIGNED NOT NULL DEFAULT '1' +); +"); + + $pdo->query(" +INSERT INTO addressbooks + (principaluri, displayname, uri, description, ctag) +VALUES + ('principals/user1', 'book1', 'book1', 'addressbook 1', 1); +"); + + $pdo->query(" +CREATE TABLE cards ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + addressbookid INT(11) UNSIGNED NOT NULL, + carddata TEXT, + uri VARCHAR(100), + lastmodified INT(11) UNSIGNED +); +"); + + $pdo->query(" +INSERT INTO cards + (addressbookid, carddata, uri, lastmodified) +VALUES + (1, 'card1', 'card1', 0); +"); + return $pdo; + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php new file mode 100644 index 000000000..347eb7324 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php @@ -0,0 +1,67 @@ +markTestSkipped('SQLite driver is not available'); + $pdo = new PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); + $pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + + $pdo->query("DROP TABLE IF EXISTS addressbooks"); + $pdo->query("DROP TABLE IF EXISTS cards"); + $pdo->query(" +CREATE TABLE addressbooks ( + id integer primary key asc, + principaluri text, + displayname text, + uri text, + description text, + ctag integer +); + +"); + + $pdo->query(" +INSERT INTO addressbooks + (principaluri, displayname, uri, description, ctag) +VALUES + ('principals/user1', 'book1', 'book1', 'addressbook 1', 1); +"); + + $pdo->query(" + +CREATE TABLE cards ( + id integer primary key asc, + addressbookid integer, + carddata text, + uri text, + lastmodified integer +); + +"); + $pdo->query(" +INSERT INTO cards + (addressbookid, carddata, uri, lastmodified) +VALUES + (1, 'card1', 'card1', 0); +"); + + return $pdo; + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/CardTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/CardTest.php new file mode 100644 index 000000000..7e3643fd0 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/CardTest.php @@ -0,0 +1,182 @@ +backend = new Sabre_CardDAV_Backend_Mock(); + $this->card = new Sabre_CardDAV_Card( + $this->backend, + array( + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ), + array( + 'uri' => 'card1', + 'addressbookid' => 'foo', + 'carddata' => 'card', + ) + ); + + } + + function testGet() { + + $result = $this->card->get(); + $this->assertEquals('card', $result); + + } + function testGet2() { + + $this->card = new Sabre_CardDAV_Card( + $this->backend, + array( + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ), + array( + 'uri' => 'card1', + 'addressbookid' => 'foo', + ) + ); + $result = $this->card->get(); + $this->assertEquals("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", $result); + + } + + + /** + * @depends testGet + */ + function testPut() { + + $file = fopen('php://memory','r+'); + fwrite($file, 'newdata'); + rewind($file); + $this->card->put($file); + $result = $this->card->get(); + $this->assertEquals('newdata', $result); + + } + + + function testDelete() { + + $this->card->delete(); + $this->assertEquals(1, count($this->backend->cards['foo'])); + + } + + function testGetContentType() { + + $this->assertEquals('text/x-vcard; charset=utf-8', $this->card->getContentType()); + + } + + function testGetETag() { + + $this->assertEquals('"' . md5('card') . '"' , $this->card->getETag()); + + } + + function testGetETag2() { + + $card = new Sabre_CardDAV_Card( + $this->backend, + array( + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ), + array( + 'uri' => 'card1', + 'addressbookid' => 'foo', + 'carddata' => 'card', + 'etag' => '"blabla"', + ) + ); + $this->assertEquals('"blabla"' , $card->getETag()); + + } + + function testGetLastModified() { + + $this->assertEquals(null, $this->card->getLastModified()); + + } + + function testGetSize() { + + $this->assertEquals(4, $this->card->getSize()); + $this->assertEquals(4, $this->card->getSize()); + + } + + function testGetSize2() { + + $card = new Sabre_CardDAV_Card( + $this->backend, + array( + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ), + array( + 'uri' => 'card1', + 'addressbookid' => 'foo', + 'etag' => '"blabla"', + 'size' => 4, + ) + ); + $this->assertEquals(4, $card->getSize()); + + } + + function testACLMethods() { + + $this->assertEquals('principals/user1', $this->card->getOwner()); + $this->assertNull($this->card->getGroup()); + $this->assertEquals(array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ), + ), $this->card->getACL()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetACL() { + + $this->card->setACL(array()); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->card->getSupportedPrivilegeSet() + ); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/IDirectoryTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/IDirectoryTest.php new file mode 100644 index 000000000..9a247a034 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/IDirectoryTest.php @@ -0,0 +1,25 @@ +addPlugin($plugin); + + $props = $server->getProperties('directory', array('{DAV:}resourcetype')); + $this->assertTrue($props['{DAV:}resourcetype']->is('{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}directory')); + + } + +} + +class Sabre_CardDAV_DirectoryMock extends Sabre_DAV_SimpleCollection implements Sabre_CardDAV_IDirectory { + + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/MultiGetTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/MultiGetTest.php new file mode 100644 index 000000000..50301c6f4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/MultiGetTest.php @@ -0,0 +1,50 @@ + 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1', + )); + + $request->setBody( +' + + + + + + /addressbooks/user1/book1/card1 +' + ); + + $response = new Sabre_HTTP_ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new Sabre_DAV_Client(array('baseUri'=>'/')); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals(array( + '/addressbooks/user1/book1/card1' => array( + 200 => array( + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", + ) + ) + ), $result); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/PluginTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/PluginTest.php new file mode 100644 index 000000000..f1792d79a --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/PluginTest.php @@ -0,0 +1,146 @@ +assertEquals('card', $this->server->xmlNamespaces[Sabre_CardDAV_Plugin::NS_CARDDAV]); + $this->assertEquals('{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook', $this->server->resourceTypeMapping['Sabre_CardDAV_IAddressBook']); + + $this->assertTrue(in_array('addressbook', $this->plugin->getFeatures())); + + } + + function testSupportedReportSet() { + + $this->assertEquals(array( + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-multiget', + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-query', + ), $this->plugin->getSupportedReportSet('addressbooks/user1/book1')); + + } + + function testSupportedReportSetEmpty() { + + $this->assertEquals(array( + ), $this->plugin->getSupportedReportSet('')); + + } + + function testAddressBookHomeSet() { + + $result = $this->server->getProperties('principals/user1', array('{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-home-set')); + + $this->assertEquals(1, count($result)); + $this->assertTrue(isset($result['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-home-set'])); + $this->assertEquals('addressbooks/user1/', $result['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-home-set']->getHref()); + + } + + function testMeCardTest() { + + $result = $this->server->getProperties( + 'addressbooks/user1', + array( + '{http://calendarserver.org/ns/}me-card', + ) + ); + + $this->assertEquals( + array( + '{http://calendarserver.org/ns/}me-card' => + new Sabre_DAV_Property_Href('addressbooks/user1/book1/vcard1.vcf') + ), + $result + ); + + } + + function testDirectoryGateway() { + + $result = $this->server->getProperties('principals/user1', array('{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}directory-gateway')); + + $this->assertEquals(1, count($result)); + $this->assertTrue(isset($result['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}directory-gateway'])); + $this->assertEquals(array('directory'), $result['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}directory-gateway']->getHrefs()); + + } + + function testReportPassThrough() { + + $this->assertNull($this->plugin->report('{DAV:}foo', new DomDocument())); + + } + + function testHTMLActionsPanel() { + + $output = ''; + $r = $this->server->broadcastEvent('onHTMLActionsPanel', array($this->server->tree->getNodeForPath('addressbooks/user1'), &$output)); + $this->assertFalse($r); + + $this->assertTrue(!!strpos($output,'Display name')); + + } + + function testBrowserPostAction() { + + $r = $this->server->broadcastEvent('onBrowserPostAction', array('addressbooks/user1', 'mkaddressbook', array( + 'name' => 'NEWADDRESSBOOK', + '{DAV:}displayname' => 'foo', + ))); + $this->assertFalse($r); + + $addressbooks = $this->backend->getAddressBooksforUser('principals/user1'); + $this->assertEquals(2, count($addressbooks)); + + $newAddressBook = null; + foreach($addressbooks as $addressbook) { + if ($addressbook['uri'] === 'NEWADDRESSBOOK') { + $newAddressBook = $addressbook; + break; + } + } + if (!$newAddressBook) + $this->fail('Could not find newly created addressbook'); + + } + + function testUpdatePropertiesMeCard() { + + $result = $this->server->updateProperties('addressbooks/user1', array( + '{http://calendarserver.org/ns/}me-card' => new Sabre_DAV_Property_Href('/addressbooks/user1/book1/vcard2',true), + )); + + $this->assertEquals( + array( + 'href' => 'addressbooks/user1', + 200 => array( + '{http://calendarserver.org/ns/}me-card' => null, + ), + ), + $result + ); + + } + + function testUpdatePropertiesMeCardBadValue() { + + $result = $this->server->updateProperties('addressbooks/user1', array( + '{http://calendarserver.org/ns/}me-card' => new Sabre_DAV_Property_HrefList(array()), + )); + + $this->assertEquals( + array( + 'href' => 'addressbooks/user1', + 400 => array( + '{http://calendarserver.org/ns/}me-card' => null, + ), + ), + $result + ); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php new file mode 100644 index 000000000..e85c5d8fd --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php @@ -0,0 +1,39 @@ +createElementNS(Sabre_CardDAV_Plugin::NS_CARDDAV, 'card:root'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $server = new Sabre_DAV_Server(); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'' . +' +', $xml); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/SogoStripContentTypeTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/SogoStripContentTypeTest.php new file mode 100644 index 000000000..8cbc42012 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/SogoStripContentTypeTest.php @@ -0,0 +1,39 @@ + 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + ), + ); + protected $carddavCards = array( + 1 => array( + 'card1.vcf' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", + ), + ); + + function testDontStrip() { + + $result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf',array('{DAV:}getcontenttype')); + $this->assertEquals(array( + '{DAV:}getcontenttype' => 'text/x-vcard; charset=utf-8' + ), $result); + + } + function testStrip() { + + $this->server->httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1', + )); + $result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf',array('{DAV:}getcontenttype')); + $this->assertEquals(array( + '{DAV:}getcontenttype' => 'text/x-vcard' + ), $result); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/UserAddressBooksTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/UserAddressBooksTest.php new file mode 100644 index 000000000..f4183ce31 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/UserAddressBooksTest.php @@ -0,0 +1,160 @@ +backend = new Sabre_CardDAV_Backend_Mock(); + $this->s = new Sabre_CardDAV_UserAddressBooks( + $this->backend, + 'principals/user1' + ); + + } + + function testGetName() { + + $this->assertEquals('user1', $this->s->getName()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetName() { + + $this->s->setName('user2'); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testDelete() { + + $this->s->delete(); + + } + + function testGetLastModified() { + + $this->assertNull($this->s->getLastModified()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testCreateFile() { + + $this->s->createFile('bla'); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testCreateDirectory() { + + $this->s->createDirectory('bla'); + + } + + function testGetChild() { + + $child = $this->s->getChild('book1'); + $this->assertInstanceOf('Sabre_CardDAV_AddressBook', $child); + $this->assertEquals('book1', $child->getName()); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotFound + */ + function testGetChild404() { + + $this->s->getChild('book2'); + + } + + function testGetChildren() { + + $children = $this->s->getChildren(); + $this->assertEquals(1, count($children)); + $this->assertInstanceOf('Sabre_CardDAV_AddressBook', $children[0]); + $this->assertEquals('book1', $children[0]->getName()); + + } + + function testCreateExtendedCollection() { + + $resourceType = array( + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook', + '{DAV:}collection', + ); + $this->s->createExtendedCollection('book2', $resourceType, array('{DAV:}displayname' => 'a-book 2')); + + $this->assertEquals(array( + 'id' => 'book2', + 'uri' => 'book2', + '{DAV:}displayname' => 'a-book 2', + 'principaluri' => 'principals/user1', + ), $this->backend->addressBooks[1]); + + } + + /** + * @expectedException Sabre_DAV_Exception_InvalidResourceType + */ + function testCreateExtendedCollectionInvalid() { + + $resourceType = array( + '{DAV:}collection', + ); + $this->s->createExtendedCollection('book2', $resourceType, array('{DAV:}displayname' => 'a-book 2')); + + } + + + function testACLMethods() { + + $this->assertEquals('principals/user1', $this->s->getOwner()); + $this->assertNull($this->s->getGroup()); + $this->assertEquals(array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ), + ), $this->s->getACL()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testSetACL() { + + $this->s->setACL(array()); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->s->getSupportedPrivilegeSet() + ); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/ValidateFilterTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/ValidateFilterTest.php new file mode 100644 index 000000000..ef7f1932f --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/ValidateFilterTest.php @@ -0,0 +1,202 @@ +assertTrue($this->plugin->validateFilters($input, $filters, $test), $message); + } else { + $this->assertFalse($this->plugin->validateFilters($input, $filters, $test), $message); + } + + } + + function data() { + + $body1 = << 'title', 'is-not-defined' => false, 'param-filters' => array(), 'text-matches' => array()); + + // Check if FOO is defined + $filter2 = + array('name' => 'foo', 'is-not-defined' => false, 'param-filters' => array(), 'text-matches' => array()); + + // Check if TITLE is not defined + $filter3 = + array('name' => 'title', 'is-not-defined' => true, 'param-filters' => array(), 'text-matches' => array()); + + // Check if FOO is not defined + $filter4 = + array('name' => 'foo', 'is-not-defined' => true, 'param-filters' => array(), 'text-matches' => array()); + + // Check if TEL[TYPE] is defined + $filter5 = + array( + 'name' => 'tel', + 'is-not-defined' => false, + 'test' => 'anyof', + 'param-filters' => array( + array( + 'name' => 'type', + 'is-not-defined' => false, + 'text-match' => null + ), + ), + 'text-matches' => array(), + ); + + // Check if TEL[FOO] is defined + $filter6 = $filter5; + $filter6['param-filters'][0]['name'] = 'FOO'; + + // Check if TEL[TYPE] is not defined + $filter7 = $filter5; + $filter7['param-filters'][0]['is-not-defined'] = true; + + // Check if TEL[FOO] is not defined + $filter8 = $filter5; + $filter8['param-filters'][0]['name'] = 'FOO'; + $filter8['param-filters'][0]['is-not-defined'] = true; + + // Combining property filters + $filter9 = $filter5; + $filter9['param-filters'][] = $filter6['param-filters'][0]; + + $filter10 = $filter5; + $filter10['param-filters'][] = $filter6['param-filters'][0]; + $filter10['test'] = 'allof'; + + // Check if URL contains 'google' + $filter11 = + array( + 'name' => 'url', + 'is-not-defined' => false, + 'test' => 'anyof', + 'param-filters' => array(), + 'text-matches' => array( + array( + 'match-type' => 'contains', + 'value' => 'google', + 'negate-condition' => false, + 'collation' => 'i;octet', + ), + ), + ); + + // Check if URL contains 'bing' + $filter12 = $filter11; + $filter12['text-matches'][0]['value'] = 'bing'; + + // Check if URL does not contain 'google' + $filter13 = $filter11; + $filter13['text-matches'][0]['negate-condition'] = true; + + // Check if URL does not contain 'bing' + $filter14 = $filter11; + $filter14['text-matches'][0]['value'] = 'bing'; + $filter14['text-matches'][0]['negate-condition'] = true; + + // Param filter with text + $filter15 = $filter5; + $filter15['param-filters'][0]['text-match'] = array( + 'match-type' => 'contains', + 'value' => 'WORK', + 'collation' => 'i;octet', + 'negate-condition' => false, + ); + $filter16 = $filter15; + $filter16['param-filters'][0]['text-match']['negate-condition'] = true; + + + // Param filter + text filter + $filter17 = $filter5; + $filter17['test'] = 'anyof'; + $filter17['text-matches'][] = array( + 'match-type' => 'contains', + 'value' => '444', + 'collation' => 'i;octet', + 'negate-condition' => false, + ); + + $filter18 = $filter17; + $filter18['text-matches'][0]['negate-condition'] = true; + + $filter18['test'] = 'allof'; + + return array( + + // Basic filters + array($body1, array($filter1), 'anyof',true), + array($body1, array($filter2), 'anyof',false), + array($body1, array($filter3), 'anyof',false), + array($body1, array($filter4), 'anyof',true), + + // Combinations + array($body1, array($filter1, $filter2), 'anyof',true), + array($body1, array($filter1, $filter2), 'allof',false), + array($body1, array($filter1, $filter4), 'anyof',true), + array($body1, array($filter1, $filter4), 'allof',true), + array($body1, array($filter2, $filter3), 'anyof',false), + array($body1, array($filter2, $filter3), 'allof',false), + + // Basic parameters + array($body1, array($filter5), 'anyof', true, 'TEL;TYPE is defined, so this should return true'), + array($body1, array($filter6), 'anyof', false, 'TEL;FOO is not defined, so this should return false'), + + array($body1, array($filter7), 'anyof', false, 'TEL;TYPE is defined, so this should return false'), + array($body1, array($filter8), 'anyof', true, 'TEL;TYPE is not defined, so this should return true'), + + // Combined parameters + array($body1, array($filter9), 'anyof', true), + array($body1, array($filter10), 'anyof', false), + + // Text-filters + array($body1, array($filter11), 'anyof', true), + array($body1, array($filter12), 'anyof', false), + array($body1, array($filter13), 'anyof', false), + array($body1, array($filter14), 'anyof', true), + + // Param filter with text-match + array($body1, array($filter15), 'anyof', true), + array($body1, array($filter16), 'anyof', false), + + // Param filter + text filter + array($body1, array($filter17), 'anyof', true), + array($body1, array($filter18), 'anyof', false), + array($body1, array($filter18), 'anyof', false), + ); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/ValidateVCardTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/ValidateVCardTest.php new file mode 100644 index 000000000..80a5d081c --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/ValidateVCardTest.php @@ -0,0 +1,134 @@ + 'addressbook1', + 'principaluri' => 'principals/admin', + 'uri' => 'addressbook1', + ) + ); + + $this->cardBackend = new Sabre_CardDAV_Backend_Mock($addressbooks,array()); + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $tree = array( + new Sabre_CardDAV_AddressBookRoot($principalBackend, $this->cardBackend), + ); + + $this->server = new Sabre_DAV_Server($tree); + $this->server->debugExceptions = true; + + $plugin = new Sabre_CardDAV_Plugin(); + $this->server->addPlugin($plugin); + + $response = new Sabre_HTTP_ResponseMock(); + $this->server->httpResponse = $response; + + } + + function request(Sabre_HTTP_Request $request) { + + $this->server->httpRequest = $request; + $this->server->exec(); + + return $this->server->httpResponse; + + } + + function testCreateFile() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', + )); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status); + + } + + function testCreateFileValid() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', + )); + $request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 201 Created', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + $expected = array( + 'uri' => 'blabla.vcf', + 'carddata' => "BEGIN:VCARD\r\nEND:VCARD\r\n", + ); + + $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1','blabla.vcf')); + + } + + function testCreateFileVCalendar() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', + )); + $request->setBody("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testUpdateFile() { + + $this->cardBackend->createCard('addressbook1','blabla.vcf','foo'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', + )); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status); + + } + + function testUpdateFileParsableBody() { + + $this->cardBackend->createCard('addressbook1','blabla.vcf','foo'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', + )); + $body = "BEGIN:VCARD\r\nEND:VCARD\r\n"; + $request->setBody($body); + + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 204 No Content', $response->status); + + $expected = array( + 'uri' => 'blabla.vcf', + 'carddata' => $body, + ); + + $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1','blabla.vcf')); + + } +} + +?> diff --git a/dav/SabreDAV/tests/Sabre/CardDAV/VersionTest.php b/dav/SabreDAV/tests/Sabre/CardDAV/VersionTest.php new file mode 100644 index 000000000..caec5719d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/CardDAV/VersionTest.php @@ -0,0 +1,15 @@ +assertEquals(-1, version_compare('0.1',$v)); + + $s = Sabre_CardDAV_Version::STABILITY; + $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/AbstractServer.php b/dav/SabreDAV/tests/Sabre/DAV/AbstractServer.php new file mode 100644 index 000000000..3ba41f9e6 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/AbstractServer.php @@ -0,0 +1,60 @@ +response = new Sabre_HTTP_ResponseMock(); + $this->server = new Sabre_DAV_Server($this->getRootNode()); + $this->server->httpResponse = $this->response; + $this->server->debugExceptions = true; + file_put_contents(SABRE_TEMPDIR . '/test.txt', 'Test contents'); + mkdir(SABRE_TEMPDIR . '/dir'); + file_put_contents(SABRE_TEMPDIR . '/dir/child.txt', 'Child contents'); + + + } + + function tearDown() { + + $this->deleteTree(SABRE_TEMPDIR,false); + + } + + protected function getRootNode() { + + return new Sabre_DAV_FS_Directory(SABRE_TEMPDIR); + + } + + private function deleteTree($path,$deleteRoot = true) { + + foreach(scandir($path) as $node) { + + if ($node=='.' || $node=='.svn' || $node=='..') continue; + $myPath = $path.'/'. $node; + if (is_file($myPath)) { + unlink($myPath); + } else { + $this->deleteTree($myPath); + } + + } + if ($deleteRoot) rmdir($path); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php new file mode 100644 index 000000000..58439a1e0 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php @@ -0,0 +1,87 @@ +httpResponse = $response; + + $backend = new Sabre_DAV_Auth_Backend_AbstractBasicMock(); + $backend->authenticate($server,'myRealm'); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotAuthenticated + */ + public function testAuthenticateUnknownUser() { + + $response = new Sabre_HTTP_ResponseMock(); + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla')); + $server = new Sabre_DAV_Server($tree); + $server->httpResponse = $response; + + $request = new Sabre_HTTP_Request(array( + 'PHP_AUTH_USER' => 'username', + 'PHP_AUTH_PW' => 'wrongpassword', + )); + $server->httpRequest = $request; + + $backend = new Sabre_DAV_Auth_Backend_AbstractBasicMock(); + $backend->authenticate($server,'myRealm'); + + } + + public function testAuthenticate() { + + $response = new Sabre_HTTP_ResponseMock(); + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla')); + $server = new Sabre_DAV_Server($tree); + $server->httpResponse = $response; + + $request = new Sabre_HTTP_Request(array( + 'PHP_AUTH_USER' => 'username', + 'PHP_AUTH_PW' => 'password', + )); + $server->httpRequest = $request; + + $backend = new Sabre_DAV_Auth_Backend_AbstractBasicMock(); + $this->assertTrue($backend->authenticate($server,'myRealm')); + + $result = $backend->getCurrentUser(); + + $this->assertEquals('username', $result); + + } + + +} + + +class Sabre_DAV_Auth_Backend_AbstractBasicMock extends Sabre_DAV_Auth_Backend_AbstractBasic { + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + function validateUserPass($username, $password) { + + return ($username == 'username' && $password == 'password'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php new file mode 100644 index 000000000..6cc46f5cb --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php @@ -0,0 +1,150 @@ +httpResponse = $response; + + $backend = new Sabre_DAV_Auth_Backend_AbstractDigestMock(); + $backend->authenticate($server,'myRealm'); + + } + + /** + * @expectedException Sabre_DAV_Exception + */ + public function testAuthenticateBadGetUserInfoResponse() { + + $response = new Sabre_HTTP_ResponseMock(); + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla')); + $server = new Sabre_DAV_Server($tree); + $server->httpResponse = $response; + + $header = 'username=null, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = new Sabre_HTTP_Request(array( + 'PHP_AUTH_DIGEST' => $header, + )); + $server->httpRequest = $request; + + $backend = new Sabre_DAV_Auth_Backend_AbstractDigestMock(); + $backend->authenticate($server,'myRealm'); + + } + + /** + * @expectedException Sabre_DAV_Exception + */ + public function testAuthenticateBadGetUserInfoResponse2() { + + $response = new Sabre_HTTP_ResponseMock(); + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla')); + $server = new Sabre_DAV_Server($tree); + $server->httpResponse = $response; + + $header = 'username=array, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = new Sabre_HTTP_Request(array( + 'PHP_AUTH_DIGEST' => $header, + )); + $server->httpRequest = $request; + + $backend = new Sabre_DAV_Auth_Backend_AbstractDigestMock(); + $backend->authenticate($server,'myRealm'); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotAuthenticated + */ + public function testAuthenticateUnknownUser() { + + $response = new Sabre_HTTP_ResponseMock(); + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla')); + $server = new Sabre_DAV_Server($tree); + $server->httpResponse = $response; + + $header = 'username=false, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = new Sabre_HTTP_Request(array( + 'PHP_AUTH_DIGEST' => $header, + )); + $server->httpRequest = $request; + + $backend = new Sabre_DAV_Auth_Backend_AbstractDigestMock(); + $backend->authenticate($server,'myRealm'); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotAuthenticated + */ + public function testAuthenticateBadPassword() { + + $response = new Sabre_HTTP_ResponseMock(); + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla')); + $server = new Sabre_DAV_Server($tree); + $server->httpResponse = $response; + + $header = 'username=user, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = new Sabre_HTTP_Request(array( + 'PHP_AUTH_DIGEST' => $header, + 'REQUEST_METHOD' => 'PUT', + )); + $server->httpRequest = $request; + + $backend = new Sabre_DAV_Auth_Backend_AbstractDigestMock(); + $backend->authenticate($server,'myRealm'); + + } + + public function testAuthenticate() { + + $response = new Sabre_HTTP_ResponseMock(); + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla')); + $server = new Sabre_DAV_Server($tree); + $server->httpResponse = $response; + + $digestHash = md5('HELLO:12345:1:1:auth:' . md5('GET:/')); + $header = 'username=user, realm=myRealm, nonce=12345, uri=/, response='.$digestHash.', opaque=1, qop=auth, nc=1, cnonce=1'; + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + 'PHP_AUTH_DIGEST' => $header, + 'REQUEST_URI' => '/', + )); + $server->httpRequest = $request; + + $backend = new Sabre_DAV_Auth_Backend_AbstractDigestMock(); + $this->assertTrue($backend->authenticate($server,'myRealm')); + + $result = $backend->getCurrentUser(); + + $this->assertEquals('user', $result); + $this->assertEquals('HELLO', $backend->getDigestHash('myRealm', $result)); + + } + + +} + + +class Sabre_DAV_Auth_Backend_AbstractDigestMock extends Sabre_DAV_Auth_Backend_AbstractDigest { + + function getDigestHash($realm, $userName) { + + switch($userName) { + case 'null' : return null; + case 'false' : return false; + case 'array' : return array(); + case 'user' : return 'HELLO'; + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractPDOTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractPDOTest.php new file mode 100644 index 000000000..8adbf9ca6 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/AbstractPDOTest.php @@ -0,0 +1,31 @@ +getPDO(); + $backend = new Sabre_DAV_Auth_Backend_PDO($pdo); + $this->assertTrue($backend instanceof Sabre_DAV_Auth_Backend_PDO); + + } + + /** + * @depends testConstruct + */ + function testUserInfo() { + + $pdo = $this->getPDO(); + $backend = new Sabre_DAV_Auth_Backend_PDO($pdo); + + $this->assertNull($backend->getDigestHash('realm','blabla')); + + $expected = 'hash'; + + $this->assertEquals($expected, $backend->getDigestHash('realm','user')); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/ApacheTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/ApacheTest.php new file mode 100644 index 000000000..2eb52abb3 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/ApacheTest.php @@ -0,0 +1,40 @@ +authenticate($server,'Realm'); + + } + + function testRemoteUser() { + + $backend = new Sabre_DAV_Auth_Backend_Apache(); + + $server = new Sabre_DAV_Server(); + $request = new Sabre_HTTP_Request(array( + 'REMOTE_USER' => 'username', + )); + $server->httpRequest = $request; + + $this->assertTrue($backend->authenticate($server, 'Realm')); + + $userInfo = 'username'; + + $this->assertEquals($userInfo, $backend->getCurrentUser()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/FileTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/FileTest.php new file mode 100644 index 000000000..9b5f1cdc4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/FileTest.php @@ -0,0 +1,40 @@ +assertTrue($file instanceof Sabre_DAV_Auth_Backend_File); + + } + + /** + * @expectedException Sabre_DAV_Exception + */ + function testLoadFileBroken() { + + file_put_contents(SABRE_TEMPDIR . '/backend','user:realm:hash'); + $file = new Sabre_DAV_Auth_Backend_File(); + $file->loadFile(SABRE_TEMPDIR .'/backend'); + + } + + function testLoadFile() { + + file_put_contents(SABRE_TEMPDIR . '/backend','user:realm:' . md5('user:realm:password')); + $file = new Sabre_DAV_Auth_Backend_File(); + $file->loadFile(SABRE_TEMPDIR . '/backend'); + + $this->assertFalse($file->getDigestHash('realm','blabla')); + $this->assertEquals(md5('user:realm:password'), $file->getDigesthash('realm','user')); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest new file mode 100644 index 000000000..f5b1b75ff --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest @@ -0,0 +1,24 @@ +markTestSkipped('SQLite driver is not available'); + $pdo = new PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); + $pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + $pdo->query('CREATE TABLE users (username TEXT, digesta1 TEXT)'); + $pdo->query('INSERT INTO users VALUES ("user","hash")'); + + return $pdo; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php new file mode 100644 index 000000000..c35cb69ea --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php @@ -0,0 +1,29 @@ +markTestSkipped('MySQL driver is not available, or not properly configured'); + $pdo = Sabre_TestUtil::getMySQLDB(); + if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); + $pdo->query("DROP TABLE IF EXISTS users"); + $pdo->query(" +create table users ( + id integer unsigned not null primary key auto_increment, + username varchar(50), + digesta1 varchar(32), + email varchar(80), + displayname varchar(80), + unique(username) +);"); + + $pdo->query("INSERT INTO users (username,digesta1,email,displayname) VALUES ('user','hash','user@example.org','User')"); + + return $pdo; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOSqliteTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOSqliteTest.php new file mode 100644 index 000000000..bc84c4f6e --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/Backend/PDOSqliteTest.php @@ -0,0 +1,26 @@ +markTestSkipped('SQLite driver is not available'); + $pdo = new PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); + $pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + $pdo->query('CREATE TABLE users (username TEXT, digesta1 TEXT, email VARCHAR(80), displayname VARCHAR(80))'); + $pdo->query('INSERT INTO users VALUES ("user","hash","user@example.org","User")'); + + return $pdo; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/MockBackend.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/MockBackend.php new file mode 100644 index 000000000..6cc6b3efa --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/MockBackend.php @@ -0,0 +1,32 @@ +currentUser = 'admin'; + + } + + function setCurrentUser($user) { + + $this->currentUser = $user; + + } + + function getCurrentUser() { + + return $this->currentUser; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Auth/PluginTest.php b/dav/SabreDAV/tests/Sabre/DAV/Auth/PluginTest.php new file mode 100644 index 000000000..e89f4c5a1 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Auth/PluginTest.php @@ -0,0 +1,80 @@ +assertTrue($plugin instanceof Sabre_DAV_Auth_Plugin); + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('auth')); + + } + + /** + * @depends testInit + */ + function testAuthenticate() { + + $fakeServer = new Sabre_DAV_Server(new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla'))); + $plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + $fakeServer->addPlugin($plugin); + $fakeServer->broadCastEvent('beforeMethod',array('GET','/')); + + } + + + + /** + * @depends testInit + * @expectedException Sabre_DAV_Exception_NotAuthenticated + */ + function testAuthenticateFail() { + + $fakeServer = new Sabre_DAV_Server(new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla'))); + $plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'failme'); + $fakeServer->addPlugin($plugin); + $fakeServer->broadCastEvent('beforeMethod',array('GET','/')); + + } + + function testReportPassThrough() { + + $fakeServer = new Sabre_DAV_Server(new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla'))); + $plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + $fakeServer->addPlugin($plugin); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/', + )); + $request->setBody(''); + + $fakeServer->httpRequest = $request; + $fakeServer->httpResponse = new Sabre_HTTP_ResponseMock(); + $fakeServer->exec(); + + $this->assertEquals('HTTP/1.1 501 Not Implemented', $fakeServer->httpResponse->status); + + } + + /** + * @depends testInit + */ + function testGetCurrentUserPrincipal() { + + $fakeServer = new Sabre_DAV_Server(new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('bla'))); + $plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + $fakeServer->addPlugin($plugin); + $fakeServer->broadCastEvent('beforeMethod',array('GET','/')); + $this->assertEquals('admin', $plugin->getCurrentUser()); + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/DAV/BasicNodeTest.php b/dav/SabreDAV/tests/Sabre/DAV/BasicNodeTest.php new file mode 100644 index 000000000..8297cee41 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/BasicNodeTest.php @@ -0,0 +1,232 @@ +put('hi'); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + public function testGet() { + + $file = new Sabre_DAV_FileMock(); + $file->get(); + + } + + public function testGetSize() { + + $file = new Sabre_DAV_FileMock(); + $this->assertEquals(0,$file->getSize()); + + } + + + public function testGetETag() { + + $file = new Sabre_DAV_FileMock(); + $this->assertNull($file->getETag()); + + } + + public function testGetContentType() { + + $file = new Sabre_DAV_FileMock(); + $this->assertNull($file->getContentType()); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + public function testDelete() { + + $file = new Sabre_DAV_FileMock(); + $file->delete(); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + public function testSetName() { + + $file = new Sabre_DAV_FileMock(); + $file->setName('hi'); + + } + + public function testGetLastModified() { + + $file = new Sabre_DAV_FileMock(); + // checking if lastmod is within the range of a few seconds + $lastMod = $file->getLastModified(); + $compareTime = ($lastMod + 1)-time(); + $this->assertTrue($compareTime < 3); + + } + + public function testGetChild() { + + $dir = new Sabre_DAV_DirectoryMock(); + $file = $dir->getChild('mockfile'); + $this->assertTrue($file instanceof Sabre_DAV_FileMock); + + } + + public function testChildExists() { + + $dir = new Sabre_DAV_DirectoryMock(); + $this->assertTrue($dir->childExists('mockfile')); + + } + + public function testChildExistsFalse() { + + $dir = new Sabre_DAV_DirectoryMock(); + $this->assertFalse($dir->childExists('mockfile2')); + + } + + /** + * @expectedException Sabre_DAV_Exception_NotFound + */ + public function testGetChild404() { + + $dir = new Sabre_DAV_DirectoryMock(); + $file = $dir->getChild('blabla'); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + public function testCreateFile() { + + $dir = new Sabre_DAV_DirectoryMock(); + $dir->createFile('hello','data'); + + } + + /** + * @expectedException Sabre_DAV_Exception_Forbidden + */ + public function testCreateDirectory() { + + $dir = new Sabre_DAV_DirectoryMock(); + $dir->createDirectory('hello'); + + } + + public function testSimpleDirectoryConstruct() { + + $dir = new Sabre_DAV_SimpleCollection('simpledir',array()); + + } + + /** + * @depends testSimpleDirectoryConstruct + */ + public function testSimpleDirectoryConstructChild() { + + $file = new Sabre_DAV_FileMock(); + $dir = new Sabre_DAV_SimpleCollection('simpledir',array($file)); + $file2 = $dir->getChild('mockfile'); + + $this->assertEquals($file,$file2); + + } + + /** + * @expectedException Sabre_DAV_Exception + * @depends testSimpleDirectoryConstruct + */ + public function testSimpleDirectoryBadParam() { + + $dir = new Sabre_DAV_SimpleCollection('simpledir',array('string shouldn\'t be here')); + + } + + /** + * @depends testSimpleDirectoryConstruct + */ + public function testSimpleDirectoryAddChild() { + + $file = new Sabre_DAV_FileMock(); + $dir = new Sabre_DAV_SimpleCollection('simpledir'); + $dir->addChild($file); + $file2 = $dir->getChild('mockfile'); + + $this->assertEquals($file,$file2); + + } + + /** + * @depends testSimpleDirectoryConstruct + * @depends testSimpleDirectoryAddChild + */ + public function testSimpleDirectoryGetChildren() { + + $file = new Sabre_DAV_FileMock(); + $dir = new Sabre_DAV_SimpleCollection('simpledir'); + $dir->addChild($file); + + $this->assertEquals(array($file),$dir->getChildren()); + + } + + /* + * @depends testSimpleDirectoryConstruct + */ + public function testSimpleDirectoryGetName() { + + $dir = new Sabre_DAV_SimpleCollection('simpledir'); + $this->assertEquals('simpledir',$dir->getName()); + + } + + /** + * @depends testSimpleDirectoryConstruct + * @expectedException Sabre_DAV_Exception_NotFound + */ + public function testSimpleDirectoryGetChild404() { + + $dir = new Sabre_DAV_SimpleCollection('simpledir'); + $dir->getChild('blabla'); + + } +} + +class Sabre_DAV_DirectoryMock extends Sabre_DAV_Collection { + + function getName() { + + return 'mockdir'; + + } + + function getChildren() { + + return array(new Sabre_DAV_FileMock()); + + } + +} + +class Sabre_DAV_FileMock extends Sabre_DAV_File { + + function getName() { + + return 'mockfile'; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Browser/GuessContentTypeTest.php b/dav/SabreDAV/tests/Sabre/DAV/Browser/GuessContentTypeTest.php new file mode 100644 index 000000000..ad8c3d2e1 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Browser/GuessContentTypeTest.php @@ -0,0 +1,64 @@ +server->getPropertiesForPath('/somefile.jpg',$properties); + $this->assertArrayHasKey(0,$result); + $this->assertArrayHasKey(404,$result[0]); + $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][404]); + + } + + /** + * @depends testGetProperties + */ + function testGetPropertiesPluginEnabled() { + + $this->server->addPlugin(new Sabre_DAV_Browser_GuessContentType()); + $properties = array( + '{DAV:}getcontenttype', + ); + $result = $this->server->getPropertiesForPath('/somefile.jpg',$properties); + $this->assertArrayHasKey(0,$result); + $this->assertArrayHasKey(200,$result[0]); + $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][200]); + $this->assertEquals('image/jpeg',$result[0][200]['{DAV:}getcontenttype']); + + } + + /** + * @depends testGetPropertiesPluginEnabled + */ + function testGetPropertiesUnknown() { + + $this->server->addPlugin(new Sabre_DAV_Browser_GuessContentType()); + $properties = array( + '{DAV:}getcontenttype', + ); + $result = $this->server->getPropertiesForPath('/somefile.hoi',$properties); + $this->assertArrayHasKey(0,$result); + $this->assertArrayHasKey(404,$result[0]); + $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][404]); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php b/dav/SabreDAV/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php new file mode 100644 index 000000000..5f42d93a8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php @@ -0,0 +1,38 @@ +server->addPlugin(new Sabre_DAV_Browser_MapGetToPropFind()); + + } + + function testCollectionGet() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + 'DAV' => '1, 3, extended-mkcol', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Incorrect status response received. Full response body: ' . $this->response->body); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Browser/PluginTest.php b/dav/SabreDAV/tests/Sabre/DAV/Browser/PluginTest.php new file mode 100644 index 000000000..d03c6fd3a --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Browser/PluginTest.php @@ -0,0 +1,108 @@ +server->addPlugin(new Sabre_DAV_Browser_Plugin()); + + } + + function testCollectionGet() { + + $serverVars = array( + 'REQUEST_URI' => '/dir', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals(array( + 'Content-Type' => 'text/html; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertTrue(strpos($this->response->body, 'Index for dir/') !== false); + + } + + function testNotFound() { + + $serverVars = array( + 'REQUEST_URI' => '/random', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 404 Not Found',$this->response->status); + + } + + function testPostOtherContentType() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'POST', + 'CONTENT_TYPE' => 'text/xml', + ); + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 501 Not Implemented', $this->response->status); + + } + + function testPostNoSabreAction() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'POST', + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded', + ); + $postVars = array(); + + $request = new Sabre_HTTP_Request($serverVars,$postVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 501 Not Implemented', $this->response->status); + + } + + function testPostMkCol() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'POST', + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded', + ); + $postVars = array( + 'sabreAction' => 'mkcol', + 'name' => 'new_collection', + ); + + $request = new Sabre_HTTP_Request($serverVars,$postVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 302 Found', $this->response->status); + $this->assertEquals(array( + 'Location' => '/', + ), $this->response->headers); + + $this->assertTrue(is_dir(SABRE_TEMPDIR . '/new_collection')); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ClientMock.php b/dav/SabreDAV/tests/Sabre/DAV/ClientMock.php new file mode 100644 index 000000000..83c51a3de --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ClientMock.php @@ -0,0 +1,30 @@ +url = $url; + $this->curlSettings = $curlSettings; + return $this->response; + + } + + /** + * Just making this method public + * + * @param string $url + * @return string + */ + public function getAbsoluteUrl($url) { + + return parent::getAbsoluteUrl($url); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ClientTest.php b/dav/SabreDAV/tests/Sabre/DAV/ClientTest.php new file mode 100644 index 000000000..4d23e2785 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ClientTest.php @@ -0,0 +1,791 @@ + '/', + )); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testConstructNoBaseUri() { + + $client = new Sabre_DAV_ClientMock(array()); + + } + + function testRequest() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + + $this->assertEquals('http://example.org/foo/bar/baz', $client->url); + $this->assertEquals(array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => 'sillybody', + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => array('Content-Type: text/plain'), + ), $client->curlSettings); + + $this->assertEquals(array( + 'statusCode' => 200, + 'headers' => array( + 'content-type' => 'text/plain', + ), + 'body' => 'Hello there!' + ), $result); + + + } + + + function testRequestProxy() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + 'proxy' => 'http://localhost:8000/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + + $this->assertEquals('http://example.org/foo/bar/baz', $client->url); + $this->assertEquals(array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => 'sillybody', + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => array('Content-Type: text/plain'), + CURLOPT_PROXY => 'http://localhost:8000/', + ), $client->curlSettings); + + $this->assertEquals(array( + 'statusCode' => 200, + 'headers' => array( + 'content-type' => 'text/plain', + ), + 'body' => 'Hello there!' + ), $result); + + } + + + function testRequestAuth() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + 'userName' => 'user', + 'password' => 'password', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + + $this->assertEquals('http://example.org/foo/bar/baz', $client->url); + $this->assertEquals(array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => 'sillybody', + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => array('Content-Type: text/plain'), + CURLOPT_HTTPAUTH => CURLAUTH_BASIC | CURLAUTH_DIGEST, + CURLOPT_USERPWD => 'user:password' + ), $client->curlSettings); + + $this->assertEquals(array( + 'statusCode' => 200, + 'headers' => array( + 'content-type' => 'text/plain', + ), + 'body' => 'Hello there!' + ), $result); + + } + + function testRequestAuthBasic() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + 'userName' => 'user', + 'password' => 'password', + 'authType' => Sabre_DAV_Client::AUTH_BASIC, + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + + $this->assertEquals('http://example.org/foo/bar/baz', $client->url); + $this->assertEquals(array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => 'sillybody', + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => array('Content-Type: text/plain'), + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_USERPWD => 'user:password' + ), $client->curlSettings); + + $this->assertEquals(array( + 'statusCode' => 200, + 'headers' => array( + 'content-type' => 'text/plain', + ), + 'body' => 'Hello there!' + ), $result); + + } + + function testRequestAuthDigest() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + 'userName' => 'user', + 'password' => 'password', + 'authType' => Sabre_DAV_Client::AUTH_DIGEST, + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + + $this->assertEquals('http://example.org/foo/bar/baz', $client->url); + $this->assertEquals(array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => 'sillybody', + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => array('Content-Type: text/plain'), + CURLOPT_HTTPAUTH => CURLAUTH_DIGEST, + CURLOPT_USERPWD => 'user:password' + ), $client->curlSettings); + + $this->assertEquals(array( + 'statusCode' => 200, + 'headers' => array( + 'content-type' => 'text/plain', + ), + 'body' => 'Hello there!' + ), $result); + + } + function testRequestError() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + CURLE_COULDNT_CONNECT, + "Could not connect, or something" + ); + + $caught = false; + try { + $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + } catch (Sabre_DAV_Exception $e) { + $caught = true; + } + if (!$caught) { + $this->markTestFailed('Exception was not thrown'); + } + + } + + function testRequestHTTPError() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 400 Bad Request", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 400, + ), + 0, + "" + ); + + $caught = false; + try { + $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + } catch (Sabre_DAV_Exception $e) { + $caught = true; + } + if (!$caught) { + $this->fail('Exception was not thrown'); + } + + } + + function testRequestHTTP404() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 404 Not Found", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 404, + ), + 0, + "" + ); + + $caught = false; + try { + $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + } catch (Sabre_DAV_Exception_NotFound $e) { + $caught = true; + } + if (!$caught) { + $this->fail('Exception was not thrown'); + } + + } + + /** + * @dataProvider supportedHTTPCodes + */ + function testSpecificHTTPErrors($error) { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 $error blabla", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 42, + 'http_code' => $error, + ), + 0, + "" + ); + + $caught = false; + try { + $client->request('POST', 'baz', 'sillybody', array('Content-Type' => 'text/plain')); + } catch (Sabre_DAV_Exception $e) { + $caught = true; + $this->assertEquals($e->getHTTPCode(), $error); + } + if (!$caught) { + $this->fail('Exception was not thrown'); + } + + + } + + public function supportedHTTPCodes() { + + return array( + array(400), + array(401), + array(402), + array(403), + array(404), + array(405), + array(409), + array(412), + array(416), + array(500), + array(501), + array(507), + ); + + } + + function testGetAbsoluteUrl() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/', + )); + + $this->assertEquals( + 'http://example.org/foo/bar', + $client->getAbsoluteUrl('bar') + ); + + $this->assertEquals( + 'http://example.org/bar', + $client->getAbsoluteUrl('/bar') + ); + + $this->assertEquals( + 'http://example.com/bar', + $client->getAbsoluteUrl('http://example.com/bar') + ); + + } + + function testOptions() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "DAV: feature1, feature2", + "", + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 40, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->options(); + $this->assertEquals( + array('feature1', 'feature2'), + $result + ); + + } + + function testOptionsNoDav() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "", + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 20, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->options(); + $this->assertEquals( + array(), + $result + ); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testPropFindNoXML() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "", + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 20, + 'http_code' => 200, + ), + 0, + "" + ); + + $client->propfind('', array('{DAV:}foo','{DAV:}bar')); + + } + + function testPropFind() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "", + "", + "", + " ", + " /foo/bar/", + " ", + " ", + " hello", + " ", + " HTTP/1.1 200 OK", + " ", + " ", + " ", + " ", + " ", + " HTTP/1.1 404 Not Found", + " ", + " ", + "", + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 19, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->propfind('', array('{DAV:}foo','{DAV:}bar')); + + $this->assertEquals(array( + '{DAV:}foo' => 'hello', + ), $result); + + $requestBody = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + '' + ); + $requestBody = implode("\n", $requestBody); + + $this->assertEquals($requestBody, $client->curlSettings[CURLOPT_POSTFIELDS]); + + } + + function testPropFindDepth1CustomProp() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "", + "", + "", + " ", + " /foo/bar/", + " ", + " ", + " hello", + " world", + " ", + " HTTP/1.1 200 OK", + " ", + " ", + "", + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 19, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->propfind('', array('{DAV:}foo','{urn:custom}bar'),1); + + $this->assertEquals(array( + "/foo/bar/" => array( + '{DAV:}foo' => 'hello', + '{urn:custom}bar' => 'world', + ), + ), $result); + + $requestBody = array( + '', + '', + ' ', + ' ', + ' ', + ' ', + '' + ); + $requestBody = implode("\n", $requestBody); + + $this->assertEquals($requestBody, $client->curlSettings[CURLOPT_POSTFIELDS]); + + } + + function testPropPatch() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "", + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 20, + 'http_code' => 200, + ), + 0, + "" + ); + + $client->proppatch('', array( + '{DAV:}foo' => 'newvalue', + '{urn:custom}foo' => 'newvalue2', + '{DAV:}bar' => null, + '{urn:custom}bar' => null, + )); + + $requestBody = array( + '', + '', + '', + ' newvalue', + '', + '', + ' newvalue2', + '', + '', + ' ', + '', + '', + ' ', + '', + '' + ); + $requestBody = implode("\n", $requestBody); + + $this->assertEquals($requestBody, $client->curlSettings[CURLOPT_POSTFIELDS]); + + } + + function testHEADRequest() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->request('HEAD', 'baz'); + + $this->assertEquals('http://example.org/foo/bar/baz', $client->url); + $this->assertEquals(array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CUSTOMREQUEST => 'HEAD', + CURLOPT_NOBODY => true, + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => array(), + CURLOPT_POSTFIELDS => null, + ), $client->curlSettings); + + } + + function testPUTRequest() { + + $client = new Sabre_DAV_ClientMock(array( + 'baseUri' => 'http://example.org/foo/bar/', + )); + + $responseBlob = array( + "HTTP/1.1 200 OK", + "Content-Type: text/plain", + "", + "Hello there!" + ); + + $client->response = array( + implode("\r\n", $responseBlob), + array( + 'header_size' => 45, + 'http_code' => 200, + ), + 0, + "" + ); + + $result = $client->request('PUT', 'bar','newcontent'); + + $this->assertEquals('http://example.org/foo/bar/bar', $client->url); + $this->assertEquals(array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CUSTOMREQUEST => "PUT", + CURLOPT_POSTFIELDS => 'newcontent', + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => array(), + ), $client->curlSettings); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Exception/PaymentRequiredTest.php b/dav/SabreDAV/tests/Sabre/DAV/Exception/PaymentRequiredTest.php new file mode 100644 index 000000000..1c37796b3 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Exception/PaymentRequiredTest.php @@ -0,0 +1,12 @@ +assertEquals(402, $ex->getHTTPCode()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ExceptionTest.php b/dav/SabreDAV/tests/Sabre/DAV/ExceptionTest.php new file mode 100644 index 000000000..a40f0ee48 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ExceptionTest.php @@ -0,0 +1,28 @@ +assertEquals(500,$e->getHTTPCode()); + + } + + function testExceptionStatuses() { + + $c = array( + 'Sabre_DAV_Exception_NotAuthenticated' => 401, + 'Sabre_DAV_Exception_InsufficientStorage' => 507, + ); + + foreach($c as $class=>$status) { + + $obj = new $class(); + $this->assertEquals($status, $obj->getHTTPCode()); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/FSExt/FileTest.php b/dav/SabreDAV/tests/Sabre/DAV/FSExt/FileTest.php new file mode 100644 index 000000000..4e188feb1 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/FSExt/FileTest.php @@ -0,0 +1,76 @@ +put('New contents'); + + $this->assertEquals('New contents',file_get_contents(SABRE_TEMPDIR . '/file.txt')); + $this->assertEquals('"' . md5('New contents') . '"', $result); + + } + + function testRange() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/file.txt'); + $file->put('0000000'); + $file->putRange('111',3); + + $this->assertEquals('0011100',file_get_contents(SABRE_TEMPDIR . '/file.txt')); + + } + + function testGet() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/file.txt'); + $this->assertEquals('Contents',stream_get_contents($file->get())); + + } + + function testDelete() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/file.txt'); + $file->delete(); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/file.txt')); + + } + + function testGetETag() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/file.txt'); + $this->assertEquals('"' . md5('Contents') . '"',$file->getETag()); + + } + + function testGetContentType() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/file.txt'); + $this->assertNull($file->getContentType()); + + } + + function testGetSize() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/file.txt'); + $this->assertEquals(8,$file->getSize()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/FSExt/NodeTest.php b/dav/SabreDAV/tests/Sabre/DAV/FSExt/NodeTest.php new file mode 100644 index 000000000..e556e0a56 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/FSExt/NodeTest.php @@ -0,0 +1,175 @@ + 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ); + + $result = $file->updateProperties($properties); + $expected = true; + + $this->assertEquals($expected, $result); + + $getProperties = $file->getProperties(array_keys($properties)); + + $this->assertEquals($properties, $getProperties); + + } + + /** + * @depends testUpdateProperties + */ + function testUpdatePropertiesAgain() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/dir/file.txt'); + $mutations = array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ); + + $result = $file->updateProperties($mutations); + + $this->assertEquals(true, $result); + + $mutations = array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test3' => 'baz', + ); + + $result = $file->updateProperties($mutations); + + $this->assertEquals(true, $result); + } + + /** + * @depends testUpdateProperties + */ + function testUpdatePropertiesDelete() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/dir/file.txt'); + + $mutations = array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ); + + $result = $file->updateProperties($mutations); + + $this->assertEquals(true, $result); + + $mutations = array( + '{http://sabredav.org/NS/2010}test1' => null, + '{http://sabredav.org/NS/2010}test3' => null + ); + + $result = $file->updateProperties($mutations); + + $this->assertEquals(true, $result); + + $properties = $file->getProperties(array('http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); + + $this->assertEquals(array( + '{http://sabredav.org/NS/2010}test2' => 'bar', + ), $properties); + } + + /** + * @depends testUpdateProperties + */ + function testUpdatePropertiesMove() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/dir/file.txt'); + + $mutations = array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ); + + $result = $file->updateProperties($mutations); + + $this->assertEquals(true, $result); + + $properties = $file->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); + + $this->assertEquals(array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ), $properties); + + // Renaming + $file->setName('file3.txt'); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/dir/file.txt')); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/dir/file3.txt')); + $this->assertEquals('file3.txt',$file->getName()); + + $newFile = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/dir/file3.txt'); + $this->assertEquals('file3.txt',$newFile->getName()); + + $properties = $newFile->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); + + $this->assertEquals(array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ), $properties); + } + + /** + * @depends testUpdatePropertiesMove + */ + function testUpdatePropertiesDeleteBleed() { + + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/dir/file.txt'); + $mutations = array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ); + + $result = $file->updateProperties($mutations); + + $this->assertEquals(true, $result); + + $properties = $file->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); + + $this->assertEquals(array( + '{http://sabredav.org/NS/2010}test1' => 'foo', + '{http://sabredav.org/NS/2010}test2' => 'bar', + ), $properties); + + // Deleting + $file->delete(); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/dir/file.txt')); + + // Creating it again + file_put_contents(SABRE_TEMPDIR . '/dir/file.txt','New Contents'); + $file = new Sabre_DAV_FSExt_File(SABRE_TEMPDIR . '/dir/file.txt'); + + $properties = $file->getProperties(array('http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); + + $this->assertEquals(array(), $properties); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/FSExt/ServerTest.php b/dav/SabreDAV/tests/Sabre/DAV/FSExt/ServerTest.php new file mode 100644 index 000000000..2e308d539 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/FSExt/ServerTest.php @@ -0,0 +1,219 @@ +tempDir); + + } + + function testGet() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' .md5_file($this->tempDir . '/test.txt') . '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } + + function testHEAD() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'HEAD', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5_file($this->tempDir . '/test.txt') . '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('', $this->response->body); + + } + + function testPut() { + + $serverVars = array( + 'REQUEST_URI' => '/testput.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => 0, + 'ETag' => '"' . md5('Testing new file') . '"', + ), $this->response->headers); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertEquals('Testing new file',file_get_contents($this->tempDir . '/testput.txt')); + + } + + function testPutAlreadyExists() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF_NONE_MATCH' => '*', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status); + $this->assertNotEquals('Testing new file',file_get_contents($this->tempDir . '/test.txt')); + + } + + function testMkcol() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(""); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertTrue(is_dir($this->tempDir . '/testcol')); + + } + + function testPutUpdate() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing updated file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('0', $this->response->headers['Content-Length']); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertEquals('Testing updated file',file_get_contents($this->tempDir . '/test.txt')); + + } + + function testDelete() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'DELETE', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertFalse(file_exists($this->tempDir . '/test.txt')); + + } + + function testDeleteDirectory() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'DELETE', + ); + + mkdir($this->tempDir.'/testcol'); + file_put_contents($this->tempDir.'/testcol/test.txt','Hi! I\'m a file with a short lifespan'); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ),$this->response->headers); + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertFalse(file_exists($this->tempDir . '/col')); + + } + + function testOptions() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'OPTIONS', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'DAV' => '1, 3, extended-mkcol', + 'MS-Author-Via' => 'DAV', + 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT', + 'Accept-Ranges' => 'bytes', + 'Content-Length' => '0', + 'X-Sabre-Version'=> Sabre_DAV_Version::VERSION, + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('', $this->response->body); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Issue33Test.php b/dav/SabreDAV/tests/Sabre/DAV/Issue33Test.php new file mode 100644 index 000000000..1795b5cd3 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Issue33Test.php @@ -0,0 +1,102 @@ +setBaseUri('/webdav/'); + + $serverVars = array( + 'REQUEST_URI' => '/webdav/foo', + 'HTTP_DESTINATION' => 'http://dev2.tribalos.com/webdav/%C3%A0fo%C3%B3', + 'HTTP_OVERWRITE' => 'F', + ); + + $request = new Sabre_HTTP_Request($serverVars); + + $server->httpRequest = $request; + + $info = $server->getCopyAndMoveInfo(); + + $this->assertEquals('%C3%A0fo%C3%B3', urlencode($info['destination'])); + $this->assertFalse($info['destinationExists']); + $this->assertFalse($info['destinationNode']); + + } + + function testTreeMove() { + + mkdir(SABRE_TEMPDIR . '/issue33'); + $dir = new Sabre_DAV_FS_Directory(SABRE_TEMPDIR . '/issue33'); + + $dir->createDirectory('foo'); + + $tree = new Sabre_DAV_ObjectTree($dir); + $tree->move('foo',urldecode('%C3%A0fo%C3%B3')); + + $node = $tree->getNodeForPath(urldecode('%C3%A0fo%C3%B3')); + $this->assertEquals(urldecode('%C3%A0fo%C3%B3'),$node->getName()); + + } + + function testDirName() { + + $dirname1 = 'foo'; + $dirname2 = urlencode('%C3%A0fo%C3%B3');; + + $this->assertTrue(dirname($dirname1)==dirname($dirname2)); + + } + + /** + * @depends testTreeMove + * @depends testCopyMoveInfo + */ + function testEverything() { + + // Request object + $serverVars = array( + 'REQUEST_METHOD' => 'MOVE', + 'REQUEST_URI' => '/webdav/foo', + 'HTTP_DESTINATION' => 'http://dev2.tribalos.com/webdav/%C3%A0fo%C3%B3', + 'HTTP_OVERWRITE' => 'F', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + + $response = new Sabre_HTTP_ResponseMock(); + + // Server setup + mkdir(SABRE_TEMPDIR . '/issue33'); + $dir = new Sabre_DAV_FS_Directory(SABRE_TEMPDIR . '/issue33'); + + $dir->createDirectory('foo'); + + $tree = new Sabre_DAV_ObjectTree($dir); + + $server = new Sabre_DAV_Server($tree); + $server->setBaseUri('/webdav/'); + + $server->httpRequest = $request; + $server->httpResponse = $response; + $server->exec(); + + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/issue33/' . urldecode('%C3%A0fo%C3%B3'))); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/AbstractTest.php b/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/AbstractTest.php new file mode 100644 index 000000000..3fecac43f --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/AbstractTest.php @@ -0,0 +1,192 @@ +getBackend(); + $this->assertInstanceOf('Sabre_DAV_Locks_Backend_Abstract', $backend); + + } + + /** + * @depends testSetup + */ + function testGetLocks() { + + $backend = $this->getBackend(); + + $lock = new Sabre_DAV_Locks_LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + $lock->uri ='someuri'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + + $this->assertEquals(1,count($locks)); + $this->assertEquals('Sinterklaas',$locks[0]->owner); + $this->assertEquals('someuri',$locks[0]->uri); + + } + + /** + * @depends testGetLocks + */ + function testGetLocksParent() { + + $backend = $this->getBackend(); + + $lock = new Sabre_DAV_Locks_LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->depth = Sabre_DAV_Server::DEPTH_INFINITY; + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri/child', false); + + $this->assertEquals(1,count($locks)); + $this->assertEquals('Sinterklaas',$locks[0]->owner); + $this->assertEquals('someuri',$locks[0]->uri); + + } + + + /** + * @depends testGetLocks + */ + function testGetLocksParentDepth0() { + + $backend = $this->getBackend(); + + $lock = new Sabre_DAV_Locks_LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->depth = 0; + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri/child', false); + + $this->assertEquals(0,count($locks)); + + } + + function testGetLocksChildren() { + + $backend = $this->getBackend(); + + $lock = new Sabre_DAV_Locks_LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->depth = 0; + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri/child', $lock)); + + $locks = $backend->getLocks('someuri/child', false); + $this->assertEquals(1,count($locks)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(0,count($locks)); + + $locks = $backend->getLocks('someuri', true); + $this->assertEquals(1,count($locks)); + + } + + /** + * @depends testGetLocks + */ + function testLockRefresh() { + + $backend = $this->getBackend(); + + $lock = new Sabre_DAV_Locks_LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + /* Second time */ + + $lock->owner = 'Santa Clause'; + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + + $this->assertEquals(1,count($locks)); + + $this->assertEquals('Santa Clause',$locks[0]->owner); + $this->assertEquals('someuri',$locks[0]->uri); + + } + + /** + * @depends testGetLocks + */ + function testUnlock() { + + $backend = $this->getBackend(); + + $lock = new Sabre_DAV_Locks_LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(1,count($locks)); + + $this->assertTrue($backend->unlock('someuri',$lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(0,count($locks)); + + } + + /** + * @depends testUnlock + */ + function testUnlockUnknownToken() { + + $backend = $this->getBackend(); + + $lock = new Sabre_DAV_Locks_LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(1,count($locks)); + + $lock->token = 'SOME-OTHER-TOKEN'; + $this->assertFalse($backend->unlock('someuri',$lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(1,count($locks)); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/FSTest.php b/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/FSTest.php new file mode 100644 index 000000000..ddf4f500e --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/FSTest.php @@ -0,0 +1,29 @@ +markTestSkipped('MySQL driver is not available, or it was not properly configured'); + $pdo = Sabre_TestUtil::getMySQLDB(); + if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); + $pdo->query('DROP TABLE IF EXISTS locks;'); + $pdo->query(" +CREATE TABLE locks ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + owner VARCHAR(100), + timeout INTEGER UNSIGNED, + created INTEGER, + token VARCHAR(100), + scope TINYINT, + depth TINYINT, + uri text +);"); + + $backend = new Sabre_DAV_Locks_Backend_PDO($pdo); + return $backend; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/PDOTest.php b/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/PDOTest.php new file mode 100644 index 000000000..5aebc6b11 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Locks/Backend/PDOTest.php @@ -0,0 +1,27 @@ +markTestSkipped('SQLite driver is not available'); + Sabre_TestUtil::clearTempDir(); + mkdir(SABRE_TEMPDIR . '/pdolocks'); + $pdo = new PDO('sqlite:' . SABRE_TEMPDIR . '/pdolocks/db.sqlite'); + $pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + $pdo->query('CREATE TABLE locks ( id integer primary key asc, owner text, timeout text, created integer, token text, scope integer, depth integer, uri text)'); + $backend = new Sabre_DAV_Locks_Backend_PDO($pdo); + return $backend; + + } + + function tearDown() { + + Sabre_TestUtil::clearTempDir(); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Locks/GetIfConditionsTest.php b/dav/SabreDAV/tests/Sabre/DAV/Locks/GetIfConditionsTest.php new file mode 100644 index 000000000..a564ddc7e --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Locks/GetIfConditionsTest.php @@ -0,0 +1,370 @@ +server->addPlugin($locksPlugin); + $this->locksPlugin = $locksPlugin; + + } + + function testNoConditions() { + + $serverVars = array( + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + $this->assertEquals(array(),$conditions); + + } + + function testLockToken() { + + $serverVars = array( + 'HTTP_IF' => '()', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => '', + 'tokens' => array( + array( + 1, + 'opaquelocktoken:token1', + '', + ), + ), + + ), + + ); + + $this->assertEquals($compare,$conditions); + + } + + function testNotLockToken() { + + $serverVars = array( + 'HTTP_IF' => '(Not )', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => '', + 'tokens' => array( + array( + 0, + 'opaquelocktoken:token1', + '', + ), + ), + + ), + + ); + $this->assertEquals($compare,$conditions); + + } + + function testLockTokenUrl() { + + $serverVars = array( + 'HTTP_IF' => ' ()', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => 'http://www.example.com/', + 'tokens' => array( + array( + 1, + 'opaquelocktoken:token1', + '', + ), + ), + + ), + + ); + $this->assertEquals($compare,$conditions); + + } + + function test2LockTokens() { + + $serverVars = array( + 'HTTP_IF' => '() (Not )', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => '', + 'tokens' => array( + array( + 1, + 'opaquelocktoken:token1', + '', + ), + array( + 0, + 'opaquelocktoken:token2', + '', + ), + ), + + ), + + ); + $this->assertEquals($compare,$conditions); + + } + + function test2UriLockTokens() { + + $serverVars = array( + 'HTTP_IF' => ' () (Not )', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => 'http://www.example.org/node1', + 'tokens' => array( + array( + 1, + 'opaquelocktoken:token1', + '', + ), + ), + ), + array( + 'uri' => 'http://www.example.org/node2', + 'tokens' => array( + array( + 0, + 'opaquelocktoken:token2', + '', + ), + ), + + ), + + ); + $this->assertEquals($compare,$conditions); + + } + + function test2UriMultiLockTokens() { + + $serverVars = array( + 'HTTP_IF' => ' () () (Not )', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => 'http://www.example.org/node1', + 'tokens' => array( + array( + 1, + 'opaquelocktoken:token1', + '', + ), + array( + 1, + 'opaquelocktoken:token2', + '', + ), + ), + ), + array( + 'uri' => 'http://www.example.org/node2', + 'tokens' => array( + array( + 0, + 'opaquelocktoken:token3', + '', + ), + ), + + ), + + ); + $this->assertEquals($compare,$conditions); + + } + + function testEtag() { + + $serverVars = array( + 'HTTP_IF' => '([etag1])', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => '', + 'tokens' => array( + array( + 1, + '', + 'etag1', + ), + ), + ), + + ); + $this->assertEquals($compare,$conditions); + + } + + function test2Etags() { + + $serverVars = array( + 'HTTP_IF' => ' ([etag1]) ([etag2])', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => 'http://www.example.org/', + 'tokens' => array( + array( + 1, + '', + 'etag1', + ), + array( + 1, + '', + 'etag2', + ), + ), + ), + + ); + $this->assertEquals($compare,$conditions); + + } + + function testComplexIf() { + + $serverVars = array( + 'HTTP_IF' => ' ( [etag1]) ' . + '(Not ) ([etag2]) ' . + '() (Not ) ([etag3])', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + + $conditions = $this->locksPlugin->getIfConditions(); + + $compare = array( + + array( + 'uri' => 'http://www.example.org/node1', + 'tokens' => array( + array( + 1, + 'opaquelocktoken:token1', + 'etag1', + ), + array( + 0, + 'opaquelocktoken:token2', + '', + ), + array( + 1, + '', + 'etag2', + ), + ), + ), + array( + 'uri' => 'http://www.example.org/node2', + 'tokens' => array( + array( + 1, + 'opaquelocktoken:token3', + '', + ), + array( + 0, + 'opaquelocktoken:token4', + '', + ), + array( + 1, + '', + 'etag3', + ), + ), + ), + + ); + $this->assertEquals($compare,$conditions); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Locks/MSWordTest.php b/dav/SabreDAV/tests/Sabre/DAV/Locks/MSWordTest.php new file mode 100644 index 000000000..63e8a0b48 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Locks/MSWordTest.php @@ -0,0 +1,118 @@ +debugExceptions = true; + $locksBackend = new Sabre_DAV_Locks_Backend_File(SABRE_TEMPDIR . '/locksdb'); + $locksPlugin = new Sabre_DAV_Locks_Plugin($locksBackend); + $server->addPlugin($locksPlugin); + + $response1 = new Sabre_HTTP_ResponseMock(); + + $server->httpRequest = $this->getLockRequest(); + $server->httpResponse = $response1; + $server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created', $server->httpResponse->status); + $this->assertTrue(isset($server->httpResponse->headers['Lock-Token'])); + $lockToken = $server->httpResponse->headers['Lock-Token']; + + //sleep(10); + + $response2 = new Sabre_HTTP_ResponseMock(); + + $server->httpRequest = $this->getLockRequest2(); + $server->httpResponse = $response2; + $server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created', $server->httpResponse->status); + $this->assertTrue(isset($server->httpResponse->headers['Lock-Token'])); + + //sleep(10); + + $response3 = new Sabre_HTTP_ResponseMock(); + $server->httpRequest = $this->getPutRequest($lockToken); + $server->httpResponse = $response3; + $server->exec(); + + $this->assertEquals('HTTP/1.1 204 No Content', $server->httpResponse->status); + + } + + function tearDown() { + + Sabre_TestUtil::clearTempDir(); + + } + + function getLockRequest() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'LOCK', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'HTTP_TIMEOUT' => 'Second-3600', + 'REQUEST_URI' => '/Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', + )); + + $request->setBody(' + + + + + + + + PC-Vista\User + +'); + + return $request; + + } + function getLockRequest2() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'LOCK', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'HTTP_TIMEOUT' => 'Second-3600', + 'REQUEST_URI' => '/~$Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', + )); + + $request->setBody(' + + + + + + + + PC-Vista\User + +'); + + return $request; + + } + + function getPutRequest($lockToken) { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', + 'HTTP_IF' => 'If: ('.$lockToken.')', + )); + $request->setBody('FAKE BODY'); + return $request; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Locks/PluginTest.php b/dav/SabreDAV/tests/Sabre/DAV/Locks/PluginTest.php new file mode 100644 index 000000000..758a492bf --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Locks/PluginTest.php @@ -0,0 +1,961 @@ +server->addPlugin($locksPlugin); + $this->locksPlugin = $locksPlugin; + + } + + function testGetFeatures() { + + $this->assertEquals(array(2),$this->locksPlugin->getFeatures()); + + } + + function testGetHTTPMethods() { + + $this->assertEquals(array('LOCK','UNLOCK'),$this->locksPlugin->getHTTPMethods('')); + + } + + function testGetHTTPMethodsNoBackend() { + + $locksPlugin = new Sabre_DAV_Locks_Plugin(); + $this->server->addPlugin($locksPlugin); + $this->assertEquals(array(),$locksPlugin->getHTTPMethods('')); + + } + + function testUnknownMethodPassthough() { + + $this->assertNull($this->locksPlugin->unknownMethod('BLA','/')); + + } + + function testLockNoBody() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status); + + } + + function testLock() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status,'Got an incorrect status back. Response body: ' . $this->response->body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + + $elements = array( + '/d:prop', + '/d:prop/d:lockdiscovery', + '/d:prop/d:lockdiscovery/d:activelock', + '/d:prop/d:lockdiscovery/d:activelock/d:locktype', + '/d:prop/d:lockdiscovery/d:activelock/d:lockroot', + '/d:prop/d:lockdiscovery/d:activelock/d:lockroot/d:href', + '/d:prop/d:lockdiscovery/d:activelock/d:locktype/d:write', + '/d:prop/d:lockdiscovery/d:activelock/d:lockscope', + '/d:prop/d:lockdiscovery/d:activelock/d:lockscope/d:exclusive', + '/d:prop/d:lockdiscovery/d:activelock/d:depth', + '/d:prop/d:lockdiscovery/d:activelock/d:owner', + '/d:prop/d:lockdiscovery/d:activelock/d:timeout', + '/d:prop/d:lockdiscovery/d:activelock/d:locktoken', + '/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href', + ); + + foreach($elements as $elem) { + $data = $xml->xpath($elem); + $this->assertEquals(1,count($data),'We expected 1 match for the xpath expression "' . $elem . '". ' . count($data) . ' were found. Full response body: ' . $this->response->body); + } + + $depth = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:depth'); + $this->assertEquals('infinity',(string)$depth[0]); + + $token = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href'); + $this->assertEquals($this->response->headers['Lock-Token'],'<' . (string)$token[0] . '>','Token in response body didn\'t match token in response header.'); + + } + + /** + * @depends testLock + */ + function testDoubleLock() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->response = new Sabre_HTTP_ResponseMock(); + $this->server->httpResponse = $this->response; + + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status); + + } + + /** + * @depends testLock + */ + function testLockRefresh() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $lockToken = $this->response->headers['Lock-Token']; + + $this->response = new Sabre_HTTP_ResponseMock(); + $this->server->httpResponse = $this->response; + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + 'HTTP_IF' => '(' . $lockToken . ')', + ); + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + $this->server->httpRequest = $request; + + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status,'We received an incorrect status code. Full response body: ' . $this->response->body); + + } + + /** + * @depends testLock + */ + function testLockNoFile() { + + $serverVars = array( + 'REQUEST_URI' => '/notfound.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + + } + + /** + * @depends testLock + */ + function testUnlockNoToken() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'UNLOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status); + + } + + /** + * @depends testLock + */ + function testUnlockBadToken() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'UNLOCK', + 'HTTP_LOCK_TOKEN' => '', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status,'Got an incorrect status code. Full response body: ' . $this->response->body); + + } + + /** + * @depends testLock + */ + function testLockPutNoToken() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status); + + } + + /** + * @depends testLock + */ + function testUnlock() { + + $request = new Sabre_HTTP_Request(array()); + $this->server->httpRequest = $request; + + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->invokeMethod('LOCK','test.txt'); + $lockToken = $this->server->httpResponse->headers['Lock-Token']; + + $serverVars = array( + 'HTTP_LOCK_TOKEN' => $lockToken, + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->httpResponse = new Sabre_HTTP_ResponseMock(); + $this->server->invokeMethod('UNLOCK', 'test.txt'); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body); + $this->assertEquals(array( + 'Content-Length' => '0', + ), + $this->server->httpResponse->headers + ); + + + } + + /** + * @depends testLock + */ + function testUnlockWindowsBug() { + + $request = new Sabre_HTTP_Request(array()); + $this->server->httpRequest = $request; + + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->invokeMethod('LOCK','test.txt'); + $lockToken = $this->server->httpResponse->headers['Lock-Token']; + + // See Issue 123 + $lockToken = trim($lockToken,'<>'); + + $serverVars = array( + 'HTTP_LOCK_TOKEN' => $lockToken, + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->httpResponse = new Sabre_HTTP_ResponseMock(); + $this->server->invokeMethod('UNLOCK', 'test.txt'); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body); + $this->assertEquals(array( + 'Content-Length' => '0', + ), + $this->server->httpResponse->headers + ); + + + } + + /** + * @depends testLock + */ + function testLockRetainOwner() { + + $request = new Sabre_HTTP_Request(array()); + $this->server->httpRequest = $request; + + $request->setBody(' + + + + Evert +'); + + $this->server->invokeMethod('LOCK','test.txt'); + $lockToken = $this->server->httpResponse->headers['Lock-Token']; + + $locks = $this->locksPlugin->getLocks('test.txt'); + $this->assertEquals(1,count($locks)); + $this->assertEquals('Evert',$locks[0]->owner); + + + } + + /** + * @depends testLock + */ + function testLockPutBadToken() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '()', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status); + + } + + /** + * @depends testLock + */ + function testLockDeleteParent() { + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir', + 'REQUEST_METHOD' => 'DELETE', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + } + /** + * @depends testLock + */ + function testLockDeleteSucceed() { + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'DELETE', + 'HTTP_IF' => '(' . $this->response->headers['Lock-Token'] . ')', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + } + + /** + * @depends testLock + */ + function testLockCopyLockSource() { + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + } + /** + * @depends testLock + */ + function testLockCopyLockDestination() { + + $serverVars = array( + 'REQUEST_URI' => '/dir/child2.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status,'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + } + + /** + * @depends testLock + */ + function testLockMoveLockSourceLocked() { + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status,'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + } + + /** + * @depends testLock + */ + function testLockMoveLockSourceSucceed() { + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + 'HTTP_IF' => '(' . $this->response->headers['Lock-Token'] . ')', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'A valid lock-token was provided for the source, so this MOVE operation must succeed. Full response body: ' . $this->response->body); + + } + + /** + * @depends testLock + */ + function testLockMoveLockDestination() { + + $serverVars = array( + 'REQUEST_URI' => '/dir/child2.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 423 Locked',$this->response->status,'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + } + /** + * @depends testLock + */ + function testLockMoveLockParent() { + + $serverVars = array( + 'REQUEST_URI' => '/dir', + 'REQUEST_METHOD' => 'LOCK', + 'HTTP_DEPTH' => 'infinite', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + 'HTTP_IF' => ' (' . $this->response->headers['Lock-Token'] . ')', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'We locked the parent of both the source and destination, but the move didn\'t succeed.'); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + + } + + /** + * @depends testLock + */ + function testLockPutGoodToken() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '('.$this->response->headers['Lock-Token'].')', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + + } + + function testPutWithIncorrectETag() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '(["etag1"])', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status); + + } + + /** + * @depends testPutWithIncorrectETag + */ + function testPutWithCorrectETag() { + + // We need an etag-enabled file node. + $tree = new Sabre_DAV_ObjectTree(new Sabre_DAV_FSExt_Directory(SABRE_TEMPDIR)); + $this->server->tree = $tree; + + $etag = md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')); + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '(["'.$etag.'"])', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status, 'Incorrect status received. Full response body:' . $this->response->body); + + } + + function testGetTimeoutHeader() { + + $request = new Sabre_HTTP_Request(array( + 'HTTP_TIMEOUT' => 'second-100', + )); + + $this->server->httpRequest = $request; + $this->assertEquals(100, $this->locksPlugin->getTimeoutHeader()); + + } + + + function testGetTimeoutHeaderNotSet() { + + $request = new Sabre_HTTP_Request(array( + )); + + $this->server->httpRequest = $request; + $this->assertEquals(0, $this->locksPlugin->getTimeoutHeader()); + + } + + + function testGetTimeoutHeaderInfinite() { + + $request = new Sabre_HTTP_Request(array( + 'HTTP_TIMEOUT' => 'infinite', + )); + + $this->server->httpRequest = $request; + $this->assertEquals(Sabre_DAV_Locks_LockInfo::TIMEOUT_INFINITE, $this->locksPlugin->getTimeoutHeader()); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testGetTimeoutHeaderInvalid() { + + $request = new Sabre_HTTP_Request(array( + 'HTTP_TIMEOUT' => 'yourmom', + )); + + $this->server->httpRequest = $request; + $this->locksPlugin->getTimeoutHeader(); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Mount/PluginTest.php b/dav/SabreDAV/tests/Sabre/DAV/Mount/PluginTest.php new file mode 100644 index 000000000..21f31089a --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Mount/PluginTest.php @@ -0,0 +1,53 @@ +server->addPlugin(new Sabre_DAV_Mount_Plugin()); + + } + + function testPassThrough() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status,'We expected GET to not be implemented for Directories. Response body: ' . $this->response->body); + + } + + function testMountResponse() { + + $serverVars = array( + 'REQUEST_URI' => '/?mount', + 'REQUEST_METHOD' => 'GET', + 'QUERY_STRING' => 'mount', + 'HTTP_HOST' => 'example.org', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + $xml = simplexml_load_string($this->response->body); + $this->assertTrue($xml==true,'Response was not a valid xml document'); + + $xml->registerXPathNamespace('dm','http://purl.org/NET/webdav/mount'); + $url = $xml->xpath('//dm:url'); + $this->assertEquals('http://example.org/',(string)$url[0]); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ObjectTreeTest.php b/dav/SabreDAV/tests/Sabre/DAV/ObjectTreeTest.php new file mode 100644 index 000000000..9059cefb8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ObjectTreeTest.php @@ -0,0 +1,98 @@ +tree = new Sabre_DAV_ObjectTree($rootNode); + + } + + function teardown() { + + Sabre_TestUtil::clearTempDir(); + + } + + function testGetRootNode() { + + $root = $this->tree->getNodeForPath(''); + $this->assertInstanceOf('Sabre_DAV_FSExt_Directory',$root); + + } + + function testGetSubDir() { + + $root = $this->tree->getNodeForPath('subdir'); + $this->assertInstanceOf('Sabre_DAV_FSExt_Directory',$root); + + } + + function testCopyFile() { + + $this->tree->copy('file.txt','file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/file2.txt')); + $this->assertEquals('contents',file_get_contents(SABRE_TEMPDIR.'/root/file2.txt')); + + } + + /** + * @depends testCopyFile + */ + function testCopyDirectory() { + + $this->tree->copy('subdir','subdir2'); + $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2')); + $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); + $this->assertEquals('subcontents',file_get_contents(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); + + } + + /** + * @depends testCopyFile + */ + function testMoveFile() { + + $this->tree->move('file.txt','file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/file2.txt')); + $this->assertFalse(file_exists(SABRE_TEMPDIR.'/root/file.txt')); + $this->assertEquals('contents',file_get_contents(SABRE_TEMPDIR.'/root/file2.txt')); + + } + + /** + * @depends testMoveFile + */ + function testMoveFileNewParent() { + + $this->tree->move('file.txt','subdir/file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir/file2.txt')); + $this->assertFalse(file_exists(SABRE_TEMPDIR.'/root/file.txt')); + $this->assertEquals('contents',file_get_contents(SABRE_TEMPDIR.'/root/subdir/file2.txt')); + + } + + /** + * @depends testCopyDirectory + */ + function testMoveDirectory() { + + $this->tree->move('subdir','subdir2'); + $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2')); + $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); + $this->assertFalse(file_exists(SABRE_TEMPDIR.'/root/subdir')); + $this->assertEquals('subcontents',file_get_contents(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/PartialUpdate/FileMock.php b/dav/SabreDAV/tests/Sabre/DAV/PartialUpdate/FileMock.php new file mode 100644 index 000000000..17c52e38b --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/PartialUpdate/FileMock.php @@ -0,0 +1,76 @@ +data = $str; + + } + + function putRange($str,$start) { + + if (is_resource($str)) { + $str = stream_get_contents($str); + } + $this->data = substr($this->data, 0, $start) . $str . substr($this->data, $start + strlen($str)); + + + + } + + function get() { + + return $this->data; + + } + + function getContentType() { + + return 'text/plain'; + + } + + function getSize() { + + return strlen($this->data); + + } + + function getETag() { + + return '"' . $this->data . '"'; + + } + + function delete() { + + throw new Sabre_DAV_Exception_MethodNotAllowed(); + + } + + function setName($name) { + + throw new Sabre_DAV_Exception_MethodNotAllowed(); + + } + + function getName() { + + return 'partial'; + + } + + function getLastModified() { + + return null; + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/PartialUpdate/PluginTest.php b/dav/SabreDAV/tests/Sabre/DAV/PartialUpdate/PluginTest.php new file mode 100644 index 000000000..6555f50fd --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/PartialUpdate/PluginTest.php @@ -0,0 +1,125 @@ +node = new Sabre_DAV_PartialUpdate_FileMock(); + $this->tree[] = $this->node; + + parent::setUp(); + + $this->plugin = new Sabre_DAV_PartialUpdate_Plugin(); + $this->server->addPlugin($this->plugin); + + + + } + + public function testInit() { + + $this->assertEquals('partialupdate', $this->plugin->getPluginName()); + $this->assertEquals(array('sabredav-partialupdate'), $this->plugin->getFeatures()); + $this->assertEquals(array( + 'PATCH' + ), $this->plugin->getHTTPMethods('partial')); + $this->assertEquals(array( + ), $this->plugin->getHTTPMethods('')); + + $this->assertNull($this->plugin->unknownMethod('FOO','partial')); + + } + + public function testPatchNoRange() { + + $this->node->put('00000000'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PATCH', + 'REQUEST_URI' => '/partial', + )); + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 400 Bad request', $response->status, 'Full response body:' . $response->body); + + } + + public function testPatchNotSupported() { + + $this->node->put('00000000'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PATCH', + 'REQUEST_URI' => '/', + 'X_UPDATE_RANGE' => '3-4', + + )); + $request->setBody( + '111' + ); + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 405 Method Not Allowed', $response->status, 'Full response body:' . $response->body); + + } + + public function testPatchNoContentType() { + + $this->node->put('00000000'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PATCH', + 'REQUEST_URI' => '/partial', + 'HTTP_X_UPDATE_RANGE' => 'bytes=3-4', + + )); + $request->setBody( + '111' + ); + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type', $response->status, 'Full response body:' . $response->body); + + } + + public function testPatchBadRange() { + + $this->node->put('00000000'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PATCH', + 'REQUEST_URI' => '/partial', + 'HTTP_X_UPDATE_RANGE' => 'bytes=3-4', + 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate', + )); + $request->setBody( + '111' + ); + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 416 Requested Range Not Satisfiable', $response->status, 'Full response body:' . $response->body); + + } + + public function testPatchSuccess() { + + $this->node->put('00000000'); + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PATCH', + 'REQUEST_URI' => '/partial', + 'HTTP_X_UPDATE_RANGE' => 'bytes=3-5', + 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate', + 'HTTP_CONTENT_LENGTH' => 3, + )); + $request->setBody( + '111' + ); + $response = $this->request($request); + + $this->assertEquals('HTTP/1.1 204 No Content', $response->status, 'Full response body:' . $response->body); + $this->assertEquals('00111000', $this->node->get()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Property/GetLastModifiedTest.php b/dav/SabreDAV/tests/Sabre/DAV/Property/GetLastModifiedTest.php new file mode 100644 index 000000000..4c493cf88 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Property/GetLastModifiedTest.php @@ -0,0 +1,63 @@ +assertEquals($dt->format(DateTime::ATOM), $lastMod->getTime()->format(DateTime::ATOM)); + + } + + function testConstructString() { + + $dt = new DateTime('2010-03-14 16:35', new DateTimeZone('UTC')); + $lastMod = new Sabre_DAV_Property_GetLastModified('2010-03-14 16:35'); + $this->assertEquals($dt->format(DateTime::ATOM), $lastMod->getTime()->format(DateTime::ATOM)); + + } + + function testConstructInt() { + + $dt = new DateTime('2010-03-14 16:35', new DateTimeZone('UTC')); + $lastMod = new Sabre_DAV_Property_GetLastModified((int)$dt->format('U')); + $this->assertEquals($dt->format(DateTime::ATOM), $lastMod->getTime()->format(DateTime::ATOM)); + + } + + function testSerialize() { + + $dt = new DateTime('2010-03-14 16:35', new DateTimeZone('UTC')); + $lastMod = new Sabre_DAV_Property_GetLastModified($dt); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:getlastmodified'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $lastMod->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +Sabre_HTTP_Util::toHTTPDate($dt) . +' +', $xml); + + $ok = false; + try { + Sabre_DAV_Property_GetLastModified::unserialize(Sabre_DAV_XMLUtil::loadDOMDocument($xml)->firstChild); + } catch (Sabre_DAV_Exception $e) { + $ok = true; + } + if (!$ok) $this->markTestFailed('Unserialize should not be supported'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Property/HrefListTest.php b/dav/SabreDAV/tests/Sabre/DAV/Property/HrefListTest.php new file mode 100644 index 000000000..ef8c1a093 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Property/HrefListTest.php @@ -0,0 +1,88 @@ +assertEquals(array('foo','bar'),$href->getHrefs()); + + } + + function testSerialize() { + + $href = new Sabre_DAV_Property_HrefList(array('foo','bar')); + $this->assertEquals(array('foo','bar'),$href->getHrefs()); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:anything'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $server = new Sabre_DAV_Server(); + $server->setBaseUri('/bla/'); + + $href->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +/bla/foo/bla/bar +', $xml); + + } + + function testSerializeNoPrefix() { + + $href = new Sabre_DAV_Property_HrefList(array('foo','bar'), false); + $this->assertEquals(array('foo','bar'),$href->getHrefs()); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:anything'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $server = new Sabre_DAV_Server(); + $server->setBaseUri('/bla/'); + + $href->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +foobar +', $xml); + + } + + function testUnserialize() { + + $xml = ' +/bla/foo/bla/bar +'; + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $href = Sabre_DAV_Property_HrefList::unserialize($dom->firstChild); + $this->assertEquals(array('/bla/foo','/bla/bar'),$href->getHrefs()); + + } + + function testUnserializeIncompatible() { + + $xml = ' +/bla/foo +'; + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $href = Sabre_DAV_Property_HrefList::unserialize($dom->firstChild); + $this->assertEquals(array(), $href->getHrefs()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Property/HrefTest.php b/dav/SabreDAV/tests/Sabre/DAV/Property/HrefTest.php new file mode 100644 index 000000000..8dcec751e --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Property/HrefTest.php @@ -0,0 +1,88 @@ +assertEquals('path',$href->getHref()); + + } + + function testSerialize() { + + $href = new Sabre_DAV_Property_Href('path'); + $this->assertEquals('path',$href->getHref()); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:anything'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $server = new Sabre_DAV_Server(); + $server->setBaseUri('/bla/'); + + $href->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +/bla/path +', $xml); + + } + + function testSerializeNoPrefix() { + + $href = new Sabre_DAV_Property_Href('path',false); + $this->assertEquals('path',$href->getHref()); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:anything'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $server = new Sabre_DAV_Server(); + $server->setBaseUri('/bla/'); + + $href->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +path +', $xml); + + } + + function testUnserialize() { + + $xml = ' +/bla/path +'; + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $href = Sabre_DAV_Property_Href::unserialize($dom->firstChild); + $this->assertEquals('/bla/path',$href->getHref()); + + } + + function testUnserializeIncompatible() { + + $xml = ' +/bla/path +'; + + $dom = new DOMDocument(); + $dom->loadXML($xml); + + $href = Sabre_DAV_Property_Href::unserialize($dom->firstChild); + $this->assertNull($href); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Property/ResourceTypeTest.php b/dav/SabreDAV/tests/Sabre/DAV/Property/ResourceTypeTest.php new file mode 100644 index 000000000..36f82041d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Property/ResourceTypeTest.php @@ -0,0 +1,107 @@ +assertEquals(array('{DAV:}collection'),$resourceType->getValue()); + + $resourceType = new Sabre_DAV_Property_ResourceType(Sabre_DAV_Server::NODE_FILE); + $this->assertEquals(array(),$resourceType->getValue()); + + $resourceType = new Sabre_DAV_Property_ResourceType(Sabre_DAV_Server::NODE_DIRECTORY); + $this->assertEquals(array('{DAV:}collection'),$resourceType->getValue()); + + $resourceType = new Sabre_DAV_Property_ResourceType('{DAV:}principal'); + $this->assertEquals(array('{DAV:}principal'),$resourceType->getValue()); + + } + + /** + * @depends testConstruct + */ + function testSerialize() { + + $resourceType = new Sabre_DAV_Property_ResourceType(array('{DAV:}collection','{DAV:}principal')); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:anything'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $server = new Sabre_DAV_Server(); + $resourceType->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' + +', $xml); + + } + + /** + * @depends testSerialize + */ + function testSerializeCustomNS() { + + $resourceType = new Sabre_DAV_Property_ResourceType(array('{http://example.org/NS}article')); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:anything'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $server = new Sabre_DAV_Server(); + $resourceType->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' + +', $xml); + + } + + /** + * @depends testConstruct + */ + function testIs() { + + $resourceType = new Sabre_DAV_Property_ResourceType(array('{DAV:}collection','{DAV:}principal')); + $this->assertTrue($resourceType->is('{DAV:}collection')); + $this->assertFalse($resourceType->is('{DAV:}blabla')); + + } + + /** + * @depends testConstruct + */ + function testAdd() { + + $resourceType = new Sabre_DAV_Property_ResourceType(array('{DAV:}collection','{DAV:}principal')); + $resourceType->add('{DAV:}foo'); + $this->assertEquals(array('{DAV:}collection','{DAV:}principal','{DAV:}foo'), $resourceType->getValue()); + + } + + /** + * @depends testConstruct + */ + function testUnserialize() { + + $xml =' + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + + $resourceType = Sabre_DAV_Property_ResourceType::unserialize($dom->firstChild); + $this->assertEquals(array('{DAV:}collection','{DAV:}principal'),$resourceType->getValue()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Property/ResponseTest.php b/dav/SabreDAV/tests/Sabre/DAV/Property/ResponseTest.php new file mode 100644 index 000000000..f6a0ef78b --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Property/ResponseTest.php @@ -0,0 +1,231 @@ + array( + '{DAV:}displayname' => 'my file', + ), + 404 => array( + '{DAV:}owner' => null, + ) + ); + + $property = new Sabre_DAV_Property_Response('uri',$innerProps); + + $this->assertEquals('uri',$property->getHref()); + $this->assertEquals($innerProps,$property->getResponseProperties()); + + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $innerProps = array( + 200 => array( + '{DAV:}displayname' => 'my file', + ), + 404 => array( + '{DAV:}owner' => null, + ) + ); + + $property = new Sabre_DAV_Property_Response('uri',$innerProps); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'/uri' . +'' . +'' . +'my file' . +'' . +'HTTP/1.1 200 OK' . +'' . +'' . +'' . +'' . +'' . +'HTTP/1.1 404 Not Found' . +'' . +'' . +' +', $xml); + + } + + /** + * This one is specifically for testing properties with no namespaces, which is legal xml + * + * @depends testSerialize + */ + function testSerializeEmptyNamespace() { + + $innerProps = array( + 200 => array( + '{}propertyname' => 'value', + ), + ); + + $property = new Sabre_DAV_Property_Response('uri',$innerProps); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'/uri' . +'' . +'' . +'value' . +'' . +'HTTP/1.1 200 OK' . +'' . +'' . +' +', $xml); + + } + + /** + * This one is specifically for testing properties with no namespaces, which is legal xml + * + * @depends testSerialize + */ + function testSerializeCustomNamespace() { + + $innerProps = array( + 200 => array( + '{http://sabredav.org/NS/example}propertyname' => 'value', + ), + ); + + $property = new Sabre_DAV_Property_Response('uri',$innerProps); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'/uri' . +'' . +'' . +'value' . +'' . +'HTTP/1.1 200 OK' . +'' . +'' . +' +', $xml); + + } + + /** + * @depends testSerialize + */ + function testSerializeComplexProperty() { + + $innerProps = array( + 200 => array( + '{DAV:}link' => new Sabre_DAV_Property_Href('http://sabredav.org/', false) + ), + ); + + $property = new Sabre_DAV_Property_Response('uri',$innerProps); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'/uri' . +'' . +'' . +'http://sabredav.org/' . +'' . +'HTTP/1.1 200 OK' . +'' . +'' . +' +', $xml); + + } + + /** + * @depends testSerialize + * @expectedException Sabre_DAV_Exception + */ + function testSerializeBreak() { + + $innerProps = array( + 200 => array( + '{DAV:}link' => new STDClass() + ), + ); + + $property = new Sabre_DAV_Property_Response('uri',$innerProps); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $property->serialize($server, $root); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Property/SupportedReportSetTest.php b/dav/SabreDAV/tests/Sabre/DAV/Property/SupportedReportSetTest.php new file mode 100644 index 000000000..27d5825e6 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Property/SupportedReportSetTest.php @@ -0,0 +1,123 @@ + '/', + 'REQUEST_METHOD' => 'PROPFIND', + 'HTTP_DEPTH' => '0', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($body); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + } + + /** + * @covers Sabre_DAV_Property_SupportedReportSet + */ + function testNoReports() { + + $xml = ' + + + + +'; + + $this->sendPROPFIND($xml); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); + $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set'); + $this->assertEquals(1,count($data),'We expected 1 \'d:supported-report-set\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(1,count($data),'We expected 1 \'d:status\' element'); + + $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0],'The status for this property should have been 200'); + + } + + /** + * @covers Sabre_DAV_Property_SupportedReportSet + * @depends testNoReports + */ + function testCustomReport() { + + // Intercepting the report property + $this->server->subscribeEvent('afterGetProperties',array($this,'addProp')); + + $xml = ' + + + + +'; + + $this->sendPROPFIND($xml); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('x','http://www.rooftopsolutions.nl/testnamespace'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); + $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set'); + $this->assertEquals(1,count($data),'We expected 1 \'d:supported-report-set\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report'); + $this->assertEquals(2,count($data),'We expected 2 \'d:supported-report\' elements'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report'); + $this->assertEquals(2,count($data),'We expected 2 \'d:report\' elements'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/x:myreport'); + $this->assertEquals(1,count($data),'We expected 1 \'x:myreport\' element. Full body: ' . $this->response->body); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/d:anotherreport'); + $this->assertEquals(1,count($data),'We expected 1 \'d:anotherreport\' element. Full body: ' . $this->response->body); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(1,count($data),'We expected 1 \'d:status\' element'); + + $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0],'The status for this property should have been 200'); + + } + + /** + * This method is used as a callback for afterGetProperties + */ + function addProp($path, &$properties) { + + if (isset($properties[200]['{DAV:}supported-report-set'])) { + $properties[200]['{DAV:}supported-report-set']->addReport('{http://www.rooftopsolutions.nl/testnamespace}myreport'); + $properties[200]['{DAV:}supported-report-set']->addReport('{DAV:}anotherreport'); + } + + } + + + +} + +?> diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerCopyMoveTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerCopyMoveTest.php new file mode 100644 index 000000000..b7f5b4748 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerCopyMoveTest.php @@ -0,0 +1,264 @@ +response = new Sabre_HTTP_ResponseMock(); + $dir = new Sabre_DAV_FS_Directory(SABRE_TEMPDIR); + $tree = new Sabre_DAV_ObjectTree($dir); + $this->server = new Sabre_DAV_Server($tree); + $this->server->debugExceptions = true; + $this->server->httpResponse = $this->response; + file_put_contents(SABRE_TEMPDIR . '/test.txt', 'Test contents'); + file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2'); + mkdir(SABRE_TEMPDIR . '/col'); + file_put_contents(SABRE_TEMPDIR . 'col/test.txt', 'Test contents'); + + } + + function tearDown() { + + $cleanUp = array('test.txt','testput.txt','testcol','test2.txt','test3.txt','col/test.txt','col','col2/test.txt','col2'); + foreach($cleanUp as $file) { + $tmpFile = SABRE_TEMPDIR . '/' . $file; + if (file_exists($tmpFile)) { + + if (is_dir($tmpFile)) { + rmdir($tmpFile); + } else { + unlink($tmpFile); + } + + } + } + + } + + + function testCopyOverWrite() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/test2.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status,'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body); + $this->assertEquals(array( + 'Content-Length' => '0', + ), + $this->response->headers + ); + + $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR. '/test2.txt')); + + } + + function testCopyToSelf() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/test.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body); + $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR. '/test.txt')); + + } + + function testMoveToSelf() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/test.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body); + $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR. '/test.txt')); + + } + + function testMoveOverWrite() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/test2.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => 0, + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/test2.txt')); + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/test.txt'),'The sourcefile test.txt should no longer exist at this point'); + + } + + function testBlockedOverWrite() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/test2.txt', + 'HTTP_OVERWRITE' => 'F', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status); + $this->assertEquals('Test contents2',file_get_contents(SABRE_TEMPDIR . '/test2.txt')); + + + } + + function testNonExistantParent() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/testcol2/test2.txt', + 'HTTP_OVERWRITE' => 'F', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status); + + } + + function testRandomOverwriteHeader() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/testcol2/test2.txt', + 'HTTP_OVERWRITE' => 'SURE!', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status); + + } + + function testCopyDirectory() { + + $serverVars = array( + 'REQUEST_URI' => '/col', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/col2', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/col2/test.txt')); + + } + + function testSimpleCopyFile() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/test3.txt', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/test3.txt')); + + } + + function testSimpleCopyCollection() { + + $serverVars = array( + 'REQUEST_URI' => '/col', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/col2', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Incorrect status received. Full response body: ' . $this->response->body); + + $this->assertEquals(array( + 'Content-Length' => '0', + ), + $this->response->headers + ); + + + $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/col2/test.txt')); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerEventsTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerEventsTest.php new file mode 100644 index 000000000..a5e88f3a8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerEventsTest.php @@ -0,0 +1,51 @@ +server->subscribeEvent('afterBind',array($this,'afterBindHandler')); + $newPath = 'afterBind'; + + $this->tempPath = ''; + $this->server->createFile($newPath,'body'); + $this->assertEquals($newPath, $this->tempPath); + + } + + function afterBindHandler($path) { + + $this->tempPath = $path; + + } + + function testBeforeBindCancel() { + + $this->server->subscribeEvent('beforeBind', array($this,'beforeBindCancelHandler')); + $this->assertFalse($this->server->createFile('bla','body')); + + // Also testing put() + $req = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/foobar', + )); + + $this->server->httpRequest = $req; + $this->server->exec(); + + $this->assertEquals('',$this->server->httpResponse->status); + + } + + function beforeBindCancelHandler() { + + return false; + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerFinderBlockTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerFinderBlockTest.php new file mode 100644 index 000000000..622e6b629 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerFinderBlockTest.php @@ -0,0 +1,50 @@ + '/testput.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_X_EXPECTED_ENTITY_LENGTH' => '20', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing finder'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('0', $this->response->headers['Content-Length']); + + $this->assertEquals('Testing finder',file_get_contents(SABRE_TEMPDIR . '/testput.txt')); + + } + + function testPutFail() { + + $serverVars = array( + 'REQUEST_URI' => '/testput.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_X_EXPECTED_ENTITY_LENGTH' => '20', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/testput.txt')); + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerMKCOLTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerMKCOLTest.php new file mode 100644 index 000000000..0a181dd70 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerMKCOLTest.php @@ -0,0 +1,336 @@ + '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(""); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertTrue(is_dir($this->tempDir . '/testcol')); + + } + + /** + * @depends testMkcol + */ + function testMKCOLUnknownBody() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody("Hello"); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type',$this->response->status); + + } + + /** + * @depends testMkcol + */ + function testMKCOLBrokenXML() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody("Hello"); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status); + + } + + /** + * @depends testMkcol + */ + function testMKCOLUnknownXML() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 415 Unsupported Media Type',$this->response->status); + + } + + /** + * @depends testMkcol + */ + function testMKCOLNoResourceType() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + Evert + + +'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 400 Bad request',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + } + + /** + * @depends testMKCOLNoResourceType + */ + function testMKCOLIncorrectResourceType() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + + +'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType + */ + function testMKCOLIncorrectResourceType2() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + + +'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + } + + + /** + * @depends testMKCOLIncorrectResourceType2 + */ + function testMKCOLSuccess() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + + +'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + } + + + /** + * @depends testMKCOLIncorrectResourceType2 + */ + function testMKCOLNoParent() { + + $serverVars = array( + 'REQUEST_URI' => '/testnoparent/409me', + 'REQUEST_METHOD' => 'MKCOL', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType2 + */ + function testMKCOLParentIsNoCollection() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt/409me', + 'REQUEST_METHOD' => 'MKCOL', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 409 Conflict',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType2 + */ + function testMKCOLAlreadyExists() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'MKCOL', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 405 Method Not Allowed',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + } + + /** + * @depends testMKCOLSuccess + * @depends testMKCOLAlreadyExists + */ + function testMKCOLAndProps() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(' + + + + + my new collection + + +'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Wrong statuscode received. Full response body: ' .$this->response->body); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerPluginTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerPluginTest.php new file mode 100644 index 000000000..fd719792a --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerPluginTest.php @@ -0,0 +1,95 @@ +server->addPlugin($testPlugin); + $this->testPlugin = $testPlugin; + + } + + /** + * @covers Sabre_DAV_ServerPlugin + */ + function testBaseClass() { + + $p = new Sabre_DAV_ServerPluginMock(); + $this->assertEquals(array(),$p->getFeatures()); + $this->assertEquals(array(),$p->getHTTPMethods('')); + + } + + function testOptions() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'OPTIONS', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'DAV' => '1, 3, extended-mkcol, drinking', + 'MS-Author-Via' => 'DAV', + 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, BEER, WINE', + 'Accept-Ranges' => 'bytes', + 'Content-Length' => '0', + 'X-Sabre-Version' => Sabre_DAV_Version::VERSION, + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertEquals('OPTIONS',$this->testPlugin->beforeMethod); + + + } + + function testGetPlugin() { + + $this->assertEquals($this->testPlugin,$this->server->getPlugin(get_class($this->testPlugin))); + + } + + function testUnknownPlugin() { + + $this->assertNull($this->server->getPlugin('SomeRandomClassName')); + + } + + function testGetSupportedReportSet() { + + $this->assertEquals(array(), $this->testPlugin->getSupportedReportSet('/')); + + } + + function testGetPlugins() { + + $this->assertEquals( + array(get_class($this->testPlugin) => $this->testPlugin), + $this->server->getPlugins() + ); + + } + + +} + +class Sabre_DAV_ServerPluginMock extends Sabre_DAV_ServerPlugin { + + function initialize(Sabre_DAV_Server $s) { } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerPreconditionTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerPreconditionTest.php new file mode 100644 index 000000000..6ce4733a9 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerPreconditionTest.php @@ -0,0 +1,391 @@ + '*', + 'REQUEST_URI' => '/bar' + )); + $server->httpRequest = $httpRequest; + + $server->checkPreconditions(); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + function testIfMatchHasNode() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MATCH' => '*', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + * @expectedException Sabre_DAV_Exception_PreconditionFailed + */ + function testIfMatchWrongEtag() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MATCH' => '1234', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $server->checkPreconditions(); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + function testIfMatchCorrectEtag() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MATCH' => '"abc123"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * Evolution sometimes uses \" instead of " for If-Match headers. + * + * @covers Sabre_DAV_Server::checkPreconditions + * @depends testIfMatchCorrectEtag + */ + function testIfMatchEvolutionEtag() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MATCH' => '\\"abc123\\"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + function testIfMatchMultiple() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MATCH' => '"hellothere", "abc123"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + function testIfNoneMatchNoNode() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_NONE_MATCH' => '*', + 'REQUEST_URI' => '/bar' + )); + $server->httpRequest = $httpRequest; + + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + * @expectedException Sabre_DAV_Exception_PreconditionFailed + */ + function testIfNoneMatchHasNode() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_NONE_MATCH' => '*', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $server->checkPreconditions(); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + function testIfNoneMatchWrongEtag() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_NONE_MATCH' => '"1234"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + function testIfNoneMatchWrongEtagMultiple() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_NONE_MATCH' => '"1234", "5678"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + * @expectedException Sabre_DAV_Exception_PreconditionFailed + */ + public function testIfNoneMatchCorrectEtag() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_NONE_MATCH' => '"abc123"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $server->checkPreconditions(); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + * @expectedException Sabre_DAV_Exception_PreconditionFailed + */ + public function testIfNoneMatchCorrectEtagMultiple() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_NONE_MATCH' => '"1234", "abc123"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + + $server->checkPreconditions(); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + public function testIfNoneMatchCorrectEtagAsGet() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_NONE_MATCH' => '"abc123"', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $server->httpResponse = new Sabre_HTTP_ResponseMock(); + + $this->assertFalse($server->checkPreconditions(true)); + $this->assertEquals('HTTP/1.1 304 Not Modified',$server->httpResponse->status); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + public function testIfModifiedSinceUnModified() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $server->httpResponse = new Sabre_HTTP_ResponseMock(); + $this->assertFalse($server->checkPreconditions()); + + $this->assertEquals('HTTP/1.1 304 Not Modified',$server->httpResponse->status); + $this->assertEquals(array( + 'Last-Modified' => 'Sat, 06 Apr 1985 23:30:00 GMT', + ), $server->httpResponse->headers); + + } + + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + public function testIfModifiedSinceModified() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $server->httpResponse = new Sabre_HTTP_ResponseMock(); + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + public function testIfModifiedSinceInvalidDate() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MODIFIED_SINCE' => 'Your mother', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $server->httpResponse = new Sabre_HTTP_ResponseMock(); + + // Invalid dates must be ignored, so this should return true + $this->assertTrue($server->checkPreconditions()); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + public function testIfModifiedSinceInvalidDate2() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 EST', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $server->httpResponse = new Sabre_HTTP_ResponseMock(); + $this->assertTrue($server->checkPreconditions()); + + } + + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + public function testIfUnmodifiedSinceUnModified() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $this->assertTrue($server->checkPreconditions()); + + } + + + /** + * @covers Sabre_DAV_Server::checkPreconditions + * @expectedException Sabre_DAV_Exception_PreconditionFailed + */ + public function testIfUnmodifiedSinceModified() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_UNMODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $server->httpResponse = new Sabre_HTTP_ResponseMock(); + $server->checkPreconditions(); + + } + + /** + * @covers Sabre_DAV_Server::checkPreconditions + */ + public function testIfUnmodifiedSinceInvalidDate() { + + $root = new Sabre_DAV_SimpleCollection('root',array(new Sabre_DAV_ServerPreconditionsNode())); + $server = new Sabre_DAV_Server($root); + $httpRequest = new Sabre_HTTP_Request(array( + 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1984 08:49:37 CET', + 'REQUEST_URI' => '/foo' + )); + $server->httpRequest = $httpRequest; + $server->httpResponse = new Sabre_HTTP_ResponseMock(); + $this->assertTrue($server->checkPreconditions()); + + } + + +} + +class Sabre_DAV_ServerPreconditionsNode extends Sabre_DAV_File { + + function getETag() { + + return '"abc123"'; + + } + + function getLastModified() { + + /* my birthday & time, I believe */ + return strtotime('1985-04-07 01:30 +02:00'); + + } + + function getName() { + + return 'foo'; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerPropsTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerPropsTest.php new file mode 100644 index 000000000..484b038d3 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerPropsTest.php @@ -0,0 +1,393 @@ +server->addPlugin(new Sabre_DAV_Locks_Plugin(new Sabre_DAV_Locks_Backend_File(SABRE_TEMPDIR . '/.locksdb'))); + + } + + function tearDown() { + + parent::tearDown(); + if (file_exists(SABRE_TEMPDIR.'../.locksdb')) unlink(SABRE_TEMPDIR.'../.locksdb'); + + } + + private function sendRequest($body) { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'PROPFIND', + 'HTTP_DEPTH' => '0', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($body); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + } + + public function testPropFindEmptyBody() { + + $this->sendRequest(""); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + 'DAV' => '1, 3, extended-mkcol, 2', + ), + $this->response->headers + ); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + + list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); + $this->assertEquals('/',(string)$data,'href element should have been /'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); + $this->assertEquals(1,count($data)); + + } + + function testSupportedLocks() { + + $xml = ' + + + + +'; + + $this->sendRequest($xml); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry'); + $this->assertEquals(2,count($data),'We expected two \'d:lockentry\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope'); + $this->assertEquals(2,count($data),'We expected two \'d:lockscope\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype'); + $this->assertEquals(2,count($data),'We expected two \'d:locktype\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:shared'); + $this->assertEquals(1,count($data),'We expected a \'d:shared\' tag'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:exclusive'); + $this->assertEquals(1,count($data),'We expected a \'d:exclusive\' tag'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype/d:write'); + $this->assertEquals(2,count($data),'We expected two \'d:write\' tags'); + } + + function testLockDiscovery() { + + $xml = ' + + + + +'; + + $this->sendRequest($xml); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery'); + $this->assertEquals(1,count($data),'We expected a \'d:lockdiscovery\' tag'); + + } + + function testUnknownProperty() { + + $xml = ' + + + + +'; + + $this->sendRequest($xml); + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + $pathTests = array( + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:status', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:macaroni', + ); + foreach($pathTests as $test) { + $this->assertTrue(count($xml->xpath($test))==true,'We expected the ' . $test . ' element to appear in the response, we got: ' . $body); + } + + $val = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(1,count($val),$body); + $this->assertEquals('HTTP/1.1 404 Not Found',(string)$val[0]); + + } + + /** + * @covers Sabre_DAV_Server::parsePropPatchRequest + */ + public function testParsePropPatchRequest() { + + $body = ' + + somevalue + + removeme + +'; + + $result = $this->server->parsePropPatchRequest($body); + $this->assertEquals(array( + '{http://sabredav.org/NS/test}someprop' => 'somevalue', + '{http://sabredav.org/NS/test}someprop2' => null, + '{http://sabredav.org/NS/test}someprop3' => null, + ), $result); + + } + + /** + * @covers Sabre_DAV_Server::updateProperties + */ + public function testUpdateProperties() { + + $props = array( + '{http://sabredav.org/NS/test}someprop' => 'somevalue', + ); + + $result = $this->server->updateProperties('/test2.txt',$props); + + $this->assertEquals(array( + '200' => array('{http://sabredav.org/NS/test}someprop' => null), + 'href' => '/test2.txt', + ), $result); + + } + + /** + * @covers Sabre_DAV_Server::updateProperties + * @depends testUpdateProperties + */ + public function testUpdatePropertiesProtected() { + + $props = array( + '{http://sabredav.org/NS/test}someprop' => 'somevalue', + '{DAV:}getcontentlength' => 50, + ); + + $result = $this->server->updateProperties('/test2.txt',$props); + + $this->assertEquals(array( + '424' => array('{http://sabredav.org/NS/test}someprop' => null), + '403' => array('{DAV:}getcontentlength' => null), + 'href' => '/test2.txt', + ), $result); + + } + + /** + * @covers Sabre_DAV_Server::updateProperties + * @depends testUpdateProperties + */ + public function testUpdatePropertiesFail1() { + + $dir = new Sabre_DAV_PropTestDirMock('updatepropsfalse'); + $objectTree = new Sabre_DAV_ObjectTree($dir); + $this->server->tree = $objectTree; + + $props = array( + '{http://sabredav.org/NS/test}someprop' => 'somevalue', + ); + + $result = $this->server->updateProperties('/',$props); + + $this->assertEquals(array( + '403' => array('{http://sabredav.org/NS/test}someprop' => null), + 'href' => '/', + ), $result); + + } + + /** + * @covers Sabre_DAV_Server::updateProperties + * @depends testUpdateProperties + */ + public function testUpdatePropertiesFail2() { + + $dir = new Sabre_DAV_PropTestDirMock('updatepropsarray'); + $objectTree = new Sabre_DAV_ObjectTree($dir); + $this->server->tree = $objectTree; + + $props = array( + '{http://sabredav.org/NS/test}someprop' => 'somevalue', + ); + + $result = $this->server->updateProperties('/',$props); + + $this->assertEquals(array( + '402' => array('{http://sabredav.org/NS/test}someprop' => null), + 'href' => '/', + ), $result); + + } + + /** + * @covers Sabre_DAV_Server::updateProperties + * @depends testUpdateProperties + * @expectedException Sabre_DAV_Exception + */ + public function testUpdatePropertiesFail3() { + + $dir = new Sabre_DAV_PropTestDirMock('updatepropsobj'); + $objectTree = new Sabre_DAV_ObjectTree($dir); + $this->server->tree = $objectTree; + + $props = array( + '{http://sabredav.org/NS/test}someprop' => 'somevalue', + ); + + $result = $this->server->updateProperties('/',$props); + + } + + /** + * @depends testParsePropPatchRequest + * @depends testUpdateProperties + * @covers Sabre_DAV_Server::httpPropPatch + */ + public function testPropPatch() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'PROPPATCH', + ); + + $body = ' + + somevalue +'; + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($body); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'We got the wrong status. Full XML response: ' . $this->response->body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); + $this->assertEquals(1,count($data),'We expected one \'d:prop\' element. Response body: ' . $body); + + $data = $xml->xpath('//bla:someprop'); + $this->assertEquals(1,count($data),'We expected one \'s:someprop\' element. Response body: ' . $body); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(1,count($data),'We expected one \'s:status\' element. Response body: ' . $body); + + $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0]); + + } + + /** + * @depends testPropPatch + */ + public function testPropPatchAndFetch() { + + $this->testPropPatch(); + $xml = ' + + + + +'; + + $this->sendRequest($xml); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace'); + + $xpath='//bla:someprop'; + $result = $xml->xpath($xpath); + $this->assertEquals(1,count($result),'We couldn\'t find our new property in the response. Full response body:' . "\n" . $body); + $this->assertEquals('somevalue',(string)$result[0],'We couldn\'t find our new property in the response. Full response body:' . "\n" . $body); + + } + +} + +class Sabre_DAV_PropTestDirMock extends Sabre_DAV_SimpleCollection implements Sabre_DAV_IProperties { + + public $type; + + function __construct($type) { + + $this->type =$type; + parent::__construct('root'); + + } + + function updateProperties($updateProperties) { + + switch($this->type) { + case 'updatepropsfalse' : return false; + case 'updatepropsarray' : + $r = array(402 => array()); + foreach($updateProperties as $k=>$v) $r[402][$k] = null; + return $r; + case 'updatepropsobj' : + return new STDClass(); + } + + } + + function getProperties($requestedPropeties) { + + return array(); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerRangeTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerRangeTest.php new file mode 100644 index 000000000..9426e1bef --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerRangeTest.php @@ -0,0 +1,271 @@ + '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=2-5', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 4, + 'Content-Range' => 'bytes 2-5/13', + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')). '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status); + $this->assertEquals('st c', stream_get_contents($this->response->body)); + + } + + /** + * @depends testRange + */ + function testStartRange() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=2-', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 11, + 'Content-Range' => 'bytes 2-12/13', + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status); + $this->assertEquals('st contents', stream_get_contents($this->response->body)); + + } + + /** + * @depends testRange + */ + function testEndRange() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=-8', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 8, + 'Content-Range' => 'bytes 5-12/13', + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')). '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status); + $this->assertEquals('contents', stream_get_contents($this->response->body)); + + } + + /** + * @depends testRange + */ + function testTooHighRange() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=100-200', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 416 Requested Range Not Satisfiable',$this->response->status); + + } + + /** + * @depends testRange + */ + function testCrazyRange() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=8-4', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 416 Requested Range Not Satisfiable',$this->response->status); + + } + + /** + * @depends testRange + * @covers Sabre_DAV_Server::httpGet + */ + function testIfRangeEtag() { + + $node = $this->server->tree->getNodeForPath('test.txt'); + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=2-5', + 'HTTP_IF_RANGE' => $node->getETag(), + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 4, + 'Content-Range' => 'bytes 2-5/13', + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status); + $this->assertEquals('st c', stream_get_contents($this->response->body)); + + } + + /** + * @depends testRange + * @covers Sabre_DAV_Server::httpGet + */ + function testIfRangeEtagIncorrect() { + + $node = $this->server->tree->getNodeForPath('test.txt'); + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=2-5', + 'HTTP_IF_RANGE' => $node->getETag() . 'blabla', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } + + /** + * @depends testRange + * @covers Sabre_DAV_Server::httpGet + */ + function testIfRangeModificationDate() { + + $node = $this->server->tree->getNodeForPath('test.txt'); + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=2-5', + 'HTTP_IF_RANGE' => 'tomorrow', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 4, + 'Content-Range' => 'bytes 2-5/13', + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 206 Partial Content',$this->response->status); + $this->assertEquals('st c', stream_get_contents($this->response->body)); + + } + + /** + * @depends testRange + * @covers Sabre_DAV_Server::httpGet + */ + function testIfRangeModificationDateModified() { + + $node = $this->server->tree->getNodeForPath('test.txt'); + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + 'HTTP_RANGE' => 'bytes=2-5', + 'HTTP_IF_RANGE' => '-2 years', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + 'ETag' => '"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerSimpleTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerSimpleTest.php new file mode 100644 index 000000000..37f03e2b2 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerSimpleTest.php @@ -0,0 +1,696 @@ +assertEquals($nodes[0], $server->tree->getNodeForPath('hello')); + + } + + /** + * @expectedException Sabre_DAV_Exception + */ + function testConstructIncorrectObj() { + + $nodes = array( + new Sabre_DAV_SimpleCollection('hello'), + new STDClass(), + ); + + $server = new Sabre_DAV_Server($nodes); + + } + + /** + * @expectedException Sabre_DAV_Exception + */ + function testConstructInvalidArg() { + + $server = new Sabre_DAV_Server(1); + + } + + function testGet() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } + + function testGetDoesntExist() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt_randomblbla', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + $this->assertEquals('HTTP/1.1 404 Not Found',$this->response->status); + + } + + function testGetDoesntExist2() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt/randomblbla', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + $this->assertEquals('HTTP/1.1 404 Not Found',$this->response->status); + + } + + /** + * This test should have the exact same result as testGet. + * + * The idea is that double slashes // are converted to single ones / + * + */ + function testGetDoubleSlash() { + + $serverVars = array( + 'REQUEST_URI' => '//test.txt', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } + + + function testHEAD() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'HEAD', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('', $this->response->body); + + } + + function testPut() { + + $serverVars = array( + 'REQUEST_URI' => '/testput.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals(array( + "Content-Length" => "0", + ), $this->response->headers); + + $this->assertEquals('Testing new file',file_get_contents($this->tempDir . '/testput.txt')); + + } + + function testPutAlreadyExists() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF_NONE_MATCH' => '*', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status); + $this->assertNotEquals('Testing new file',file_get_contents($this->tempDir . '/test.txt')); + + } + + function testPutUpdate() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing updated file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('0', $this->response->headers['Content-Length']); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertEquals('Testing updated file',file_get_contents($this->tempDir . '/test.txt')); + + } + + function testPutContentRange() { + + $serverVars = array( + 'REQUEST_URI' => '/testput.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_CONTENT_RANGE' => 'bytes/100-200', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status); + + } + + + function testDelete() { + + $serverVars = array( + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'DELETE', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertFalse(file_exists($this->tempDir . '/test.txt')); + + } + + function testDeleteDirectory() { + + $serverVars = array( + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'DELETE', + ); + + mkdir($this->tempDir.'/testcol'); + file_put_contents($this->tempDir.'/testcol/test.txt','Hi! I\'m a file with a short lifespan'); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Length' => '0', + ),$this->response->headers); + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertFalse(file_exists($this->tempDir . '/col')); + + } + + function testOptions() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'OPTIONS', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'DAV' => '1, 3, extended-mkcol', + 'MS-Author-Via' => 'DAV', + 'Allow' => 'OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT', + 'Accept-Ranges' => 'bytes', + 'Content-Length' => '0', + 'X-Sabre-Version' => Sabre_DAV_Version::VERSION, + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('', $this->response->body); + + + } + function testNonExistantMethod() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'BLABLA', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status); + + + } + + function testGETOnCollection() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status); + + } + + function testHEADOnCollection() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'HEAD', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + + } + + function testBaseUri() { + + $serverVars = array( + 'REQUEST_URI' => '/blabla/test.txt', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->setBaseUri('/blabla/'); + $this->assertEquals('/blabla/',$this->server->getBaseUri()); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/octet-stream', + 'Content-Length' => 13, + 'Last-Modified' => Sabre_HTTP_Util::toHTTPDate(new DateTime('@' . filemtime($this->tempDir . '/test.txt'))), + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } + + function testBaseUriAddSlash() { + + $tests = array( + '/' => '/', + '/foo' => '/foo/', + '/foo/' => '/foo/', + '/foo/bar' => '/foo/bar/', + '/foo/bar/' => '/foo/bar/', + ); + + foreach($tests as $test=>$result) { + $this->server->setBaseUri($test); + + $this->assertEquals($result, $this->server->getBaseUri()); + + } + + } + + function testCalculateUri() { + + $uris = array( + 'http://www.example.org/root/somepath', + '/root/somepath', + '/root/somepath/', + ); + + $this->server->setBaseUri('/root/'); + + foreach($uris as $uri) { + + $this->assertEquals('somepath',$this->server->calculateUri($uri)); + + } + + $this->server->setBaseUri('/root'); + + foreach($uris as $uri) { + + $this->assertEquals('somepath',$this->server->calculateUri($uri)); + + } + + $this->assertEquals('', $this->server->calculateUri('/root')); + + } + + function testCalculateUriSpecialChars() { + + $uris = array( + 'http://www.example.org/root/%C3%A0fo%C3%B3', + '/root/%C3%A0fo%C3%B3', + '/root/%C3%A0fo%C3%B3/' + ); + + $this->server->setBaseUri('/root/'); + + foreach($uris as $uri) { + + $this->assertEquals("\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri)); + + } + + $this->server->setBaseUri('/root'); + + foreach($uris as $uri) { + + $this->assertEquals("\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri)); + + } + + $this->server->setBaseUri('/'); + + foreach($uris as $uri) { + + $this->assertEquals("root/\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri)); + + } + + } + + function testBaseUriCheck() { + + $uris = array( + 'http://www.example.org/root/somepath', + '/root/somepath', + '/root/somepath/' + ); + + try { + + $this->server->setBaseUri('root/'); + $this->server->calculateUri('/root/testuri'); + + $this->fail('Expected an exception'); + + } catch (Sabre_DAV_Exception_Forbidden $e) { + + // This was expected + + } + + } + + /** + * @covers Sabre_DAV_Server::guessBaseUri + */ + function testGuessBaseUri() { + + $serverVars = array( + 'REQUEST_URI' => '/index.php/root', + 'PATH_INFO' => '/root', + ); + + $httpRequest = new Sabre_HTTP_Request($serverVars); + $server = new Sabre_DAV_Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + /** + * @depends testGuessBaseUri + * @covers Sabre_DAV_Server::guessBaseUri + */ + function testGuessBaseUriPercentEncoding() { + + $serverVars = array( + 'REQUEST_URI' => '/index.php/dir/path2/path%20with%20spaces', + 'PATH_INFO' => '/dir/path2/path with spaces', + ); + + $httpRequest = new Sabre_HTTP_Request($serverVars); + $server = new Sabre_DAV_Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + /** + * @depends testGuessBaseUri + * @covers Sabre_DAV_Server::guessBaseUri + */ + /* + function testGuessBaseUriPercentEncoding2() { + + $this->markTestIncomplete('This behaviour is not yet implemented'); + $serverVars = array( + 'REQUEST_URI' => '/some%20directory+mixed/index.php/dir/path2/path%20with%20spaces', + 'PATH_INFO' => '/dir/path2/path with spaces', + ); + + $httpRequest = new Sabre_HTTP_Request($serverVars); + $server = new Sabre_DAV_Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/some%20directory+mixed/index.php/', $server->guessBaseUri()); + + }*/ + + function testGuessBaseUri2() { + + $serverVars = array( + 'REQUEST_URI' => '/index.php/root/', + 'PATH_INFO' => '/root/', + ); + + $httpRequest = new Sabre_HTTP_Request($serverVars); + $server = new Sabre_DAV_Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + function testGuessBaseUriNoPathInfo() { + + $serverVars = array( + 'REQUEST_URI' => '/index.php/root', + ); + + $httpRequest = new Sabre_HTTP_Request($serverVars); + $server = new Sabre_DAV_Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/', $server->guessBaseUri()); + + } + + function testGuessBaseUriNoPathInfo2() { + + $serverVars = array( + 'REQUEST_URI' => '/a/b/c/test.php', + ); + + $httpRequest = new Sabre_HTTP_Request($serverVars); + $server = new Sabre_DAV_Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/', $server->guessBaseUri()); + + } + + + /** + * @covers Sabre_DAV_Server::guessBaseUri + * @depends testGuessBaseUri + */ + function testGuessBaseUriQueryString() { + + $serverVars = array( + 'REQUEST_URI' => '/index.php/root?query_string=blabla', + 'PATH_INFO' => '/root', + ); + + $httpRequest = new Sabre_HTTP_Request($serverVars); + $server = new Sabre_DAV_Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + function testTriggerException() { + + $this->server->subscribeEvent('beforeMethod',array($this,'exceptionTrigger')); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $this->assertEquals('HTTP/1.1 500 Internal Server Error',$this->response->status); + + } + + function exceptionTrigger() { + + throw new Sabre_DAV_Exception('Hola'); + + } + + function testReportNotFound() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'REPORT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->httpRequest->setBody(''); + $this->server->exec(); + + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 501 Not Implemented',$this->response->status,'We got an incorrect status back. Full response body follows: ' . $this->response->body); + + } + + function testReportIntercepted() { + + $serverVars = array( + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'REPORT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->httpRequest->setBody(''); + $this->server->subscribeEvent('report',array($this,'reportHandler')); + $this->server->exec(); + + $this->assertEquals(array( + 'testheader' => 'testvalue', + ), + $this->response->headers + ); + + $this->assertEquals('HTTP/1.1 418 I\'m a teapot',$this->response->status,'We got an incorrect status back. Full response body follows: ' . $this->response->body); + + } + + function reportHandler($reportName) { + + if ($reportName=='{http://www.rooftopsolutions.nl/NS}myreport') { + $this->server->httpResponse->sendStatus(418); + $this->server->httpResponse->setHeader('testheader','testvalue'); + return false; + } + else return; + + } + + function testGetPropertiesForChildren() { + + $result = $this->server->getPropertiesForChildren('',array( + '{DAV:}getcontentlength', + )); + + $expected = array( + 'test.txt' => array('{DAV:}getcontentlength' => 13), + 'dir/' => array(), + ); + + $this->assertEquals($expected,$result); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/ServerUpdatePropertiesTest.php b/dav/SabreDAV/tests/Sabre/DAV/ServerUpdatePropertiesTest.php new file mode 100644 index 000000000..646f8ab9c --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/ServerUpdatePropertiesTest.php @@ -0,0 +1,127 @@ +updateProperties('foo', array( + '{DAV:}foo' => 'bar' + )); + + $expected = array( + 'href' => 'foo', + '403' => array( + '{DAV:}foo' => null, + ), + ); + $this->assertEquals($expected, $result); + + } + + function testUpdatePropertiesProtected() { + + $tree = array( + new Sabre_DAV_SimpleCollection('foo'), + ); + $server = new Sabre_DAV_Server($tree); + + $result = $server->updateProperties('foo', array( + '{DAV:}getetag' => 'bla', + '{DAV:}foo' => 'bar' + )); + + $expected = array( + 'href' => 'foo', + '403' => array( + '{DAV:}getetag' => null, + ), + '424' => array( + '{DAV:}foo' => null, + ), + ); + $this->assertEquals($expected, $result); + + } + + function testUpdatePropertiesEventFail() { + + $tree = array( + new Sabre_DAV_SimpleCollection('foo'), + ); + $server = new Sabre_DAV_Server($tree); + $server->subscribeEvent('updateProperties', array($this,'updatepropfail')); + + $result = $server->updateProperties('foo', array( + '{DAV:}foo' => 'bar', + '{DAV:}foo2' => 'bla', + )); + + $expected = array( + 'href' => 'foo', + '404' => array( + '{DAV:}foo' => null, + ), + '424' => array( + '{DAV:}foo2' => null, + ), + ); + $this->assertEquals($expected, $result); + + } + + function updatePropFail(&$propertyDelta, &$result, $node) { + + $result[404] = array( + '{DAV:}foo' => null, + ); + unset($propertyDelta['{DAV:}foo']); + return false; + + } + + + function testUpdatePropertiesEventSuccess() { + + $tree = array( + new Sabre_DAV_SimpleCollection('foo'), + ); + $server = new Sabre_DAV_Server($tree); + $server->subscribeEvent('updateProperties', array($this,'updatepropsuccess')); + + $result = $server->updateProperties('foo', array( + '{DAV:}foo' => 'bar', + '{DAV:}foo2' => 'bla', + )); + + $expected = array( + 'href' => 'foo', + '200' => array( + '{DAV:}foo' => null, + ), + '201' => array( + '{DAV:}foo2' => null, + ), + ); + $this->assertEquals($expected, $result); + + } + + function updatePropSuccess(&$propertyDelta, &$result, $node) { + + $result[200] = array( + '{DAV:}foo' => null, + ); + $result[201] = array( + '{DAV:}foo2' => null, + ); + unset($propertyDelta['{DAV:}foo']); + unset($propertyDelta['{DAV:}foo2']); + return; + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/SimpleFileTest.php b/dav/SabreDAV/tests/Sabre/DAV/SimpleFileTest.php new file mode 100644 index 000000000..a7bef38d2 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/SimpleFileTest.php @@ -0,0 +1,17 @@ +assertEquals('filename.txt', $file->getName()); + $this->assertEquals('contents', $file->get()); + $this->assertEquals('8', $file->getSize()); + $this->assertEquals('"' . md5('contents') . '"', $file->getETag()); + $this->assertEquals('text/plain', $file->getContentType()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/StringUtilTest.php b/dav/SabreDAV/tests/Sabre/DAV/StringUtilTest.php new file mode 100644 index 000000000..b87e79cd7 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/StringUtilTest.php @@ -0,0 +1,120 @@ +assertEquals($result, Sabre_DAV_StringUtil::textMatch($haystack, $needle, $collation, $matchType)); + + } + + function dataset() { + + return array( + array('FOOBAR', 'FOO', 'i;octet', 'contains', true), + array('FOOBAR', 'foo', 'i;octet', 'contains', false), + array('FÖÖBAR', 'FÖÖ', 'i;octet', 'contains', true), + array('FÖÖBAR', 'föö', 'i;octet', 'contains', false), + array('FOOBAR', 'FOOBAR', 'i;octet', 'equals', true), + array('FOOBAR', 'fooBAR', 'i;octet', 'equals', false), + array('FOOBAR', 'FOO', 'i;octet', 'starts-with', true), + array('FOOBAR', 'foo', 'i;octet', 'starts-with', false), + array('FOOBAR', 'BAR', 'i;octet', 'starts-with', false), + array('FOOBAR', 'bar', 'i;octet', 'starts-with', false), + array('FOOBAR', 'FOO', 'i;octet', 'ends-with', false), + array('FOOBAR', 'foo', 'i;octet', 'ends-with', false), + array('FOOBAR', 'BAR', 'i;octet', 'ends-with', true), + array('FOOBAR', 'bar', 'i;octet', 'ends-with', false), + + array('FOOBAR', 'FOO', 'i;ascii-casemap', 'contains', true), + array('FOOBAR', 'foo', 'i;ascii-casemap', 'contains', true), + array('FÖÖBAR', 'FÖÖ', 'i;ascii-casemap', 'contains', true), + array('FÖÖBAR', 'föö', 'i;ascii-casemap', 'contains', false), + array('FOOBAR', 'FOOBAR', 'i;ascii-casemap', 'equals', true), + array('FOOBAR', 'fooBAR', 'i;ascii-casemap', 'equals', true), + array('FOOBAR', 'FOO', 'i;ascii-casemap', 'starts-with', true), + array('FOOBAR', 'foo', 'i;ascii-casemap', 'starts-with', true), + array('FOOBAR', 'BAR', 'i;ascii-casemap', 'starts-with', false), + array('FOOBAR', 'bar', 'i;ascii-casemap', 'starts-with', false), + array('FOOBAR', 'FOO', 'i;ascii-casemap', 'ends-with', false), + array('FOOBAR', 'foo', 'i;ascii-casemap', 'ends-with', false), + array('FOOBAR', 'BAR', 'i;ascii-casemap', 'ends-with', true), + array('FOOBAR', 'bar', 'i;ascii-casemap', 'ends-with', true), + + array('FOOBAR', 'FOO', 'i;unicode-casemap', 'contains', true), + array('FOOBAR', 'foo', 'i;unicode-casemap', 'contains', true), + array('FÖÖBAR', 'FÖÖ', 'i;unicode-casemap', 'contains', true), + array('FÖÖBAR', 'föö', 'i;unicode-casemap', 'contains', true), + array('FOOBAR', 'FOOBAR', 'i;unicode-casemap', 'equals', true), + array('FOOBAR', 'fooBAR', 'i;unicode-casemap', 'equals', true), + array('FOOBAR', 'FOO', 'i;unicode-casemap', 'starts-with', true), + array('FOOBAR', 'foo', 'i;unicode-casemap', 'starts-with', true), + array('FOOBAR', 'BAR', 'i;unicode-casemap', 'starts-with', false), + array('FOOBAR', 'bar', 'i;unicode-casemap', 'starts-with', false), + array('FOOBAR', 'FOO', 'i;unicode-casemap', 'ends-with', false), + array('FOOBAR', 'foo', 'i;unicode-casemap', 'ends-with', false), + array('FOOBAR', 'BAR', 'i;unicode-casemap', 'ends-with', true), + array('FOOBAR', 'bar', 'i;unicode-casemap', 'ends-with', true), + ); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + public function testBadCollation() { + + Sabre_DAV_StringUtil::textMatch('foobar','foo','blabla','contains'); + + } + + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + public function testBadMatchType() { + + Sabre_DAV_StringUtil::textMatch('foobar','foo','i;octet','booh'); + + } + + public function testEnsureUTF8_ascii() { + + $inputString = "harkema"; + $outputString = "harkema"; + + $this->assertEquals( + $outputString, + Sabre_DAV_StringUtil::ensureUTF8($inputString) + ); + + } + + public function testEnsureUTF8_latin1() { + + $inputString = "m\xfcnster"; + $outputString = "münster"; + + $this->assertEquals( + $outputString, + Sabre_DAV_StringUtil::ensureUTF8($inputString) + ); + + } + + public function testEnsureUTF8_utf8() { + + $inputString = "m\xc3\xbcnster"; + $outputString = "münster"; + + $this->assertEquals( + $outputString, + Sabre_DAV_StringUtil::ensureUTF8($inputString) + ); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/TemporaryFileFilterTest.php b/dav/SabreDAV/tests/Sabre/DAV/TemporaryFileFilterTest.php new file mode 100644 index 000000000..4ac0b1ae0 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/TemporaryFileFilterTest.php @@ -0,0 +1,248 @@ +server->addPlugin($plugin); + + } + + function testPutNormal() { + + $serverVars = array( + 'REQUEST_URI' => '/testput.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('0', $this->response->headers['Content-Length']); + + $this->assertEquals('Testing new file',file_get_contents(SABRE_TEMPDIR . '/testput.txt')); + + } + + function testPutTemp() { + + // mimicking an OS/X resource fork + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + ),$this->response->headers); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'),'._testput.txt should not exist in the regular file structure.'); + + } + + function testPutTempIfNoneMatch() { + + // mimicking an OS/X resource fork + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF_NONE_MATCH' => '*', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + ),$this->response->headers); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'),'._testput.txt should not exist in the regular file structure.'); + + + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 412 Precondition failed',$this->response->status); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + } + + function testPutGet() { + + // mimicking an OS/X resource fork + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + ),$this->response->headers); + + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'GET', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 200 OK',$this->response->status); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + 'Content-Length' => 16, + 'Content-Type' => 'application/octet-stream', + ),$this->response->headers); + + $this->assertEquals('Testing new file',stream_get_contents($this->response->body)); + + } + + function testLockNonExistant() { + + mkdir(SABRE_TEMPDIR . '/locksdir'); + $locksBackend = new Sabre_DAV_Locks_Backend_FS(SABRE_TEMPDIR . '/locksdir'); + $locksPlugin = new Sabre_DAV_Locks_Plugin($locksBackend); + $this->server->addPlugin($locksPlugin); + + // mimicking an OS/X resource fork + $serverVars = array( + 'REQUEST_URI' => '/._testlock.txt', + 'REQUEST_METHOD' => 'LOCK', + ); + + $request = new Sabre_HTTP_Request($serverVars); + + $request->setBody(' + + + + + http://example.org/~ejw/contact.html + +'); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals('application/xml; charset=utf-8',$this->response->headers['Content-Type']); + $this->assertTrue(preg_match('/^$/',$this->response->headers['Lock-Token'])===1,'We did not get a valid Locktoken back (' . $this->response->headers['Lock-Token'] . ')'); + $this->assertEquals('true',$this->response->headers['X-Sabre-Temp']); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testlock.txt'),'._testlock.txt should not exist in the regular file structure.'); + + } + + function testPutDelete() { + + // mimicking an OS/X resource fork + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + ),$this->response->headers); + + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'DELETE', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 204 No Content',$this->response->status, "Incorrect status code received. Full body:\n". $this->response->body); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + ),$this->response->headers); + + $this->assertEquals('',$this->response->body); + + } + + function testPutPropfind() { + + // mimicking an OS/X resource fork + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'PUT', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals('HTTP/1.1 201 Created',$this->response->status); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + ),$this->response->headers); + + $serverVars = array( + 'REQUEST_URI' => '/._testput.txt', + 'REQUEST_METHOD' => 'PROPFIND', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody(''); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status',$this->response->status,'Incorrect status code returned. Body: ' . $this->response->body); + $this->assertEquals(array( + 'X-Sabre-Temp' => 'true', + 'Content-Type' => 'application/xml; charset=utf-8', + ),$this->response->headers); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d','urn:DAV'); + + list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); + $this->assertEquals('/._testput.txt',(string)$data,'href element should have been /._testput.txt'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); + $this->assertEquals(1,count($data)); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/TestPlugin.php b/dav/SabreDAV/tests/Sabre/DAV/TestPlugin.php new file mode 100644 index 000000000..cbd96d96c --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/TestPlugin.php @@ -0,0 +1,32 @@ +subscribeEvent('beforeMethod',array($this,'beforeMethod')); + + } + + function beforeMethod($method) { + + $this->beforeMethod = $method; + return true; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/Tree/FilesystemTest.php b/dav/SabreDAV/tests/Sabre/DAV/Tree/FilesystemTest.php new file mode 100644 index 000000000..cf310ec89 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/Tree/FilesystemTest.php @@ -0,0 +1,72 @@ +getNodeForPath('file.txt'); + $this->assertTrue($node instanceof Sabre_DAV_FS_File); + + } + + function testGetNodeForPath_Directory() { + + $fs = new Sabre_DAV_Tree_Filesystem(SABRE_TEMPDIR); + $node = $fs->getNodeForPath('dir'); + $this->assertTrue($node instanceof Sabre_DAV_FS_Directory); + + } + + function testCopy() { + + $fs = new Sabre_DAV_Tree_Filesystem(SABRE_TEMPDIR); + $fs->copy('file.txt','file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/file2.txt')); + $this->assertEquals('Body',file_get_contents(SABRE_TEMPDIR . '/file2.txt')); + + } + + function testCopyDir() { + + $fs = new Sabre_DAV_Tree_Filesystem(SABRE_TEMPDIR); + $fs->copy('dir','dir2'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/dir2')); + $this->assertEquals('Body',file_get_contents(SABRE_TEMPDIR . '/dir2/subfile.txt')); + + } + + function testMove() { + + $fs = new Sabre_DAV_Tree_Filesystem(SABRE_TEMPDIR); + $fs->move('file.txt','file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/file2.txt')); + $this->assertTrue(!file_exists(SABRE_TEMPDIR . '/file.txt')); + $this->assertEquals('Body',file_get_contents(SABRE_TEMPDIR . '/file2.txt')); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/TreeTest.php b/dav/SabreDAV/tests/Sabre/DAV/TreeTest.php new file mode 100644 index 000000000..d0a8023b4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/TreeTest.php @@ -0,0 +1,173 @@ +assertTrue($tree->nodeExists('hi')); + $this->assertFalse($tree->nodeExists('hello')); + + } + + function testCopy() { + + $tree = new Sabre_DAV_TreeMock(); + $tree->copy('hi','hi2'); + + $this->assertArrayHasKey('hi2', $tree->getNodeForPath('')->newDirectories); + $this->assertEquals('foobar', $tree->getNodeForPath('hi/file')->get()); + $this->assertEquals(array('test1'=>'value'), $tree->getNodeForPath('hi/file')->getProperties(array())); + + } + + function testMove() { + + $tree = new Sabre_DAV_TreeMock(); + $tree->move('hi','hi2'); + + $this->assertEquals('hi2', $tree->getNodeForPath('hi')->getName()); + $this->assertTrue($tree->getNodeForPath('hi')->isRenamed); + + } + + function testDeepMove() { + + $tree = new Sabre_DAV_TreeMock(); + $tree->move('hi/sub','hi2'); + + $this->assertArrayHasKey('hi2', $tree->getNodeForPath('')->newDirectories); + $this->assertTrue($tree->getNodeForPath('hi/sub')->isDeleted); + + } + + function testDelete() { + + $tree = new Sabre_DAV_TreeMock(); + $tree->delete('hi'); + $this->assertTrue($tree->getNodeForPath('hi')->isDeleted); + + } + + function testGetChildren() { + + $tree = new Sabre_DAV_TreeMock(); + $children = $tree->getChildren(''); + $this->assertEquals(1,count($children)); + $this->assertEquals('hi', $children[0]->getName()); + + } + +} + +class Sabre_DAV_TreeMock extends Sabre_DAV_Tree { + + private $nodes = array(); + + function __construct() { + + $this->nodes['hi/sub'] = new Sabre_DAV_TreeDirectoryTester('sub'); + $this->nodes['hi/file'] = new Sabre_DAV_TreeFileTester('file'); + $this->nodes['hi/file']->properties = array('test1' => 'value'); + $this->nodes['hi/file']->data = 'foobar'; + $this->nodes['hi'] = new Sabre_DAV_TreeDirectoryTester('hi',array($this->nodes['hi/sub'], $this->nodes['hi/file'])); + $this->nodes[''] = new Sabre_DAV_TreeDirectoryTester('hi', array($this->nodes['hi'])); + + } + + function getNodeForPath($path) { + + if (isset($this->nodes[$path])) return $this->nodes[$path]; + throw new Sabre_DAV_Exception_NotFound('item not found'); + + } + +} + +class Sabre_DAV_TreeDirectoryTester extends Sabre_DAV_SimpleCollection { + + public $newDirectories = array(); + public $newFiles = array(); + public $isDeleted = false; + public $isRenamed = false; + + function createDirectory($name) { + + $this->newDirectories[$name] = true; + + } + + function createFile($name,$data = null) { + + $this->newFiles[$name] = $data; + + } + + function getChild($name) { + + if (isset($this->newDirectories[$name])) return new Sabre_DAV_TreeDirectoryTester($name); + if (isset($this->newFiles[$name])) return new Sabre_DAV_TreeFileTester($name, $this->newFiles[$name]); + return parent::getChild($name); + + } + + function delete() { + + $this->isDeleted = true; + + } + + function setName($name) { + + $this->isRenamed = true; + $this->name = $name; + + } + +} + +class Sabre_DAV_TreeFileTester extends Sabre_DAV_File implements Sabre_DAV_IProperties { + + public $name; + public $data; + public $properties; + + function __construct($name, $data = null) { + + $this->name = $name; + if (is_null($data)) $data = 'bla'; + $this->data = $data; + + } + + function getName() { + + return $this->name; + + } + + function get() { + + return $this->data; + + } + + function getProperties($properties) { + + return $this->properties; + + } + + function updateProperties($properties) { + + $this->properties = $properties; + return true; + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/DAV/URLUtilTest.php b/dav/SabreDAV/tests/Sabre/DAV/URLUtilTest.php new file mode 100644 index 000000000..90b913dcc --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/URLUtilTest.php @@ -0,0 +1,129 @@ +assertEquals( + '%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f'. + '%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f'. + '%20%21%22%23%24%25%26%27()%2a%2b%2c-./'. + '0123456789%3a%3b%3c%3d%3e%3f'. + '%40ABCDEFGHIJKLMNO' . + 'PQRSTUVWXYZ%5b%5c%5d%5e_' . + '%60abcdefghijklmno' . + 'pqrstuvwxyz%7b%7c%7d~%7f', + $newStr); + + $this->assertEquals($str,Sabre_DAV_URLUtil::decodePath($newStr)); + + } + + function testEncodePathSegment() { + + $str = ''; + for($i=0;$i<128;$i++) $str.=chr($i); + + $newStr = Sabre_DAV_URLUtil::encodePathSegment($str); + + // Note: almost exactly the same as the last test, with the + // exception of the encoding of / (ascii code 2f) + $this->assertEquals( + '%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f'. + '%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f'. + '%20%21%22%23%24%25%26%27()%2a%2b%2c-.%2f'. + '0123456789%3a%3b%3c%3d%3e%3f'. + '%40ABCDEFGHIJKLMNO' . + 'PQRSTUVWXYZ%5b%5c%5d%5e_' . + '%60abcdefghijklmno' . + 'pqrstuvwxyz%7b%7c%7d~%7f', + $newStr); + + $this->assertEquals($str,Sabre_DAV_URLUtil::decodePathSegment($newStr)); + + } + + function testDecode() { + + $str = 'Hello%20Test+Test2.txt'; + $newStr = Sabre_DAV_URLUtil::decodePath($str); + $this->assertEquals('Hello Test+Test2.txt',$newStr); + + } + + /** + * @depends testDecode + */ + function testDecodeUmlaut() { + + $str = 'Hello%C3%BC.txt'; + $newStr = Sabre_DAV_URLUtil::decodePath($str); + $this->assertEquals("Hello\xC3\xBC.txt",$newStr); + + } + + /** + * @depends testDecodeUmlaut + */ + function testDecodeUmlautLatin1() { + + $str = 'Hello%FC.txt'; + $newStr = Sabre_DAV_URLUtil::decodePath($str); + $this->assertEquals("Hello\xC3\xBC.txt",$newStr); + + } + + /** + * This testcase was sent by a bug reporter + * + * @depends testDecode + */ + function testDecodeAccentsWindows7() { + + $str = '/webdav/%C3%A0fo%C3%B3'; + $newStr = Sabre_DAV_URLUtil::decodePath($str); + $this->assertEquals(strtolower($str),Sabre_DAV_URLUtil::encodePath($newStr)); + + } + + function testSplitPath() { + + $strings = array( + + // input // expected result + '/foo/bar' => array('/foo','bar'), + '/foo/bar/' => array('/foo','bar'), + 'foo/bar/' => array('foo','bar'), + 'foo/bar' => array('foo','bar'), + 'foo/bar/baz' => array('foo/bar','baz'), + 'foo/bar/baz/' => array('foo/bar','baz'), + 'foo' => array('','foo'), + 'foo/' => array('','foo'), + '/foo/' => array('','foo'), + '/foo' => array('','foo'), + '' => array(null,null), + + // UTF-8 + "/\xC3\xA0fo\xC3\xB3/bar" => array("/\xC3\xA0fo\xC3\xB3",'bar'), + "/\xC3\xA0foo/b\xC3\xBCr/" => array("/\xC3\xA0foo","b\xC3\xBCr"), + "foo/\xC3\xA0\xC3\xBCr" => array("foo","\xC3\xA0\xC3\xBCr"), + + ); + + foreach($strings as $input => $expected) { + + $output = Sabre_DAV_URLUtil::splitPath($input); + $this->assertEquals($expected, $output, 'The expected output for \'' . $input . '\' was incorrect'); + + + } + + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/UUIDUtilTest.php b/dav/SabreDAV/tests/Sabre/DAV/UUIDUtilTest.php new file mode 100644 index 000000000..613461b39 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/UUIDUtilTest.php @@ -0,0 +1,23 @@ +assertTrue( + Sabre_DAV_UUIDUtil::validateUUID('11111111-2222-3333-4444-555555555555') + ); + $this->assertFalse( + Sabre_DAV_UUIDUtil::validateUUID(' 11111111-2222-3333-4444-555555555555') + ); + $this->assertTrue( + Sabre_DAV_UUIDUtil::validateUUID('ffffffff-2222-3333-4444-555555555555') + ); + $this->assertFalse( + Sabre_DAV_UUIDUtil::validateUUID('fffffffg-2222-3333-4444-555555555555') + ); + + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAV/XMLUtilTest.php b/dav/SabreDAV/tests/Sabre/DAV/XMLUtilTest.php new file mode 100644 index 000000000..d70fe9136 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAV/XMLUtilTest.php @@ -0,0 +1,282 @@ +loadXML('Testdoc'); + + $this->assertEquals( + '{http://www.example.org/}test1', + Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild) + ); + + } + + function testToClarkNotation2() { + + $dom = new DOMDocument(); + $dom->loadXML('Testdoc'); + + $this->assertEquals( + '{http://www.example.org/}test1', + Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild) + ); + + } + + function testToClarkNotationDAVNamespace() { + + $dom = new DOMDocument(); + $dom->loadXML('Testdoc'); + + $this->assertEquals( + '{DAV:}test1', + Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild) + ); + + } + + function testToClarkNotationNoElem() { + + $dom = new DOMDocument(); + $dom->loadXML('Testdoc'); + + $this->assertNull( + Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild->firstChild) + ); + + } + + function testConvertDAVNamespace() { + + $xml='blablabla'; + $this->assertEquals( + 'blablabla', + Sabre_DAV_XMLUtil::convertDAVNamespace($xml) + ); + + } + + function testConvertDAVNamespace2() { + + $xml='blablabla'; + $this->assertEquals( + 'blablabla', + Sabre_DAV_XMLUtil::convertDAVNamespace($xml) + ); + + } + + function testConvertDAVNamespace3() { + + $xml='blablabla'; + $this->assertEquals( + 'blablabla', + Sabre_DAV_XMLUtil::convertDAVNamespace($xml) + ); + + } + + function testConvertDAVNamespace4() { + + $xml='blablabla'; + $this->assertEquals( + 'blablabla', + Sabre_DAV_XMLUtil::convertDAVNamespace($xml) + ); + + } + + function testConvertDAVNamespaceMixedQuotes() { + + $xml=''; + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $this->assertTrue($dom instanceof DOMDocument); + + } + + /** + * @depends testLoadDOMDocument + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testLoadDOMDocumentEmpty() { + + Sabre_DAV_XMLUtil::loadDOMDocument(''); + + } + + /** + * @depends testConvertDAVNamespace + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testLoadDOMDocumentInvalid() { + + $xml='assertEquals('blabla',$dom->firstChild->nodeValue); + + } + + + function testParseProperties() { + + $xml=' + + + Calendars + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $properties = Sabre_DAV_XMLUtil::parseProperties($dom->firstChild); + + $this->assertEquals(array( + '{DAV:}displayname' => 'Calendars', + ), $properties); + + + + } + + /** + * @depends testParseProperties + */ + function testParsePropertiesEmpty() { + + $xml=' + + + Calendars + + + + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $properties = Sabre_DAV_XMLUtil::parseProperties($dom->firstChild); + + $this->assertEquals(array( + '{DAV:}displayname' => 'Calendars', + '{http://www.rooftopsolutions.nl/example}example' => null + ), $properties); + + } + + + /** + * @depends testParseProperties + */ + function testParsePropertiesComplex() { + + $xml=' + + + Calendars + + + Complex value right here + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $properties = Sabre_DAV_XMLUtil::parseProperties($dom->firstChild); + + $this->assertEquals(array( + '{DAV:}displayname' => 'Calendars', + '{DAV:}someprop' => 'Complex value right here', + ), $properties); + + } + + + /** + * @depends testParseProperties + */ + function testParsePropertiesNoProperties() { + + $xml=' + + + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $properties = Sabre_DAV_XMLUtil::parseProperties($dom->firstChild); + + $this->assertEquals(array(), $properties); + + } + + function testParsePropertiesMapHref() { + + $xml=' + + + Calendars + + + http://sabredav.org/ + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + $properties = Sabre_DAV_XMLUtil::parseProperties($dom->firstChild,array('{DAV:}someprop'=>'Sabre_DAV_Property_Href')); + + $this->assertEquals(array( + '{DAV:}displayname' => 'Calendars', + '{DAV:}someprop' => new Sabre_DAV_Property_Href('http://sabredav.org/',false), + ), $properties); + + } + + function testParseClarkNotation() { + + $this->assertEquals(array( + 'DAV:', + 'foo', + ), Sabre_DAV_XMLUtil::parseClarkNotation('{DAV:}foo')); + + $this->assertEquals(array( + 'http://example.org/ns/bla', + 'bar-soap', + ), Sabre_DAV_XMLUtil::parseClarkNotation('{http://example.org/ns/bla}bar-soap')); + } + + /** + * @expectedException InvalidArgumentException + */ + function testParseClarkNotationFail() { + + Sabre_DAV_XMLUtil::parseClarkNotation('}foo'); + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/ACLMethodTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/ACLMethodTest.php new file mode 100644 index 000000000..4b4920128 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/ACLMethodTest.php @@ -0,0 +1,326 @@ +addPlugin($acl); + + $acl->unknownMethod('ACL','test'); + + } + + function testCallbackPassthru() { + + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server(); + $server->addPlugin($acl); + + $this->assertNull($acl->unknownMethod('FOO','test')); + + } + + /** + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + function testNotSupportedByNode() { + + $tree = array( + new Sabre_DAV_SimpleCollection('test'), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + function testSuccessSimple() { + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',array()), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $this->assertNull($acl->httpACL('test')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NotRecognizedPrincipal + */ + function testUnrecognizedPrincipal() { + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',array()), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/notfound + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NotRecognizedPrincipal + */ + function testUnrecognizedPrincipal2() { + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',array()), + new Sabre_DAV_SimpleCollection('principals',array( + new Sabre_DAV_SimpleCollection('notaprincipal'), + )), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/notaprincipal + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NotSupportedPrivilege + */ + function testUnknownPrivilege() { + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',array()), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/notfound + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NoAbstract + */ + function testAbstractPrivilege() { + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',array()), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/notfound + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_AceConflict + */ + function testUpdateProtectedPrivilege() { + + $oldACL = array( + array( + 'principal' => 'principals/notfound', + 'privilege' => '{DAV:}write', + 'protected' => true, + ), + ); + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',$oldACL), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/notfound + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_AceConflict + */ + function testUpdateProtectedPrivilege2() { + + $oldACL = array( + array( + 'principal' => 'principals/notfound', + 'privilege' => '{DAV:}write', + 'protected' => true, + ), + ); + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',$oldACL), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/foo + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_AceConflict + */ + function testUpdateProtectedPrivilege3() { + + $oldACL = array( + array( + 'principal' => 'principals/notfound', + 'privilege' => '{DAV:}write', + 'protected' => true, + ), + ); + + $tree = array( + new Sabre_DAVACL_MockACLNode('test',$oldACL), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/notfound + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $acl->httpACL('test'); + + } + + function testSuccessComplex () { + + $oldACL = array( + array( + 'principal' => 'principals/foo', + 'privilege' => '{DAV:}write', + 'protected' => true, + ), + array( + 'principal' => 'principals/bar', + 'privilege' => '{DAV:}read', + ), + ); + + $tree = array( + $node = new Sabre_DAVACL_MockACLNode('test',$oldACL), + new Sabre_DAV_SimpleCollection('principals', array( + new Sabre_DAVACL_MockPrincipal('foo','principals/foo'), + new Sabre_DAVACL_MockPrincipal('baz','principals/baz'), + )), + ); + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->httpRequest = new Sabre_HTTP_Request(); + $body = ' + + + + /principals/foo + + + + + /principals/baz + +'; + $server->httpRequest->setBody($body); + $server->addPlugin($acl); + + $this->assertFalse($acl->unknownMethod('ACL','test')); + + $this->assertEquals(array( + array( + 'principal' => 'principals/foo', + 'privilege' => '{DAV:}write', + 'protected' => true, + ), + array( + 'principal' => 'principals/baz', + 'privilege' => '{DAV:}write', + 'protected' => false, + ), + ), $node->getACL()); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/AllowAccessTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/AllowAccessTest.php new file mode 100644 index 000000000..342369449 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/AllowAccessTest.php @@ -0,0 +1,134 @@ +server = new Sabre_DAV_Server($nodes); + $aclPlugin = new Sabre_DAVACL_Plugin(); + $aclPlugin->allowAccessToNodesWithoutACL = true; + $this->server->addPlugin($aclPlugin); + + } + + function testGet() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('GET','testdir'))); + + } + + function testGetDoesntExist() { + + $r = $this->server->broadcastEvent('beforeMethod',array('GET','foo')); + $this->assertTrue($r); + + } + + function testHEAD() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('HEAD','testdir'))); + + } + + function testOPTIONS() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('OPTIONS','testdir'))); + + } + + function testPUT() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('PUT','testdir'))); + + } + + function testACL() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('ACL','testdir'))); + + } + + function testPROPPATCH() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('PROPPATCH','testdir'))); + + } + + function testCOPY() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('COPY','testdir'))); + + } + + function testMOVE() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('MOVE','testdir'))); + + } + + function testLOCK() { + + $this->assertTrue($this->server->broadcastEvent('beforeMethod',array('LOCK','testdir'))); + + } + + function testBeforeBind() { + + $this->assertTrue($this->server->broadcastEvent('beforeBind',array('testdir/file'))); + + } + + + function testBeforeUnbind() { + + $this->assertTrue($this->server->broadcastEvent('beforeUnbind',array('testdir'))); + + } + + function testAfterGetProperties() { + + $properties = array( + 'href' => 'foo', + '200' => array( + '{DAV:}displayname' => 'foo', + '{DAV:}getcontentlength' => 500, + ), + '404' => array( + '{DAV:}bar' => null, + ), + '403' => array( + '{DAV:}owner' => null, + ), + ); + + $expected = array( + 'href' => 'foo', + '200' => array( + '{DAV:}displayname' => 'foo', + '{DAV:}getcontentlength' => 500, + ), + '404' => array( + '{DAV:}bar' => null, + ), + '403' => array( + '{DAV:}owner' => null, + ), + ); + + $r = $this->server->broadcastEvent('afterGetProperties',array('testdir',&$properties)); + $this->assertTrue($r); + + $this->assertEquals($expected, $properties); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/BlockAccessTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/BlockAccessTest.php new file mode 100644 index 000000000..a1f6ab12f --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/BlockAccessTest.php @@ -0,0 +1,185 @@ +server = new Sabre_DAV_Server($nodes); + $this->plugin = new Sabre_DAVACL_Plugin(); + $this->plugin->allowAccessToNodesWithoutACL = false; + $this->server->addPlugin($this->plugin); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testGet() { + + $this->server->broadcastEvent('beforeMethod',array('GET','testdir')); + + } + + function testGetDoesntExist() { + + $r = $this->server->broadcastEvent('beforeMethod',array('GET','foo')); + $this->assertTrue($r); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testHEAD() { + + $this->server->broadcastEvent('beforeMethod',array('HEAD','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testOPTIONS() { + + $this->server->broadcastEvent('beforeMethod',array('OPTIONS','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testPUT() { + + $this->server->broadcastEvent('beforeMethod',array('PUT','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testPROPPATCH() { + + $this->server->broadcastEvent('beforeMethod',array('PROPPATCH','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testCOPY() { + + $this->server->broadcastEvent('beforeMethod',array('COPY','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testMOVE() { + + $this->server->broadcastEvent('beforeMethod',array('MOVE','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testACL() { + + $this->server->broadcastEvent('beforeMethod',array('ACL','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testLOCK() { + + $this->server->broadcastEvent('beforeMethod',array('LOCK','testdir')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testBeforeBind() { + + $this->server->broadcastEvent('beforeBind',array('testdir/file')); + + } + + /** + * @expectedException Sabre_DAVACL_Exception_NeedPrivileges + */ + function testBeforeUnbind() { + + $this->server->broadcastEvent('beforeUnbind',array('testdir')); + + } + + function testBeforeGetProperties() { + + $requestedProperties = array( + '{DAV:}displayname', + '{DAV:}getcontentlength', + '{DAV:}bar', + '{DAV:}owner', + ); + $returnedProperties = array(); + + $arguments = array( + 'testdir', + new Sabre_DAV_SimpleCollection('testdir'), + &$requestedProperties, + &$returnedProperties + ); + $r = $this->server->broadcastEvent('beforeGetProperties',$arguments); + $this->assertTrue($r); + + $expected = array( + '403' => array( + '{DAV:}displayname' => null, + '{DAV:}getcontentlength' => null, + '{DAV:}bar' => null, + '{DAV:}owner' => null, + ), + ); + + $this->assertEquals($expected, $returnedProperties); + $this->assertEquals(array(), $requestedProperties); + + } + + function testBeforeGetPropertiesNoListing() { + + $this->plugin->hideNodesFromListings = true; + + $requestedProperties = array( + '{DAV:}displayname', + '{DAV:}getcontentlength', + '{DAV:}bar', + '{DAV:}owner', + ); + $returnedProperties = array(); + + $arguments = array( + 'testdir', + new Sabre_DAV_SimpleCollection('testdir'), + &$requestedProperties, + &$returnedProperties + ); + $r = $this->server->broadcastEvent('beforeGetProperties',$arguments); + $this->assertFalse($r); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Exception/AceConflictTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/AceConflictTest.php new file mode 100644 index 000000000..4e0f3eefc --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/AceConflictTest.php @@ -0,0 +1,35 @@ +createElementNS('DAV:','d:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = array( + '/d:root' => 1, + '/d:root/d:no-ace-conflict' => 1, + ); + + // Reloading because PHP DOM sucks + $dom2 = new DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new DOMXPath($dom2); + $dxpath->registerNamespace('d','DAV:'); + foreach($xpaths as $xpath=>$count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NeedPrivilegesExceptionTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NeedPrivilegesExceptionTest.php new file mode 100644 index 000000000..7f4da2764 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NeedPrivilegesExceptionTest.php @@ -0,0 +1,45 @@ +createElementNS('DAV:','d:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = array( + '/d:root' => 1, + '/d:root/d:need-privileges' => 1, + '/d:root/d:need-privileges/d:resource' => 2, + '/d:root/d:need-privileges/d:resource/d:href' => 2, + '/d:root/d:need-privileges/d:resource/d:privilege' => 2, + '/d:root/d:need-privileges/d:resource/d:privilege/d:read' => 1, + '/d:root/d:need-privileges/d:resource/d:privilege/d:write' => 1, + ); + + // Reloading because PHP DOM sucks + $dom2 = new DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new DOMXPath($dom2); + $dxpath->registerNamespace('d','DAV:'); + foreach($xpaths as $xpath=>$count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NoAbstractTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NoAbstractTest.php new file mode 100644 index 000000000..e469dacd7 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NoAbstractTest.php @@ -0,0 +1,35 @@ +createElementNS('DAV:','d:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = array( + '/d:root' => 1, + '/d:root/d:no-abstract' => 1, + ); + + // Reloading because PHP DOM sucks + $dom2 = new DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new DOMXPath($dom2); + $dxpath->registerNamespace('d','DAV:'); + foreach($xpaths as $xpath=>$count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NotRecognizedPrincipalTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NotRecognizedPrincipalTest.php new file mode 100644 index 000000000..d67690836 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NotRecognizedPrincipalTest.php @@ -0,0 +1,35 @@ +createElementNS('DAV:','d:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = array( + '/d:root' => 1, + '/d:root/d:recognized-principal' => 1, + ); + + // Reloading because PHP DOM sucks + $dom2 = new DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new DOMXPath($dom2); + $dxpath->registerNamespace('d','DAV:'); + foreach($xpaths as $xpath=>$count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NotSupportedPrivilegeTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NotSupportedPrivilegeTest.php new file mode 100644 index 000000000..09c58bbaa --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Exception/NotSupportedPrivilegeTest.php @@ -0,0 +1,35 @@ +createElementNS('DAV:','d:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = array( + '/d:root' => 1, + '/d:root/d:not-supported-privilege' => 1, + ); + + // Reloading because PHP DOM sucks + $dom2 = new DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new DOMXPath($dom2); + $dxpath->registerNamespace('d','DAV:'); + foreach($xpaths as $xpath=>$count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/ExpandPropertiesTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/ExpandPropertiesTest.php new file mode 100644 index 000000000..e571bc8c4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/ExpandPropertiesTest.php @@ -0,0 +1,353 @@ + 'foo', + '{http://sabredav.org/ns}href' => new Sabre_DAV_Property_Href('node2'), + '{DAV:}displayname' => 'Node 1', + )), + new Sabre_DAVACL_MockPropertyNode('node2', array( + '{http://sabredav.org/ns}simple' => 'simple', + '{http://sabredav.org/ns}hreflist' => new Sabre_DAV_Property_HrefList(array('node1','node3')), + '{DAV:}displayname' => 'Node 2', + )), + new Sabre_DAVACL_MockPropertyNode('node3', array( + '{http://sabredav.org/ns}simple' => 'simple', + '{DAV:}displayname' => 'Node 3', + )), + ); + + $fakeServer = new Sabre_DAV_Server($tree); + $fakeServer->debugExceptions = true; + $fakeServer->httpResponse = new Sabre_HTTP_ResponseMock(); + $plugin = new Sabre_DAVACL_Plugin(); + $plugin->allowAccessToNodesWithoutACL = true; + + $this->assertTrue($plugin instanceof Sabre_DAVACL_Plugin); + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); + + return $fakeServer; + + } + + function testSimple() { + + $xml = ' + + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node1', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status,'Incorrect status code received. Full body: ' . $server->httpResponse->body); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 2, + '/d:multistatus/d:response/d:propstat/d:prop' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:simple' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:href' => 1, + ); + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d','DAV:'); + $xml->registerXPathNamespace('s','http://sabredav.org/ns'); + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response: ' . $server->httpResponse->body); + + } + + } + + /** + * @depends testSimple + */ + function testExpand() { + + $xml = ' + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node1', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status, 'Incorrect response status received. Full response body: ' . $server->httpResponse->body); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop/d:displayname' => 1, + ); + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d','DAV:'); + $xml->registerXPathNamespace('s','http://sabredav.org/ns'); + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); + + } + + } + + /** + * @depends testSimple + */ + function testExpandHrefList() { + + $xml = ' + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node2', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:href' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/d:displayname' => 2, + ); + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d','DAV:'); + $xml->registerXPathNamespace('s','http://sabredav.org/ns'); + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); + + } + + } + + /** + * @depends testExpand + */ + function testExpandDeep() { + + $xml = ' + + + + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node2', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:href' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat' => 3, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop' => 3, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/d:displayname' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop/d:displayname' => 1, + ); + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d','DAV:'); + $xml->registerXPathNamespace('s','http://sabredav.org/ns'); + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); + + } + + } +} +class Sabre_DAVACL_MockPropertyNode implements Sabre_DAV_INode, Sabre_DAV_IProperties { + + function __construct($name, array $properties) { + + $this->name = $name; + $this->properties = $properties; + + } + + function getName() { + + return $this->name; + + } + + function getProperties($requestedProperties) { + + $returnedProperties = array(); + foreach($requestedProperties as $requestedProperty) { + if (isset($this->properties[$requestedProperty])) { + $returnedProperties[$requestedProperty] = + $this->properties[$requestedProperty]; + } + } + return $returnedProperties; + + } + + function delete() { + + throw new Sabre_DAV_Exception('Not implemented'); + + } + + function setName($name) { + + throw new Sabre_DAV_Exception('Not implemented'); + + } + + function getLastModified() { + + return null; + + } + + function updateProperties($properties) { + + throw new Sabre_DAV_Exception('Not implemented'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/MockACLNode.php b/dav/SabreDAV/tests/Sabre/DAVACL/MockACLNode.php new file mode 100644 index 000000000..15fd72a68 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/MockACLNode.php @@ -0,0 +1,51 @@ +name = $name; + $this->acl = $acl; + + } + + function getName() { + + return $this->name; + + } + + function getOwner() { + + return null; + + } + + function getGroup() { + + return null; + + } + + function getACL() { + + return $this->acl; + + } + + function setACL(array $acl) { + + $this->acl = $acl; + + } + + function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/MockPrincipal.php b/dav/SabreDAV/tests/Sabre/DAVACL/MockPrincipal.php new file mode 100644 index 000000000..1ea2de3e8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/MockPrincipal.php @@ -0,0 +1,61 @@ +name = $name; + $this->principalUrl = $principalUrl; + $this->groupMembership = $groupMembership; + $this->groupMemberSet = $groupMemberSet; + + } + + function getName() { + + return $this->name; + + } + + function getDisplayName() { + + return $this->getName(); + + } + + function getAlternateUriSet() { + + return array(); + + } + + function getPrincipalUrl() { + + return $this->principalUrl; + + } + + function getGroupMemberSet() { + + return $this->groupMemberSet; + + } + + function getGroupMemberShip() { + + return $this->groupMembership; + + } + + function setGroupMemberSet(array $groupMemberSet) { + + $this->groupMemberSet = $groupMemberSet; + + } +} + diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/MockPrincipalBackend.php b/dav/SabreDAV/tests/Sabre/DAVACL/MockPrincipalBackend.php new file mode 100644 index 000000000..e6b48cc68 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/MockPrincipalBackend.php @@ -0,0 +1,182 @@ +principals = array( + array( + 'uri' => 'principals/user1', + '{DAV:}displayname' => 'User 1', + '{http://sabredav.org/ns}email-address' => 'user1.sabredav@sabredav.org', + '{http://sabredav.org/ns}vcard-url' => 'addressbooks/user1/book1/vcard1.vcf', + ), + array( + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Admin', + ), + array( + 'uri' => 'principals/user2', + '{DAV:}displayname' => 'User 2', + '{http://sabredav.org/ns}email-address' => 'user2.sabredav@sabredav.org', + ), + ); + + + } + + function getPrincipalsByPrefix($prefix) { + + $prefix = trim($prefix,'/') . '/'; + $return = array(); + + foreach($this->principals as $principal) { + + if (strpos($principal['uri'], $prefix)!==0) continue; + + $return[] = $principal; + + } + + return $return; + + } + + function addPrincipal(array $principal) { + + $this->principals[] = $principal; + + } + + function getPrincipalByPath($path) { + + foreach($this->getPrincipalsByPrefix('principals') as $principal) { + if ($principal['uri'] === $path) return $principal; + } + + } + + function searchPrincipals($prefixPath, array $searchProperties) { + + $matches = array(); + foreach($this->getPrincipalsByPrefix($prefixPath) as $principal) { + + foreach($searchProperties as $key=>$value) { + + if (!isset($principal[$key])) { + continue 2; + } + if (mb_stripos($principal[$key],$value, 0, 'UTF-8')===false) { + continue 2; + } + + } + $matches[] = $principal['uri']; + + } + return $matches; + + } + + function getGroupMemberSet($path) { + + return isset($this->groupMembers[$path]) ? $this->groupMembers[$path] : array(); + + } + + function getGroupMembership($path) { + + $membership = array(); + foreach($this->groupMembers as $group=>$members) { + if (in_array($path, $members)) $membership[] = $group; + } + return $membership; + + } + + function setGroupMemberSet($path, array $members) { + + $this->groupMembers[$path] = $members; + + } + + /** + * Updates one ore more webdav properties on a principal. + * + * The list of mutations is supplied as an array. Each key in the array is + * a propertyname, such as {DAV:}displayname. + * + * Each value is the actual value to be updated. If a value is null, it + * must be deleted. + * + * This method should be atomic. It must either completely succeed, or + * completely fail. Success and failure can simply be returned as 'true' or + * 'false'. + * + * It is also possible to return detailed failure information. In that case + * an array such as this should be returned: + * + * array( + * 200 => array( + * '{DAV:}prop1' => null, + * ), + * 201 => array( + * '{DAV:}prop2' => null, + * ), + * 403 => array( + * '{DAV:}prop3' => null, + * ), + * 424 => array( + * '{DAV:}prop4' => null, + * ), + * ); + * + * In this previous example prop1 was successfully updated or deleted, and + * prop2 was succesfully created. + * + * prop3 failed to update due to '403 Forbidden' and because of this prop4 + * also could not be updated with '424 Failed dependency'. + * + * This last example was actually incorrect. While 200 and 201 could appear + * in 1 response, if there's any error (403) the other properties should + * always fail with 423 (failed dependency). + * + * But anyway, if you don't want to scratch your head over this, just + * return true or false. + * + * @param string $path + * @param array $mutations + * @return array|bool + */ + public function updatePrincipal($path, $mutations) { + + $value = null; + foreach($this->principals as $principalIndex=>$value) { + if ($value['uri'] === $path) { + $principal = $value; + break; + } + } + if (!$principal) return false; + + foreach($mutations as $prop=>$value) { + + if (is_null($value) && isset($principal[$prop])) { + unset($principal[$prop]); + } else { + $principal[$prop] = $value; + } + + } + + $this->principals[$principalIndex] = $principal; + + return true; + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PluginAdminTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PluginAdminTest.php new file mode 100644 index 000000000..56bfb2efb --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PluginAdminTest.php @@ -0,0 +1,79 @@ +addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $fakeServer->addPlugin($plugin); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'OPTIONS', + 'HTTP_DEPTH' => 1, + 'REQUEST_URI' => '/adminonly', + )); + + $response = new Sabre_HTTP_ResponseMock(); + + $fakeServer->httpRequest = $request; + $fakeServer->httpResponse = $response; + + $fakeServer->exec(); + + $this->assertEquals('HTTP/1.1 403 Forbidden', $response->status); + + } + + /** + * @depends testNoAdminAccess + */ + function testAdminAccess() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + + $tree = array( + new Sabre_DAVACL_MockACLNode('adminonly', array()), + new Sabre_DAVACL_PrincipalCollection($principalBackend), + ); + + $fakeServer = new Sabre_DAV_Server($tree); + $plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + $fakeServer->addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $plugin->adminPrincipals = array( + 'principals/admin', + ); + $fakeServer->addPlugin($plugin); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'OPTIONS', + 'HTTP_DEPTH' => 1, + 'REQUEST_URI' => '/adminonly', + )); + + $response = new Sabre_HTTP_ResponseMock(); + + $fakeServer->httpRequest = $request; + $fakeServer->httpResponse = $response; + + $fakeServer->exec(); + + $this->assertEquals('HTTP/1.1 200 OK', $response->status); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PluginPropertiesTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PluginPropertiesTest.php new file mode 100644 index 000000000..c7a1b333f --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PluginPropertiesTest.php @@ -0,0 +1,404 @@ +principalCollectionSet = array( + 'principals1', + 'principals2', + ); + + $requestedProperties = array( + '{DAV:}principal-collection-set', + ); + + $returnedProperties = array( + 200 => array(), + 404 => array(), + ); + + $server = new Sabre_DAV_Server(); + $server->addPlugin($plugin); + + $this->assertNull($plugin->beforeGetProperties('', new Sabre_DAV_SimpleCollection('root'), $requestedProperties, $returnedProperties)); + + $this->assertEquals(1,count($returnedProperties[200])); + $this->assertArrayHasKey('{DAV:}principal-collection-set',$returnedProperties[200]); + $this->assertInstanceOf('Sabre_DAV_Property_HrefList', $returnedProperties[200]['{DAV:}principal-collection-set']); + + $expected = array( + 'principals1/', + 'principals2/', + ); + + + $this->assertEquals($expected, $returnedProperties[200]['{DAV:}principal-collection-set']->getHrefs()); + + + } + + function testCurrentUserPrincipal() { + + $fakeServer = new Sabre_DAV_Server(); + $plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + $fakeServer->addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $fakeServer->addPlugin($plugin); + + + $requestedProperties = array( + '{DAV:}current-user-principal', + ); + + $returnedProperties = array( + 200 => array(), + 404 => array(), + ); + + $this->assertNull($plugin->beforeGetProperties('', new Sabre_DAV_SimpleCollection('root'), $requestedProperties, $returnedProperties)); + + $this->assertEquals(1,count($returnedProperties[200])); + $this->assertArrayHasKey('{DAV:}current-user-principal',$returnedProperties[200]); + $this->assertInstanceOf('Sabre_DAVACL_Property_Principal', $returnedProperties[200]['{DAV:}current-user-principal']); + $this->assertEquals(Sabre_DAVACL_Property_Principal::UNAUTHENTICATED, $returnedProperties[200]['{DAV:}current-user-principal']->getType()); + + // This will force the login + $fakeServer->broadCastEvent('beforeMethod',array('GET','')); + + + $requestedProperties = array( + '{DAV:}current-user-principal', + ); + + $returnedProperties = array( + 200 => array(), + 404 => array(), + ); + + + $this->assertNull($plugin->beforeGetProperties('', new Sabre_DAV_SimpleCollection('root'), $requestedProperties, $returnedProperties)); + + + $this->assertEquals(1,count($returnedProperties[200])); + $this->assertArrayHasKey('{DAV:}current-user-principal',$returnedProperties[200]); + $this->assertInstanceOf('Sabre_DAVACL_Property_Principal', $returnedProperties[200]['{DAV:}current-user-principal']); + $this->assertEquals(Sabre_DAVACL_Property_Principal::HREF, $returnedProperties[200]['{DAV:}current-user-principal']->getType()); + $this->assertEquals('principals/admin/', $returnedProperties[200]['{DAV:}current-user-principal']->getHref()); + + } + + function testSupportedPrivilegeSet() { + + $plugin = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server(); + $server->addPlugin($plugin); + + $requestedProperties = array( + '{DAV:}supported-privilege-set', + ); + + $returnedProperties = array( + 200 => array(), + 404 => array(), + ); + + + $this->assertNull($plugin->beforeGetProperties('', new Sabre_DAV_SimpleCollection('root'), $requestedProperties, $returnedProperties)); + + $this->assertEquals(1,count($returnedProperties[200])); + $this->assertArrayHasKey('{DAV:}supported-privilege-set',$returnedProperties[200]); + $this->assertInstanceOf('Sabre_DAVACL_Property_SupportedPrivilegeSet', $returnedProperties[200]['{DAV:}supported-privilege-set']); + + $server = new Sabre_DAV_Server(); + $prop = $returnedProperties[200]['{DAV:}supported-privilege-set']; + + $dom = new DOMDocument('1.0', 'utf-8'); + $root = $dom->createElement('d:root'); + $root->setAttribute('xmlns:d','DAV:'); + $dom->appendChild($root); + $prop->serialize($server, $root); + + + $xpaths = array( + '/d:root' => 1, + '/d:root/d:supported-privilege' => 1, + '/d:root/d:supported-privilege/d:privilege' => 1, + '/d:root/d:supported-privilege/d:privilege/d:all' => 1, + '/d:root/d:supported-privilege/d:abstract' => 1, + '/d:root/d:supported-privilege/d:supported-privilege' => 2, + '/d:root/d:supported-privilege/d:supported-privilege/d:privilege' => 2, + '/d:root/d:supported-privilege/d:supported-privilege/d:privilege/d:read' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:privilege/d:write' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege' => 8, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege' => 8, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:read-acl' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:read-current-user-privilege-set' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-content' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-properties' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-acl' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:bind' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unbind' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unlock' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:abstract' => 8, + ); + + + // reloading because php dom sucks + $dom2 = new DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new DOMXPath($dom2); + $dxpath->registerNamespace('d','DAV:'); + foreach($xpaths as $xpath=>$count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + + function testACL() { + + $plugin = new Sabre_DAVACL_Plugin(); + + $nodes = array( + new Sabre_DAVACL_MockACLNode('foo', array( + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ) + )), + new Sabre_DAV_SimpleCollection('principals', array( + $principal = new Sabre_DAVACL_MockPrincipal('admin','principals/admin'), + )), + + ); + + $server = new Sabre_DAV_Server($nodes); + $server->addPlugin($plugin); + $authPlugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + $server->addPlugin($authPlugin); + + // Force login + $authPlugin->beforeMethod('BLA','foo'); + + $requestedProperties = array( + '{DAV:}acl', + ); + + $returnedProperties = array( + 200 => array(), + 404 => array(), + ); + + + $this->assertNull($plugin->beforeGetProperties('foo', $nodes[0], $requestedProperties, $returnedProperties)); + + $this->assertEquals(1,count($returnedProperties[200]),'The {DAV:}acl property did not return from the list. Full list: ' . print_r($returnedProperties,true)); + $this->assertArrayHasKey('{DAV:}acl',$returnedProperties[200]); + $this->assertInstanceOf('Sabre_DAVACL_Property_ACL', $returnedProperties[200]['{DAV:}acl']); + + } + + function testACLRestrictions() { + + $plugin = new Sabre_DAVACL_Plugin(); + + $nodes = array( + new Sabre_DAVACL_MockACLNode('foo', array( + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ) + )), + new Sabre_DAV_SimpleCollection('principals', array( + $principal = new Sabre_DAVACL_MockPrincipal('admin','principals/admin'), + )), + + ); + + $server = new Sabre_DAV_Server($nodes); + $server->addPlugin($plugin); + $authPlugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + $server->addPlugin($authPlugin); + + // Force login + $authPlugin->beforeMethod('BLA','foo'); + + $requestedProperties = array( + '{DAV:}acl-restrictions', + ); + + $returnedProperties = array( + 200 => array(), + 404 => array(), + ); + + + $this->assertNull($plugin->beforeGetProperties('foo', $nodes[0], $requestedProperties, $returnedProperties)); + + $this->assertEquals(1,count($returnedProperties[200]),'The {DAV:}acl-restrictions property did not return from the list. Full list: ' . print_r($returnedProperties,true)); + $this->assertArrayHasKey('{DAV:}acl-restrictions',$returnedProperties[200]); + $this->assertInstanceOf('Sabre_DAVACL_Property_ACLRestrictions', $returnedProperties[200]['{DAV:}acl-restrictions']); + + } + + function testAlternateUriSet() { + + $tree = array( + new Sabre_DAV_SimpleCollection('principals', array( + $principal = new Sabre_DAVACL_MockPrincipal('user','principals/user'), + )), + ); + + $fakeServer = new Sabre_DAV_Server($tree); + //$plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + //$fakeServer->addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $fakeServer->addPlugin($plugin); + + $requestedProperties = array( + '{DAV:}alternate-URI-set', + ); + $returnedProperties = array(); + + $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties); + + $this->assertNull($result); + + $this->assertTrue(isset($returnedProperties[200])); + $this->assertTrue(isset($returnedProperties[200]['{DAV:}alternate-URI-set'])); + $this->assertInstanceOf('Sabre_DAV_Property_HrefList', $returnedProperties[200]['{DAV:}alternate-URI-set']); + + $this->assertEquals(array(), $returnedProperties[200]['{DAV:}alternate-URI-set']->getHrefs()); + + } + + function testPrincipalURL() { + + $tree = array( + new Sabre_DAV_SimpleCollection('principals', array( + $principal = new Sabre_DAVACL_MockPrincipal('user','principals/user'), + )), + ); + + $fakeServer = new Sabre_DAV_Server($tree); + //$plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + //$fakeServer->addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $fakeServer->addPlugin($plugin); + + $requestedProperties = array( + '{DAV:}principal-URL', + ); + $returnedProperties = array(); + + $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties); + + $this->assertNull($result); + + $this->assertTrue(isset($returnedProperties[200])); + $this->assertTrue(isset($returnedProperties[200]['{DAV:}principal-URL'])); + $this->assertInstanceOf('Sabre_DAV_Property_Href', $returnedProperties[200]['{DAV:}principal-URL']); + + $this->assertEquals('principals/user/', $returnedProperties[200]['{DAV:}principal-URL']->getHref()); + + } + + function testGroupMemberSet() { + + $tree = array( + new Sabre_DAV_SimpleCollection('principals', array( + $principal = new Sabre_DAVACL_MockPrincipal('user','principals/user'), + )), + ); + + $fakeServer = new Sabre_DAV_Server($tree); + //$plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + //$fakeServer->addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $fakeServer->addPlugin($plugin); + + $requestedProperties = array( + '{DAV:}group-member-set', + ); + $returnedProperties = array(); + + $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties); + + $this->assertNull($result); + + $this->assertTrue(isset($returnedProperties[200])); + $this->assertTrue(isset($returnedProperties[200]['{DAV:}group-member-set'])); + $this->assertInstanceOf('Sabre_DAV_Property_HrefList', $returnedProperties[200]['{DAV:}group-member-set']); + + $this->assertEquals(array(), $returnedProperties[200]['{DAV:}group-member-set']->getHrefs()); + + } + + function testGroupMemberShip() { + + $tree = array( + new Sabre_DAV_SimpleCollection('principals', array( + $principal = new Sabre_DAVACL_MockPrincipal('user','principals/user'), + )), + ); + + $fakeServer = new Sabre_DAV_Server($tree); + //$plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + //$fakeServer->addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $fakeServer->addPlugin($plugin); + + $requestedProperties = array( + '{DAV:}group-membership', + ); + $returnedProperties = array(); + + $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties); + + $this->assertNull($result); + + $this->assertTrue(isset($returnedProperties[200])); + $this->assertTrue(isset($returnedProperties[200]['{DAV:}group-membership'])); + $this->assertInstanceOf('Sabre_DAV_Property_HrefList', $returnedProperties[200]['{DAV:}group-membership']); + + $this->assertEquals(array(), $returnedProperties[200]['{DAV:}group-membership']->getHrefs()); + + } + + function testGetDisplayName() { + + $tree = array( + new Sabre_DAV_SimpleCollection('principals', array( + $principal = new Sabre_DAVACL_MockPrincipal('user','principals/user'), + )), + ); + + $fakeServer = new Sabre_DAV_Server($tree); + //$plugin = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'realm'); + //$fakeServer->addPlugin($plugin); + $plugin = new Sabre_DAVACL_Plugin(); + $fakeServer->addPlugin($plugin); + + $requestedProperties = array( + '{DAV:}displayname', + ); + $returnedProperties = array(); + + $result = $plugin->beforeGetProperties('principals/user',$principal,$requestedProperties,$returnedProperties); + + $this->assertNull($result); + + $this->assertTrue(isset($returnedProperties[200])); + $this->assertTrue(isset($returnedProperties[200]['{DAV:}displayname'])); + + $this->assertEquals('user', $returnedProperties[200]['{DAV:}displayname']); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php new file mode 100644 index 000000000..ad0d940a5 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php @@ -0,0 +1,121 @@ +addPlugin(new Sabre_DAVACL_Plugin()); + + $result = $server->updateProperties('foo', array( + '{DAV:}foo' => 'bar', + )); + + $expected = array( + 'href' => 'foo', + '403' => array( + '{DAV:}foo' => null, + ), + ); + + $this->assertEquals($expected, $result); + + } + + public function testRemoveGroupMembers() { + + $tree = array( + new Sabre_DAVACL_MockPrincipal('foo','foo'), + ); + $server = new Sabre_DAV_Server($tree); + $server->addPlugin(new Sabre_DAVACL_Plugin()); + + $result = $server->updateProperties('foo', array( + '{DAV:}group-member-set' => null, + )); + + $expected = array( + 'href' => 'foo', + '200' => array( + '{DAV:}group-member-set' => null, + ), + ); + + $this->assertEquals($expected, $result); + $this->assertEquals(array(),$tree[0]->getGroupMemberSet()); + + } + + public function testSetGroupMembers() { + + $tree = array( + new Sabre_DAVACL_MockPrincipal('foo','foo'), + ); + $server = new Sabre_DAV_Server($tree); + $server->addPlugin(new Sabre_DAVACL_Plugin()); + + $result = $server->updateProperties('foo', array( + '{DAV:}group-member-set' => new Sabre_DAV_Property_HrefList(array('bar','baz')), + )); + + $expected = array( + 'href' => 'foo', + '200' => array( + '{DAV:}group-member-set' => null, + ), + ); + + $this->assertEquals($expected, $result); + $this->assertEquals(array('bar','baz'),$tree[0]->getGroupMemberSet()); + + } + + /** + * @expectedException sabre_DAV_Exception + */ + public function testSetBadValue() { + + $tree = array( + new Sabre_DAVACL_MockPrincipal('foo','foo'), + ); + $server = new Sabre_DAV_Server($tree); + $server->addPlugin(new Sabre_DAVACL_Plugin()); + + $result = $server->updateProperties('foo', array( + '{DAV:}group-member-set' => new StdClass(), + )); + + } + + public function testSetBadNode() { + + $tree = array( + new Sabre_DAV_SimpleCollection('foo'), + ); + $server = new Sabre_DAV_Server($tree); + $server->addPlugin(new Sabre_DAVACL_Plugin()); + + $result = $server->updateProperties('foo', array( + '{DAV:}group-member-set' => new Sabre_DAV_Property_HrefList(array('bar','baz')), + '{DAV:}bar' => 'baz', + )); + + $expected = array( + 'href' => 'foo', + '403' => array( + '{DAV:}group-member-set' => null, + ), + '424' => array( + '{DAV:}bar' => null, + ), + ); + + $this->assertEquals($expected, $result); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php new file mode 100644 index 000000000..0c8e6e28b --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php @@ -0,0 +1,172 @@ +getPDO(); + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + $this->assertTrue($backend instanceof Sabre_DAVACL_PrincipalBackend_PDO); + + } + + /** + * @depends testConstruct + */ + function testGetPrincipalsByPrefix() { + + $pdo = $this->getPDO(); + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + + $expected = array( + array( + 'uri' => 'principals/user', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + '{DAV:}displayname' => 'User', + ), + array( + 'uri' => 'principals/group', + '{http://sabredav.org/ns}email-address' => 'group@example.org', + '{DAV:}displayname' => 'Group', + ), + ); + + $this->assertEquals($expected, $backend->getPrincipalsByPrefix('principals')); + $this->assertEquals(array(), $backend->getPrincipalsByPrefix('foo')); + + } + + /** + * @depends testConstruct + */ + function testGetPrincipalByPath() { + + $pdo = $this->getPDO(); + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + + $expected = array( + 'id' => 1, + 'uri' => 'principals/user', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + '{DAV:}displayname' => 'User', + ); + + $this->assertEquals($expected, $backend->getPrincipalByPath('principals/user')); + $this->assertEquals(null, $backend->getPrincipalByPath('foo')); + + } + + function testGetGroupMemberSet() { + + $pdo = $this->getPDO(); + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + $expected = array('principals/user'); + + $this->assertEquals($expected,$backend->getGroupMemberSet('principals/group')); + + } + + function testGetGroupMembership() { + + $pdo = $this->getPDO(); + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + $expected = array('principals/group'); + + $this->assertEquals($expected,$backend->getGroupMembership('principals/user')); + + } + + function testSetGroupMemberSet() { + + $pdo = $this->getPDO(); + + // Start situation + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + $this->assertEquals(array('principals/user'), $backend->getGroupMemberSet('principals/group')); + + // Removing all principals + $backend->setGroupMemberSet('principals/group', array()); + $this->assertEquals(array(), $backend->getGroupMemberSet('principals/group')); + + // Adding principals again + $backend->setGroupMemberSet('principals/group', array('principals/user')); + $this->assertEquals(array('principals/user'), $backend->getGroupMemberSet('principals/group')); + + + } + + function testSearchPrincipals() { + + $pdo = $this->getPDO(); + + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + + $result = $backend->searchPrincipals('principals', array('{DAV:}blabla' => 'foo')); + $this->assertEquals(array(), $result); + + $result = $backend->searchPrincipals('principals', array('{DAV:}displayname' => 'ou')); + $this->assertEquals(array('principals/group'), $result); + + $result = $backend->searchPrincipals('principals', array('{DAV:}displayname' => 'UsEr', '{http://sabredav.org/ns}email-address' => 'USER@EXAMPLE')); + $this->assertEquals(array('principals/user'), $result); + + $result = $backend->searchPrincipals('mom', array('{DAV:}displayname' => 'UsEr', '{http://sabredav.org/ns}email-address' => 'USER@EXAMPLE')); + $this->assertEquals(array(), $result); + + } + + function testUpdatePrincipal() { + + $pdo = $this->getPDO(); + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + + $result = $backend->updatePrincipal('principals/user', array( + '{DAV:}displayname' => 'pietje', + '{http://sabredav.org/ns}vcard-url' => 'blabla', + )); + + $this->assertTrue($result); + + $this->assertEquals(array( + 'id' => 1, + 'uri' => 'principals/user', + '{DAV:}displayname' => 'pietje', + '{http://sabredav.org/ns}vcard-url' => 'blabla', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + ), $backend->getPrincipalByPath('principals/user')); + + } + + function testUpdatePrincipalUnknownField() { + + $pdo = $this->getPDO(); + $backend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + + $result = $backend->updatePrincipal('principals/user', array( + '{DAV:}displayname' => 'pietje', + '{http://sabredav.org/ns}vcard-url' => 'blabla', + '{DAV:}unknown' => 'foo', + )); + + $this->assertEquals(array( + 424 => array( + '{DAV:}displayname' => null, + '{http://sabredav.org/ns}vcard-url' => null, + ), + 403 => array( + '{DAV:}unknown' => null, + ), + ), $result); + + $this->assertEquals(array( + 'id' => '1', + 'uri' => 'principals/user', + '{DAV:}displayname' => 'User', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + ), $backend->getPrincipalByPath('principals/user')); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php new file mode 100644 index 000000000..d9a836552 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php @@ -0,0 +1,39 @@ +markTestSkipped('MySQL driver is not available, or not properly configured'); + $pdo = Sabre_TestUtil::getMySQLDB(); + if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); + $pdo->query("DROP TABLE IF EXISTS principals"); + $pdo->query(" +create table principals ( + id integer unsigned not null primary key auto_increment, + uri varchar(50), + email varchar(80), + displayname VARCHAR(80), + vcardurl VARCHAR(80), + unique(uri) +);"); + + $pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/user','user@example.org','User')"); + $pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/group','group@example.org','Group')"); + $pdo->query("DROP TABLE IF EXISTS groupmembers"); + $pdo->query("CREATE TABLE groupmembers ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principal_id INTEGER UNSIGNED NOT NULL, + member_id INTEGER UNSIGNED NOT NULL, + UNIQUE(principal_id, member_id) + );"); + + $pdo->query("INSERT INTO groupmembers (principal_id,member_id) VALUES (2,1)"); + + return $pdo; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php new file mode 100644 index 000000000..274065b21 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php @@ -0,0 +1,36 @@ +markTestSkipped('SQLite driver is not available'); + $pdo = new PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); + $pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + $pdo->query('CREATE TABLE principals (id INTEGER PRIMARY KEY ASC, uri TEXT, email VARCHAR(80), displayname VARCHAR(80), vcardurl VARCHAR(80))'); + $pdo->query('INSERT INTO principals VALUES (1, "principals/user","user@example.org","User",null)'); + $pdo->query('INSERT INTO principals VALUES (2, "principals/group","group@example.org","Group",null)'); + + $pdo->query("CREATE TABLE groupmembers ( + id INTEGER PRIMARY KEY ASC, + principal_id INT, + member_id INT, + UNIQUE(principal_id, member_id) + );"); + + $pdo->query("INSERT INTO groupmembers (principal_id,member_id) VALUES (2,1)"); + + return $pdo; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalCollectionTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalCollectionTest.php new file mode 100644 index 000000000..f0a475e84 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalCollectionTest.php @@ -0,0 +1,48 @@ +assertTrue($pc instanceof Sabre_DAVACL_PrincipalCollection); + + $this->assertEquals('principals',$pc->getName()); + + } + + /** + * @depends testBasic + */ + public function testGetChildren() { + + $backend = new Sabre_DAVACL_MockPrincipalBackend(); + $pc = new Sabre_DAVACL_PrincipalCollection($backend); + + $children = $pc->getChildren(); + $this->assertTrue(is_array($children)); + + foreach($children as $child) { + $this->assertTrue($child instanceof Sabre_DAVACL_IPrincipal); + } + + } + + /** + * @depends testBasic + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + public function testGetChildrenDisable() { + + $backend = new Sabre_DAVACL_MockPrincipalBackend(); + $pc = new Sabre_DAVACL_PrincipalCollection($backend); + $pc->disableListing = true; + + $children = $pc->getChildren(); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php new file mode 100644 index 000000000..0f1cc8abd --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php @@ -0,0 +1,240 @@ +addChild($principals); + + $fakeServer = new Sabre_DAV_Server(new Sabre_DAV_ObjectTree($dir)); + $fakeServer->httpResponse = new Sabre_HTTP_ResponseMock(); + $fakeServer->debugExceptions = true; + $plugin = new Sabre_DAVACL_MockPlugin($backend,'realm'); + $plugin->allowAccessToNodesWithoutACL = true; + + $this->assertTrue($plugin instanceof Sabre_DAVACL_Plugin); + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); + + return $fakeServer; + + } + + function testDepth1() { + + $xml = ' + + + + + + user + + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '1', + 'REQUEST_URI' => '/principals', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request', $server->httpResponse->status); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + } + + + function testUnknownSearchField() { + + $xml = ' + + + + + + user + + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/principals', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + } + + function testCorrect() { + + $xml = ' + + + + + + + user + + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response' => 2, + '/d:multistatus/d:response/d:href' => 2, + '/d:multistatus/d:response/d:propstat' => 4, + '/d:multistatus/d:response/d:propstat/d:prop' => 4, + '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 2, + '/d:multistatus/d:response/d:propstat/d:status' => 4, + ); + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d','DAV:'); + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } + function testWrongUri() { + + $xml = ' + + + + + + user + + + + + +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 207 Multi-Status', $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + + $check = array( + '/d:multistatus', + '/d:multistatus/d:response' => 0, + ); + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d','DAV:'); + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } +} + +class Sabre_DAVACL_MockPlugin extends Sabre_DAVACL_Plugin { + + function getCurrentUserPrivilegeSet($node) { + + return array( + '{DAV:}read', + '{DAV:}write', + ); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php new file mode 100644 index 000000000..35801ebe8 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php @@ -0,0 +1,131 @@ +addChild($principals); + + $fakeServer = new Sabre_DAV_Server(new Sabre_DAV_ObjectTree($dir)); + $fakeServer->httpResponse = new Sabre_HTTP_ResponseMock(); + $plugin = new Sabre_DAVACL_Plugin($backend,'realm'); + $this->assertTrue($plugin instanceof Sabre_DAVACL_Plugin); + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); + + return $fakeServer; + + } + + function testDepth1() { + + $xml = ' +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '1', + 'REQUEST_URI' => '/principals', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request', $server->httpResponse->status); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + } + + function testDepthIncorrectXML() { + + $xml = ' +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/principals', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 400 Bad request', $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + } + + function testCorrect() { + + $xml = ' +'; + + $serverVars = array( + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/principals', + ); + + $request = new Sabre_HTTP_Request($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals('HTTP/1.1 200 OK', $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals(array( + 'Content-Type' => 'application/xml; charset=utf-8', + ), $server->httpResponse->headers); + + + $check = array( + '/d:principal-search-property-set', + '/d:principal-search-property-set/d:principal-search-property' => 2, + '/d:principal-search-property-set/d:principal-search-property/d:prop' => 2, + '/d:principal-search-property-set/d:principal-search-property/d:prop/d:displayname' => 1, + '/d:principal-search-property-set/d:principal-search-property/d:prop/s:email-address' => 1, + '/d:principal-search-property-set/d:principal-search-property/d:description' => 2, + ); + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d','DAV:'); + $xml->registerXPathNamespace('s','http://sabredav.org/ns'); + foreach($check as $v1=>$v2) { + + $xpath = is_int($v1)?$v2:$v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalTest.php new file mode 100644 index 000000000..b00e1239b --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/PrincipalTest.php @@ -0,0 +1,201 @@ + 'principals/admin')); + $this->assertTrue($principal instanceof Sabre_DAVACL_Principal); + + } + + /** + * @expectedException Sabre_DAV_Exception + */ + public function testConstructNoUri() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array()); + + } + + public function testGetName() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertEquals('admin',$principal->getName()); + + } + + public function testGetDisplayName() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertEquals('admin',$principal->getDisplayname()); + + $principal = new Sabre_DAVACL_Principal($principalBackend, array( + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Mr. Admin' + )); + $this->assertEquals('Mr. Admin',$principal->getDisplayname()); + + } + + public function testGetProperties() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array( + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Mr. Admin', + '{http://www.example.org/custom}custom' => 'Custom', + '{http://sabredav.org/ns}email-address' => 'admin@example.org', + )); + + $keys = array( + '{DAV:}displayname', + '{http://www.example.org/custom}custom', + '{http://sabredav.org/ns}email-address', + ); + $props = $principal->getProperties($keys); + + foreach($keys as $key) $this->assertArrayHasKey($key,$props); + + $this->assertEquals('Mr. Admin',$props['{DAV:}displayname']); + + $this->assertEquals('admin@example.org', $props['{http://sabredav.org/ns}email-address']); + } + + public function testUpdateProperties() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $result = $principal->updateProperties(array('{DAV:}yourmom'=>'test')); + $this->assertEquals(true,$result); + + } + + public function testGetPrincipalUrl() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertEquals('principals/admin',$principal->getPrincipalUrl()); + + } + + public function testGetAlternateUriSet() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array( + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Mr. Admin', + '{http://www.example.org/custom}custom' => 'Custom', + '{http://sabredav.org/ns}email-address' => 'admin@example.org', + '{DAV:}alternate-URI-set' => array( + 'mailto:admin+1@example.org', + 'mailto:admin+2@example.org', + 'mailto:admin@example.org', + ), + )); + + $expected = array( + 'mailto:admin+1@example.org', + 'mailto:admin+2@example.org', + 'mailto:admin@example.org', + ); + + $this->assertEquals($expected,$principal->getAlternateUriSet()); + + } + public function testGetAlternateUriSetEmpty() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array( + 'uri' => 'principals/admin', + )); + + $expected = array(); + + $this->assertEquals($expected,$principal->getAlternateUriSet()); + + } + + public function testGetGroupMemberSet() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertEquals(array(),$principal->getGroupMemberSet()); + + } + public function testGetGroupMembership() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertEquals(array(),$principal->getGroupMembership()); + + } + + public function testSetGroupMemberSet() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $principal->setGroupMemberSet(array('principals/foo')); + + $this->assertEquals(array( + 'principals/admin' => array('principals/foo'), + ), $principalBackend->groupMembers); + + } + + public function testGetOwner() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertEquals('principals/admin',$principal->getOwner()); + + } + + public function testGetGroup() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertNull($principal->getGroup()); + + } + + public function testGetACl() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertEquals(array( + array( + 'privilege' => '{DAV:}read', + 'principal' => 'principals/admin', + 'protected' => true, + ) + ),$principal->getACL()); + + } + + /** + * @expectedException Sabre_DAV_Exception_MethodNotAllowed + */ + public function testSetACl() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $principal->setACL(array()); + + } + + public function testGetSupportedPrivilegeSet() { + + $principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + $principal = new Sabre_DAVACL_Principal($principalBackend, array('uri' => 'principals/admin')); + $this->assertNull($principal->getSupportedPrivilegeSet()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php new file mode 100644 index 000000000..921052bcb --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php @@ -0,0 +1,30 @@ +createElementNS('DAV:','d:root'); + + $dom->appendChild($root); + + $acl = new Sabre_DAVACL_Property_AclRestrictions(); + $acl->serialize(new Sabre_DAV_Server(), $root); + + $xml = $dom->saveXML(); + $expected = ' + +'; + $this->assertEquals($expected, $xml); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Property/ACLTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Property/ACLTest.php new file mode 100644 index 000000000..a6efee2db --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Property/ACLTest.php @@ -0,0 +1,329 @@ +createElementNS('DAV:','d:root'); + + $dom->appendChild($root); + + $acl = new Sabre_DAVACL_Property_Acl(array()); + $acl->serialize(new Sabre_DAV_Server(), $root); + + $xml = $dom->saveXML(); + $expected = ' + +'; + $this->assertEquals($expected, $xml); + + } + + function testSerialize() { + + $dom = new DOMDocument('1.0'); + $root = $dom->createElementNS('DAV:','d:root'); + + $dom->appendChild($root); + + $privileges = array( + array( + 'principal' => 'principals/evert', + 'privilege' => '{DAV:}write', + 'uri' => 'articles', + ), + array( + 'principal' => 'principals/foo', + 'privilege' => '{DAV:}read', + 'uri' => 'articles', + 'protected' => true, + ), + ); + + $acl = new Sabre_DAVACL_Property_Acl($privileges); + $acl->serialize(new Sabre_DAV_Server(), $root); + + $dom->formatOutput = true; + + $xml = $dom->saveXML(); + $expected = ' + + + + /principals/evert/ + + + + + + + + + + /principals/foo/ + + + + + + + + + +'; + $this->assertEquals($expected, $xml); + + } + + function testSerializeSpecialPrincipals() { + + $dom = new DOMDocument('1.0'); + $root = $dom->createElementNS('DAV:','d:root'); + + $dom->appendChild($root); + + $privileges = array( + array( + 'principal' => '{DAV:}authenticated', + 'privilege' => '{DAV:}write', + 'uri' => 'articles', + ), + array( + 'principal' => '{DAV:}unauthenticated', + 'privilege' => '{DAV:}write', + 'uri' => 'articles', + ), + array( + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}write', + 'uri' => 'articles', + ), + + ); + + $acl = new Sabre_DAVACL_Property_Acl($privileges); + $acl->serialize(new Sabre_DAV_Server(), $root); + + $dom->formatOutput = true; + + $xml = $dom->saveXML(); + $expected = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +'; + $this->assertEquals($expected, $xml); + + } + + function testUnserialize() { + + $source = ' + + + + /principals/evert/ + + + + + + + + + + /principals/foo/ + + + + + + + + + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($source); + $result = Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild); + + $this->assertInstanceOf('Sabre_DAVACL_Property_Acl', $result); + + $expected = array( + array( + 'principal' => '/principals/evert/', + 'protected' => false, + 'privilege' => '{DAV:}write', + ), + array( + 'principal' => '/principals/foo/', + 'protected' => true, + 'privilege' => '{DAV:}read', + ), + ); + + $this->assertEquals($expected, $result->getPrivileges()); + + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testUnserializeNoPrincipal() { + + $source = ' + + + + + + + + + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($source); + Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild); + + } + + function testUnserializeOtherPrincipal() { + + $source = ' + + + + + + + + + + + + + + + + + + + + + + + + + + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($source); + $result = Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild); + + $this->assertInstanceOf('Sabre_DAVACL_Property_Acl', $result); + + $expected = array( + array( + 'principal' => '{DAV:}authenticated', + 'protected' => false, + 'privilege' => '{DAV:}write', + ), + array( + 'principal' => '{DAV:}unauthenticated', + 'protected' => false, + 'privilege' => '{DAV:}write', + ), + array( + 'principal' => '{DAV:}all', + 'protected' => false, + 'privilege' => '{DAV:}write', + ), + ); + + $this->assertEquals($expected, $result->getPrivileges()); + + + } + + /** + * @expectedException Sabre_DAV_Exception_NotImplemented + */ + function testUnserializeDeny() { + + $source = ' + + + + + + + + /principals/evert + + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($source); + Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild); + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testUnserializeMissingPriv() { + + $source = ' + + + + + + /principals/evert + + +'; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($source); + Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php new file mode 100644 index 000000000..40a8983b1 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php @@ -0,0 +1,41 @@ +createElementNS('DAV:','d:root'); + $dom->appendChild($root); + + $prop->serialize($server, $root); + + $xpaths = array( + '/d:root' => 1, + '/d:root/d:privilege' => 2, + '/d:root/d:privilege/d:read' => 1, + '/d:root/d:privilege/d:write' => 1, + ); + + // Reloading because PHP DOM sucks + $dom2 = new DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new DOMXPath($dom2); + $dxpath->registerNamespace('d','DAV:'); + foreach($xpaths as $xpath=>$count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Property/PrincipalTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Property/PrincipalTest.php new file mode 100644 index 000000000..3f8592e80 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Property/PrincipalTest.php @@ -0,0 +1,176 @@ +assertEquals(Sabre_DAVACL_Property_Principal::UNAUTHENTICATED, $principal->getType()); + $this->assertNull($principal->getHref()); + + $principal = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::AUTHENTICATED); + $this->assertEquals(Sabre_DAVACL_Property_Principal::AUTHENTICATED, $principal->getType()); + $this->assertNull($principal->getHref()); + + $principal = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,'admin'); + $this->assertEquals(Sabre_DAVACL_Property_Principal::HREF, $principal->getType()); + $this->assertEquals('admin',$principal->getHref()); + + } + + /** + * @depends testSimple + * @expectedException Sabre_DAV_Exception + */ + function testNoHref() { + + $principal = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF); + + } + + /** + * @depends testSimple + */ + function testSerializeUnAuthenticated() { + + $prin = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::UNAUTHENTICATED); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:principal'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $prin->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +' +', $xml); + + } + + + /** + * @depends testSerializeUnAuthenticated + */ + function testSerializeAuthenticated() { + + $prin = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::AUTHENTICATED); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:principal'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $prin->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +' +', $xml); + + } + + + /** + * @depends testSerializeUnAuthenticated + */ + function testSerializeHref() { + + $prin = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,'principals/admin'); + + $doc = new DOMDocument(); + $root = $doc->createElement('d:principal'); + $root->setAttribute('xmlns:d','DAV:'); + + $doc->appendChild($root); + $objectTree = new Sabre_DAV_ObjectTree(new Sabre_DAV_SimpleCollection('rootdir')); + $server = new Sabre_DAV_Server($objectTree); + + $prin->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'/principals/admin' . +' +', $xml); + + } + + function testUnserializeHref() { + + $xml = ' +' . +'/principals/admin' . +''; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + + $principal = Sabre_DAVACL_Property_Principal::unserialize($dom->firstChild); + $this->assertEquals(Sabre_DAVACL_Property_Principal::HREF, $principal->getType()); + $this->assertEquals('/principals/admin', $principal->getHref()); + + } + + function testUnserializeAuthenticated() { + + $xml = ' +' . +' ' . +''; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + + $principal = Sabre_DAVACL_Property_Principal::unserialize($dom->firstChild); + $this->assertEquals(Sabre_DAVACL_Property_Principal::AUTHENTICATED, $principal->getType()); + + } + + function testUnserializeUnauthenticated() { + + $xml = ' +' . +' ' . +''; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + + $principal = Sabre_DAVACL_Property_Principal::unserialize($dom->firstChild); + $this->assertEquals(Sabre_DAVACL_Property_Principal::UNAUTHENTICATED, $principal->getType()); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testUnserializeUnknown() { + + $xml = ' +' . +' ' . +''; + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($xml); + + Sabre_DAVACL_Property_Principal::unserialize($dom->firstChild); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php new file mode 100644 index 000000000..0dc948363 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php @@ -0,0 +1,99 @@ + '{DAV:}all', + )); + + } + + + /** + * @depends testSimple + */ + function testSerializeSimple() { + + $prop = new Sabre_DAVACL_Property_SupportedPrivilegeSet(array( + 'privilege' => '{DAV:}all', + )); + + $doc = new DOMDocument(); + $root = $doc->createElementNS('DAV:', 'd:supported-privilege-set'); + + $doc->appendChild($root); + + $server = new Sabre_DAV_Server(); + $prop->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'' . +'' . +'' . +'' . +' +', $xml); + + } + + /** + * @depends testSimple + */ + function testSerializeAggregate() { + + $prop = new Sabre_DAVACL_Property_SupportedPrivilegeSet(array( + 'privilege' => '{DAV:}all', + 'abstract' => true, + 'aggregates' => array( + array( + 'privilege' => '{DAV:}read', + ), + array( + 'privilege' => '{DAV:}write', + 'description' => 'booh', + ), + ), + )); + + $doc = new DOMDocument(); + $root = $doc->createElementNS('DAV:', 'd:supported-privilege-set'); + + $doc->appendChild($root); + + $server = new Sabre_DAV_Server(); + $prop->serialize($server, $root); + + $xml = $doc->saveXML(); + + $this->assertEquals( +' +' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'' . +'booh' . +'' . +'' . +' +', $xml); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/SimplePluginTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/SimplePluginTest.php new file mode 100644 index 000000000..de224ae63 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/SimplePluginTest.php @@ -0,0 +1,311 @@ +assertEquals('acl',$aclPlugin->getPluginName()); + $this->assertEquals(array('access-control'), $aclPlugin->getFeatures()); + + $this->assertEquals( + array( + '{DAV:}expand-property', + '{DAV:}principal-property-search', + '{DAV:}principal-search-property-set' + ), + $aclPlugin->getSupportedReportSet('')); + + $this->assertEquals(array('ACL'), $aclPlugin->getMethods('')); + + } + + function testGetFlatPrivilegeSet() { + + $expected = array( + '{DAV:}all' => array( + 'privilege' => '{DAV:}all', + 'abstract' => true, + 'aggregates' => array( + '{DAV:}read', + '{DAV:}write', + ), + 'concrete' => null, + ), + '{DAV:}read' => array( + 'privilege' => '{DAV:}read', + 'abstract' => false, + 'aggregates' => array( + '{DAV:}read-acl', + '{DAV:}read-current-user-privilege-set', + ), + 'concrete' => '{DAV:}read', + ), + '{DAV:}read-acl' => array( + 'privilege' => '{DAV:}read-acl', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}read', + ), + '{DAV:}read-current-user-privilege-set' => array( + 'privilege' => '{DAV:}read-current-user-privilege-set', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}read', + ), + '{DAV:}write' => array( + 'privilege' => '{DAV:}write', + 'abstract' => false, + 'aggregates' => array( + '{DAV:}write-acl', + '{DAV:}write-properties', + '{DAV:}write-content', + '{DAV:}bind', + '{DAV:}unbind', + '{DAV:}unlock', + ), + 'concrete' => '{DAV:}write', + ), + '{DAV:}write-acl' => array( + 'privilege' => '{DAV:}write-acl', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}write', + ), + '{DAV:}write-properties' => array( + 'privilege' => '{DAV:}write-properties', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}write', + ), + '{DAV:}write-content' => array( + 'privilege' => '{DAV:}write-content', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}write', + ), + '{DAV:}unlock' => array( + 'privilege' => '{DAV:}unlock', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}write', + ), + '{DAV:}bind' => array( + 'privilege' => '{DAV:}bind', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}write', + ), + '{DAV:}unbind' => array( + 'privilege' => '{DAV:}unbind', + 'abstract' => true, + 'aggregates' => array(), + 'concrete' => '{DAV:}write', + ), + + ); + + $plugin = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server(); + $server->addPlugin($plugin); + $this->assertEquals($expected, $plugin->getFlatPrivilegeSet('')); + + } + + function testCurrentUserPrincipalsNotLoggedIn() { + + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server(); + $server->addPlugin($acl); + + $this->assertEquals(array(),$acl->getCurrentUserPrincipals()); + + } + + function testCurrentUserPrincipalsSimple() { + + $tree = array( + + new Sabre_DAV_SimpleCollection('principals', array( + new Sabre_DAVACL_MockPrincipal('admin','principals/admin'), + )) + + ); + + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->addPlugin($acl); + + $auth = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'SabreDAV'); + $server->addPlugin($auth); + + //forcing login + $auth->beforeMethod('GET','/'); + + $this->assertEquals(array('principals/admin'),$acl->getCurrentUserPrincipals()); + + } + + function testCurrentUserPrincipalsGroups() { + + $tree = array( + + new Sabre_DAV_SimpleCollection('principals', array( + new Sabre_DAVACL_MockPrincipal('admin','principals/admin',array('principals/administrators', 'principals/everyone')), + new Sabre_DAVACL_MockPrincipal('administrators','principals/administrators',array('principals/groups'), array('principals/admin')), + new Sabre_DAVACL_MockPrincipal('everyone','principals/everyone',array(), array('principals/admin')), + new Sabre_DAVACL_MockPrincipal('groups','principals/groups',array(), array('principals/administrators')), + )) + + ); + + $acl = new Sabre_DAVACL_Plugin(); + $server = new Sabre_DAV_Server($tree); + $server->addPlugin($acl); + + $auth = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'SabreDAV'); + $server->addPlugin($auth); + + //forcing login + $auth->beforeMethod('GET','/'); + + $expected = array( + 'principals/admin', + 'principals/administrators', + 'principals/everyone', + 'principals/groups', + ); + + $this->assertEquals($expected,$acl->getCurrentUserPrincipals()); + + } + + function testGetACL() { + + $acl = array( + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ), + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}write', + ), + ); + + + $tree = array( + new Sabre_DAVACL_MockACLNode('foo',$acl), + ); + + $server = new Sabre_DAV_Server($tree); + $aclPlugin = new Sabre_DAVACL_Plugin(); + $server->addPlugin($aclPlugin); + + $this->assertEquals($acl,$aclPlugin->getACL('foo')); + + } + + function testGetCurrentUserPrivilegeSet() { + + $acl = array( + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ), + array( + 'principal' => 'principals/user1', + 'privilege' => '{DAV:}read', + ), + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}write', + ), + ); + + + $tree = array( + new Sabre_DAVACL_MockACLNode('foo',$acl), + + new Sabre_DAV_SimpleCollection('principals', array( + new Sabre_DAVACL_MockPrincipal('admin','principals/admin'), + )), + + ); + + $server = new Sabre_DAV_Server($tree); + $aclPlugin = new Sabre_DAVACL_Plugin(); + $server->addPlugin($aclPlugin); + + $auth = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'SabreDAV'); + $server->addPlugin($auth); + + //forcing login + $auth->beforeMethod('GET','/'); + + $expected = array( + '{DAV:}write', + '{DAV:}write-acl', + '{DAV:}write-properties', + '{DAV:}write-content', + '{DAV:}bind', + '{DAV:}unbind', + '{DAV:}unlock', + '{DAV:}read', + '{DAV:}read-acl', + '{DAV:}read-current-user-privilege-set', + ); + + $this->assertEquals($expected,$aclPlugin->getCurrentUserPrivilegeSet('foo')); + + } + + function testCheckPrivileges() { + + $acl = array( + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ), + array( + 'principal' => 'principals/user1', + 'privilege' => '{DAV:}read', + ), + array( + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}write', + ), + ); + + + $tree = array( + new Sabre_DAVACL_MockACLNode('foo',$acl), + + new Sabre_DAV_SimpleCollection('principals', array( + new Sabre_DAVACL_MockPrincipal('admin','principals/admin'), + )), + + ); + + $server = new Sabre_DAV_Server($tree); + $aclPlugin = new Sabre_DAVACL_Plugin(); + $server->addPlugin($aclPlugin); + + $auth = new Sabre_DAV_Auth_Plugin(new Sabre_DAV_Auth_MockBackend(),'SabreDAV'); + $server->addPlugin($auth); + + //forcing login + //$auth->beforeMethod('GET','/'); + + $this->assertFalse($aclPlugin->checkPrivileges('foo', array('{DAV:}read'), Sabre_DAVACL_Plugin::R_PARENT, false)); + + } +} + + + + diff --git a/dav/SabreDAV/tests/Sabre/DAVACL/VersionTest.php b/dav/SabreDAV/tests/Sabre/DAVACL/VersionTest.php new file mode 100644 index 000000000..216bd05c1 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVACL/VersionTest.php @@ -0,0 +1,15 @@ +assertEquals(-1, version_compare('1.0.0',$v)); + + $s = Sabre_DAVACL_Version::STABILITY; + $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/DAVServerTest.php b/dav/SabreDAV/tests/Sabre/DAVServerTest.php new file mode 100644 index 000000000..910e77b89 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/DAVServerTest.php @@ -0,0 +1,131 @@ +setUpBackends(); + $this->setUpTree(); + + $this->server = new Sabre_DAV_Server($this->tree); + + if ($this->setupCalDAV) { + $this->caldavPlugin = new Sabre_CalDAV_Plugin(); + $this->server->addPlugin($this->caldavPlugin); + } + if ($this->setupCardDAV) { + $this->carddavPlugin = new Sabre_CardDAV_Plugin(); + $this->server->addPlugin($this->carddavPlugin); + } + + } + + /** + * Makes a request, and returns a response object. + * + * You can either pass an isntance of Sabre_HTTP_Request, or an array, + * which will then be used as the _SERVER array. + * + * @param array|Sabre_HTTP_Request $request + * @return Sabre_HTTP_Response + */ + function request($request) { + + if (is_array($request)) { + $request = new Sabre_HTTP_Request($request); + } + $this->server->httpRequest = $request; + $this->server->httpResponse = new Sabre_HTTP_ResponseMock(); + $this->server->exec(); + + return $this->server->httpResponse; + + } + + function setUpTree() { + + if ($this->setupCalDAV) { + $this->tree[] = new Sabre_CalDAV_CalendarRootNode( + $this->principalBackend, + $this->caldavBackend + ); + } + if ($this->setupCardDAV) { + $this->tree[] = new Sabre_CardDAV_AddressBookRoot( + $this->principalBackend, + $this->carddavBackend + ); + } + + if ($this->setupCardDAV || $this->setupCalDAV) { + $this->tree[] = new Sabre_DAVACL_PrincipalCollection( + $this->principalBackend + ); + } + + } + + function setUpBackends() { + + if ($this->setupCalDAV) { + $this->caldavBackend = new Sabre_CalDAV_Backend_Mock($this->caldavCalendars, $this->caldavCalendarObjects); + } + if ($this->setupCardDAV) { + $this->carddavBackend = new Sabre_CardDAV_Backend_Mock($this->carddavAddressBooks, $this->carddavCards); + } + if ($this->setupCardDAV || $this->setupCalDAV) { + $this->principalBackend = new Sabre_DAVACL_MockPrincipalBackend(); + } + + } + + + function assertHTTPStatus($expectedStatus, Sabre_HTTP_Request $req) { + + $resp = $this->request($req); + $this->assertEquals($resp->getStatusMessage($expectedStatus), $resp->status,'Incorrect HTTP status received: ' . $resp->body); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/AWSAuthTest.php b/dav/SabreDAV/tests/Sabre/HTTP/AWSAuthTest.php new file mode 100644 index 000000000..b402146a4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/AWSAuthTest.php @@ -0,0 +1,240 @@ +response = new Sabre_HTTP_ResponseMock(); + $this->auth = new Sabre_HTTP_AWSAuth(); + $this->auth->setRealm(self::REALM); + $this->auth->setHTTPResponse($this->response); + + } + + public function testNoHeader() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + )); + + $this->auth->setHTTPRequest($request); + + $result = $this->auth->init(); + + $this->assertFalse($result,'No AWS Authorization header was supplied, so we should have gotten false'); + $this->assertEquals(Sabre_HTTP_AWSAuth::ERR_NOAWSHEADER,$this->auth->errorCode); + + } + + public function testIncorrectContentMD5() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig", + 'HTTP_CONTENT_MD5' => 'garbage', + 'REQUEST_URI' => '/', + )); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(Sabre_HTTP_AWSAuth::ERR_MD5CHECKSUMWRONG,$this->auth->errorCode); + + } + + public function testNoDate() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content,true)); + + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig", + 'HTTP_CONTENT_MD5' => $contentMD5, + )); + + $request->setBody($content); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(Sabre_HTTP_AWSAuth::ERR_INVALIDDATEFORMAT,$this->auth->errorCode); + + } + + public function testFutureDate() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content,true)); + + $date = new DateTime('@' . (time() + (60*20))); + $date->setTimeZone(new DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig", + 'HTTP_CONTENT_MD5' => $contentMD5, + 'HTTP_DATE' => $date, + )); + + $request->setBody($content); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(Sabre_HTTP_AWSAuth::ERR_REQUESTTIMESKEWED,$this->auth->errorCode); + + } + + public function testPastDate() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content,true)); + + $date = new DateTime('@' . (time() - (60*20))); + $date->setTimeZone(new DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig", + 'HTTP_CONTENT_MD5' => $contentMD5, + 'HTTP_X_AMZ_DATE' => $date, + )); + + $request->setBody($content); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(Sabre_HTTP_AWSAuth::ERR_REQUESTTIMESKEWED,$this->auth->errorCode); + + } + + public function testIncorrectSignature() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + + $contentMD5 = base64_encode(md5($content,true)); + + $date = new DateTime('now'); + $date->setTimeZone(new DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'HTTP_AUTHORIZATION' => "AWS $accessKey:sig", + 'HTTP_CONTENT_MD5' => $contentMD5, + 'HTTP_X_AMZ_DATE' => $date, + 'REQUEST_URI' => '/', + )); + + $request->setBody($content); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(Sabre_HTTP_AWSAuth::ERR_INVALIDSIGNATURE,$this->auth->errorCode); + + } + + public function testValidRequest() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content,true)); + + $date = new DateTime('now'); + $date->setTimeZone(new DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + + $sig = base64_encode($this->hmacsha1($secretKey, + "POST\n$contentMD5\n\n$date\nx-amz-date:$date\n/evert" + )); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'HTTP_AUTHORIZATION' => "AWS $accessKey:$sig", + 'HTTP_CONTENT_MD5' => $contentMD5, + 'HTTP_X_AMZ_DATE' => $date, + 'REQUEST_URI' => '/evert', + )); + + $request->setBody($content); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertTrue($result,'Signature did not validate, got errorcode ' . $this->auth->errorCode); + $this->assertEquals($accessKey,$this->auth->getAccessKey()); + + } + + public function test401() { + + $this->auth->requireLogin(); + $test = preg_match('/^AWS$/',$this->response->headers['WWW-Authenticate'],$matches); + $this->assertTrue($test==true,'The WWW-Authenticate response didn\'t match our pattern'); + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + $blocksize=64; + if (strlen($key)>$blocksize) + $key=pack('H*', sha1($key)); + $key=str_pad($key,$blocksize,chr(0x00)); + $ipad=str_repeat(chr(0x36),$blocksize); + $opad=str_repeat(chr(0x5c),$blocksize); + $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message)))); + return $hmac; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/BasicAuthTest.php b/dav/SabreDAV/tests/Sabre/HTTP/BasicAuthTest.php new file mode 100644 index 000000000..b5dd51445 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/BasicAuthTest.php @@ -0,0 +1,111 @@ +response = new Sabre_HTTP_ResponseMock(); + $this->basicAuth = new Sabre_HTTP_BasicAuth(); + $this->basicAuth->setHTTPResponse($this->response); + + } + + function testGetUserPassApache() { + + $server = array( + 'PHP_AUTH_USER' => 'admin', + 'PHP_AUTH_PW' => '1234', + ); + + $request = new Sabre_HTTP_Request($server); + $this->basicAuth->setHTTPRequest($request); + + $userPass = $this->basicAuth->getUserPass(); + + $this->assertEquals( + array('admin','1234'), + $userPass, + 'We did not get the username and password we expected' + ); + + } + + function testGetUserPassIIS() { + + $server = array( + 'HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('admin:1234'), + ); + + $request = new Sabre_HTTP_Request($server); + $this->basicAuth->setHTTPRequest($request); + + $userPass = $this->basicAuth->getUserPass(); + + $this->assertEquals( + array('admin','1234'), + $userPass, + 'We did not get the username and password we expected' + ); + + } + + function testGetUserPassApacheEdgeCase() { + + $server = array( + 'REDIRECT_HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('admin:1234'), + ); + + $request = new Sabre_HTTP_Request($server); + $this->basicAuth->setHTTPRequest($request); + + $userPass = $this->basicAuth->getUserPass(); + + $this->assertEquals( + array('admin','1234'), + $userPass, + 'We did not get the username and password we expected' + ); + + } + + function testGetUserPassNothing() { + + $this->assertEquals( + false, + $this->basicAuth->getUserPass() + ); + + } + + function testRequireLogin() { + + $this->basicAuth->requireLogin(); + $this->assertEquals('SabreDAV',$this->basicAuth->getRealm()); + $this->assertEquals( + 'HTTP/1.1 401 Unauthorized', + $this->response->status, + 'We expected a 401 status to be set' + ); + + $this->assertEquals( + 'Basic realm="SabreDAV"', + $this->response->headers['WWW-Authenticate'], + 'The WWW-Autenticate header was not set!' + ); + + + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/DigestAuthTest.php b/dav/SabreDAV/tests/Sabre/HTTP/DigestAuthTest.php new file mode 100644 index 000000000..c5bf53580 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/DigestAuthTest.php @@ -0,0 +1,226 @@ +response = new Sabre_HTTP_ResponseMock(); + $this->auth = new Sabre_HTTP_DigestAuth(); + $this->auth->setRealm(self::REALM); + $this->auth->setHTTPResponse($this->response); + + } + + public function testDigest() { + + list($nonce,$opaque) = $this->getServerTokens(); + + $username = 'admin'; + $password = 12345; + $nc = '00002'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth:' . + md5('GET' . ':' . '/') + ); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + 'PHP_AUTH_DIGEST' => 'username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"', + )); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + + $this->assertEquals($username,$this->auth->getUserName()); + $this->assertEquals(self::REALM,$this->auth->getRealm()); + $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1'); + $this->assertTrue($this->auth->validatePassword($password),'Authentication is deemed invalid through validatePassword'); + + } + + public function testDigestCGIFormat() { + + list($nonce,$opaque) = $this->getServerTokens(); + + $username = 'admin'; + $password = 12345; + $nc = '00002'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth:' . + md5('GET' . ':' . '/') + ); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + 'HTTP_AUTHORIZATION' => 'Digest username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"', + )); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + + $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1'); + $this->assertTrue($this->auth->validatePassword($password),'Authentication is deemed invalid through validatePassword'); + + } + + public function testDigestApacheEdgeCase() { + + list($nonce,$opaque) = $this->getServerTokens(); + + $username = 'admin'; + $password = 12345; + $nc = '00002'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth:' . + md5('GET' . ':' . '/') + ); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + 'REDIRECT_HTTP_AUTHORIZATION' => 'Digest username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"', + )); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + + $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1'); + $this->assertTrue($this->auth->validatePassword($password),'Authentication is deemed invalid through validatePassword'); + + } + + public function testInvalidDigest() { + + list($nonce,$opaque) = $this->getServerTokens(); + + $username = 'admin'; + $password = 12345; + $nc = '00002'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth:' . + md5('GET' . ':' . '/') + ); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + 'PHP_AUTH_DIGEST' => 'username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc='.$nc.',cnonce="' . $cnonce . '"', + )); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + + $this->assertFalse($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . ($password . 'randomness'))),'Authentication is deemed invalid through validateA1'); + + } + + public function testInvalidDigest2() { + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'GET', + 'HTTP_AUTHORIZATION' => 'basic blablabla', + )); + + $this->auth->setHTTPRequest($request); + $this->auth->init(); + + $this->assertFalse($this->auth->validateA1(md5('user:realm:password'))); + + } + + + public function testDigestAuthInt() { + + $this->auth->setQOP(Sabre_HTTP_DigestAuth::QOP_AUTHINT | Sabre_HTTP_DigestAuth::QOP_AUTH); + list($nonce,$opaque) = $this->getServerTokens(Sabre_HTTP_DigestAuth::QOP_AUTHINT| Sabre_HTTP_DigestAuth::QOP_AUTH); + + $username = 'admin'; + $password = 12345; + $nc = '00003'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth-int:' . + md5('POST' . ':' . '/' . ':' . md5('body')) + ); + + $request = new Sabre_HTTP_Request(array( + 'REQUEST_METHOD' => 'POST', + 'PHP_AUTH_DIGEST' => 'username="'.$username.'", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth-int,nc='.$nc.',cnonce="' . $cnonce . '"', + )); + $request->setBody('body'); + + $this->auth->setHTTPRequest($request); + + $this->auth->init(); + + $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)),'Authentication is deemed invalid through validateA1'); + + } + + private function getServerTokens($qop = Sabre_HTTP_DigestAuth::QOP_AUTH) { + + $this->auth->requireLogin(); + + switch($qop) { + case Sabre_HTTP_DigestAuth::QOP_AUTH : $qopstr='auth'; break; + case Sabre_HTTP_DigestAuth::QOP_AUTHINT : $qopstr='auth-int'; break; + default : $qopstr='auth,auth-int'; break; + } + + $test = preg_match('/Digest realm="'.self::REALM.'",qop="'.$qopstr.'",nonce="([0-9a-f]*)",opaque="([0-9a-f]*)"/', + $this->response->headers['WWW-Authenticate'],$matches); + + $this->assertTrue($test==true,'The WWW-Authenticate response didn\'t match our pattern. We received: ' . $this->response->headers['WWW-Authenticate']); + + $nonce = $matches[1]; + $opaque = $matches[2]; + + // Reset our environment + $this->setUp(); + $this->auth->setQOP($qop); + + return array($nonce,$opaque); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/RequestTest.php b/dav/SabreDAV/tests/Sabre/HTTP/RequestTest.php new file mode 100644 index 000000000..ef2326e09 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/RequestTest.php @@ -0,0 +1,148 @@ + 'www.example.org', + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/testuri/', + 'CONTENT_TYPE' => 'text/xml', + ); + + $this->request = new Sabre_HTTP_Request($server); + + } + + function testGetHeader() { + + $this->assertEquals('www.example.org', $this->request->getHeader('Host')); + $this->assertEquals('text/xml', $this->request->getHeader('Content-Type')); + + } + + function testGetNonExistantHeader() { + + $this->assertNull($this->request->getHeader('doesntexist')); + $this->assertNull($this->request->getHeader('Content-Length')); + + } + + function testGetHeaders() { + + $expected = array( + 'host' => 'www.example.org', + 'content-type' => 'text/xml', + ); + + $this->assertEquals($expected, $this->request->getHeaders()); + + } + + function testGetMethod() { + + $this->assertEquals('PUT', $this->request->getMethod(), 'It seems as if we didn\'t get a valid HTTP Request method back'); + + } + + function testGetUri() { + + $this->assertEquals('/testuri/', $this->request->getUri(), 'We got an invalid uri back'); + + } + + function testSetGetBody() { + + $h = fopen('php://memory','r+'); + fwrite($h,'testing'); + rewind($h); + $this->request->setBody($h); + $this->assertEquals('testing',$this->request->getBody(true),'We didn\'t get our testbody back'); + + } + + function testSetGetBodyStream() { + + $h = fopen('php://memory','r+'); + fwrite($h,'testing'); + rewind($h); + $this->request->setBody($h); + $this->assertEquals('testing',stream_get_contents($this->request->getBody()),'We didn\'t get our testbody back'); + + } + + + function testDefaultInputStream() { + + $h = fopen('php://memory','r+'); + fwrite($h,'testing'); + rewind($h); + + $previousValue = Sabre_HTTP_Request::$defaultInputStream; + Sabre_HTTP_Request::$defaultInputStream = $h; + + $this->assertEquals('testing',$this->request->getBody(true),'We didn\'t get our testbody back'); + Sabre_HTTP_Request::$defaultInputStream = $previousValue; + + } + + function testGetAbsoluteUri() { + + $s = array( + 'HTTP_HOST' => 'sabredav.org', + 'REQUEST_URI' => '/foo' + ); + + $r = new Sabre_HTTP_Request($s); + + $this->assertEquals('http://sabredav.org/foo', $r->getAbsoluteUri()); + + $s = array( + 'HTTP_HOST' => 'sabredav.org', + 'REQUEST_URI' => '/foo', + 'HTTPS' => 'on', + ); + + $r = new Sabre_HTTP_Request($s); + + $this->assertEquals('https://sabredav.org/foo', $r->getAbsoluteUri()); + + } + + function testGetQueryString() { + + $s = array( + 'QUERY_STRING' => 'bla', + ); + + $r = new Sabre_HTTP_Request($s); + $this->assertEquals('bla', $r->getQueryString()); + + $s = array(); + + $r = new Sabre_HTTP_Request($s); + $this->assertEquals('', $r->getQueryString()); + + } + + function testGetPostVars() { + + $post = array( + 'bla' => 'foo', + ); + $r = new Sabre_HTTP_Request(array(),$post); + $this->assertEquals($post, $r->getPostVars('bla')); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/ResponseMock.php b/dav/SabreDAV/tests/Sabre/HTTP/ResponseMock.php new file mode 100644 index 000000000..70b3ebff4 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/ResponseMock.php @@ -0,0 +1,27 @@ +headers[$name] = $value; + + } + + function sendStatus($code) { + + $this->status = $this->getStatusMessage($code); + + } + + function sendBody($body) { + + $this->body = $body; + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/ResponseTest.php b/dav/SabreDAV/tests/Sabre/HTTP/ResponseTest.php new file mode 100644 index 000000000..d40b0cf44 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/ResponseTest.php @@ -0,0 +1,61 @@ +response = new Sabre_HTTP_ResponseMock(); + + } + + function testGetStatusMessage() { + + $msg = $this->response->getStatusMessage(200); + $this->assertEquals('HTTP/1.1 200 OK',$msg); + + } + + function testSetHeader() { + + $this->response->setHeader('Content-Type','text/html'); + $this->assertEquals('text/html', $this->response->headers['Content-Type']); + + + } + + function testSendStatus() { + + $this->response->sendStatus(404); + $this->assertEquals('HTTP/1.1 404 Not Found', $this->response->status); + + } + + function testSendBody() { + + ob_start(); + $response = new Sabre_HTTP_Response(); + $response->sendBody('hello'); + $this->assertEquals('hello',ob_get_clean()); + + } + + function testSendBodyStream() { + + ob_start(); + $stream = fopen('php://memory','r+'); + fwrite($stream,'hello'); + rewind($stream); + $response = new Sabre_HTTP_Response(); + $response->sendBody($stream); + $this->assertEquals('hello',ob_get_clean()); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/UtilTest.php b/dav/SabreDAV/tests/Sabre/HTTP/UtilTest.php new file mode 100644 index 000000000..2374561cd --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/UtilTest.php @@ -0,0 +1,64 @@ +assertEquals($expected, $result->format('U')); + } + + $result = Sabre_HTTP_Util::parseHTTPDate('Wed Oct 6 10:26:00 2010'); + $this->assertEquals(1286360760, $result->format('U')); + + } + + function testParseHTTPDateFail() { + + $times = array( + //random string + 'NOW', + // not-GMT timezone + 'Wednesday, 13-Oct-10 10:26:00 UTC', + // No space before the 6 + 'Wed Oct 6 10:26:00 2010', + ); + + foreach($times as $time) { + $this->assertFalse(Sabre_HTTP_Util::parseHTTPDate($time), 'We used the string: ' . $time); + } + + } + + function testTimezones() { + + $default = date_default_timezone_get(); + date_default_timezone_set('Europe/Amsterdam'); + + $this->testParseHTTPDate(); + + date_default_timezone_set($default); + + } + + function testToHTTPDate() { + + $dt = new DateTime('2011-12-10 12:00:00 +0200'); + + $this->assertEquals( + 'Sat, 10 Dec 2011 10:00:00 GMT', + Sabre_HTTP_Util::toHTTPDate($dt) + ); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/HTTP/VersionTest.php b/dav/SabreDAV/tests/Sabre/HTTP/VersionTest.php new file mode 100644 index 000000000..5cb124ef5 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/HTTP/VersionTest.php @@ -0,0 +1,15 @@ +assertEquals(-1, version_compare('1.0.0',$v)); + + $s = Sabre_HTTP_Version::STABILITY; + $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/TestUtil.php b/dav/SabreDAV/tests/Sabre/TestUtil.php new file mode 100644 index 000000000..62ee1fba7 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/TestUtil.php @@ -0,0 +1,49 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + return $pdo; + } catch (PDOException $e) { + return null; + } + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/Component/VAlarmTest.php b/dav/SabreDAV/tests/Sabre/VObject/Component/VAlarmTest.php new file mode 100644 index 000000000..5229de72b --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Component/VAlarmTest.php @@ -0,0 +1,141 @@ +assertEquals($outcome, $valarm->isInTimeRange($start, $end)); + + } + + public function timeRangeTestData() { + + $tests = array(); + + // Hard date and time + $valarm1 = Sabre_VObject_Component::create('VALARM'); + $valarm1->TRIGGER = '20120312T130000Z'; + $valarm1->TRIGGER['VALUE'] = 'DATE-TIME'; + + $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true); + $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false); + + // Relation to start time of event + $valarm2 = Sabre_VObject_Component::create('VALARM'); + $valarm2->TRIGGER = '-P1D'; + $valarm2->TRIGGER['VALUE'] = 'DURATION'; + + $vevent2 = Sabre_VObject_Component::create('VEVENT'); + $vevent2->DTSTART = '20120313T130000Z'; + $vevent2->add($valarm2); + + $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true); + $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false); + + // Relation to end time of event + $valarm3 = Sabre_VObject_Component::create('VALARM'); + $valarm3->TRIGGER = '-P1D'; + $valarm3->TRIGGER['VALUE'] = 'DURATION'; + $valarm3->TRIGGER['RELATED']= 'END'; + + $vevent3 = Sabre_VObject_Component::create('VEVENT'); + $vevent3->DTSTART = '20120301T130000Z'; + $vevent3->DTEND = '20120401T130000Z'; + $vevent3->add($valarm3); + + $tests[] = array($valarm3, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false); + $tests[] = array($valarm3, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true); + + // Relation to end time of todo + $valarm4 = Sabre_VObject_Component::create('VALARM'); + $valarm4->TRIGGER = '-P1D'; + $valarm4->TRIGGER['VALUE'] = 'DURATION'; + $valarm4->TRIGGER['RELATED']= 'END'; + + $vtodo4 = Sabre_VObject_Component::create('VTODO'); + $vtodo4->DTSTART = '20120301T130000Z'; + $vtodo4->DUE = '20120401T130000Z'; + $vtodo4->add($valarm4); + + $tests[] = array($valarm4, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false); + $tests[] = array($valarm4, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true); + + // Relation to start time of event + repeat + $valarm5 = Sabre_VObject_Component::create('VALARM'); + $valarm5->TRIGGER = '-P1D'; + $valarm5->TRIGGER['VALUE'] = 'DURATION'; + $valarm5->REPEAT = 10; + $valarm5->DURATION = 'P1D'; + + $vevent5 = Sabre_VObject_Component::create('VEVENT'); + $vevent5->DTSTART = '20120301T130000Z'; + $vevent5->add($valarm5); + + $tests[] = array($valarm5, new DateTime('2012-03-09 01:00:00'), new DateTime('2012-03-10 01:00:00'), true); + + // Relation to start time of event + duration, but no repeat + $valarm6 = Sabre_VObject_Component::create('VALARM'); + $valarm6->TRIGGER = '-P1D'; + $valarm6->TRIGGER['VALUE'] = 'DURATION'; + $valarm6->DURATION = 'P1D'; + + $vevent6 = Sabre_VObject_Component::create('VEVENT'); + $vevent6->DTSTART = '20120313T130000Z'; + $vevent6->add($valarm6); + + $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true); + $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false); + + + // Relation to end time of event (DURATION instead of DTEND) + $valarm7 = Sabre_VObject_Component::create('VALARM'); + $valarm7->TRIGGER = '-P1D'; + $valarm7->TRIGGER['VALUE'] = 'DURATION'; + $valarm7->TRIGGER['RELATED']= 'END'; + + $vevent7 = Sabre_VObject_Component::create('VEVENT'); + $vevent7->DTSTART = '20120301T130000Z'; + $vevent7->DURATION = 'P30D'; + $vevent7->add($valarm7); + + $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false); + $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true); + + // Relation to end time of event (No DTEND or DURATION) + $valarm7 = Sabre_VObject_Component::create('VALARM'); + $valarm7->TRIGGER = '-P1D'; + $valarm7->TRIGGER['VALUE'] = 'DURATION'; + $valarm7->TRIGGER['RELATED']= 'END'; + + $vevent7 = Sabre_VObject_Component::create('VEVENT'); + $vevent7->DTSTART = '20120301T130000Z'; + $vevent7->add($valarm7); + + $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), true); + $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), false); + + + return $tests; + } + + /** + * @expectedException Sabre_DAV_Exception + */ + public function testInTimeRangeInvalidComponent() { + + $valarm = Sabre_VObject_Component::create('VALARM'); + $valarm->TRIGGER = '-P1D'; + $valarm->TRIGGER['RELATED'] = 'END'; + + $vjournal = Sabre_VObject_Component::create('VJOURNAL'); + $vjournal->add($valarm); + + $valarm->isInTimeRange(new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00')); + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/VObject/Component/VCalendarTest.php b/dav/SabreDAV/tests/Sabre/VObject/Component/VCalendarTest.php new file mode 100644 index 000000000..b1b503b4d --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Component/VCalendarTest.php @@ -0,0 +1,240 @@ +expand( + new DateTime('2011-12-01'), + new DateTime('2011-12-31') + ); + + // This will normalize the output + $output = Sabre_VObject_Reader::read($output)->serialize(); + + $this->assertEquals($output, $vcal->serialize()); + + } + + public function expandData() { + + $tests = array(); + + // No data + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +END:VCALENDAR +'; + + $output = $input; + $tests[] = array($input,$output); + + + // Simple events + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla +SUMMARY:InExpand +DTSTART;VALUE=DATE:20111202 +END:VEVENT +BEGIN:VEVENT +UID:bla2 +SUMMARY:NotInExpand +DTSTART;VALUE=DATE:20120101 +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla +SUMMARY:InExpand +DTSTART;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + + $tests[] = array($input, $output); + + // Removing timezone info + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/Paris +END:VTIMEZONE +BEGIN:VEVENT +UID:bla4 +SUMMARY:RemoveTZ info +DTSTART;TZID=Europe/Paris:20111203T130102 +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla4 +SUMMARY:RemoveTZ info +DTSTART;VALUE=DATE-TIME:20111203T120102Z +END:VEVENT +END:VCALENDAR +'; + + $tests[] = array($input, $output); + + // Recurrence rule + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART:20111125T120000Z +DTEND:20111125T130000Z +RRULE:FREQ=WEEKLY +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART;VALUE=DATE-TIME:20111202T120000Z +DTEND;VALUE=DATE-TIME:20111202T130000Z +RECURRENCE-ID:20111202T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART;VALUE=DATE-TIME:20111209T120000Z +DTEND;VALUE=DATE-TIME:20111209T130000Z +RECURRENCE-ID:20111209T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART;VALUE=DATE-TIME:20111216T120000Z +DTEND;VALUE=DATE-TIME:20111216T130000Z +RECURRENCE-ID:20111216T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART;VALUE=DATE-TIME:20111223T120000Z +DTEND;VALUE=DATE-TIME:20111223T130000Z +RECURRENCE-ID:20111223T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART;VALUE=DATE-TIME:20111230T120000Z +DTEND;VALUE=DATE-TIME:20111230T130000Z +RECURRENCE-ID:20111230T120000Z +END:VEVENT +END:VCALENDAR +'; + + // Recurrence rule + override + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART:20111125T120000Z +DTEND:20111125T130000Z +RRULE:FREQ=WEEKLY +END:VEVENT +BEGIN:VEVENT +UID:bla6 +RECURRENCE-ID:20111209T120000Z +DTSTART:20111209T140000Z +DTEND:20111209T150000Z +SUMMARY:Override! +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART;VALUE=DATE-TIME:20111202T120000Z +DTEND;VALUE=DATE-TIME:20111202T130000Z +RECURRENCE-ID:20111202T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +RECURRENCE-ID:20111209T120000Z +DTSTART:20111209T140000Z +DTEND:20111209T150000Z +SUMMARY:Override! +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART;VALUE=DATE-TIME:20111216T120000Z +DTEND;VALUE=DATE-TIME:20111216T130000Z +RECURRENCE-ID:20111216T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART;VALUE=DATE-TIME:20111223T120000Z +DTEND;VALUE=DATE-TIME:20111223T130000Z +RECURRENCE-ID:20111223T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART;VALUE=DATE-TIME:20111230T120000Z +DTEND;VALUE=DATE-TIME:20111230T130000Z +RECURRENCE-ID:20111230T120000Z +END:VEVENT +END:VCALENDAR +'; + + $tests[] = array($input, $output); + return $tests; + + } + + /** + * @expectedException LogicException + */ + public function testBrokenEventExpand() { + + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +RRULE:FREQ=WEEKLY +DTSTART;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + $vcal = Sabre_VObject_Reader::read($input); + $vcal->expand( + new DateTime('2011-12-01'), + new DateTime('2011-12-31') + ); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/Component/VEventTest.php b/dav/SabreDAV/tests/Sabre/VObject/Component/VEventTest.php new file mode 100644 index 000000000..a5a855f60 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Component/VEventTest.php @@ -0,0 +1,64 @@ +assertEquals($outcome, $vevent->isInTimeRange($start, $end)); + + } + + public function timeRangeTestData() { + + $tests = array(); + + $vevent = new Sabre_VObject_Component_VEvent('VEVENT'); + $vevent->DTSTART = '20111223T120000Z'; + $tests[] = array($vevent, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vevent, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vevent2 = clone $vevent; + $vevent2->DTEND = '20111225T120000Z'; + $tests[] = array($vevent2, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vevent2, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vevent3 = clone $vevent; + $vevent3->DURATION = 'P1D'; + $tests[] = array($vevent3, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vevent3, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vevent4 = clone $vevent; + $vevent4->DTSTART = '20111225'; + $vevent4->DTSTART['VALUE'] = 'DATE'; + $tests[] = array($vevent4, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vevent4, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + // Event with no end date should be treated as lasting the entire day. + $tests[] = array($vevent4, new DateTime('2011-12-25 16:00:00'), new DateTime('2011-12-25 17:00:00'), true); + + + $vevent5 = clone $vevent; + $vevent5->DURATION = 'P1D'; + $vevent5->RRULE = 'FREQ=YEARLY'; + $tests[] = array($vevent5, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vevent5, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + $tests[] = array($vevent5, new DateTime('2013-12-01'), new DateTime('2013-12-31'), true); + + $vevent6 = clone $vevent; + $vevent6->DTSTART = '20111225'; + $vevent6->DTSTART['VALUE'] = 'DATE'; + $vevent6->DTEND = '20111225'; + $vevent6->DTEND['VALUE'] = 'DATE'; + $tests[] = array($vevent6, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vevent6, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + // Event with no end date should be treated as lasting the entire day. + $tests[] = array($vevent6, new DateTime('2011-12-25 16:00:00'), new DateTime('2011-12-25 17:00:00'), true); + + return $tests; + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/VObject/Component/VJournalTest.php b/dav/SabreDAV/tests/Sabre/VObject/Component/VJournalTest.php new file mode 100644 index 000000000..04fcc9dba --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Component/VJournalTest.php @@ -0,0 +1,37 @@ +assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); + + } + + public function timeRangeTestData() { + + $tests = array(); + + $vjournal = Sabre_VObject_Component::create('VJOURNAL'); + $vjournal->DTSTART = '20111223T120000Z'; + $tests[] = array($vjournal, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vjournal, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vjournal2 = Sabre_VObject_Component::create('VJOURNAL'); + $vjournal2->DTSTART = '20111223'; + $vjournal2->DTSTART['VALUE'] = 'DATE'; + $tests[] = array($vjournal2, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vjournal2, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vjournal3 = Sabre_VObject_Component::create('VJOURNAL'); + $tests[] = array($vjournal3, new DateTime('2011-01-01'), new DateTime('2012-01-01'), false); + $tests[] = array($vjournal3, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + return $tests; + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/VObject/Component/VTodoTest.php b/dav/SabreDAV/tests/Sabre/VObject/Component/VTodoTest.php new file mode 100644 index 000000000..a8bd619af --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Component/VTodoTest.php @@ -0,0 +1,63 @@ +assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); + + } + + public function timeRangeTestData() { + + $tests = array(); + + $vtodo = Sabre_VObject_Component::create('VTODO'); + $vtodo->DTSTART = '20111223T120000Z'; + $tests[] = array($vtodo, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vtodo2 = clone $vtodo; + $vtodo2->DURATION = 'P1D'; + $tests[] = array($vtodo2, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo2, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vtodo3 = clone $vtodo; + $vtodo3->DUE = '20111225'; + $tests[] = array($vtodo3, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo3, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vtodo4 = Sabre_VObject_Component::create('VTODO'); + $vtodo4->DUE = '20111225'; + $tests[] = array($vtodo4, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo4, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vtodo5 = Sabre_VObject_Component::create('VTODO'); + $vtodo5->COMPLETED = '20111225'; + $tests[] = array($vtodo5, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo5, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vtodo6 = Sabre_VObject_Component::create('VTODO'); + $vtodo6->CREATED = '20111225'; + $tests[] = array($vtodo6, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo6, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vtodo7 = Sabre_VObject_Component::create('VTODO'); + $vtodo7->CREATED = '20111225'; + $vtodo7->COMPLETED = '20111226'; + $tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2011-11-01'), false); + + $vtodo7 = Sabre_VObject_Component::create('VTODO'); + $tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2012-01-01'), true); + $tests[] = array($vtodo7, new DateTime('2011-01-01'), new DateTime('2011-11-01'), true); + + return $tests; + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/VObject/ComponentTest.php b/dav/SabreDAV/tests/Sabre/VObject/ComponentTest.php new file mode 100644 index 000000000..71b33c079 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/ComponentTest.php @@ -0,0 +1,361 @@ +children[] = $sub; + + $sub = new Sabre_VObject_Component('VTODO'); + $comp->children[] = $sub; + + $count = 0; + foreach($comp->children() as $key=>$subcomponent) { + + $count++; + $this->assertInstanceOf('Sabre_VObject_Component',$subcomponent); + + } + $this->assertEquals(2,$count); + $this->assertEquals(1,$key); + + } + + function testMagicGet() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $sub = new Sabre_VObject_Component('VEVENT'); + $comp->children[] = $sub; + + $sub = new Sabre_VObject_Component('VTODO'); + $comp->children[] = $sub; + + $event = $comp->vevent; + $this->assertInstanceOf('Sabre_VObject_Component', $event); + $this->assertEquals('VEVENT', $event->name); + + $this->assertInternalType('null', $comp->vjournal); + + } + + function testMagicGetGroups() { + + $comp = new Sabre_VObject_Component('VCARD'); + + $sub = new Sabre_VObject_Property('GROUP1.EMAIL','1@1.com'); + $comp->children[] = $sub; + + $sub = new Sabre_VObject_Property('GROUP2.EMAIL','2@2.com'); + $comp->children[] = $sub; + + $sub = new Sabre_VObject_Property('EMAIL','3@3.com'); + $comp->children[] = $sub; + + $emails = $comp->email; + $this->assertEquals(3, count($emails)); + + $email1 = $comp->{"group1.email"}; + $this->assertEquals('EMAIL', $email1[0]->name); + $this->assertEquals('GROUP1', $email1[0]->group); + + $email3 = $comp->{".email"}; + $this->assertEquals('EMAIL', $email3[0]->name); + $this->assertEquals(null, $email3[0]->group); + + } + + function testMagicIsset() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $sub = new Sabre_VObject_Component('VEVENT'); + $comp->children[] = $sub; + + $sub = new Sabre_VObject_Component('VTODO'); + $comp->children[] = $sub; + + $this->assertTrue(isset($comp->vevent)); + $this->assertTrue(isset($comp->vtodo)); + $this->assertFalse(isset($comp->vjournal)); + + } + + function testMagicSetScalar() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->myProp = 'myValue'; + + $this->assertInstanceOf('Sabre_VObject_Property',$comp->MYPROP); + $this->assertEquals('myValue',$comp->MYPROP->value); + + + } + + function testMagicSetScalarTwice() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->myProp = 'myValue'; + $comp->myProp = 'myValue'; + + $this->assertEquals(1,count($comp->children)); + $this->assertInstanceOf('Sabre_VObject_Property',$comp->MYPROP); + $this->assertEquals('myValue',$comp->MYPROP->value); + + } + + function testMagicSetComponent() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + // Note that 'myProp' is ignored here. + $comp->myProp = new Sabre_VObject_Component('VEVENT'); + + $this->assertEquals(1, count($comp->children)); + + $this->assertEquals('VEVENT',$comp->VEVENT->name); + + } + + function testMagicSetTwice() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $comp->VEVENT = new Sabre_VObject_Component('VEVENT'); + $comp->VEVENT = new Sabre_VObject_Component('VEVENT'); + + $this->assertEquals(1, count($comp->children)); + + $this->assertEquals('VEVENT',$comp->VEVENT->name); + + } + + function testArrayAccessGet() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $event = new Sabre_VObject_Component('VEVENT'); + $event->summary = 'Event 1'; + + $comp->add($event); + + $event2 = clone $event; + $event2->summary = 'Event 2'; + + $comp->add($event2); + + $this->assertEquals(2,count($comp->children())); + $this->assertTrue($comp->vevent[1] instanceof Sabre_VObject_Component); + $this->assertEquals('Event 2', (string)$comp->vevent[1]->summary); + + } + + function testArrayAccessExists() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $event = new Sabre_VObject_Component('VEVENT'); + $event->summary = 'Event 1'; + + $comp->add($event); + + $event2 = clone $event; + $event2->summary = 'Event 2'; + + $comp->add($event2); + + $this->assertTrue(isset($comp->vevent[0])); + $this->assertTrue(isset($comp->vevent[1])); + + } + + /** + * @expectedException LogicException + */ + function testArrayAccessSet() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp['hey'] = 'hi there'; + + } + /** + * @expectedException LogicException + */ + function testArrayAccessUnset() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + unset($comp[0]); + + } + + function testAddScalar() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $comp->add('myprop','value'); + + $this->assertEquals(1, count($comp->children)); + + $this->assertTrue($comp->children[0] instanceof Sabre_VObject_Property); + $this->assertEquals('MYPROP',$comp->children[0]->name); + $this->assertEquals('value',$comp->children[0]->value); + + } + + function testAddComponent() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $comp->add(new Sabre_VObject_Component('VEVENT')); + + $this->assertEquals(1, count($comp->children)); + + $this->assertEquals('VEVENT',$comp->VEVENT->name); + + } + + function testAddComponentTwice() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + $comp->add(new Sabre_VObject_Component('VEVENT')); + $comp->add(new Sabre_VObject_Component('VEVENT')); + + $this->assertEquals(2, count($comp->children)); + + $this->assertEquals('VEVENT',$comp->VEVENT->name); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->add(new Sabre_VObject_Component('VEVENT'),'hello'); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail2() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->add(array()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail3() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->add('hello',array()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testMagicSetInvalid() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + // Note that 'myProp' is ignored here. + $comp->myProp = new StdClass(); + + $this->assertEquals(1, count($comp->children)); + + $this->assertEquals('VEVENT',$comp->VEVENT->name); + + } + + function testMagicUnset() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->add(new Sabre_VObject_Component('VEVENT')); + + unset($comp->vevent); + + $this->assertEquals(array(), $comp->children); + + } + + + function testCount() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $this->assertEquals(1,$comp->count()); + + } + + function testChildren() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + // Note that 'myProp' is ignored here. + $comp->children = array( + new Sabre_VObject_Component('VEVENT'), + new Sabre_VObject_Component('VTODO') + ); + + $r = $comp->children(); + $this->assertTrue($r instanceof Sabre_VObject_ElementList); + $this->assertEquals(2,count($r)); + } + + function testGetComponents() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + + // Note that 'myProp' is ignored here. + $comp->children = array( + new Sabre_VObject_Property('FOO','BAR'), + new Sabre_VObject_Component('VTODO') + ); + + $r = $comp->getComponents(); + $this->assertInternalType('array', $r); + $this->assertEquals(1, count($r)); + $this->assertEquals('VTODO', $r[0]->name); + } + + function testSerialize() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $this->assertEquals("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n", $comp->serialize()); + + } + + function testSerializeChildren() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->children = array( + new Sabre_VObject_Component('VEVENT'), + new Sabre_VObject_Component('VTODO') + ); + $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $comp->serialize()); + + } + + function testSerializeOrder() { + + $comp = new Sabre_VObject_Component('VCALENDAR'); + $comp->add(new Sabre_VObject_Component('VEVENT')); + $comp->add('PROP1','BLABLA'); + $comp->add('VERSION','2.0'); + $comp->add(new Sabre_VObject_Component('VTIMEZONE')); + + $str = $comp->serialize(); + + $this->assertEquals("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPROP1:BLABLA\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $str); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/DateTimeParserTest.php b/dav/SabreDAV/tests/Sabre/VObject/DateTimeParserTest.php new file mode 100644 index 000000000..83e4c4299 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/DateTimeParserTest.php @@ -0,0 +1,117 @@ +assertEquals('+1 weeks', Sabre_VObject_DateTimeParser::parseDuration('P1W',true)); + $this->assertEquals('+5 days', Sabre_VObject_DateTimeParser::parseDuration('P5D',true)); + $this->assertEquals('+5 days 3 hours 50 minutes 12 seconds', Sabre_VObject_DateTimeParser::parseDuration('P5DT3H50M12S',true)); + $this->assertEquals('-1 weeks 50 minutes', Sabre_VObject_DateTimeParser::parseDuration('-P1WT50M',true)); + $this->assertEquals('+50 days 3 hours 2 seconds', Sabre_VObject_DateTimeParser::parseDuration('+P50DT3H2S',true)); + $this->assertEquals(new DateInterval('PT0S'), Sabre_VObject_DateTimeParser::parseDuration('PT0S')); + + } + + function testParseICalendarDurationDateInterval() { + + $expected = new DateInterval('P7D'); + $this->assertEquals($expected, Sabre_VObject_DateTimeParser::parseDuration('P1W')); + $this->assertEquals($expected, Sabre_VObject_DateTimeParser::parse('P1W')); + + $expected = new DateInterval('PT3M'); + $expected->invert = true; + $this->assertEquals($expected, Sabre_VObject_DateTimeParser::parseDuration('-PT3M')); + + } + + /** + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testParseICalendarDurationFail() { + + Sabre_VObject_DateTimeParser::parseDuration('P1X',true); + + } + + function testParseICalendarDateTime() { + + $dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405'); + + $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC')); + + $this->assertEquals($compare, $dateTime); + + } + + /** + * @depends testParseICalendarDateTime + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testParseICalendarDateTimeBadFormat() { + + $dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405 '); + + } + + /** + * @depends testParseICalendarDateTime + */ + function testParseICalendarDateTimeUTC() { + + $dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405Z'); + + $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC')); + $this->assertEquals($compare, $dateTime); + + } + + /** + * @depends testParseICalendarDateTime + */ + function testParseICalendarDateTimeUTC2() { + + $dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20101211T160000Z'); + + $compare = new DateTime('2010-12-11 16:00:00',new DateTimeZone('UTC')); + $this->assertEquals($compare, $dateTime); + + } + + /** + * @depends testParseICalendarDateTime + */ + function testParseICalendarDateTimeCustomTimeZone() { + + $dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam')); + + $compare = new DateTime('2010-03-16 13:14:05',new DateTimeZone('UTC')); + $this->assertEquals($compare, $dateTime); + + } + + function testParseICalendarDate() { + + $dateTime = Sabre_VObject_DateTimeParser::parseDate('20100316'); + + $expected = new DateTime('2010-03-16 00:00:00',new DateTimeZone('UTC')); + + $this->assertEquals($expected, $dateTime); + + $dateTime = Sabre_VObject_DateTimeParser::parse('20100316'); + $this->assertEquals($expected, $dateTime); + + } + + /** + * @depends testParseICalendarDate + * @expectedException Sabre_DAV_Exception_BadRequest + */ + function testParseICalendarDateBadFormat() { + + $dateTime = Sabre_VObject_DateTimeParser::parseDate('20100316T141405'); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/ElementListTest.php b/dav/SabreDAV/tests/Sabre/VObject/ElementListTest.php new file mode 100644 index 000000000..5bc8b43c5 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/ElementListTest.php @@ -0,0 +1,30 @@ +$subcomponent) { + + $count++; + $this->assertInstanceOf('Sabre_VObject_Component',$subcomponent); + + } + $this->assertEquals(3,$count); + $this->assertEquals(2,$key); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/EmClientTest.php b/dav/SabreDAV/tests/Sabre/VObject/EmClientTest.php new file mode 100644 index 000000000..69330230f --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/EmClientTest.php @@ -0,0 +1,53 @@ +VEVENT->DTSTART->getDateTime(); + $this->assertEquals(new DateTime('2011-10-08 19:30:00', new DateTimeZone('America/Chicago')), $dt); + + } + +} + diff --git a/dav/SabreDAV/tests/Sabre/VObject/FreeBusyGeneratorTest.php b/dav/SabreDAV/tests/Sabre/VObject/FreeBusyGeneratorTest.php new file mode 100644 index 000000000..d84f5a489 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/FreeBusyGeneratorTest.php @@ -0,0 +1,256 @@ +setObjects($this->getInput()); + $gen->setTimeRange( + new DateTime('20110101T110000Z'), + new DateTime('20110103T110000Z') + ); + + $result = $gen->getResult(); + + $expected = array( + '20110101T120000Z/20110101T130000Z', + '20110101T130000Z/20110101T140000Z', + '20110101T180000Z/20110101T190000Z', + '20110101T190000Z/20110101T200000Z', + '20110102T000000Z/20110103T000000Z', + '20110101T210000Z/20110101T220000Z', + + '20110103T010000Z/20110103T020000Z', + '20110103T030000Z/20110103T040000Z', + '20110103T040000Z/20110103T050000Z', + '20110103T050000Z/20110103T060000Z', + + '20110101T220000Z/20110101T230000Z', + '20110101T230000Z/20110102T000000Z', + ); + + foreach($result->VFREEBUSY->FREEBUSY as $fb) { + + $this->assertContains((string)$fb, $expected); + + $k = array_search((string)$fb, $expected); + unset($expected[$k]); + + } + if (count($expected)>0) { + $this->fail('There were elements in the expected array that were not found in the output: ' . "\n" . print_r($expected,true) . "\n" . $result->serialize()); + + } + + } + + function testGeneratorBaseObject() { + + $obj = new Sabre_VObject_Component('VCALENDAR'); + $obj->METHOD = 'PUBLISH'; + + $gen = new Sabre_VObject_FreeBusyGenerator(); + $gen->setObjects(array()); + $gen->setBaseObject($obj); + + $result = $gen->getResult(); + + $this->assertEquals('PUBLISH', $result->METHOD->value); + + } + function testGeneratorNoVersion() { + + $v = Sabre_DAV_Server::$exposeVersion; + Sabre_DAV_Server::$exposeVersion = false; + + $gen = new Sabre_VObject_FreeBusyGenerator(); + $gen->setObjects(array()); + + $result = $gen->getResult(); + Sabre_DAV_Server::$exposeVersion = $v; + + $this->assertFalse(strpos($result->PRODID->value, Sabre_VObject_Version::VERSION)); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg() { + + $gen = new Sabre_VObject_FreeBusyGenerator(); + $gen->setObjects(array(new StdClass())); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/Issue153Test.php b/dav/SabreDAV/tests/Sabre/VObject/Issue153Test.php new file mode 100644 index 000000000..16557c525 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Issue153Test.php @@ -0,0 +1,12 @@ +assertEquals('Test Benutzer', (string)$obj->fn); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/Issue154Test.php b/dav/SabreDAV/tests/Sabre/VObject/Issue154Test.php new file mode 100644 index 000000000..a004eb081 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Issue154Test.php @@ -0,0 +1,27 @@ +VERSION = '3.0'; + $vcard->UID = 'foo-bar'; + $vcard->PHOTO = base64_encode('random_stuff'); + $vcard->PHOTO->add('BASE64',null); + + $result = $vcard->serialize(); + $expected = array( + "BEGIN:VCARD", + "VERSION:3.0", + "PHOTO;BASE64:" . base64_encode('random_stuff'), + "UID:foo-bar", + "END:VCARD", + "", + ); + + $this->assertEquals(implode("\r\n", $expected), $result); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/ParameterTest.php b/dav/SabreDAV/tests/Sabre/VObject/ParameterTest.php new file mode 100644 index 000000000..ea3099718 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/ParameterTest.php @@ -0,0 +1,21 @@ +assertEquals('NAME',$param->name); + $this->assertEquals('value',$param->value); + + } + + function testCastToString() { + + $param = new Sabre_VObject_Parameter('name','value'); + $this->assertEquals('value',$param->__toString()); + $this->assertEquals('value',(string)$param); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/Property/DateTimeTest.php b/dav/SabreDAV/tests/Sabre/VObject/Property/DateTimeTest.php new file mode 100644 index 000000000..d6a9830d3 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Property/DateTimeTest.php @@ -0,0 +1,231 @@ +setTimeZone($tz); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $elem->setDateTime($dt); + + $this->assertEquals('19850704T013000', $elem->value); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeLOCAL() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt = new DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $elem->setDateTime($dt, Sabre_VObject_Property_DateTime::LOCAL); + + $this->assertEquals('19850704T013000', $elem->value); + $this->assertNull($elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeUTC() { + + $tz = new DateTimeZone('GMT'); + $dt = new DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $elem->setDateTime($dt, Sabre_VObject_Property_DateTime::UTC); + + $this->assertEquals('19850704T013000Z', $elem->value); + $this->assertNull($elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeLOCALTZ() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt = new DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $elem->setDateTime($dt, Sabre_VObject_Property_DateTime::LOCALTZ); + + $this->assertEquals('19850704T013000', $elem->value); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeDATE() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt = new DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $elem->setDateTime($dt, Sabre_VObject_Property_DateTime::DATE); + + $this->assertEquals('19850704', $elem->value); + $this->assertNull($elem['TZID']); + $this->assertEquals('DATE', (string)$elem['VALUE']); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testSetDateTimeInvalid() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt = new DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $elem->setDateTime($dt, 7); + + } + + function testGetDateTimeCached() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt = new DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $elem->setDateTime($dt); + + $this->assertEquals($elem->getDateTime(), $dt); + + } + + function testGetDateTimeDateNULL() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART'); + $dt = $elem->getDateTime(); + + $this->assertNull($dt); + $this->assertNull($elem->getDateType()); + + } + + function testGetDateTimeDateDATE() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 00:00:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals(Sabre_VObject_Property_DateTime::DATE, $elem->getDateType()); + + } + + + function testGetDateTimeDateLOCAL() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals(Sabre_VObject_Property_DateTime::LOCAL, $elem->getDateType()); + + } + + function testGetDateTimeDateUTC() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000Z'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('UTC', $dt->getTimeZone()->getName()); + $this->assertEquals(Sabre_VObject_Property_DateTime::UTC, $elem->getDateType()); + + } + + function testGetDateTimeDateLOCALTZ() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000'); + $elem['TZID'] = 'Europe/Amsterdam'; + + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); + $this->assertEquals(Sabre_VObject_Property_DateTime::LOCALTZ, $elem->getDateType()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testGetDateTimeDateInvalid() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','bla'); + $dt = $elem->getDateTime(); + + } + + function testGetDateTimeWeirdTZ() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000'); + $elem['TZID'] = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam'; + + + $event = new Sabre_VObject_Component('VEVENT'); + $event->add($elem); + + $timezone = new Sabre_VObject_Component('VTIMEZONE'); + $timezone->TZID = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam'; + $timezone->{'X-LIC-LOCATION'} = 'Europe/Amsterdam'; + + $calendar = new Sabre_VObject_Component('VCALENDAR'); + $calendar->add($event); + $calendar->add($timezone); + + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); + $this->assertEquals(Sabre_VObject_Property_DateTime::LOCALTZ, $elem->getDateType()); + + } + + function testGetDateTimeBadTimeZone() { + + $default = date_default_timezone_get(); + date_default_timezone_set('Canada/Eastern'); + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000'); + $elem['TZID'] = 'Moon'; + + + $event = new Sabre_VObject_Component('VEVENT'); + $event->add($elem); + + $timezone = new Sabre_VObject_Component('VTIMEZONE'); + $timezone->TZID = 'Moon'; + $timezone->{'X-LIC-LOCATION'} = 'Moon'; + + $calendar = new Sabre_VObject_Component('VCALENDAR'); + $calendar->add($event); + $calendar->add($timezone); + + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('Canada/Eastern', $dt->getTimeZone()->getName()); + $this->assertEquals(Sabre_VObject_Property_DateTime::LOCALTZ, $elem->getDateType()); + date_default_timezone_set($default); + + } +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/Property/MultiDateTimeTest.php b/dav/SabreDAV/tests/Sabre/VObject/Property/MultiDateTimeTest.php new file mode 100644 index 000000000..4d70ed3d7 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/Property/MultiDateTimeTest.php @@ -0,0 +1,200 @@ +setTimeZone($tz); + $dt2->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $elem->setDateTimes(array($dt1,$dt2)); + + $this->assertEquals('19850704T013000,19860704T013000', $elem->value); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeLOCAL() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt1 = new DateTime('1985-07-04 01:30:00', $tz); + $dt2 = new DateTime('1986-07-04 01:30:00', $tz); + $dt1->setTimeZone($tz); + $dt2->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $elem->setDateTimes(array($dt1,$dt2), Sabre_VObject_Property_DateTime::LOCAL); + + $this->assertEquals('19850704T013000,19860704T013000', $elem->value); + $this->assertNull($elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeUTC() { + + $tz = new DateTimeZone('GMT'); + $dt1 = new DateTime('1985-07-04 01:30:00', $tz); + $dt2 = new DateTime('1986-07-04 01:30:00', $tz); + $dt1->setTimeZone($tz); + $dt2->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $elem->setDateTimes(array($dt1,$dt2), Sabre_VObject_Property_DateTime::UTC); + + $this->assertEquals('19850704T013000Z,19860704T013000Z', $elem->value); + $this->assertNull($elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeLOCALTZ() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt1 = new DateTime('1985-07-04 01:30:00', $tz); + $dt2 = new DateTime('1986-07-04 01:30:00', $tz); + $dt1->setTimeZone($tz); + $dt2->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $elem->setDateTimes(array($dt1,$dt2), Sabre_VObject_Property_DateTime::LOCALTZ); + + $this->assertEquals('19850704T013000,19860704T013000', $elem->value); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertEquals('DATE-TIME', (string)$elem['VALUE']); + + } + + function testSetDateTimeDATE() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt1 = new datetime('1985-07-04 01:30:00', $tz); + $dt2 = new datetime('1986-07-04 01:30:00', $tz); + $dt1->settimezone($tz); + $dt2->settimezone($tz); + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $elem->setDateTimes(array($dt1,$dt2), Sabre_VObject_Property_DateTime::DATE); + + $this->assertEquals('19850704,19860704', $elem->value); + $this->assertNull($elem['TZID']); + $this->assertEquals('DATE', (string)$elem['VALUE']); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testSetDateTimeInvalid() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt = new DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $elem->setDateTimes(array($dt), 7); + + } + + function testGetDateTimeCached() { + + $tz = new DateTimeZone('Europe/Amsterdam'); + $dt1 = new datetime('1985-07-04 01:30:00', $tz); + $dt2 = new datetime('1986-07-04 01:30:00', $tz); + $dt1->settimezone($tz); + $dt2->settimezone($tz); + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $elem->setDateTimes(array($dt1,$dt2)); + + $this->assertEquals($elem->getDateTimes(), array($dt1,$dt2)); + + } + + function testGetDateTimeDateNULL() { + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART'); + $dt = $elem->getDateTimes(); + + $this->assertNull($dt); + $this->assertNull($elem->getDateType()); + + } + + function testGetDateTimeDateDATE() { + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART','19850704,19860704'); + $dt = $elem->getDateTimes(); + + $this->assertEquals('1985-07-04 00:00:00', $dt[0]->format('Y-m-d H:i:s')); + $this->assertEquals('1986-07-04 00:00:00', $dt[1]->format('Y-m-d H:i:s')); + $this->assertEquals(Sabre_VObject_Property_DateTime::DATE, $elem->getDateType()); + + } + + function testGetDateTimeDateDATEReverse() { + + $elem = new Sabre_VObject_Property_MultiDateTime('DTSTART','19850704,19860704'); + + $this->assertEquals(Sabre_VObject_Property_DateTime::DATE, $elem->getDateType()); + + $dt = $elem->getDateTimes(); + $this->assertEquals('1985-07-04 00:00:00', $dt[0]->format('Y-m-d H:i:s')); + $this->assertEquals('1986-07-04 00:00:00', $dt[1]->format('Y-m-d H:i:s')); + + } + + + function testGetDateTimeDateLOCAL() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals(Sabre_VObject_Property_DateTime::LOCAL, $elem->getDateType()); + + } + + function testGetDateTimeDateUTC() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000Z'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('UTC', $dt->getTimeZone()->getName()); + $this->assertEquals(Sabre_VObject_Property_DateTime::UTC, $elem->getDateType()); + + } + + function testGetDateTimeDateLOCALTZ() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','19850704T013000'); + $elem['TZID'] = 'Europe/Amsterdam'; + + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTime', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); + $this->assertEquals(Sabre_VObject_Property_DateTime::LOCALTZ, $elem->getDateType()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testGetDateTimeDateInvalid() { + + $elem = new Sabre_VObject_Property_DateTime('DTSTART','bla'); + $dt = $elem->getDateTime(); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/PropertyTest.php b/dav/SabreDAV/tests/Sabre/VObject/PropertyTest.php new file mode 100644 index 000000000..40fb1468a --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/PropertyTest.php @@ -0,0 +1,278 @@ +assertEquals('PROPNAME', $property->name); + $this->assertEquals('propvalue', $property->value); + $this->assertEquals('propvalue', $property->__toString()); + $this->assertEquals('propvalue', (string)$property); + + } + + public function testParameterExists() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property->parameters[] = new Sabre_VObject_Parameter('paramname','paramvalue'); + + $this->assertTrue(isset($property['PARAMNAME'])); + $this->assertTrue(isset($property['paramname'])); + $this->assertFalse(isset($property['foo'])); + + } + + public function testParameterGet() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property->parameters[] = new Sabre_VObject_Parameter('paramname','paramvalue'); + + $this->assertInstanceOf('Sabre_VObject_Parameter',$property['paramname']); + + } + + public function testParameterNotExists() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property->parameters[] = new Sabre_VObject_Parameter('paramname','paramvalue'); + + $this->assertInternalType('null',$property['foo']); + + } + + public function testParameterMultiple() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property->parameters[] = new Sabre_VObject_Parameter('paramname','paramvalue'); + $property->parameters[] = new Sabre_VObject_Parameter('paramname','paramvalue'); + + $this->assertInstanceOf('Sabre_VObject_Parameter',$property['paramname']); + $this->assertEquals(2,count($property['paramname'])); + + } + + public function testSetParameterAsString() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property['paramname'] = 'paramvalue'; + + $this->assertEquals(1,count($property->parameters)); + $this->assertInstanceOf('Sabre_VObject_Parameter', $property->parameters[0]); + $this->assertEquals('PARAMNAME',$property->parameters[0]->name); + $this->assertEquals('paramvalue',$property->parameters[0]->value); + + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetParameterAsStringNoKey() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property[] = 'paramvalue'; + + } + + public function testSetParameterObject() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $param = new Sabre_VObject_Parameter('paramname','paramvalue'); + + $property[] = $param; + + $this->assertEquals(1,count($property->parameters)); + $this->assertEquals($param, $property->parameters[0]); + + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetParameterObjectWithKey() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $param = new Sabre_VObject_Parameter('paramname','paramvalue'); + + $property['key'] = $param; + + } + + + /** + * @expectedException InvalidArgumentException + */ + public function testSetParameterObjectRandomObject() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property[] = new StdClass(); + + } + + public function testUnsetParameter() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $param = new Sabre_VObject_Parameter('paramname','paramvalue'); + $property->parameters[] = $param; + + unset($property['PARAMNAME']); + $this->assertEquals(0,count($property->parameters)); + + } + + public function testParamCount() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $param = new Sabre_VObject_Parameter('paramname','paramvalue'); + $property->parameters[] = $param; + $property->parameters[] = clone $param; + + $this->assertEquals(2,count($property->parameters)); + + } + + public function testSerialize() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + + $this->assertEquals("PROPNAME:propvalue\r\n",$property->serialize()); + + } + + public function testSerializeParam() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $property->parameters[] = new Sabre_VObject_Parameter('paramname','paramvalue'); + $property->parameters[] = new Sabre_VObject_Parameter('paramname2','paramvalue2'); + + $this->assertEquals("PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propvalue\r\n",$property->serialize()); + + } + + public function testSerializeNewLine() { + + $property = new Sabre_VObject_Property('propname',"line1\nline2"); + + $this->assertEquals("PROPNAME:line1\\nline2\r\n",$property->serialize()); + + } + + public function testSerializeLongLine() { + + $value = str_repeat('!',200); + $property = new Sabre_VObject_Property('propname',$value); + + $expected = "PROPNAME:" . str_repeat('!',66) . "\r\n " . str_repeat('!',74) . "\r\n " . str_repeat('!',60) . "\r\n"; + + $this->assertEquals($expected,$property->serialize()); + + } + + public function testSerializeUTF8LineFold() { + + $value = str_repeat('!',65) . "\xc3\xa4bla"; // inserted umlaut-a + $property = new Sabre_VObject_Property('propname', $value); + $expected = "PROPNAME:" . str_repeat('!',65) . "\r\n \xc3\xa4bla\r\n"; + $this->assertEquals($expected, $property->serialize()); + + } + + public function testGetIterator() { + + $it = new Sabre_VObject_ElementList(array()); + $property = new Sabre_VObject_Property('propname','propvalue', $it); + $this->assertEquals($it,$property->getIterator()); + + } + + + public function testGetIteratorDefault() { + + $property = new Sabre_VObject_Property('propname','propvalue'); + $it = $property->getIterator(); + $this->assertTrue($it instanceof Sabre_VObject_ElementList); + $this->assertEquals(1,count($it)); + + } + + function testAddScalar() { + + $property = new Sabre_VObject_Property('EMAIL'); + + $property->add('myparam','value'); + + $this->assertEquals(1, count($property->parameters)); + + $this->assertTrue($property->parameters[0] instanceof Sabre_VObject_Parameter); + $this->assertEquals('MYPARAM',$property->parameters[0]->name); + $this->assertEquals('value',$property->parameters[0]->value); + + } + + function testAddParameter() { + + $prop = new Sabre_VObject_Property('EMAIL'); + + $prop->add(new Sabre_VObject_Parameter('MYPARAM','value')); + + $this->assertEquals(1, count($prop->parameters)); + $this->assertEquals('MYPARAM',$prop['myparam']->name); + + } + + function testAddParameterTwice() { + + $prop = new Sabre_VObject_Property('EMAIL'); + + $prop->add(new Sabre_VObject_Parameter('MYPARAM', 'value1')); + $prop->add(new Sabre_VObject_Parameter('MYPARAM', 'value2')); + + $this->assertEquals(2, count($prop->parameters)); + + $this->assertEquals('MYPARAM',$prop['MYPARAM']->name); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail() { + + $prop = new Sabre_VObject_Property('EMAIL'); + $prop->add(new Sabre_VObject_Parameter('MPARAM'),'hello'); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail2() { + + $property = new Sabre_VObject_Property('EMAIL','value'); + $property->add(array()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail3() { + + $property = new Sabre_VObject_Property('EMAIL','value'); + $property->add('hello',array()); + + } + + function testClone() { + + $property = new Sabre_VObject_Property('EMAIL','value'); + $property['FOO'] = 'BAR'; + + $property2 = clone $property; + + $property['FOO'] = 'BAZ'; + $this->assertEquals('BAR', (string)$property2['FOO']); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/ReaderTest.php b/dav/SabreDAV/tests/Sabre/VObject/ReaderTest.php new file mode 100644 index 000000000..a87651cdc --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/ReaderTest.php @@ -0,0 +1,263 @@ +assertInstanceOf('Sabre_VObject_Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children)); + + } + + function testReadComponentUnixNewLine() { + + $data = "BEGIN:VCALENDAR\nEND:VCALENDAR"; + + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children)); + + } + + function testReadComponentMacNewLine() { + + $data = "BEGIN:VCALENDAR\rEND:VCALENDAR"; + + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children)); + + } + + function testReadComponentLineFold() { + + $data = "BEGIN:\r\n\tVCALENDAR\r\nE\r\n ND:VCALENDAR"; + + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children)); + + } + + /** + * @expectedException Sabre_VObject_ParseException + */ + function testReadCorruptComponent() { + + $data = "BEGIN:VCALENDAR\r\nEND:FOO"; + + $result = Sabre_VObject_Reader::read($data); + + } + + function testReadProperty() { + + $data = "PROPNAME:propValue"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->value); + + } + + function testReadPropertyWithNewLine() { + + $data = 'PROPNAME:Line1\\nLine2\\NLine3\\\\Not the 4th line!'; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals("Line1\nLine2\nLine3\\Not the 4th line!", $result->value); + + } + + function testReadMappedProperty() { + + $data = "DTSTART:20110529"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property_DateTime', $result); + $this->assertEquals('DTSTART', $result->name); + $this->assertEquals('20110529', $result->value); + + } + + function testReadMappedPropertyGrouped() { + + $data = "foo.DTSTART:20110529"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property_DateTime', $result); + $this->assertEquals('DTSTART', $result->name); + $this->assertEquals('20110529', $result->value); + + } + + + /** + * @expectedException Sabre_VObject_ParseException + */ + function testReadBrokenLine() { + + $data = "PROPNAME;propValue"; + $result = Sabre_VObject_Reader::read($data); + + } + + function testReadPropertyInComponent() { + + $data = array( + "BEGIN:VCALENDAR", + "PROPNAME:propValue", + "END:VCALENDAR" + ); + + $result = Sabre_VObject_Reader::read(implode("\r\n",$data)); + + $this->assertInstanceOf('Sabre_VObject_Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(1, count($result->children)); + $this->assertInstanceOf('Sabre_VObject_Property', $result->children[0]); + $this->assertEquals('PROPNAME', $result->children[0]->name); + $this->assertEquals('propValue', $result->children[0]->value); + + + } + function testReadNestedComponent() { + + $data = array( + "BEGIN:VCALENDAR", + "BEGIN:VTIMEZONE", + "BEGIN:DAYLIGHT", + "END:DAYLIGHT", + "END:VTIMEZONE", + "END:VCALENDAR" + ); + + $result = Sabre_VObject_Reader::read(implode("\r\n",$data)); + + $this->assertInstanceOf('Sabre_VObject_Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(1, count($result->children)); + $this->assertInstanceOf('Sabre_VObject_Component', $result->children[0]); + $this->assertEquals('VTIMEZONE', $result->children[0]->name); + $this->assertEquals(1, count($result->children[0]->children)); + $this->assertInstanceOf('Sabre_VObject_Component', $result->children[0]->children[0]); + $this->assertEquals('DAYLIGHT', $result->children[0]->children[0]->name); + + + } + + function testReadPropertyParameter() { + + $data = "PROPNAME;PARAMNAME=paramvalue:propValue"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->value); + $this->assertEquals(1, count($result->parameters)); + $this->assertEquals('PARAMNAME', $result->parameters[0]->name); + $this->assertEquals('paramvalue', $result->parameters[0]->value); + + } + + function testReadPropertyNoValue() { + + $data = "PROPNAME;PARAMNAME:propValue"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->value); + $this->assertEquals(1, count($result->parameters)); + $this->assertEquals('PARAMNAME', $result->parameters[0]->name); + $this->assertEquals('', $result->parameters[0]->value); + + } + + function testReadPropertyParameterExtraColon() { + + $data = "PROPNAME;PARAMNAME=paramvalue:propValue:anotherrandomstring"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue:anotherrandomstring', $result->value); + $this->assertEquals(1, count($result->parameters)); + $this->assertEquals('PARAMNAME', $result->parameters[0]->name); + $this->assertEquals('paramvalue', $result->parameters[0]->value); + + } + + function testReadProperty2Parameters() { + + $data = "PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propValue"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->value); + $this->assertEquals(2, count($result->parameters)); + $this->assertEquals('PARAMNAME', $result->parameters[0]->name); + $this->assertEquals('paramvalue', $result->parameters[0]->value); + $this->assertEquals('PARAMNAME2', $result->parameters[1]->name); + $this->assertEquals('paramvalue2', $result->parameters[1]->value); + + } + + function testReadPropertyParameterQuoted() { + + $data = "PROPNAME;PARAMNAME=\"paramvalue\":propValue"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->value); + $this->assertEquals(1, count($result->parameters)); + $this->assertEquals('PARAMNAME', $result->parameters[0]->name); + $this->assertEquals('paramvalue', $result->parameters[0]->value); + + } + function testReadPropertyParameterNewLines() { + + $data = "PROPNAME;PARAMNAME=paramvalue1\\nvalue2\\\\nvalue3:propValue"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->value); + + $this->assertEquals(1, count($result->parameters)); + $this->assertEquals('PARAMNAME', $result->parameters[0]->name); + $this->assertEquals("paramvalue1\nvalue2\\nvalue3", $result->parameters[0]->value); + + } + + function testReadPropertyParameterQuotedColon() { + + $data = "PROPNAME;PARAMNAME=\"param:value\":propValue"; + $result = Sabre_VObject_Reader::read($data); + + $this->assertInstanceOf('Sabre_VObject_Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->value); + $this->assertEquals(1, count($result->parameters)); + $this->assertEquals('PARAMNAME', $result->parameters[0]->name); + $this->assertEquals('param:value', $result->parameters[0]->value); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php b/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php new file mode 100644 index 000000000..75972c2a0 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorFifthTuesdayProblemTest.php @@ -0,0 +1,44 @@ +VEVENT->UID); + + while($it->valid()) { + $it->next(); + } + + // If we got here, it means we were successful. The bug that was in teh + // system before would fail on the 5th tuesday of the month, if the 5th + // tuesday did not exist. + + } + +} + +?> diff --git a/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php b/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php new file mode 100644 index 000000000..150625095 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorInfiniteLoopProblemTest.php @@ -0,0 +1,63 @@ +DTSTART = '20090420T180000Z'; + $ev->RRULE = 'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1'; + + $this->assertFalse($ev->isInTimeRange(new DateTime('2012-01-01 12:00:00'),new DateTime('3000-01-01 00:00:00'))); + + } + + /** + * Different bug, also likely an infinite loop. + */ + function testYearlyByMonthLoop() { + + $ev = Sabre_VObject_Component::create('VEVENT'); + $ev->UID = 'uuid'; + $ev->DTSTART = '20120101T154500'; + $ev->DTSTART['TZID'] = 'Europe/Berlin'; + $ev->RRULE = 'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA'; + $ev->DTEND = '20120101T164500'; + $ev->DTEND['TZID'] = 'Europe/Berlin'; + + // This recurrence rule by itself is a yearly rule that should happen + // every february. + // + // The BYDAY part expands this to every day of the month, but the + // BYSETPOS limits this to only the 1st day of the month. Very crazy + // way to specify this, and could have certainly been a lot easier. + $cal = Sabre_VObject_Component::create('VCALENDAR'); + $cal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($cal,'uuid'); + $it->fastForward(new DateTime('2012-01-29 23:00:00', new DateTimeZone('UTC'))); + + $collect = array(); + + while($it->valid()) { + $collect[] = $it->getDTSTART(); + if ($it->getDTSTART() > new DateTime('2013-02-05 22:59:59', new DateTimeZone('UTC'))) { + break; + } + $it->next(); + + } + + $this->assertEquals( + array(new DateTime('2012-02-01 15:45:00', new DateTimeZone('Europe/Berlin'))), + $collect + ); + + } + + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorTest.php b/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorTest.php new file mode 100644 index 000000000..d1ef2da60 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/RecurrenceIteratorTest.php @@ -0,0 +1,1087 @@ +UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;BYHOUR=10;BYMINUTE=5;BYSECOND=16;BYWEEKNO=32;BYYEARDAY=100,200'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-10-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals(array(10), $it->byHour); + $this->assertEquals(array(5), $it->byMinute); + $this->assertEquals(array(16), $it->bySecond); + $this->assertEquals(array(32), $it->byWeekNo); + $this->assertEquals(array(100,200), $it->byYearDay); + + } + + /** + * @expectedException InvalidArgumentException + * @depends testValues + */ + function testInvalidFreq() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->RRULE = 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-10-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testVCalendarNoUID() { + + $vcal = new Sabre_VObject_Component('VCALENDAR'); + $it = new Sabre_VObject_RecurrenceIterator($vcal); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testVCalendarInvalidUID() { + + $vcal = new Sabre_VObject_Component('VCALENDAR'); + $it = new Sabre_VObject_RecurrenceIterator($vcal,'foo'); + + } + + /** + * @depends testValues + */ + function testDaily() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-10-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,$ev->uid); + + $this->assertEquals('daily', $it->frequency); + $this->assertEquals(3, $it->interval); + $this->assertEquals(new DateTime('2011-10-25', new DateTimeZone('UTC')), $it->until); + + // Max is to prevent overflow + $max = 12; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-10-07', $tz), + new DateTime('2011-10-10', $tz), + new DateTime('2011-10-13', $tz), + new DateTime('2011-10-16', $tz), + new DateTime('2011-10-19', $tz), + new DateTime('2011-10-22', $tz), + new DateTime('2011-10-25', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testNoRRULE() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-10-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,$ev->uid); + + $this->assertEquals('daily', $it->frequency); + $this->assertEquals(1, $it->interval); + + // Max is to prevent overflow + $max = 12; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-10-07', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testDailyByDay() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-10-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('daily', $it->frequency); + $this->assertEquals(2, $it->interval); + $this->assertEquals(array('TU','WE','FR'), $it->byDay); + + // Grabbing the next 12 items + $max = 12; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-10-07', $tz), + new DateTime('2011-10-11', $tz), + new DateTime('2011-10-19', $tz), + new DateTime('2011-10-21', $tz), + new DateTime('2011-10-25', $tz), + new DateTime('2011-11-02', $tz), + new DateTime('2011-11-04', $tz), + new DateTime('2011-11-08', $tz), + new DateTime('2011-11-16', $tz), + new DateTime('2011-11-18', $tz), + new DateTime('2011-11-22', $tz), + new DateTime('2011-11-30', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testWeekly() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;COUNT=10'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-10-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('weekly', $it->frequency); + $this->assertEquals(2, $it->interval); + $this->assertEquals(10, $it->count); + + // Max is to prevent overflow + $max = 12; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-10-07', $tz), + new DateTime('2011-10-21', $tz), + new DateTime('2011-11-04', $tz), + new DateTime('2011-11-18', $tz), + new DateTime('2011-12-02', $tz), + new DateTime('2011-12-16', $tz), + new DateTime('2011-12-30', $tz), + new DateTime('2012-01-13', $tz), + new DateTime('2012-01-27', $tz), + new DateTime('2012-02-10', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testWeeklyByDay() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-10-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('weekly', $it->frequency); + $this->assertEquals(2, $it->interval); + $this->assertEquals(array('TU','WE','FR'), $it->byDay); + $this->assertEquals('SU', $it->weekStart); + + // Grabbing the next 12 items + $max = 12; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-10-07', $tz), + new DateTime('2011-10-18', $tz), + new DateTime('2011-10-19', $tz), + new DateTime('2011-10-21', $tz), + new DateTime('2011-11-01', $tz), + new DateTime('2011-11-02', $tz), + new DateTime('2011-11-04', $tz), + new DateTime('2011-11-15', $tz), + new DateTime('2011-11-16', $tz), + new DateTime('2011-11-18', $tz), + new DateTime('2011-11-29', $tz), + new DateTime('2011-11-30', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testMonthly() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=3;COUNT=5'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-12-05'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('monthly', $it->frequency); + $this->assertEquals(3, $it->interval); + $this->assertEquals(5, $it->count); + + $max = 14; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-12-05', $tz), + new DateTime('2012-03-05', $tz), + new DateTime('2012-06-05', $tz), + new DateTime('2012-09-05', $tz), + new DateTime('2012-12-05', $tz), + ), + $result + ); + + + } + + /** + * @depends testValues + */ + function testMonthlyEndOfMonth() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=12'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-12-31'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('monthly', $it->frequency); + $this->assertEquals(2, $it->interval); + $this->assertEquals(12, $it->count); + + $max = 14; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-12-31', $tz), + new DateTime('2012-08-31', $tz), + new DateTime('2012-10-31', $tz), + new DateTime('2012-12-31', $tz), + new DateTime('2013-08-31', $tz), + new DateTime('2013-10-31', $tz), + new DateTime('2013-12-31', $tz), + new DateTime('2014-08-31', $tz), + new DateTime('2014-10-31', $tz), + new DateTime('2014-12-31', $tz), + new DateTime('2015-08-31', $tz), + new DateTime('2015-10-31', $tz), + ), + $result + ); + + + } + + /** + * @depends testValues + */ + function testMonthlyByMonthDay() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-01-01'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('monthly', $it->frequency); + $this->assertEquals(5, $it->interval); + $this->assertEquals(9, $it->count); + $this->assertEquals(array(1, 31, -7), $it->byMonthDay); + + $max = 14; + $result = array(); + foreach($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-01-01', $tz), + new DateTime('2011-01-25', $tz), + new DateTime('2011-01-31', $tz), + new DateTime('2011-06-01', $tz), + new DateTime('2011-06-24', $tz), + new DateTime('2011-11-01', $tz), + new DateTime('2011-11-24', $tz), + new DateTime('2012-04-01', $tz), + new DateTime('2012-04-24', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testMonthlyByDay() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-01-03'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('monthly', $it->frequency); + $this->assertEquals(2, $it->interval); + $this->assertEquals(16, $it->count); + $this->assertEquals(array('MO','-2TU','+1WE','3TH'), $it->byDay); + + $max = 20; + $result = array(); + foreach($it as $k=>$item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-01-03', $tz), + new DateTime('2011-01-05', $tz), + new DateTime('2011-01-10', $tz), + new DateTime('2011-01-17', $tz), + new DateTime('2011-01-18', $tz), + new DateTime('2011-01-20', $tz), + new DateTime('2011-01-24', $tz), + new DateTime('2011-01-31', $tz), + new DateTime('2011-03-02', $tz), + new DateTime('2011-03-07', $tz), + new DateTime('2011-03-14', $tz), + new DateTime('2011-03-17', $tz), + new DateTime('2011-03-21', $tz), + new DateTime('2011-03-22', $tz), + new DateTime('2011-03-28', $tz), + new DateTime('2011-05-02', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testMonthlyByDayByMonthDay() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-08-01'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('monthly', $it->frequency); + $this->assertEquals(1, $it->interval); + $this->assertEquals(10, $it->count); + $this->assertEquals(array('MO'), $it->byDay); + $this->assertEquals(array(1), $it->byMonthDay); + + $max = 20; + $result = array(); + foreach($it as $k=>$item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-08-01', $tz), + new DateTime('2012-10-01', $tz), + new DateTime('2013-04-01', $tz), + new DateTime('2013-07-01', $tz), + new DateTime('2014-09-01', $tz), + new DateTime('2014-12-01', $tz), + new DateTime('2015-06-01', $tz), + new DateTime('2016-02-01', $tz), + new DateTime('2016-08-01', $tz), + new DateTime('2017-05-01', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testMonthlyByDayBySetPos() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-01-03'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('monthly', $it->frequency); + $this->assertEquals(1, $it->interval); + $this->assertEquals(10, $it->count); + $this->assertEquals(array('MO','TU','WE','TH','FR'), $it->byDay); + $this->assertEquals(array(1,-1), $it->bySetPos); + + $max = 20; + $result = array(); + foreach($it as $k=>$item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-01-03', $tz), + new DateTime('2011-01-31', $tz), + new DateTime('2011-02-01', $tz), + new DateTime('2011-02-28', $tz), + new DateTime('2011-03-01', $tz), + new DateTime('2011-03-31', $tz), + new DateTime('2011-04-01', $tz), + new DateTime('2011-04-29', $tz), + new DateTime('2011-05-02', $tz), + new DateTime('2011-05-31', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testYearly() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=10;INTERVAL=3'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-01-01'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('yearly', $it->frequency); + $this->assertEquals(3, $it->interval); + $this->assertEquals(10, $it->count); + + $max = 20; + $result = array(); + foreach($it as $k=>$item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-01-01', $tz), + new DateTime('2014-01-01', $tz), + new DateTime('2017-01-01', $tz), + new DateTime('2020-01-01', $tz), + new DateTime('2023-01-01', $tz), + new DateTime('2026-01-01', $tz), + new DateTime('2029-01-01', $tz), + new DateTime('2032-01-01', $tz), + new DateTime('2035-01-01', $tz), + new DateTime('2038-01-01', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testYearlyByMonth() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-04-07'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('yearly', $it->frequency); + $this->assertEquals(4, $it->interval); + $this->assertEquals(8, $it->count); + $this->assertEquals(array(4,10), $it->byMonth); + + $max = 20; + $result = array(); + foreach($it as $k=>$item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-04-07', $tz), + new DateTime('2011-10-07', $tz), + new DateTime('2015-04-07', $tz), + new DateTime('2015-10-07', $tz), + new DateTime('2019-04-07', $tz), + new DateTime('2019-10-07', $tz), + new DateTime('2023-04-07', $tz), + new DateTime('2023-10-07', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testYearlyByMonthByDay() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-04-04'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('yearly', $it->frequency); + $this->assertEquals(5, $it->interval); + $this->assertEquals(8, $it->count); + $this->assertEquals(array(4,10), $it->byMonth); + $this->assertEquals(array('1MO','-1SU'), $it->byDay); + + $max = 20; + $result = array(); + foreach($it as $k=>$item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + array( + new DateTime('2011-04-04', $tz), + new DateTime('2011-04-24', $tz), + new DateTime('2011-10-03', $tz), + new DateTime('2011-10-30', $tz), + new DateTime('2016-04-04', $tz), + new DateTime('2016-04-24', $tz), + new DateTime('2016-10-03', $tz), + new DateTime('2016-10-30', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testFastForward() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + $dtStart->setDateTime(new DateTime('2011-04-04'),Sabre_VObject_Property_DateTime::UTC); + + $ev->add($dtStart); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + // The idea is that we're fast-forwarding too far in the future, so + // there will be no results left. + $it->fastForward(new DateTime('2020-05-05')); + + $max = 20; + $result = array(); + while($item = $it->current()) { + + $result[] = $item; + $max--; + + if (!$max) break; + $it->next(); + + } + + $tz = new DateTimeZone('UTC'); + $this->assertEquals(array(), $result); + + } + + /** + * @depends testValues + */ + function testComplexExclusions() { + + $ev = new Sabre_VObject_Component('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=10'; + $dtStart = new Sabre_VObject_Property_DateTime('DTSTART'); + + $tz = new DateTimeZone('Canada/Eastern'); + $dtStart->setDateTime(new DateTime('2011-01-01 13:50:20', $tz),Sabre_VObject_Property_DateTime::LOCALTZ); + + $exDate1 = new Sabre_VObject_Property_MultiDateTime('EXDATE'); + $exDate1->setDateTimes(array(new DateTime('2012-01-01 13:50:20', $tz), new DateTime('2014-01-01 13:50:20', $tz)), Sabre_VObject_Property_DateTime::LOCALTZ); + $exDate2 = new Sabre_VObject_Property_MultiDateTime('EXDATE'); + $exDate2->setDateTimes(array(new DateTime('2016-01-01 13:50:20', $tz)), Sabre_VObject_Property_DateTime::LOCALTZ); + + $ev->add($dtStart); + $ev->add($exDate1); + $ev->add($exDate2); + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + $vcal->add($ev); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,(string)$ev->uid); + + $this->assertEquals('yearly', $it->frequency); + $this->assertEquals(1, $it->interval); + $this->assertEquals(10, $it->count); + + $max = 20; + $result = array(); + foreach($it as $k=>$item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $this->assertEquals( + array( + new DateTime('2011-01-01 13:50:20', $tz), + new DateTime('2013-01-01 13:50:20', $tz), + new DateTime('2015-01-01 13:50:20', $tz), + new DateTime('2017-01-01 13:50:20', $tz), + new DateTime('2018-01-01 13:50:20', $tz), + new DateTime('2019-01-01 13:50:20', $tz), + new DateTime('2020-01-01 13:50:20', $tz), + ), + $result + ); + + } + + /** + * @depends testValues + */ + function testOverridenEvent() { + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + + $ev1 = Sabre_VObject_Component::create('VEVENT'); + $ev1->UID = 'overridden'; + $ev1->RRULE = 'FREQ=DAILY;COUNT=10'; + $ev1->DTSTART = '20120107T120000Z'; + $ev1->SUMMARY = 'baseEvent'; + + $vcal->add($ev1); + + // ev2 overrides an event, and puts it on 2pm instead. + $ev2 = Sabre_VObject_Component::create('VEVENT'); + $ev2->UID = 'overridden'; + $ev2->{'RECURRENCE-ID'} = '20120110T120000Z'; + $ev2->DTSTART = '20120110T140000Z'; + $ev2->SUMMARY = 'Event 2'; + + $vcal->add($ev2); + + // ev3 overrides an event, and puts it 2 days and 2 hours later + $ev3 = Sabre_VObject_Component::create('VEVENT'); + $ev3->UID = 'overridden'; + $ev3->{'RECURRENCE-ID'} = '20120113T120000Z'; + $ev3->DTSTART = '20120115T140000Z'; + $ev3->SUMMARY = 'Event 3'; + + $vcal->add($ev3); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,'overridden'); + + $dates = array(); + $summaries = array(); + while($it->valid()) { + + $dates[] = $it->getDTStart(); + $summaries[] = (string)$it->getEventObject()->SUMMARY; + $it->next(); + + } + + $tz = new DateTimeZone('GMT'); + $this->assertEquals(array( + new DateTime('2012-01-07 12:00:00',$tz), + new DateTime('2012-01-08 12:00:00',$tz), + new DateTime('2012-01-09 12:00:00',$tz), + new DateTime('2012-01-10 14:00:00',$tz), + new DateTime('2012-01-11 12:00:00',$tz), + new DateTime('2012-01-12 12:00:00',$tz), + new DateTime('2012-01-14 12:00:00',$tz), + new DateTime('2012-01-15 12:00:00',$tz), + new DateTime('2012-01-15 14:00:00',$tz), + new DateTime('2012-01-16 12:00:00',$tz), + ), $dates); + + $this->assertEquals(array( + 'baseEvent', + 'baseEvent', + 'baseEvent', + 'Event 2', + 'baseEvent', + 'baseEvent', + 'baseEvent', + 'baseEvent', + 'Event 3', + 'baseEvent', + ), $summaries); + + } + + /** + * @depends testValues + */ + function testOverridenEvent2() { + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + + $ev1 = Sabre_VObject_Component::create('VEVENT'); + $ev1->UID = 'overridden'; + $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3'; + $ev1->DTSTART = '20120112T120000Z'; + $ev1->SUMMARY = 'baseEvent'; + + $vcal->add($ev1); + + // ev2 overrides an event, and puts it 6 days earlier instead. + $ev2 = Sabre_VObject_Component::create('VEVENT'); + $ev2->UID = 'overridden'; + $ev2->{'RECURRENCE-ID'} = '20120119T120000Z'; + $ev2->DTSTART = '20120113T120000Z'; + $ev2->SUMMARY = 'Override!'; + + $vcal->add($ev2); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,'overridden'); + + $dates = array(); + $summaries = array(); + while($it->valid()) { + + $dates[] = $it->getDTStart(); + $summaries[] = (string)$it->getEventObject()->SUMMARY; + $it->next(); + + } + + $tz = new DateTimeZone('GMT'); + $this->assertEquals(array( + new DateTime('2012-01-12 12:00:00',$tz), + new DateTime('2012-01-13 12:00:00',$tz), + new DateTime('2012-01-26 12:00:00',$tz), + + ), $dates); + + $this->assertEquals(array( + 'baseEvent', + 'Override!', + 'baseEvent', + ), $summaries); + + } + + /** + * @depends testValues + */ + function testOverridenEventNoValuesExpected() { + + $vcal = Sabre_VObject_Component::create('VCALENDAR'); + + $ev1 = Sabre_VObject_Component::create('VEVENT'); + $ev1->UID = 'overridden'; + $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3'; + $ev1->DTSTART = '20120124T120000Z'; + $ev1->SUMMARY = 'baseEvent'; + + $vcal->add($ev1); + + // ev2 overrides an event, and puts it 6 days earlier instead. + $ev2 = Sabre_VObject_Component::create('VEVENT'); + $ev2->UID = 'overridden'; + $ev2->{'RECURRENCE-ID'} = '20120131T120000Z'; + $ev2->DTSTART = '20120125T120000Z'; + $ev2->SUMMARY = 'Override!'; + + $vcal->add($ev2); + + $it = new Sabre_VObject_RecurrenceIterator($vcal,'overridden'); + + $dates = array(); + $summaries = array(); + + // The reported problem was specifically related to the VCALENDAR + // expansion. In this parcitular case, we had to forward to the 28th of + // january. + $it->fastForward(new DateTime('2012-01-28 23:00:00')); + + // We stop the loop when it hits the 6th of februari. Normally this + // iterator would hit 24, 25 (overriden from 31) and 7 feb but because + // we 'filter' from the 28th till the 6th, we should get 0 results. + while($it->valid() && $it->getDTSTart() < new DateTime('2012-02-06 23:00:00')) { + + $dates[] = $it->getDTStart(); + $summaries[] = (string)$it->getEventObject()->SUMMARY; + $it->next(); + + } + + $this->assertEquals(array(), $dates); + $this->assertEquals(array(), $summaries); + + } +} + diff --git a/dav/SabreDAV/tests/Sabre/VObject/VersionTest.php b/dav/SabreDAV/tests/Sabre/VObject/VersionTest.php new file mode 100644 index 000000000..ea2a4b284 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/VersionTest.php @@ -0,0 +1,15 @@ +assertEquals(-1, version_compare('0.9.0',$v)); + + $s = Sabre_VObject_Version::STABILITY; + $this->assertTrue($s == 'alpha' || $s == 'beta' || $s =='stable'); + + } + +} diff --git a/dav/SabreDAV/tests/Sabre/VObject/issue153.vcf b/dav/SabreDAV/tests/Sabre/VObject/issue153.vcf new file mode 100644 index 000000000..5fb0fa297 --- /dev/null +++ b/dav/SabreDAV/tests/Sabre/VObject/issue153.vcf @@ -0,0 +1,352 @@ +BEGIN:VCARD +VERSION:3.0 +N:Benutzer;Test;;; +FN:Test Benutzer +PHOTO;BASE64: + /9j/4AAQSkZJRgABAQAAAQABAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQA + AAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABQKADAAQAAAABAAABQAAAAAD/2wBD + AAIBAQIBAQICAQICAgICAwUDAwMDAwYEBAMFBwYHBwcGBgYHCAsJBwgKCAYGCQ0JCgsLDAwMBwkN + Dg0MDgsMDAv/2wBDAQICAgMCAwUDAwULCAYICwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL + CwsLCwsLCwsLCwsLCwsLCwsLCwv/wAARCAFAAUADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAA + AAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKB + kaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZn + aGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT + 1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI + CQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV + YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6 + goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk + 5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8J7JbO8tYo1tIFCDLOVG5qfdaVZRwmSOFWzyA + F4H1rLt5WViMhdp6HgmtKK8O3B+4Rhx6fSgBI9FtjaNN5aErwRjilSys7lFAt41xyTtqc2yJCVlY + 7eqgGqv2jyLcebjZnGPWncdzT0+w0u5eQXtrGiBcIyoPmNMXwpb/AGMTSRRbH6YAyPwqK21GKdfL + BAVfu+1SQX4jnjKFsp03dPypCKN9oEaKSkC7R0bGKpnSlSPdHErZOORXV3Ouy337sCLB6kpx+FY0 + t+VfyrgcbuCB1oAfoMemrcImq2sZX+I7ATXS618PdK1DRlvvDEaMq5LoV2nisx4LVrUfu5BOePau + m8EQS6PY3HmFXjljKhTzjOf1oA4mz8OxvMrLbW5RD8wbByKg1LRrRriRYY408w/KAMba1pRaWt/H + a6a7CVm2u7N8lUPEujzaRekzSK6tgqVNAGNBZJauY5Yon92GTRJp0ROY0Un0A4q3c2odkaYOMjii + KL7NIDGcj1NDAZBplmmWv1xnoFHStfS/DFpewqYoYm3DutZ8lv8AapdyOqk8EVteEbSe3KBSrDrQ + BT8S+HbawiiWGCAPjsuMnPesqHS4JSFlSMP7DitbXbvfrkkM2eGw3p+FMfTh5X+hr8w7t3oAhOhW + u8MkMZUY3fL0Heo9UsrN5FFrbxKmMBgoG41fWFra0Acjpzg9aoXjtgRoo29vagCoun27kbY059qn + bwykskYjRArdTT7GEl2UqMr2q/JtVU27iR15NADdK8DC/wBPle2iicxNg5ALH6Umm6FZ/a3ttQt4 + g2Cqnb0PbJ+tamn3j6ZCW0nILfeBORWVfO4dhLw7fMW7560AZuqeHf7MuTFcRpv6qVGVx70q2Eci + QwyW0SsPvOqjJrUtb6S9tHQKGeMZYuM8VUs7gRxbrncy9mWgB1x4QtTHvsQWkHJVhhax3tkhugHh + UkfeAXIFdPZ3v2uxkQ9G4jI6/j+tYun3r2Fy6yxeb2Py5IoAqXenJ5xaGNNvXH/1qcLSGeBdkSg9 + CcdaswC3be0pfexOMnpn2qaS1KQkQASKoydvLCgDNi09RKTNCuO2BxVjSobc6gqXMERQHkleDUsc + u9VADbG6qOWAp11bLbptkjlCkZRsde9AFi5sbO3kKfZYTnkHaOlVbuO2F5thtYcADjaKXUpHj8ku + Co2VDFL5wLeg696YFwQ2z7Qtlb8HJO0c1Zsr7T7a9kL6XazZ4CmMFRWfHdkEgjGRjPpU9raP5LSP + j5h2pAWdQ0+z1KdG+y21qvcRqBn8qXSvC+iTu63ssqyE/IAuR+NQwSrGm1g+c8E9qiSQW9wPNYYP + OR2oAW68GNa28k3lwGNHwvzDJGfSqM9nHBgm3j59QMVdmma4zIjsUBHy5OKp6o8s2BJjZjjAoAro + /nysbgYY9zWmLPCR+WQQwyaz4k2F/Pbft/GtKxvUeFN+B2x+NAEptsWpZSdo9etZe8su2X7pPFdU + LeOazKqVwevNYt7pw5EA5HIxQBQA8tAIeGz1NWIJvJlhW5OQBzjrUMR/eN9pwoXjB4qQ3ERJeYcy + 9P8AZoA0jf8AmybVxsHAFS6jp63ixmwjIwOfrWfaou12GcDpmt/w5qJhXc6hh2GM0AZkHiRpblVl + G0RjGMdxXQ+H/E0Rm+bjdw1crqEHm3EksY4Y9PTmq0cskc42qUOfpmgDovHOhLBOZ9O+aEnIUdRW + QZft1sgum/1Ywua3fDfiFDL5WoEPEwxzzirPizwTFPZC60kYUjcAp4NAHPSq91EoRS3061DHD9nb + 94Mkfw020v57GbcCRt4IIqzNcedIH2jc3JyOaAIYrRZmJxtNdB4fkGn2hluBgBR+NZ2n2X9ozAQD + 5qvaxGbKIRXkuFU4C96AMDxBKZdQkuEUkStuUegpNM1eWScAkqpHTHNPlwbjMzExZ4Pal1PS/s6+ + dY/6vuwPSgC9G8c0A+1xEknrnpUVxaeXNm2dVUfjVazvEZAEkMrccZzV1YYyBIhJP8SZ6fhQBSmV + 4JfMVT+96UJdSQdcMO4A6fjVmTUoJiqTOMJ/q+elRyQs0TtaxF0PVhzmgCzpd55r7YI2HHPTmrV0 + sDTF7gnJXGO4OKyNKgn80NbFhjoBzWjqdg6SISPmIBOaAKVnI1leyhsMJOD7CqOqRtZqotjiFulW + rhsSMshKH1ogsZbmF475TKifdf0oApabevHIAhCYOdxp0t59luS0I+995uxqpdRyWsrqmXGeCR/K + rVlZfaogqv8AvD/CaAIY42kV3K5zzn1p9jNLp6u/A80YPNWWsJNPAVpC4JAZT2HfFWJoVmVVjhVk + HTPrQBPoi2wsoo4APtBHL+tP1mS5uVEFxgJGNqH15plp5WmyBriMRsowM8UybXTNdbrpd6A/KKAD + xbJAGs44FIPlnd9c/wD16ynt/LiDW2SR2qa5vP7RnMs6BNuQMd6jhkAUb2K8+tADYp0fhj8w6itC + yQ3CFYeAOoqi8Uew+UMuf4u9T2NwIW+UgMetO4FmS6RJ1ik6HqxHAqC+gimUiA8DvjrU0kcE8ieY + itu+8c0+bShaWxksSZoM4b0SkBTgha0cq33Cuc1SvrrLFV6jpWqbuGe1HnnDdAKy7i3WSY7OT2NN + AMulWSV8ZDNzxV7SlbaFjClx69Kpww7W3ct7jpUtnNJHd5UjZnt1NIDdt7h7NQ7qGfpt7VR1XVEh + dhEpP94/4VpafexTy7ZlbBGDVHxFbQh1j04HaOTkdKAM5ZVlYso3E+tVp4w8gx0Bqd7QxNu+6D6V + DIoVySxAx2NAFyNmli2pjYBz61paW3lWrFS3BwP8/hWJbTBFJy2D6HgfWtiTWPsqxraBHyOeBg0A + RSoLSTdIepzz0606exTWyQGMXljORTNT1B7+ECZR5fHzDqapfbHjbFkTsIwSTQA43ptyyS44Paun + 8N64Z7Bre4YlZBtU5+7XLTQbjwN4Pb+IfWn2lw9uyrIw2Z5HpQBv3GirHc7LxWVZOVI71FNp7WDg + QYlIIGD6VvaPdi+tljb5yeAzcn8DT9YtbPSpVhDM87jJ3Htjnn6UAUIrJreD7Si7MDoKhv8AUxqt + pGt5GqIOr9zRfLM8ZFgZGtex2nGe4zWKN8rsDhYx2JpJ3Atx+HxcRSzWcpcL/CRwaj0zW1sQy3cS + nsFPSoYJpbIl7dm8tT8wzV7+0hqEO1Y4lQ9cqMn9KoCp9kW7kaaxU+Yx+5j5etWrb/RGxfr5bkdu + lW7KFILpfspDbVyc1fjNnrLtHqOYWP8AFjGfxpAc/e6Ql/GzW4AfqBWfpupS6Xer5vPlHmMjg10V + 5pp0u4JhYNGvAYHrUn2WLWrVo41AvSMRZAC/8CPr1oAvafdWOuNG+lqDekY+zg8MPXPX/wDXWZrF + tcWNw0erKElB4Rf4R6c1BpqyaBdbrnEcwyAc4x06H0rQS9a9jUTgOXPzMwycexoAw7u1jYb3zkU3 + Srtgdk54PFamv2C2pDQbWjcfKCeSa56aJld23YA6ZOKFqBrXGjjULuOKxKuZOTn+H/OKwr/ztOvs + uCrg7RgVLYapPbXAEW4EkHJNdBNBH4gtgyhFmXuw60AVpbT7VpiPJ94jLetQWsDRSIYz8mec1c0+ + 1nexdrw7GjJXk/epsFtDPG0bOdw+b5SaAKWsXA+14Y71FQi5S4RvlAC8A0y5hHmHarhvQ9BVGSQx + sUXPHX3oAmDCJ8rzgHg96gQ+ZGWbg9vahNRG7EnalkkF6hEXyD270MCWF3aEhdue1OsmNnMAih/r + VaBgAUY8561PaubdnMxJXseuKANhIY5Assp2v12itZtAgubEi2nb5xuKYHWubstQaO6SVzujTqpP + X8K2rXWLRF8xZJPMfjAzgUAcxcNiaRSpUocc96sW+yNgZCMVF4lvJdRvTOYkj52jbgZ98D6VWmlY + 2qCUnJOKaVwCzviibANwYc8Utkdl7tbKhjxmpUspvm8tgn16ipigSEG4G4pxu9TSA27GeFbRlGGm + P3cdhUN8GEP2hV3JjafrWfpU/wBmuAcZLA4/Sr1trkarJHcRmSEZO3uTQBmrcbZCLoDZ2x1qOHSi + yebJIAPQipp4kmbzI1EQJ6GtCxsoHP8Ap91GB2yDQBlSWO+M/ZsBHHzZ71XkfMIWNgGU9vSt3U9N + t9m21uonz0Iz/hVCfRkjg82FhtHDGgCuZ8EMjDZjBzSZ8pAwU7XbGT0pWtEjjAZgV4PFOml2QKqk + OoOcU1qBNYRSrdkrhw3BIrah8KwXoV/m3PyVzyDWNp999kccgZq/ea7PFAGgZlJ6EUgN23thpdi4 + V1Eucr7ev9K53V/ER1a/MkuWdBtG04zioLrXJ5wDK2XAxmqVqmZ2YPtHJ/GgDsvC3i0ppr2d2ish + yFAHIz706bRLNdOPnErKw4y3NcvZ3pjA8o4kB61o3OpSX9nbx3QIkU/MwoAj/sGaPzFjlWSJjk46 + ioYYwqssjIHHAHpWm4ESN9nYDIFZV+I7uVI1wrY5b1oAtafcvb3W4MM9Nx6U/VZpNRys54ToU4zW + KXaDKrJuC8cVdtpi1gzs43HNAD9N195bdYtRIUR4wD1NX2KuA9uThuSQelcsZwzq9xyzfezV/SdX + e3m8pXJhkPKkUAdYZk8RywjVVJES7U2cE/WtA+HDHohuY3Uxg7RF/GeaPBlxaawMW6rHKnAU9SOO + lX/FFv8A2bpzTQk+cpAAz93nrQBx+r4c5CODEOA3Y+wrKu5V1C1GFKznkk9K6Wzv49fs8Xf7y7DY + MhGNgrmtX0s2t66WknnKvUp0/WgCnbrJFdot0NwJxkDFdDYp86oMjjIArJivxbR7LuMyEjKitS21 + MW8auuW44H93/PFAG15aXdr5Uv7uULkA/wCFc+Yvstw0at8+eoq/p+rm6vRJMNwIx9KranYySXSy + WEZZHOCw7UARXFyj5STAk7ntWVf2gALLyfUVoataLbfLO2SO/Ws2c+VwhLK3QDpQBmz2xAyCG56d + 6uWPlnCkFcjoTzUBkMc/3cZpwn8oZkDFs8HsKALN1apDIHOeaiLkRkMOtSXE6yxAsRUcdxldswIJ + HANMCuJW8xQgOP51oacWPPGAeRUUOIZQzDhecd6mbIcbPusM0gLmq6bHPohlhDeZuH4c1zzF1+Rs + HByDXTae0s0IhjjZg3GPWqOs+HpLCTbNGyb+cHrQBZitjPEzW/LL97vinw2v2m2aORec9AKXQbsw + ygBBiX72TWxfaS8kiGFQAwz8vWkncDlbqNraT5cjb/n+lMGckx8kjOa1tU2TxkPkMpxyKyrhJ4Wa + KIDbTAkgvIp7URzgBwe/BpZYrd4vmZWNZ81x5cgBXDdzVlIvtUOGIBHpQA2aEROpR8DsB2q3bvG9 + iySzEsTkLnrVMqViCZzt7nrT7GBVuQRnODQA6Q+Sx80A4HApEJB3BAR9K19EmhkvCJ0ZsKe3tUc8 + Mc1yy7cpn6YoAzoUiclnYYY8AHpUl8zRxqpPy9qtC2tULgSMAvQ460lzIl9b7YiDt4GaAKMMQlJ5 + z9Kj8gIW5yKnS3Crlzhh6d6k0mbyZT565Q5z60ANtrRpPmhzWhbwy7DJcDhhwMdKlt7aK+gb+z33 + yKdxVuMCqaz5cqGYfWgB6yu8rBB8o6Gs/UpjGQXBGPTvVmSfyImyepqrqjbIw3WgCDz1ib9yOTg4 + NbVlNBJYvlVBHt1rBaPzQWU4IHSn2FwRJslJxQA6e3M0O4oAzdB6VXR2iKGQENGOK0ms1eAkFjF/ + BjrVGaAo371smgC7pety2kwl06Vo5AOWXmuwm+Itv4g8Ota30aWlySAJQfmkP/1zXIeG4Y5SVBB3 + evamXGly2tydwG0nKkHpQBZ86fRbpBLI252y4PGRWhO8Ml1IbJhHn+BTnNU9O1oRwvDqqhB2lHJP + 4U6awb+z4JdKbzdh5ZurDHtQBat5LaRHiaOP7QejEZKD/Oauy+FI7W3Bsroyhxkq3QH8q5a7ujM8 + nWOQnBqTR9burCT98xdR60AbbaHc6ZG3ymJsZC/3hVnw/fNIXt7hygHzZp2oeIBqCxzqfmCgEe3+ + RVdrmLVAEtf3bxfOW/ve36UAV7+7DXMu5Q4/Os2e3eRWkiAGOijtWrPodxfQmeNVAPOPWsppJIpi + JxsKcY9aAMwRyTSbpflx68VOYvOXb97OKtXAiZdzkqT0AGc037BIIRLHjsR60AVprZrZwGj4qTY0 + xyRj3PUVMJDduFfqvFRzxJCzrCzEr60ALEu+YI53c4qeGB7lGCnBU4FUopTBLvfk1at9R2sAMjNA + GtaXsnhy2FzPHvC46jgnNQ33imTXrkz3oVFAwo9Kfrtq03hAzEfJ5gyc81hWM5hhKrhgT0NPcByS + P5g2uVI98Vp6X4uuNGlyzCQIQR0bI7/1rNQxqW+05J7Y4qK5ZYUP2ZCW9TSA7SR9M8V30X9nMFZw + WfcNi5qPWPDtjo0pE7O03U/Mf055rmtFmN9E0DEox+atPWbiW7lSO8Ja4jQbcDC4A9PXFADYtM0+ + 6nc3u7aOm3IP6Vnak9tYt/xL/M445zTIbieOdmWNsE46cip42EkyC4hYx469KAFsrT7XEJgFPOT6 + 1s+H9PD3XlzxnL/MDtqn9pghgb7GjL/eJORWqfEnmrA9oFRoxjJ5BoAp6NqDW2pzRXtuyIAw3FMf + rVS4iF08pydmeCDxWvqeuC+Ro9qglcMw71mwReXD5aAlFJPPU0AZ0cEsbkSZKH15FD2xJJiJVj6c + VfnzLGEXAA71PFpDPaebE6/KOh60AYVws8TBgrFe57CmHUG25RVJA7AVozzSLbNvX5T1AHNY/m/Z + nPlqwDetAEtvqzJNu3FZBwQBjI96vPqkd3mRtokH31UYx+VZqWruxaFl+frkZxT1tvs1ujJgEH5m + PR/pQAXl2S371XAHI+Wkaf7VD8hGR2arKySylRccQ98DmiS0jifdsdgeODQBQd9x3IBx1xTYlBm3 + En86sXUAwPswKg9QeaBErIEj6nrQC0NHRtUjt0K3AHzDABGcVW1fTzJL51jyOpz0NVooispebBI4 + wK2YFEthk8qR07igDAgJil+TKtnnHFaP2h5yI3ZsgdSfaqd2P3im3BGM9aktsjmRgCOaAJZrMwR7 + 3A5PT0pdMvZtOning+byzuVDyh/A8VHczSzDPy7RwOKgiuHEewjKeoFAzp7TUNM8XXEw8RhYNQmP + 7ny18uNeOM7cCtMfDiS8uY0tDEYghyynjPbn864htP8ANhLIehzWzovxDvtFsDB9+PI4I/rQI0r3 + wNc6DO0N2VaQqW2q24YxmqFhYRgE/vkkDfMGBBP4GrSeJ7tZd6SxvIfmK4yQP84p0XiyC71gS65G + 00zAKGX5Qv4UAbFpd28WnIsBLsDzmub1+AXt1LJEoQqfu4xu+lbWsWgs4/NsCXjPIbqK5+5kklmE + rDD54BFAGb5cjybCrAnnB6ipEvXil2sM4GMVpFY7m4UNmNyOWJ4qteaM0BISVZe+RQBFHC2/zISg + B69KlIVhIHA3HuR70lqotlBulY5P4Vcls44k3u6N5oyoHb60wM6O1SRir5LemOKv2vhuW4iLg7VA + 6k4FTR2ax4aaVIwR3HWqGua5PcQm1WRBH6jqaQFzWbE2nhzynuIi+8HaHyKweJSEQEN6jpVcKyOw + cMVznOeKmtZvOPDKuOKAJbi0JYFf4eue9IW8sncfvdqnlvVFyFyu09abI0bysMZx0oArC4eCTcgb + juK2dNvE1N1M0ohljGQzc5A7cfSs6aweWAk7kTuapQysIT9mOSvG49aAOkvzLMxk06QNuG1l7j3r + PlnnJAuGJij+nNQ6XqT7wEYqyn5v9utLULaW7j321uiEjLqMkKKAIotbghb/AI8hKGPIBHNXLG6t + 7uzk3RLbKG/iP+Fc+8f2d1eFztzyD2q5p2oCFWRoxOX52nPFAGgLyC2lyZFKdB70r69buxRJBHjr + nvWVdeXLE7xE8fwnoPpVKZUnQPkBhwRmgDq7a9tLyARWiiWYngL1qG4gurJ28+NowO2a5a3v3smD + aa5WUd1HNbC6zI0KSX13JO7D5lbHFAE4V7pi0b5x1GazdUtXSM7v4iPw5rQ0/XrcXX75FgUdxzuq + /qFrp+sWRe3uDkc4BFAHLRDY42ycd6uPOXiiV+RGPlWnXOg3IQvEmIB/Ft6/jUUEZmMcgydvzECg + C1G2+Ly3YAvyM9qY88kaFcmmp807uwPJ4FS3do+Fzn5ulAFVrjbgS8Z4yah2C03SMffNWZdPknVA + iluQOnHWmX9pILvyY13HHK46UAVre7LSyOCTmtjSiy7VijLeZ0IqO08OzPIUiTI74Ga6bRP7O01F + h1KYJOv3V4BoA4zU1lExMrkbOAvpVcSifhjgrzmtjxPp7pO7SggOcqfUViy25hG5fSgC8rrLAojb + d7d6SexlEgwpRfTNV7e5LFBbKAwPNWHeX7TguxI7GmBPBExhaNVIJ6egqOVknO1fkx1J61aj1gLC + UEKlk4LVWvozC67kCFxkD1pAQ24e3uDLC3z9CR3H/wCqrczJdOGiOxvYc5/CocMYhtUBj3xU8Qjk + XbKPIZOjqclvzoAu2HiO60xPKvd7wY/1fGBWnJo8WuW6y6XIPMYZEAzuH9KxISonAuzuRzgk9qtR + 79KmMuhTt5cRyxznFADLzS2tMw6pAY5OoDEZ/Sm20TQQ74YwVQckGtMatB4kUpqreVIRw5+8aqXF + jc6bAsbD9yThWz94UAOmmjvrRCMJjOQRVS0sD9pLyABM5Of6Vdtrdn+RUGcZqO6uRBG0MuFI79KA + MfV7r7ZqDI7kohAVT6U2eJNimJQOuTnpSXFussrMvBz1pJov3YUsR9O9ABblRncQ3bAqY2EUwIiA + Vqr20ojfYqZx3q9bSKAGcYJPIoAoq7OCEQBffrRDGEcleM8nNPjuGkhHmbB74ApvmxltsuTnuDQA + +SFEjDwu5buD0qpLL5vMg2kEdOlXECMAyZGOMMePyprQRI5N0rt3BXO326UAV4b0Wt0pC5HrXS2W + qq9zE7jcO+OhFc81kbg7iMqeAFHSpLa8eymaNOUIwD6UAavjPQYYybq1bBmXcF9O39Kw4iXdDKcE + DAxW3q7NdWELISdiYIz71kz6ZNZNHI0cjqQfujIFAEtzAtu/7vODzmqlyzNyAo9vWp7uWSWJd+AM + jjGGqOWCSWRVVW2+uKAKskpWU5TP0p8c+ExsPPNTmCVD+5U/QrzRJHJGymeOQc45HFAFczh497KR + jirWlEsAudvII9znitEeBp7yAPZvEVPJUsP5ZqCO3j0yYDUNwliI6dOPpQBt/wDCR3Wj6eHFujvI + do3DIX9KoHXoL6J11CJYZAONlaWueIYtY8Nwx6ZHu2MdxVeTXKG0eaXKRuCeuBQB0mn+HRe2Yeze + MqRkFmwfyra0rwsIrRmvZICcgDLVw7xXFuFd2uEQfeAJAxUkkjSxh4J7gjPAErf40Abvjq1i0y4S + KByCdrfL+FUI7SR4Wc+WzMOCW5qhf3Mt9cCV2ZiihRk5qpdTSBgRI+R2DnFAFw2k6AqJZMjuD1qn + cxzyyAkPuiP3ieT/AJzV+01R7a2RpMZPVmGQ1WVuTqLDCptcfMBwRQBEkst/YMCSTH8vJqtJaoYQ + JPv1o+ZDZKAo+UnBpmrCBpRNp4/0crgZ9f8A9dAzCdGgkOynxSus2xjkj+L1qW5/fxYj+8D+NRWz + R4fzCd2O9Ai0lzI6mPaMOcZqW4uI7rbtJ3IMc1XScKqncQT0olPlKWfBz6UATKjSDcmdoFWtPCyR + kzckHiqUV0623lKVIPzHHWp7Ic/vSRz0zQBcCqdyT4J7YqC3uZdKv1a2UupO7B6H2NMglMUsmcnd + 0Lc4q3BmaMBiDjr60AWJRBfyb9P2RueWJ6KfQVLHqMdtcEysxJXayN0x0yKyWihWQBdwTOSdxHNb + zWEF5ErXhX7QQAMNge2f0oAnhs4rq2kksHwirkg9SfauXnJnmL3AbL9jXSRWh0N28x1cEfMqtnA/ + Cs+70+O9/fWRIb+76fhSTuBimbyyyKDgnipLk7AML1pZbCWO7Hnjn26U6ZykRL+veqAryuvm/Jwf + Sk3mo2AyHyCT6Ux5pLU5Gwg88gGkBPNAILUO3KmooyjL8ueegzTvPMsRjG4qBwKrW1sxJZzsIPGa + AJbmfp5q7MZx71NZawEi8qZSyHg4NRGLzCPtB3eme1R3Nutocodyd8UAaVtqEUDlI8/N3PaqV2Ht + X2x4lIOSwHFSWkEFyo+cD1BpbmNbNdkh20AMh1UiJ1c9RzWj/wAJa1vYiK1RmRvvetY5gDENxgnp + UlhN5TiI4O4845oAmu51lXzFDGQ8jnpTra4uJkBAOQavXvhG8tIhPawvJAfmY9gKE1COwgIiAZiO + 3rQBV866T52Qsw6YrXguZNTs0WSJ8IPnHr9KwZNamNumZSpPU4pbPxBeRy/uJjtXqfWgDodMtnXK + QjYeo3VnalpiXjMzXMKS9O9VV1ydCXkmLY/SorWwTVJTmQEt81AHTeCY49Mik+0SJKmOg71W1bxH + HLdgaXaSRNnjdzWapGlBBG2ec4GKtQ6yZD5hjLMvbIzQBfutWC2ajV4ywwN2OM/Sql/JY2kKGzU/ + McnBBqlf3Lam5e8lKMv3Yz2FU4VjgzsGQ3WgDa0ya0u7kxzgqCCcn1q43hizkEjRkOoXcAOua5Ka + 6Mc3ygEVb0nW57ac/ZC4Xuo5zQBBeZjcwuMxRn5fUUmnySx6kv2cgg98deK1LjT31pTLpymSVuWi + Xqv17U2GzFgFBUCVOo7igCTT7cnTp/ty5ZnyCvGOKz2uwimOY7geQB0FWY7tzu8xiqk8A96qOvmy + MSowOc0AVpkkgk3uAiP39KkjtonYtnO4cKOP1q1Z3K+X5V2N6OeM8gfWiewaxiKhDsAyJB2oAk0u + 1juAwniYshwoB61FLZfaJDv/AHWexpulXRNwpjkP7s8nu1Wd4uC7zfezxQBTjxZTHzlMigbdy8Up + YXEv7nPvk1aNqbhDhgARnFZMCvbzuWZgc/nQBo2l6qs63AJA6VIsiG4DI4jXP8XeqcbrK5JH3xkH + 0pWhWVR52CF6UAa8kUd7H8rD5f1p5txHAfNPasWRCjgh8D0BrV0a+DgCdfM3DaB9RigCml/JFPyB + 159xV+C/wfNHAbtUN9orxO3k5dhycfw1XmT7JarIjb1k6U2BcuNSVGDSAPu6be1QTXcO0CVSwbPA + 7VRtpftEmxW2Mx6HvUv2V1J2jkdaQBFJB5jBVYemetRyW6SqTKCfTFNllCHBX5vWkLBPvk4NADTG + 0ePKB5qdLN5NjycqvNQIpZAFVj71LsaJQBuGaAH3aCVwycKODUMsZgJjxv8AXIzUs0DpHhmBycjm + gOd37wdRjNAFETeTcARAbSeTViApfrhjufHXNJNCsUu18Z61Xit3Q5JxQBdW0MYKyn5hSf2BPIjS + 24I29T6f5xUMMrs5HOF71ooVmtMyu3ynAAzQBqeCfG7aaPsmuYkiYFG3HseKq67YQW2rSNpLCS0l + GQ5GSh74xWZc2SyxK4OZl5x7d/0rV0K+j+xPFOu4Pwpx0oAo3OnFreM7AR9Kp/2eYpxtyCx6VoXd + g2nSlQzMh6UxJdjqSpKgfN6mgCOLSZGkKyYw/wCn+c1YltRodoWA+Y8Z+taPhWz866DQqxLdmq34 + x0ZbS23yY3NgkUAcZcSyrjcc7zw3YU62meOeTazdOhrZ07TYLkYvSFVfmqveQWkDj7CW9zg0AZs9 + 8wbO3L8ZpvmGRsyZQDsO9WLu0EwZojwMc1DJCrsA5we1AFmGVZLc7Y1bA6nvU1gIyNzgxtnoKr7I + NgHO8dx0pJ3AYG3UnHegDRS+NpL5lsxh3dQverj38OtL/pKCKSPhWU/f+tYEt98xMnC9qgludrrJ + GzFl7DvQBq6pYNGdzHGO3aqS33kEBhlSME0+01z7OcXGXRupJ5H0q5fafFqNuJLLnofmGDRsBmJe + DzMEZGevpW7o8sN/bzLqTBML8oB71k/2YYh83FQRqbdtr7sDv60AX7jSo4ZsiVo067hj9anuNHey + jVizMj8gkdaqQyi+UxjO7O0A96tXDz6rEFucp5HygUANGEQKjDJGaqzWbzgyn5QOPY1p2xZtOaGN + VMo5BPoKqxa1NHHtmij+Q4xkUAUraZFiYScMOgNMf76CIZHf2q5KRq8arEjK4OTsGaki0oKwAEhP + uDmgCohEsqq/O6rrMNMj3AEdgfQmn3tqUgEcaYz1JFMtLdn0wpFGxYHhjQBa026M0XM2WQ/NnHzU + 6Yw6tCPt6rbpH0CdvzrPtrZ45ceU4cHk9qtzW6XLOjqwY9+1AEa+HWun8zR28xU5LAZx+VLaGSV9 + jrkr145amvEY4hGkjKMg5XoPY/571vaHFDr95HHqDMkoU4C9G+uKAOevoo5iSBjBxVYwLdRkL1Xt + XSeK/CdzpkjRMqyJ95SjbsD3rmJbUwoeuGOCfSgC9eWc9rcbbdA0KHPmhcq39Ka8e9DkBS5zk1X0 + /wAR3dvEtuTm3AwVzW/D4w0xIEivbOaSTAVWBAH40AYMu6CZDkFcHcTz6UrtkYlwVHIwOtb91olr + qtuRZSL5h5EX8VY97pc1jKAqZ2jB/wA/nQBRJhubjE4YOOnNMC+S+DzmrMkIA819wPTbjmqwfzcM + 4w3vQA9mbYwgIz/ENvSm2t+6jZsYKeTkVYjn/eqwGAOp9aeW+2sdkgVf5UAQLKY5MHGferNv+6IM + XT07CmyaeZIS1vtmkUdQKbZ+akOZoyqMe45oAvRzjUJPLLgSds8/zqyPDzwETagy4U8YwARWMbcw + NuDDePenPrbXEfkTn5hwrdqAO709LPSbbzlZdvqD0Ncnr/iufX793uWQrGdmFGBjpmstdQeFRHKx + 2Nn5f73+f61E7iLCxDnrjvQBaubtNypAxyRzg0q263DMsJIzzyc1mwyDeSD82e9XIGUIrSyBNw+X + 2+tAD3tSpcFvufrVZbdL2XbnDdjnGKnhs2nkYtcIEJ6461HMiJIApBVe5HWgB8mmtpzDzSrrkZYU + 65mRGYoBgirEkCStiJlC7c5IqjLNsYhtu0d6AKkshbAZcAdc81Gdwb5SD6cVZjYy5WXBVu/pWppn + h63urfdLdxR47MDk0AYjnhehxntVq11OVANuTj8q2/8AhBZ7mwkm00CYKQBtHXrWe+kTWS7J4zE+ + OQ1ACQX/ANrkC3DD0wODV280KQwM0jxheueKdZWcCrvkjYYHUHvRe6jFLapHtLKeDjg0AVrDQ5xd + xuhIUEMHx8pH1roZtH+2W+dPIbHDMOcms+81YNoqWltlFKhQD1HNP0e5udHsHFkcyMRkDoaALUPh + aa1n8yUgqRgjPOO/eq+reDkvHzoQYIB85JzzW5HBLqWmCSWQJM3UEdB3/Sk0S3uNPmIkBlgJyXAw + o/Ci4EHh3QYfDsfm3mHklGGLdFqS91HSYpvMw0jjkhTx/KqXjLUg8hihYiMn746H6Vg+QYxuV9vH + 1oA3xrem38TNe28rqp+VUyD+gpbTU7O6ylvEYoEBPzjDAjp2HeuUk1aeyfNqMH+8BTrvVhqEAMuP + O7n1oA3X1Q3U0klp5S7OGHFZt7rj4DwxlTJ6riqMTiDZsHTn6/WpbfU5EP8AxMVMqdFIOMfWgCZb + lpEO/GDgn9K6bwZpktjcC7lUsAMYPvj/AArBi0lrpc2sqbZsHbjkV20SvDp8UUZBcDp60AY+ueIZ + dIu3Frh0lbD+YNxAPXBPSqLrpuunyNPBSSM7mZyQpJ/KtWQ2uqvNDcjypQjAFjnJx0rhNYhntbvy + 7jcucgIe9AEUMOy5ImYgg4xViVVa4UFSoToc9a6DxZoEdqv2rTsHzDlx/dFcujFpG27vlPGe9AEi + anPpV359o7b143jqo/yP0rWs/FSavF9l1JltlB3tOerd+axl3XGfMXC9896iu7UbtyYIxg0AdTc2 + Vrqe3+zZxIF4Uj+I1S1Hwpexu0kts8aL7Vg2t9JZ8REjJ+UD+Guh0TxjeaW3/EwAuFAxh260AY8y + ujfLkBOCOuabHcqgCxYAbrz0rsbSysfHdzks1rO33Y0AwTWd4h+D2r6M5mmt0ER5D85P1oAxLfWZ + LSYrbnAb5eKnudVnyELFkHOcCqUmjzRzBWyD9K6W38JtLo6TtkLzmgDHtryGZiZUDZqDU1Vl3wp8 + g+9jsf8AOKmGnw2cpE8jFR1I7VdGjRXMQa0kdoSPmHrQBn6bYnWz5NydjgZVgORWeztBK8ZBJQld + x6nFdZ4ZtoNI1QPI7O+OB7VX8faO9rdC7ESrC4BJHqaAOcgUTtuORiraW0M9yiXLAIeoPc+1RWar + u6Haxq7e6ekEZkBGzGVz1ptgVprUw3ku3iJDgDPUYFEzAwZRN2CDgUw3JEkezD7+xolvytwn2pVV + RkADv060gLVlMk4aLIDHp7+1Vbu1+yzgThiHOOelElyIZl8v5CDkVtxWkGtaYs0bMblCcr/KgDCe + 3LzsN20L2HepUQJnHI9KsX+gT29pHKCd79qWw0u4aPcwU4796AL+meIr2G1aDSbiWHOMhR1qxZXz + xXBl1n/iYBBlg/FR6VZW1nciS9mdJADgYGO1Q3pIOOu5hz60AO1vxLDqluP7Pt47eJSQ2KzvtiSg + eWuPpU89gsfzH5cc+1ZaSpbXRZT8tAGjjz237gNuPwrc0O48uUPOM4GBXORXC3HmJD1bB/QVZivZ + fLwp+71oA6fVfEiwXC+UBGjfKTj14qZbi7gtJWjkY2zx5C9s4rnbCRdZiaOUkFQTke3P9KbYa1c6 + XcBARLEWxhzwBU2AotqzH5Ls5YdFPOKmiu1KgxfvCOqHrXTL4EXxLbl9MO6bGRkYzXPal4TuNLu2 + ju/3csfUD9KoDO19yChhO3OcqO1VoZEUbHVckZL9x3q09s8a5uDkZxUDWX2i4OzgHvQBLCwkwyEF + c4z6VNDZm7utkROCfwqCzAhuGRhhV/WtR5okjjkQ7ST2oAlSRtMdUjHzR1p2OuOI2Ly4kHQViS3K + iYBMsW5zSNF9klEjPnPSgC1dzm4uVKSMZd4JP41oeJPD8+r6ZHLbwmW5H3yCMqvr/Os6xu/tDfvU + CqSOfWuj0yf7OxLO2CAG9x6UAZs6vcIqSiVw3GQMisR7RVvpFkGFU46e1dN4c1hYmCXm0quDIO9c + 54quVl16drdDHGzZX6UAV5bTzWIi4Ws6/DQEoQSpI5q9BfywxkS7WU9OOlMa3F8hG7bj5sn86AKc + ErggKVA96lFwLcYHX3NQPAHnYD5e26pAnluA/JoAu6JevFqsEqs4YN0HQV39p8aL+CJVnWKWOP5c + OAf6VwCzrbxAIMMefpT48zEFD9RQB6hZ+PNE8YqsfiJFt5GOC0abcH6ioPF+i2/hiGK50xmuLOQ4 + AjO9s/T8a8wlzLIdxKkHIwcc1s6R43vdJi2xurxsdriQbto9RnpQBal1C1urtzcIVjfqu3FRMNM8 + zbpplViehyAKnuU0/X4N+ixtFdR/67e2fN+g4xzWPcWzWFyDL8gP3Qw+9+NAGhqulSWzpJHt/wBn + Bzj2NejeHLG28f8Ahox6/HsmA2DHBGO9eTrrksUTKSOD0Par+n/EnVdMRVsZYgpHIK9u9KwEvjn4 + eTeF9UY2Jie3HI+bJFc6b6eMkt909j2rsrTxpYa7bGHWYpXlc8Ord/yrOu/B8gEjQul3Ao6RjLL9 + cGhaAcu0skr7mK8HtTjEAcMMk881Zm0l7JXxg7uQBywqqzysygDBPr1qgHSWqzANL6UunXjWBOxW + KsaZcggbu4HSlindrf5ANxNIDqblPteiWrESNC2fujJ7Vd0bRY7KLfZswWYZYSdT2/pWJ4Q8ST21 + 1b2krIYj8pBFdd4k024ht0nsdpjA4AHNAHO6npkSs2SwPase6ieJcSYdenB+atGbWykgF9G2cHvi + qGqMxiWW0GFyCSRnFAFeSN4yGiLE9we1QXYEhzMo+bnAqaC9YzbpSGY8CoL/ACwDQ80AV1mxdJwQ + q9h1qd71WHU/QdqgDO0gJAyevFE4WI8dW60AafhzUHt5v3ZAzxVzXNFku/38Odg9KwbK4ELA4z+N + ddourgQKJsMv92gCr4Y8Qy6VGUmkdLcDjn5/8a6vS5tM8SWTG3kkaZeP3xIyfxrmPEuk/ZXF9akG + CY/LHj7tZy38tvcxSwnYw7DpQB0viLwrIigwhcHqAeKxDpbmcgJtKjOfStXRPHgjlEeuAzZ6bf4e + lajX+navE4gZIyQcFmxQBxd5ZPG+9iuDxmqitHGR5oO09M+tdDqmjNsDl90YPBHSsJ4N7uH7dOOt + MByxj+EkE/d5qwYGkUNu+VetUgxVz6gVNAryx7Y84J5PpSAeZWjG8A/Lg1sabqn2hF8wnniqPkK6 + qk/z/TilaEWo/cgqKANPSbRba8zM6MXGDzVPxHYPPOzOOVPy471R03XmSRXlQEHv6VstqaakgJKh + h0X1oA5jBjYrP8uTkA9TQ0qoxLHqPyrQ1+z6TMu104x65/8A1ViSsVc5GdwoAseWbkDyQWC01QVv + S+5WGcbe9OguTFZqIjhxnPHWnWTCO6LyKjPnpQBDfs4n3sMc8Y7VPBKWT922498U7X0RCjRnJmAL + KP4aq2rtA/ycBu5HXFAGkYg0GT8rY5J5qIw5jyMORxU28zwAou5jxj1pnktAzCUlT1xQBHFP/Z8w + dpNsg6ccj8a6jQPFNjqdqbfxJbvPM/yxTE/LF9c1zsNsJ1U3EYIP8VPe1iicCORsnnHTBoAtat4Z + mS92Wn79WBK7aw0ia3uXW4jdChxkjvW/Z+KLjTZFd4hKwyAc44qy+nwazpxEOPNdvMdx1UdTQBzb + AbSNyqGPf+lWvDPiW58IXDtZzOIpRiVVON4qS/0ePcG04/aYV4Z8YwaoPGJrgq2AqnAPY0AdVdww + eJLX7XoxSKfbnyRwzn61zGooyMzsreYpwQTyn+P/ANap9NvX0S4DQtzu7dhW/rel2viWzWfRiPtC + L88a/wAfuaAOQEvyDepIOOamtbFJZWKzrH7Gpk02QRBLgYYHkDtSTaf5LBgM7u1AEVxbS2aiSNfm + xw3St7RfiTLFZi2vUe4VRt44xWJDczTzoLoFgvO096bMomlkaJfI5ztFAG7Jqdlrcm2WNYHA+82C + KidbiCAoVLWzfKoHOawo1dyGO4bQcc9frWppOvSwQLDcDzQSOvbmgCjcWBQsqDYwOTmo44BdAZfG + OeuK1NYdZLjzCdu8dAKzpLYQt+6OKAK88ciXREQ3AY5/Ckmt3dlMoznPSrMU2zJxgD2zSSRmX5kY + gdiO9AFWO3KSDgqMjrXQ6fYuUAjG3HO7rWRawNeSDLYKnHPeunVG0bR4ruTnc20g96AHxn7ZbNA7 + qzgcVzup2s2mzOl0CAT8jYzvrb1TxpZ3tgr6fBFFL/EUqpp+pJqpxeqJAPulucfSgDDfcjgxAqSP + mB60xXXlZFBPXpV2+tms5W2oTnpk1nht0uZCAfTFAG9oOvCJBb6jueJj8qj+Grer6XFCqvHMvHTA + zmuajlMUmWHznoKvQ6tLDEPtKeZnsT0oAkaBVLGX7x54qOG6NvkEEA/rV2dYLi08y3fMhH3e4rMR + mkDLOMkHg9KALcN7vXI4Iq9ZyG5jw7An1rFuWMWMAopxTzqMkIxZAuOpINAD7ZAcg9F6VqaXdRFg + pX5h92sPzRbfKQdvr61c0+4MjDyxsYHkkUAdA2lvdQ+ZcDIPGOuawNY0wWNywjwVbocdK2E1ubTF + +T5gw5yM1Lc2kOqaX5kXMxG4nPT8KAOSUSKu5VGM03aZmRo22k9Tird26Fgp+6hwcVAZfNmCnBVu + mKAJp7N71FDcuOI8d6pJlLlt+d44PoK0dTZLKCI2HmCZQCd33c+1R6iqXKpJBu34+bPQGmBNpzND + bgH7zHjPapLiXMhEvzMRwarQXG+ILcfMP7w7VZjdHj+QgMOmaQCRF7AsVBZO2am2G5t2kIAJ9O1V + 2vzM21l+UU9Cjj5M8eh4NAAIXjUeRl8/pUa6k1hGFtWyG6n+lWYX25Y8dsUs9t5tkVkK7Tz7+tAE + 9l4hAj8q/RUf+Db0P1qZ/DUWrTO0paK9cfLGg+Qn61zc0SeYc53DgVr+HNfk0u623LgwSDaxHLY9 + QaYFa80a60G58vU1VmbqF5AFWdC1k6PqaTW6qyEbSD+FdRJd2s8IikZJbO46MTmRB7nr2/WsrxD4 + QjtohLo+9kHXPb0pAd6uh6Lrekm6hkkQSRgNtQfK/p+dc1f/AAsuGUnSWSVScgynbisHQfGFxpki + RKw8tRyD0z/nNWPFHji/1lFihkCxKMAocUAaNt8NNSt3bzYrYsnT5xTLvwZYQTIuqzlLh/vqigqP + xrk/7QuIwRHcXG4jnMpP9ary3kzhvtUkrSH7p3E0AdXqPgvT1vI47K4kfcCcYAx0/wAar2ngu2uW + ZIJX3pnjHFc3DqUikfPIGHU5PFb2ka3PDe7dPZGGzGW7/wCc0AX7LRLSzcxb3eXrhhxVG78JeVcA + bvvcVfEgudqaoyrOrbiV9Pwpmo311pMnmWmySH3w1AGRrXh6TRfLMq8yfcHGPxqxZ6fpmnmNddml + jlk5+RQRx/8ArqO51ptT3vMwWU9iOF/CsOZHnkIkYu3YnmgDo7qPTtPszcWTu5LcAr1ycVl6p4hk + 1BRbsCEXkCqEGqz20wEWGEZGAeRxVy+vRqV2JpUVJiACQMAUAZ0+mvaNuuz88hwAOmaktbt7C4Ub + c8jvW5rGkp/YUEsRM0nLSf7PFYogSWEF/lJ6CgDWcjXyuMhwOAO9Y09hLbSyKy9+pqzpM9xo90Jr + co2OMMM5ropr2PxBYGK7VVXBbIXG4jnrQByUI8xSADs6HPWpPLIjGxssvr3pxQmcqx+VGwFHenJI + gOF5oAW0jZB5nQnnH6Usnzjrg0rW2/8AeISD1x2pWR5VySNo60AQBX2EzHIXpSQJ5kjOOFpLgrtI + iLFvWi2Y3CFYuoNAEt4myTBBQ46Gq6OyHKjGTzSyyyXUm+/cnHc0+PY42RtuDcDigDS03UzdQlHG + WHFSw3/2CX99lo+hA64NUorOeyG9FJA68VJFaLqNu0hkIlXkgelAF3VtEjvNMF1pKOctyPTFc/bw + tGVeMfMRzW54f119M8yJ2IjlGzk9B/k1p6f4fsmi2xXsUmeP88U7gYV5Et3aQlWCsox+NR2eUnWG + 7bdvrZ1TRY7FXjuQsatzHJ7VkyeXbxnz38xl6NmkBFfiXR3MDKQjHI9xUMV0ijMnNdBZWbeJbUcC + SZU+U454rFu/DF7byNJcW0qxqeeOtAE0EcbI+4nax49qnKNY7CCG46Vjw3DRHO1gtaNrqPnBRKu1 + R0Y80AXYDHPAzlPmzzTWG2Evn8KafMMWIsFfamKxcAyjAHbNAFSeRJpOBg0xrXykVjyp6VLqFv5b + AqwTI6dal02ZZ5VjuMNGentQBJZxXFtFuUZDcitDSPFrwOYrkFkfj6Vl30l7p87RpKRDn92eoIqG + 31gRxk3qMzqRnmgC/wCJtIa2uzLYfMjgEj2rNs70woyIMjPLHtW7Y3y38gkUnGBke1R6p4dS/mNx + obeZgfvIVH3Pf3oAz7W3EmGzgrSSRqszF13+4/hqOOLdGSrk5HO0d6WCUxYaUMYhw4HegCM6TLcy + Ztkd0wckd6jtZZbPiI+aqnlem2tTStXNvcbYZyiSA4QcdMf41Y8Taf8A2dZieGMR7sAkc7s8H+dA + GVJqTT3AKtjIxtrStNVy/kyLuUj1rAlhG4NtKqOc/wB+l+2SpP8AcKMn3s07gdJdeHPtLRS2zpCr + csD171laro72bGSFWZRwzHpQdUe8hTDEMg5xU0N7Pcx7GVpIf4lzSAwlk2yAoevUDpWpa2hvYeTg + 0mo2UM8w8lPs4HUDvRpsFz9oYW6NKB07U0BbjvptGhkgJDRMu01VLRyyIYQSgA3HstVdVMiSlZyx + bPKiksbyS1hdWUmKQ5K0gJpt8UgAw69iKn0/UyJdrdOmKIPIvW/cyLEqj7p4zUEUIEr+blHXJBx1 + oAk1O28q6VoSFVhk1GbZQ25TzUlvcfakIucKAcAnqaWK1cyFkQlB70AJvJdNq5I4+tBcbCnCjv71 + LIVcAowVhxj0qO2t9zkXHKt0bsKAIpbPIHlKWUjk06wgaNiqIBzViF/kKKwBHA9aguI5oX3REk9j + TQErWypGPOGc/pTLTy47gMFyob5fetB7EmcG3G6N8hSTjNWRpgsws/y7ouWB70gKd5dGSRcfKnIP + HFXrHSYL61e4kfyVVcYA61lC7OrxurAKxbIHtUtxfC2sTDA/A49KAEazRmkEw+TqG9as+H7YSTeX + bvu7ccYrIt7qRdobPLc59K6jw9pf2KUXcJBVjuI/z9aALF88MsJh1AiRoPl54Iqt5GmXUG3ABx1x + 0/WneMbGfTryO8VB5d2N6qfTJHP5VBoNtFqUb/b28uU/d2d6AJLPV4dGtP8AQyokHGKgu/Fwu9wl + PXgj0pmpaSmnOxmYEdu5rOht2knZ4FX3oAimiju3AtlAznrVWSAW7OC2HQ/d7VdNjLaMjurbSeMC + s+4WS41BjyEB5zQBcgnk2ARnJbqKZcydmZt3fFVxB+9DRkjHfNWLh/KKGTp/6FQBGLg3C5PzFeBT + LeT5yEzlB0p1zb7wGtzt9RTNhWVQOHPWgDc0iUajbPbTgM5GE9aydTtPKk8sKcDrk9adZX5+0FLc + FZM/K1dPpmgReJLR2nOyZDhQT1z60AYWgXYtrvy5cFXBXA9+OtGpLceH9YIsZ3BwGI4+YHsaNR09 + 9C1ERTFTMjBgE6YyO9S+IoDqHlag5++RGPfGKALelpb+IbtA+Ldk+ZkXofxqHxFpn2Vpv7OXdGOW + 56Vk3GpCBQB8pB429a0bHXN8kX2gKY1ILju1AGakfmFfJXLN0/z+VdZYQG503yda5xyPp/8AqqXw + 2LKJJvsqbjIdwDL936Viarq8u9nhA8sNg88/TFAGrdeFbeWBHscSL/AM9DWRqnhObyS7KUYdfetH + wkx1Gdnm3rECAB6Vu674psYbIRxeZuHBJHWgDzZw2nybQMluDVnT9T2PsJK56Ve1OS1vJ/OhOfXj + pWVdWctu/mJhgTxQBeYrOS0xAxTojJHKHspCQ3GPSqaXCTuqpnf+lTQIJ5XRXwy0AaN7YxzWzT3I + /fSHp6VnS2LI8Yt13kj5ucAU17me4hYbvkHXJ5qvJfDMYDNlevqeaAJTAVJGBuHPFSWuoMN32iNW + UgjOelVo5vNUvg8HGKVollOIG4HNAGhb6dHewhrVy8gPK4qaFTZZRssT1GKzLWd7C5zDlS1a9rq5 + vU2uFAIznuaAK93po2GSIEjqefu1C8QZApc+uBxWnbQpeyCG1OB1cnjmi5sUuTlxgpTQFBAYCWEQ + bjrmmsHvDypH0qYqYGPlk56DPSnWFuz3BN2MCkB0niGK10bw/ExCyMxwhVskH8K5O98SPfWixqPm + AxkjBNEkkz2iQSzgqn3U54rPm4RkY4YEfhQBd0gPBMGnwc8fSpvElpFBIGU5Y4Ix0qjcanIkKBG5 + 7VGzPdIHvF3P9aAHpGtymc4Ira0fU5YYUG7KA5P0rAEgjOFjfHtVqzndD8ilFkGKAPTri4h1fRrW + DVAojmjwjdwPY/XNcJK6aTfubdjhDgc9a19PnbUYLW2upsRJ8o61S8WeH1sryKJ2AeRSUb1oApTX + TXpaQMWJGcdal8PSf6UTcj5WOKz5YW0zgTKZG44Bq4THLpSqj7LhWJdsdfSgDo9e16OGFba0ji3p + wZCBzXOoYZp2N2u0Mecd6Zp12cIbkfIBzTbwRG53W4wp5oAbeWVmgY2ZYeuTVC4SWFAzjdGO5qws + HmK28jaTVi1vhaR+XfRGeJhtVR69jz6dfwpgZEcrPcAp92pl2IzMxLuRwamfSJZCXtnRhnLgcFR6 + VWc7J9mNpbtikAW9w0MheQj5ea3NG1Y2sPmWhCvjuf5Vk7UadY48RseW960rDS11C3b7EMzL3oAt + 6hpn9pZu4GzGq7djH5g2PzpPDsMV/Y3Fveg/uVZl+vNJYRy2KhXfcB972q5aRw310/2eZLbcuCWH + X8qaA4yTeT845B4qaEqjZlVtzflV+80qY31z/Z8T3ENqMs8ZAAGcd6zoZMncEwH6H0pAdDpusLZQ + 7Rjc3ApkFoZJHmY4iAPXpms8R7oh/Gc5HtXQaALbUtGMN6ApPHrzQA/TvEdsdOWD92rRk8gcmud8 + QXkl1cZzlfapr3QP7NujGjfKTlSKzr2Jmdgx/wBX096AIkn8ucBQQjdat/bWMLZKOOnOOKzdjL0P + BoiXe2Cu7vQBpxC0KAyK2488Hiql3LskbaDtbpjrV+3tlubYC2TExGBVe+tJNOAF4PmHNAFO0meG + R1bI9jU0iK23zcbsdagWYO+xOH7mrkMWYcNgkUAQwKGA4JC5pzyFmPlEADt61asYIgSJWA3dOKv6 + zosFpdxPaBGVlG445BwKAMwuWADAbqs6eI/3hl++Pu1cj8NFyrRncAdxb0psElpY37NMhljD4YKe + poAsWmm/aIjKknlsvUnoalhtHLcbiueucA1Uu9UMs8wt4SsOfkUnkCrOmXcotj9rkV0HSLnmgDoD + 4JSXSzPNNFJhdwCkZX9a5+K9gD+XPgDdjNTpez6ZZywwPskcZbk/KK5qZ2llPmvvYnrQATr8zE5D + N1zxRbou7951anhZNYuUVFw7dvSp59IltXdZ1IZKAGvpLNGfLAfufaqDCSKUEkgdMkVd07VWs7oG + XLL0x60+7ePUjyCpByMUAV3bBGxsk1ZikV4gAMkHOKpzW5SUmN849qjjnlil3KODxj0oA6KykW7t + yJW8pk4BFdxrGhwax4TS5JWWaEBEY9QDn/CvNrPUfJmBcZDHLV0s2vsfDMwt2ZYy4z7cGgDHv9NK + yjfD+8bgYFUNRtTps4S6HlkjIBPU/wCcVeN86xKZmJlyMc5p/ifU5L/RYVmto9wJUyZ5oAy01Dfb + qZV2xnoKbfX6NEv2ZcHHWmPLFJYQx2ZLTL1U1EIJA+2bAJ6Y5oAIboyDb0PU1c8xLkBJLna4Hy44 + 5x06VAbZbdcyZ3elNBXeCRjnOaAG2808N5syYmJ7fx+5q7tW5QCZQso/iqsULT7rXLr6k4xVi0dX + +9kmgBlxpbI7SxqZAoGWz0p+i3txZ3AezJAHXjrWlZ26mFyzEnPC+vStzTLO3vZ1M8Yjwp6Hr0oA + 5/xFqyrIggQKrLlsdc96xpQZ5wySbu2DVnVYQ9/MJCSitxVOQFW4G1aAOm+H3iGPSbie1upBDBqC + CKRugwOfwrI8VWsenazNHZtvs0fEb/3h6j171Elg02N65x6Gt200i18VwwwXcjQ3Fou2NQMiTvye + 3WgDn4riKEhkfKf3h6+9aFlGLeyS8eT5DIMoDnv3FXZ9I0iwhJFxJLMpwY2ACg1TvvISzMs77S5w + EUcUAW9dH9qW6y6ZKBgcgdawoNOu7iWMmNiWOMDtT4Jxb5e1bKuMEHsfWpNM1ZrG4WWFmct0BHSg + CprWivp0u193mMeR6VHa2jmQbVH0zV3WNRkv5mkn5YnjFRJGBMjRMScdKANvR7OO1u4pS+SGGV68 + d61/GnhSHUYReQyqsZXiPI64rK0S5hRNzfePXvWr5w1KIwwucAccUAefW1q8kqiT+WK0RpdzFFuE + bFT0bHBqxrFj/Z87LjDZ/Km2ctw7Kgk3KO3SgDPQPuHmqNynv2rRs7hrhjDIcDqD6VPeafDfWbbC + UnUjav8AeHfn8qsaL4bl2pLcYWJT85PYdzQBq6dfjRtKX7QnmC4JQH07f1rIl0SztbsSrcoQnJQH + qaseJ7mBVT7PIXtDwrYwQ3esOO4RrxvLZmjI+90P5UAXrm881T9lHOeAOareXPH+8BKOB19Kb9rF + pcq0ILDPc8mp7m+S6k3fdKj7vWgB8Gtj7Oq3AZ3fCs7DmorqxQTbl+oAqJJlu4gJMKwIxT3kNq+H + G5/7o7D1zTA7Pwpd6NBrk5vQwMv3Pl+7UnjAwwXX7tFe3l5UjBbHvXP3GnCOxhuo2IL1G+qPcFYX + cknoT/n2pbgVZtGFxZvNbH5VOBk+vt+FZ8lrPakrcqyHGcEYzWidWS3lCxAlVPUdDWxf6pa6nLH/ + AGlH99QoI4wTwKbA45pHEirjk1asbxYZCsoDYH1rV17wyumSKVbeGG4Y6gVk/wBn7UdgCpPc0gLw + aEwtLKMDtWhoNykVwHdd8JGCjDIrDkSW1g2zOhVhkVLo+puSVlKlccYoA6Dxf4PbSLRb21wto7DG + W7ntj61mpKdXtxaOQvlfMCSBuJrqLfWIfEvhg2muKzQoN4CnBJHT9cVyU5hEjNbB0CHABPNAGTPa + fZriQONjqcZ6flUtqqB1SRmMr/dJzWlDaLrEUh1Qbnx+628ZNZE1s9nfctxEccjpQBO9tLcy7Zjw + vfNQ31q9oee3A75qe2Yyzby5OKiutRMsjKQDg4FG4EVvEyfM5xnsD1q5bbzKHBAB9KrCJN4YMd3p + V+wt8szRZUCnYDXsWSGPz7jGI+SMVVuvErXKEWuRk9QMYqXVyLXTUyRmRcmsSC4EAO8D2pAXxbma + IMR8w7+tVdRtkUAT9ew71as7wsF2nFGsKodDOMzHo/YU0rgULe7j098qW545Gaki1FIbwzeYyzfw + EdvyqkyGSfaw+bvRcQLayqyEnAyaQHR6gi6/pXnBER0IGFHzN15rnmlXyTGRuQHByeQau2GrS20G + 9OhO3H1//VWhf6RprXbXmnrMtuYsOjNk78DkfiDQBi2rpHIVQjb1otHPnBZAMAdRVUQiW6Bgyis2 + Buq29q2nXJjn/eDsycUAOLCG8yg9zkcVCzeVIZY+cenekN0LqYRSHAHA9aLMCOTy5BlTyPegCxa6 + ltkL2+ORzxjFWbTXpLSV3Y84+XFVJvLilKjgVFMpAyBxQBq6prEF7bQSzA+ZJ97jpVRGjDbUJAB+ + U+tUywlJUdE6VteHLK3kuoDqQZ0zyAcYFAG3feVo+io90u2d13R/LyR35rm77VZNSmzC5SEj5hnH + 14/Otu+hv/FN3gTWywW4KRqQM4/OsUeFZp5miaVAc9R0oAaXWa0EUWCIjuA9PeqEMbCYM3G77oAr + bi8Gz2YDmeLc3ygev61X1CxnnuTE8TvPb9fKXigDMuIJFlBdtzHnAPSrEF0IwDCm5hw2VNRzxTWt + 0BeKVMnTIxj8KZ/ahtgY49uT7UAX7VH1K63oERVOTxiuu0ex0nS7L7chJkm+R1kwwyPQZrh4JJDw + zbVbk4/OrNpefLsnyyg5UUAf/9k= +END:VCARD diff --git a/dav/SabreDAV/tests/bootstrap.php b/dav/SabreDAV/tests/bootstrap.php new file mode 100644 index 000000000..8dcae248b --- /dev/null +++ b/dav/SabreDAV/tests/bootstrap.php @@ -0,0 +1,22 @@ + + + Sabre/ + + + + + ../lib/ + + ../lib/Sabre.includes.php + ../lib/Sabre/CalDAV/includes.php + ../lib/Sabre/CardDAV/includes.php + ../lib/Sabre/DAVACL/includes.php + ../lib/Sabre/HTTP/includes.php + ../lib/Sabre/DAV/includes.php + ../lib/Sabre/VObject/includes.php + + + + diff --git a/dav/calendar.friendica.fnk.php b/dav/calendar.friendica.fnk.php new file mode 100644 index 000000000..1d4600c6a --- /dev/null +++ b/dav/calendar.friendica.fnk.php @@ -0,0 +1,141 @@ +get_baseurl()); +$path = "/"; +if (strlen($uri["path"]) > 1) { + $path = $uri["path"] . "/"; +} + +define("CALDAV_SQL_DB", ""); +define("CALDAV_SQL_PREFIX", "dav_"); +define("CALDAV_URL_PREFIX", $path . "dav/"); + +define("CALDAV_NAMESPACE_PRIVATE", 1); +define("CALDAV_NAMESPACE_FRIENDICA_NATIVE", 2); + +define("CALDAV_FRIENDICA_MINE", 1); +define("CALDAV_FRIENDICA_CONTACTS", 2); + +define("CARDDAV_NAMESPACE_COMMUNITYCONTACTS", 1); +define("CARDDAV_NAMESPACE_PHONECONTACTS", 2); + +define("CALDAV_DB_VERSION", 1); + +function getCurMicrotime () { + list($usec, $sec) = explode(" ", microtime()); + return sprintf("%14.0f", $sec * 10000 + $usec * 10000); +} // function getCurMicrotime + +function debug_time() { + $cur = getCurMicrotime(); + if ($GLOBALS["debug_time_last"] > 0) { + echo "Zeit: " . ($cur - $GLOBALS["debug_time_last"]) . "
\n"; + } + $GLOBALS["debug_time_last"] = $cur; +} + + +/** + * @param string $username + * @return int|null + */ +function dav_compat_username2id($username = "") +{ + $x = q("SELECT `uid` FROM user WHERE nickname='%s' AND account_removed = 0 AND account_expired = 0", dbesc($username)); + if (count($x) == 1) return $x[0]["uid"]; + return null; +} + +/** + * @param int $id + * @return string + */ +function dav_compat_id2username($id = 0) +{ + $x = q("SELECT `nickname` FROM user WHERE uid = %i AND account_removed = 0 AND account_expired = 0", IntVal($id)); + if (count($x) == 1) return $x[0]["nickname"]; + return ""; +} + +/** + * @return int + */ +function dav_compat_get_curr_user_id() { + $a = get_app(); + return IntVal($a->user["uid"]); +} + + +/** + * @param string $principalUri + * @return int|null + */ +function dav_compat_principal2uid($principalUri = "") +{ + if (strlen($principalUri) == 0) return null; + if ($principalUri[0] == "/") $principalUri = substr($principalUri, 1); + if (strpos($principalUri, "principals/users/") !== 0) return null; + $username = substr($principalUri, strlen("principals/users/")); + return dav_compat_username2id($username); +} + +/** + * @param $text + * @return mixed + */ +function wdcal_parse_text_serverside($text) +{ + return $text; +} + +/** + * @param int $user_id + * @param int $namespace + * @param int $namespace_id + * @return AnimexxCalSource + * @throws Exception + */ +function wdcal_calendar_factory($user_id, $namespace, $namespace_id) +{ + switch ($namespace) { + case CALDAV_NAMESPACE_PRIVATE: + return new AnimexxCalSourcePrivate($user_id, $namespace_id); + case CALDAV_NAMESPACE_FRIENDICA_NATIVE: + return new FriendicaCalSourceEvents($user_id, $namespace_id); + } + throw new Exception("Calendar Namespace not found"); +} + + +/** + */ +function wdcal_create_std_calendars() +{ + $a = get_app(); + if (!local_user()) return; + + $cals = q("SELECT * FROM %s%scalendars WHERE `uid` = %d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], CALDAV_NAMESPACE_PRIVATE); + if (count($cals) == 0) { + $maxid = q("SELECT MAX(`namespace_id`) maxid FROM %s%scalendars WHERE `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE); + if (!$maxid) { + notification("Something went wrong when trying to create your calendar."); + goaway("/"); + killme(); + } + $nextid = IntVal($maxid[0]["maxid"]) + 1; + q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $nextid, $a->user["uid"], dbesc(t("Private Calendar")), dbesc($a->timezone) + ); + } + + $cals = q("SELECT * FROM %s%scalendars WHERE `uid` = %d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], CALDAV_NAMESPACE_FRIENDICA_NATIVE); + if (count($cals) < 2) { + q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_FRIENDICA_NATIVE, CALDAV_FRIENDICA_MINE, $a->user["uid"], dbesc(t("Friendica Events: Mine")), dbesc($a->timezone) + ); + q("INSERT INTO %s%scalendars (`namespace`, `namespace_id`, `uid`, `displayname`, `timezone`, `ctag`) VALUES (%d, %d, %d, '%s', '%s', 1)", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_FRIENDICA_NATIVE, CALDAV_FRIENDICA_CONTACTS, $a->user["uid"], dbesc(t("Friendica Events: Contacts")), dbesc($a->timezone) + ); + } +} diff --git a/dav/colorpicker/README.md b/dav/colorpicker/README.md new file mode 100644 index 000000000..b41ee438a --- /dev/null +++ b/dav/colorpicker/README.md @@ -0,0 +1,90 @@ +## Really Simple Color Picker + +This is a very minimal, yet robust Color Picker based on jQuery. + +For more details check the introductory blog post - http://laktek.com/2008/10/27/really-simple-color-picker-in-jquery/ + +### Usage + +You can either clone [this repo](https://github.com/laktek/really-simple-color-picker) or download the latest build as a zip from here - http://github.com/laktek/really-simple-color-picker/zipball/master + +Color Picker requires jQuery 1.2.6 or higher. Make sure to load it before Color Picker (there's no other dependencies!). +For default styles of the color picker load the CSS file that comes with the plugin. + + ```html + + + + + ``` + +Add a text field to take the color input. + + ```html +
+ ``` + +Then call 'colorPicker' method on the text field when document loads. + + ```html + + ``` + +### Options + +There are several options you can set at the time of binding. + +**Selected color** + +Color Picker will use the value of the input field, which the picker is attached to as the selected color. If not, it will use the color passed with `pickerDefault` property. + + ```javascript + $('#color1').colorPicker({pickerDefault: "ffffff"}); + ``` + +**Color Palette** + +Overrides the default color palette by passing an array of color values. + + ```javascript + $('#color1').colorPicker({colors: ["333333", "111111"]}); + ``` + +**Transparency** + +Enable transparency value as an option. + + ```javascript + $('#color1').colorPicker({transparency: true}); + ``` + +If you want to set an option gloablly (to apply for all color pickers), use: + + ```javascript + $.fn.colorPicker.defaults.colors = ['151337', '111111'] + ``` +### Demo + +Demo can be found at http://laktek.github.com/really-simple-color-picker/demo.html + +### Real-world Examples + +* [CurdBee](http://demo.curdbee.com/settings/branding) +* [Readability](https://www.readability.com/publishers/tools) + +Let us know how you are using Really Simple Color Picker... + +### Contributors + +* Lakshan Perera - http://laktek.com +* Daniel Lacy - http://daniellacy.com + +### Issues & Suggestions + +Please report any bugs or feature requests here: +https://github.com/laktek/really-simple-color-picker/issues + diff --git a/dav/colorpicker/arrow.gif b/dav/colorpicker/arrow.gif new file mode 100644 index 000000000..b9bdca92f Binary files /dev/null and b/dav/colorpicker/arrow.gif differ diff --git a/dav/colorpicker/arrow.png b/dav/colorpicker/arrow.png new file mode 100644 index 000000000..876968dd5 Binary files /dev/null and b/dav/colorpicker/arrow.png differ diff --git a/dav/colorpicker/colorPicker.css b/dav/colorpicker/colorPicker.css new file mode 100644 index 000000000..735d75294 --- /dev/null +++ b/dav/colorpicker/colorPicker.css @@ -0,0 +1,30 @@ +div.colorPicker-picker { + height: 16px; + width: 16px; + padding: 0 !important; + border: 1px solid #ccc; + background: url(arrow.gif) no-repeat top right; + cursor: pointer; + line-height: 16px; +} + +div.colorPicker-palette { + width: 110px; + position: absolute; + border: 1px solid #598FEF; + background-color: #EFEFEF; + padding: 2px; +} + div.colorPicker_hexWrap {width: 100%; float:left } + div.colorPicker_hexWrap label {font-size: 95%; color: #2F2F2F; margin: 5px 2px; width: 25%} + div.colorPicker_hexWrap input {margin: 5px 2px; padding: 0; font-size: 95%; border: 1px solid #000; width: 65%; } + +div.colorPicker-swatch { + height: 12px; + width: 12px; + border: 1px solid #000; + margin: 2px; + float: left; + cursor: pointer; + line-height: 12px; +} diff --git a/dav/colorpicker/demo.html b/dav/colorpicker/demo.html new file mode 100644 index 000000000..38975f8ed --- /dev/null +++ b/dav/colorpicker/demo.html @@ -0,0 +1,93 @@ + + + + +Really Simple Color Picker + + + + + + + + + + + + + +
+ +

Really Simple Color Picker (jQuery)

+ +

More information about this can be found in this blog article.

+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+
+ + + + + diff --git a/dav/colorpicker/jquery.colorPicker.js b/dav/colorpicker/jquery.colorPicker.js new file mode 100644 index 000000000..78bb1291b --- /dev/null +++ b/dav/colorpicker/jquery.colorPicker.js @@ -0,0 +1,307 @@ +/** + * Really Simple Color Picker in jQuery + * + * Licensed under the MIT (MIT-LICENSE.txt) licenses. + * + * Copyright (c) 2008 Lakshan Perera (www.laktek.com) + * Daniel Lacy (daniellacy.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +(function ($) { + /** + * Create a couple private variables. + **/ + var selectorOwner, + activePalette, + cItterate = 0, + templates = { + control : $('
 
'), + palette : $('
'), + swatch : $('
 
'), + hexLabel: $(''), + hexField: $('') + }, + transparent = "transparent", + lastColor; + + /** + * Create our colorPicker function + **/ + $.fn.colorPicker = function (options) { + return this.each(function () { + // Setup time. Clone new elements from our templates, set some IDs, make shortcuts, jazzercise. + var element = $(this), + opts = $.extend({}, $.fn.colorPicker.defaults, options), + defaultColor = $.fn.colorPicker.toHex( + (element.val().length > 0) ? element.val() : opts.pickerDefault + ), + newControl = templates.control.clone(), + newPalette = templates.palette.clone().attr('id', 'colorPicker_palette-' + cItterate), + newHexLabel = templates.hexLabel.clone(), + newHexField = templates.hexField.clone(), + paletteId = newPalette[0].id, + swatch; + + /** + * Build a color palette. + **/ + $.each(opts.colors, function (i) { + swatch = templates.swatch.clone(); + + if (opts.colors[i] === transparent) { + swatch.addClass(transparent).text('X'); + + $.fn.colorPicker.bindPalette(newHexField, swatch, transparent); + + } else { + swatch.css("background-color", "#" + this); + + $.fn.colorPicker.bindPalette(newHexField, swatch); + + } + + swatch.appendTo(newPalette); + }); + + newHexLabel.attr('for', 'colorPicker_hex-' + cItterate); + + newHexField.attr({ + 'id' : 'colorPicker_hex-' + cItterate, + 'value' : defaultColor + }); + + newHexField.bind("keydown", function (event) { + if (event.keyCode === 13) { + $.fn.colorPicker.changeColor($.fn.colorPicker.toHex($(this).val())); + } + if (event.keyCode === 27) { + $.fn.colorPicker.hidePalette(paletteId); + } + }); + + $('
').append(newHexLabel).appendTo(newPalette); + + newPalette.find('.colorPicker_hexWrap').append(newHexField); + + $("body").append(newPalette); + + newPalette.hide(); + + + /** + * Build replacement interface for original color input. + **/ + newControl.css("background-color", defaultColor); + + newControl.bind("click", function () { + $.fn.colorPicker.togglePalette($('#' + paletteId), $(this)); + }); + + element.after(newControl); + + element.bind("change", function () { + element.next(".colorPicker-picker").css( + "background-color", $.fn.colorPicker.toHex($(this).val()) + ); + }); + + // Hide the original input. + element.val(defaultColor).hide(); + + cItterate++; + }); + }; + + /** + * Extend colorPicker with... all our functionality. + **/ + $.extend(true, $.fn.colorPicker, { + /** + * Return a Hex color, convert an RGB value and return Hex, or return false. + * + * Inspired by http://code.google.com/p/jquery-color-utils + **/ + toHex : function (color) { + // If we have a standard or shorthand Hex color, return that value. + if (color.match(/[0-9A-F]{6}|[0-9A-F]{3}$/i)) { + return (color.charAt(0) === "#") ? color : ("#" + color); + + // Alternatively, check for RGB color, then convert and return it as Hex. + } else if (color.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/)) { + var c = ([parseInt(RegExp.$1, 10), parseInt(RegExp.$2, 10), parseInt(RegExp.$3, 10)]), + pad = function (str) { + if (str.length < 2) { + for (var i = 0, len = 2 - str.length; i < len; i++) { + str = '0' + str; + } + } + + return str; + }; + + if (c.length === 3) { + var r = pad(c[0].toString(16)), + g = pad(c[1].toString(16)), + b = pad(c[2].toString(16)); + + return '#' + r + g + b; + } + + // Otherwise we wont do anything. + } else { + return false; + + } + }, + + /** + * Check whether user clicked on the selector or owner. + **/ + checkMouse : function (event, paletteId) { + var selector = activePalette, + selectorParent = $(event.target).parents("#" + selector.attr('id')).length; + + if (event.target === $(selector)[0] || event.target === selectorOwner || selectorParent > 0) { + return; + } + + $.fn.colorPicker.hidePalette(); + }, + + /** + * Hide the color palette modal. + **/ + hidePalette : function (paletteId) { + $(document).unbind("mousedown", $.fn.colorPicker.checkMouse); + + $('.colorPicker-palette').hide(); + }, + + /** + * Show the color palette modal. + **/ + showPalette : function (palette) { + var hexColor = selectorOwner.prev("input").val(); + + palette.css({ + top: selectorOwner.offset().top + (selectorOwner.outerHeight()), + left: selectorOwner.offset().left + }); + + $("#color_value").val(hexColor); + + palette.show(); + + $(document).bind("mousedown", $.fn.colorPicker.checkMouse); + }, + + /** + * Toggle visibility of the colorPicker palette. + **/ + togglePalette : function (palette, origin) { + // selectorOwner is the clicked .colorPicker-picker. + if (origin) { + selectorOwner = origin; + } + + activePalette = palette; + + if (activePalette.is(':visible')) { + $.fn.colorPicker.hidePalette(); + + } else { + $.fn.colorPicker.showPalette(palette); + + } + }, + + /** + * Update the input with a newly selected color. + **/ + changeColor : function (value) { + selectorOwner.css("background-color", value); + + selectorOwner.prev("input").val(value).change(); + + $.fn.colorPicker.hidePalette(); + }, + + /** + * Bind events to the color palette swatches. + */ + bindPalette : function (paletteInput, element, color) { + color = color ? color : $.fn.colorPicker.toHex(element.css("background-color")); + + element.bind({ + click : function (ev) { + lastColor = color; + + $.fn.colorPicker.changeColor(color); + }, + mouseover : function (ev) { + lastColor = paletteInput.val(); + + $(this).css("border-color", "#598FEF"); + + paletteInput.val(color); + }, + mouseout : function (ev) { + $(this).css("border-color", "#000"); + + paletteInput.val(selectorOwner.css("background-color")); + + paletteInput.val(lastColor); + } + }); + } + }); + + /** + * Default colorPicker options. + * + * These are publibly available for global modification using a setting such as: + * + * $.fn.colorPicker.defaults.colors = ['151337', '111111'] + * + * They can also be applied on a per-bound element basis like so: + * + * $('#element1').colorPicker({pickerDefault: 'efefef', transparency: true}); + * $('#element2').colorPicker({pickerDefault: '333333', colors: ['333333', '111111']}); + * + **/ + $.fn.colorPicker.defaults = { + // colorPicker default selected color. + pickerDefault : "FFFFFF", + + // Default color set. + colors : [ + '000000', '993300', '333300', '000080', '333399', '333333', '800000', 'FF6600', + '808000', '008000', '008080', '0000FF', '666699', '808080', 'FF0000', 'FF9900', + '99CC00', '339966', '33CCCC', '3366FF', '800080', '999999', 'FF00FF', 'FFCC00', + 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0', 'FF99CC', 'FFCC99', + 'FFFF99', 'CCFFFF', '99CCFF', 'FFFFFF' + ], + + // If we want to simply add more colors to the default set, use addColors. + addColors : [] + }; + +})(jQuery); diff --git a/dav/colorpicker/jquery.colorPicker.min.js b/dav/colorpicker/jquery.colorPicker.min.js new file mode 100644 index 000000000..c6c2afa8f --- /dev/null +++ b/dav/colorpicker/jquery.colorPicker.min.js @@ -0,0 +1,26 @@ +/** + * Really Simple Color Picker in jQuery + * + * Licensed under the MIT (MIT-LICENSE.txt) licenses. + * + * Copyright (c) 2008 Lakshan Perera (www.laktek.com) + * Daniel Lacy (daniellacy.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */(function(a){var b,c,d=0,e={control:a('
 
'),palette:a('
'),swatch:a('
 
'),hexLabel:a(''),hexField:a('')},f="transparent",g;a.fn.colorPicker=function(b){return this.each(function(){var c=a(this),g=a.extend({},a.fn.colorPicker.defaults,b),h=a.fn.colorPicker.toHex(c.val().length>0?c.val():g.pickerDefault),i=e.control.clone(),j=e.palette.clone().attr("id","colorPicker_palette-"+d),k=e.hexLabel.clone(),l=e.hexField.clone(),m=j[0].id,n;a.each(g.colors,function(b){n=e.swatch.clone(),g.colors[b]===f?(n.addClass(f).text("X"),a.fn.colorPicker.bindPalette(l,n,f)):(n.css("background-color","#"+this),a.fn.colorPicker.bindPalette(l,n)),n.appendTo(j)}),k.attr("for","colorPicker_hex-"+d),l.attr({id:"colorPicker_hex-"+d,value:h}),l.bind("keydown",function(b){b.keyCode===13&&a.fn.colorPicker.changeColor(a.fn.colorPicker.toHex(a(this).val())),b.keyCode===27&&a.fn.colorPicker.hidePalette(m)}),a('
').append(k).appendTo(j),j.find(".colorPicker_hexWrap").append(l),a("body").append(j),j.hide(),i.css("background-color",h),i.bind("click",function(){a.fn.colorPicker.togglePalette(a("#"+m),a(this))}),c.after(i),c.bind("change",function(){c.next(".colorPicker-picker").css("background-color",a.fn.colorPicker.toHex(a(this).val()))}),c.val(h).hide(),d++})},a.extend(!0,a.fn.colorPicker,{toHex:function(a){if(a.match(/[0-9A-F]{6}|[0-9A-F]{3}$/i))return a.charAt(0)==="#"?a:"#"+a;if(!a.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/))return!1;var b=[parseInt(RegExp.$1,10),parseInt(RegExp.$2,10),parseInt(RegExp.$3,10)],c=function(a){if(a.length<2)for(var b=0,c=2-a.length;b0||a.fn.colorPicker.hidePalette()},hidePalette:function(b){a(document).unbind("mousedown",a.fn.colorPicker.checkMouse),a(".colorPicker-palette").hide()},showPalette:function(c){var d=b.prev("input").val();c.css({top:b.offset().top+b.outerHeight(),left:b.offset().left}),a("#color_value").val(d),c.show(),a(document).bind("mousedown",a.fn.colorPicker.checkMouse)},togglePalette:function(d,e){e&&(b=e),c=d,c.is(":visible")?a.fn.colorPicker.hidePalette():a.fn.colorPicker.showPalette(d)},changeColor:function(c){b.css("background-color",c),b.prev("input").val(c).change(),a.fn.colorPicker.hidePalette()},bindPalette:function(c,d,e){e=e?e:a.fn.colorPicker.toHex(d.css("background-color")),d.bind({click:function(b){g=e,a.fn.colorPicker.changeColor(e)},mouseover:function(b){g=c.val(),a(this).css("border-color","#598FEF"),c.val(e)},mouseout:function(d){a(this).css("border-color","#000"),c.val(b.css("background-color")),c.val(g)}})}}),a.fn.colorPicker.defaults={pickerDefault:"FFFFFF",colors:["000000","993300","333300","000080","333399","333333","800000","FF6600","808000","008000","008080","0000FF","666699","808080","FF0000","FF9900","99CC00","339966","33CCCC","3366FF","800080","999999","FF00FF","FFCC00","FFFF00","00FF00","00FFFF","00CCFF","993366","C0C0C0","FF99CC","FFCC99","FFFF99","CCFFFF","99CCFF","FFFFFF"],addColors:[]}})(jQuery) \ No newline at end of file diff --git a/dav/colorpicker/screenshot.png b/dav/colorpicker/screenshot.png new file mode 100644 index 000000000..566378767 Binary files /dev/null and b/dav/colorpicker/screenshot.png differ diff --git a/dav/common/calendar.fnk.php b/dav/common/calendar.fnk.php new file mode 100644 index 000000000..51b7f5e03 --- /dev/null +++ b/dav/common/calendar.fnk.php @@ -0,0 +1,611 @@ +email = $email; + $this->type = $type; + } +} + +class vcard_source_data_homepage +{ + public $homepage, $type; + + function __construct($type, $homepage) + { + $this->homepage = $homepage; + $this->type = $type; + } +} + +class vcard_source_data_telephone +{ + public $telephone, $type; + + function __construct($type, $telephone) + { + $this->telephone = $telephone; + $this->type = $type; + } +} + +class vcard_source_data_socialnetwork +{ + public $nick, $type, $url; + + function __construct($type, $nick, $url) + { + $this->nick = $nick; + $this->type = $type; + $this->url = $url; + } +} + +class vcard_source_data_address +{ + public $street, $street2, $zip, $city, $country, $type; +} + +class vcard_source_data_photo +{ + public $binarydata; + public $width, $height; + public $type; +} + +class vcard_source_data +{ + function __construct($name_first, $name_middle, $name_last) + { + $this->name_first = $name_first; + $this->name_middle = $name_middle; + $this->name_last = $name_last; + } + + public $name_first, $name_middle, $name_last; + public $last_update; + public $picture_data; + + /** @var array|vcard_source_data_telephone[] $telephones */ + public $telephones; + + /** @var array|vcard_source_data_homepage[] $homepages */ + public $homepages; + + /** @var array|vcard_source_data_socialnetwork[] $socialnetworks */ + public $socialnetworks; + + /** @var array|vcard_source_data_email[] $email */ + public $emails; + + /** @var array|vcard_source_data_addresses[] $addresses */ + public $addresses; + + /** @var vcard_source_data_photo */ + public $photo; +} + +; + + +/** + * @param vcard_source_data $vcardsource + * @return string + */ +function vcard_source_compile($vcardsource) +{ + $str = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Friendica//DAV-Plugin//EN\r\n"; + $str .= "N:" . str_replace(";", ",", $vcardsource->name_last) . ";" . str_replace(";", ",", $vcardsource->name_first) . ";" . str_replace(";", ",", $vcardsource->name_middle) . ";;\r\n"; + $str .= "FN:" . str_replace(";", ",", $vcardsource->name_first) . " " . str_replace(";", ",", $vcardsource->name_middle) . " " . str_replace(";", ",", $vcardsource->name_last) . "\r\n"; + $str .= "REV:" . str_replace(" ", "T", $vcardsource->last_update) . "Z\r\n"; + + $item_count = 0; + for ($i = 0; $i < count($vcardsource->homepages); $i++) { + if ($i == 0) $str .= "URL;type=" . $vcardsource->homepages[0]->type . ":" . $vcardsource->homepages[0]->homepage . "\r\n"; + else { + $c = ++$item_count; + $str .= "item$c.URL;type=" . $vcardsource->homepages[0]->type . ":" . $vcardsource->homepages[0]->homepage . "\r\n"; + $str .= "item$c.X-ABLabel:_\$!!\$_\r\n"; + } + } + + if (is_object($vcardsource->photo)) { + $data = base64_encode($vcardsource->photo->binarydata); + $str .= "PHOTO;ENCODING=BASE64;TYPE=" . $vcardsource->photo->type . ":" . $data . "\r\n"; + } + + if (isset($vcardsource->socialnetworks) && is_array($vcardsource->socialnetworks)) foreach ($vcardsource->socialnetworks as $netw) switch ($netw->type) { + case "dfrn": + $str .= "X-SOCIALPROFILE;type=dfrn;x-user=" . $netw->nick . ":" . $netw->url . "\r\n"; + break; + case "facebook": + $str .= "X-SOCIALPROFILE;type=facebook;x-user=" . $netw->nick . ":" . $netw->url . "\r\n"; + break; + case "twitter": + $str .= "X-SOCIALPROFILE;type=twitter;x-user=" . $netw->nick . ":" . $netw->url . "\r\n"; + break; + } + + $str .= "END:VCARD\r\n"; + return $str; +} + + +/** + * @param array $start + * @param array $end + * @param bool $allday + * @return vevent + */ +function dav_create_vevent($start, $end, $allday) +{ + if ($end["year"] < $start["year"] || + ($end["year"] == $start["year"] && $end["month"] < $start["month"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] < $start["day"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] < $start["hour"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] == $start["hour"] && $end["minute"] < $start["minute"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] == $start["hour"] && $end["minute"] == $start["minute"] && $end["second"] < $start["second"]) + ) { + $end = $start; + } // DTEND muss <= DTSTART + + $vevent = new vevent(); + if ($allday) { + $vevent->setDtstart($start["year"], $start["month"], $start["day"], FALSE, FALSE, FALSE, FALSE, array("VALUE"=> "DATE")); + $end = IntVal(mktime(0, 0, 0, $end["month"], $end["day"], $end["year"]) + 3600 * 24); + + // If a DST change occurs on the current day + $end += IntVal(date("Z", ($end - 3600 * 24)) - date("Z", $end)); + + $vevent->setDtend(date("Y", $end), date("m", $end), date("d", $end), FALSE, FALSE, FALSE, FALSE, array("VALUE"=> "DATE")); + } else { + $vevent->setDtstart($start["year"], $start["month"], $start["day"], $start["hour"], $start["minute"], $start["second"], FALSE, array("VALUE"=> "DATE-TIME")); + $vevent->setDtend($end["year"], $end["month"], $end["day"], $end["hour"], $end["minute"], $end["second"], FALSE, array("VALUE"=> "DATE-TIME")); + } + return $vevent; +} + + +/** + * @param int $phpDate (UTC) + * @return string (Lokalzeit) + */ +function wdcal_php2MySqlTime($phpDate) +{ + return date("Y-m-d H:i:s", $phpDate); +} + +/** + * @param string $sqlDate + * @return int + */ +function wdcal_mySql2PhpTime($sqlDate) +{ + $ts = DateTime::createFromFormat("Y-m-d H:i:s", $sqlDate); + return $ts->format("U"); +} + +/** + * @param string $myqlDate + * @return array + */ +function wdcal_mySql2icalTime($myqlDate) +{ + $x = explode(" ", $myqlDate); + $y = explode("-", $x[0]); + $ret = array("year"=> $y[0], "month"=> $y[1], "day"=> $y[2]); + $y = explode(":", $x[1]); + $ret["hour"] = $y[0]; + $ret["minute"] = $y[1]; + $ret["second"] = $y[2]; + return $ret; +} + + +/** + * @param string $str + * @return string + */ +function icalendar_sanitize_string($str = "") +{ + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\n\r", "\n", $str); + $str = str_replace("\r", "\n", $str); + return $str; +} + + +/** + * @param DBClass_friendica_calendars $calendar + * @param DBClass_friendica_calendarobjects $calendarobject + */ +function renderCalDavEntry_data(&$calendar, &$calendarobject) +{ + $a = get_app(); + + $v = new vcalendar(); + $v->setConfig('unique_id', $a->get_hostname()); + $v->parse($calendarobject->calendardata); + $v->sort(); + + $eventArray = $v->selectComponents(2009, 1, 1, date("Y") + 2, 12, 30); + + $start_min = $end_max = ""; + + $allday = $summary = $vevent = $rrule = $color = $start = $end = null; + $location = $description = ""; + + foreach ($eventArray as $yearArray) { + foreach ($yearArray as $monthArray) { + foreach ($monthArray as $day => $dailyEventsArray) { + foreach ($dailyEventsArray as $vevent) { + /** @var $vevent vevent */ + $start = ""; + $rrule = "NULL"; + $allday = 0; + + $dtstart = $vevent->getProperty('X-CURRENT-DTSTART'); + if (is_array($dtstart)) { + $start = "'" . $dtstart[1] . "'"; + if (strpos($dtstart[1], ":") === false) $allday = 1; + } else { + $dtstart = $vevent->getProperty('dtstart'); + if (isset($dtstart["day"]) && $dtstart["day"] == $day) { // Mehrtägige Events nur einmal rein + if (isset($dtstart["hour"])) $start = "'" . $dtstart["year"] . "-" . $dtstart["month"] . "-" . $dtstart["day"] . " " . $dtstart["hour"] . ":" . $dtstart["minute"] . ":" . $dtstart["secont"] . "'"; + else { + $start = "'" . $dtstart["year"] . "-" . $dtstart["month"] . "-" . $dtstart["day"] . " 00:00:00'"; + $allday = 1; + } + } + } + + $dtend = $vevent->getProperty('X-CURRENT-DTEND'); + if (is_array($dtend)) { + $end = "'" . $dtend[1] . "'"; + if (strpos($dtend[1], ":") === false) $allday = 1; + } else { + $dtend = $vevent->getProperty('dtend'); + if (isset($dtend["hour"])) $end = "'" . $dtend["year"] . "-" . $dtend["month"] . "-" . $dtend["day"] . " " . $dtend["hour"] . ":" . $dtend["minute"] . ":" . $dtend["second"] . "'"; + else { + $end = "'" . $dtend["year"] . "-" . $dtend["month"] . "-" . $dtend["day"] . " 00:00:00' - INTERVAL 1 SECOND"; + $allday = 1; + } + } + $summary = $vevent->getProperty('summary'); + $description = $vevent->getProperty('description'); + $location = $vevent->getProperty('location'); + $rrule_prob = $vevent->getProperty('rrule'); + if ($rrule_prob != null) { + $rrule = $vevent->createRrule(); + $rrule = "'" . dbesc($rrule) . "'"; + } + $color_ = $vevent->getProperty("X-ANIMEXX-COLOR"); + $color = (is_array($color_) ? $color_[1] : "NULL"); + + if ($start_min == "" || preg_replace("/[^0-9]/", "", $start) < preg_replace("/[^0-9]/", "", $start_min)) $start_min = $start; + if ($end_max == "" || preg_replace("/[^0-9]/", "", $end) > preg_replace("/[^0-9]/", "", $start_min)) $end_max = $end; + } + } + } + } + + if ($start_min != "") { + + if ($allday && mb_strlen($end_max) == 12) { + $x = explode("-", str_replace("'", "", $end_max)); + $time = mktime(0, 0, 0, IntVal($x[1]), IntVal($x[2]), IntVal($x[0])); + $end_max = date("'Y-m-d H:i:s'", ($time - 1)); + } + + q("INSERT INTO %s%sjqcalendar (`uid`, `namespace`, `namespace_id`, `ical_uri`, `Subject`, `Location`, `Description`, `StartTime`, `EndTime`, `IsAllDayEvent`, `RecurringRule`, `Color`) + VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', %s, %s, %d, '%s', '%s')", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, + IntVal($calendar->uid), IntVal($calendarobject->namespace), IntVal($calendarobject->namespace_id), dbesc($calendarobject->uri), dbesc($summary), + dbesc($location), dbesc(str_replace("\\n", "\n", $description)), $start_min, $end_max, IntVal($allday), dbesc($rrule), dbesc($color) + ); + + foreach ($vevent->components as $comp) { + /** @var $comp calendarComponent */ + $trigger = $comp->getProperty("TRIGGER"); + $sql_field = ($trigger["relatedStart"] ? $start : $end); + $sql_op = ($trigger["before"] ? "DATE_SUB" : "DATE_ADD"); + $num = ""; + $rel_type = ""; + $rel_value = 0; + if (isset($trigger["second"])) { + $num = IntVal($trigger["second"]) . " SECOND"; + $rel_type = "second"; + $rel_value = IntVal($trigger["second"]); + } + if (isset($trigger["minute"])) { + $num = IntVal($trigger["minute"]) . " MINUTE"; + $rel_type = "minute"; + $rel_value = IntVal($trigger["minute"]); + } + if (isset($trigger["hour"])) { + $num = IntVal($trigger["hour"]) . " HOUR"; + $rel_type = "hour"; + $rel_value = IntVal($trigger["hour"]); + } + if (isset($trigger["day"])) { + $num = IntVal($trigger["day"]) . " DAY"; + $rel_type = "day"; + $rel_value = IntVal($trigger["day"]); + } + if (isset($trigger["week"])) { + $num = IntVal($trigger["week"]) . " WEEK"; + $rel_type = "week"; + $rel_value = IntVal($trigger["week"]); + } + if (isset($trigger["month"])) { + $num = IntVal($trigger["month"]) . " MONTH"; + $rel_type = "month"; + $rel_value = IntVal($trigger["month"]); + } + if (isset($trigger["year"])) { + $num = IntVal($trigger["year"]) . " YEAR"; + $rel_type = "year"; + $rel_value = IntVal($trigger["year"]); + } + if ($trigger["before"]) $rel_value *= -1; + + if ($rel_type != "") { + $not_date = "$sql_op($sql_field, INTERVAL $num)"; + q("INSERT INTO %s%snotifications (`uid`, `ical_uri`, `rel_type`, `rel_value`, `alert_date`, `notified`) VALUES ('%s', '%s', '%s', '%s', %s, IF(%s < NOW(), 1, 0))", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, + IntVal($calendar->uid), dbesc($calendarobject->uri), dbesc($rel_type), IntVal($rel_value), $not_date, $not_date); + } + } + } +} + + +/** + * + */ +function renderAllCalDavEntries() +{ + q("DELETE FROM %s%sjqcalendar", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); + q("DELETE FROM %s%snotifications", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); + $calendars = q("SELECT * FROM %s%scalendars", CALDAV_SQL_DB, CALDAV_SQL_PREFIX); + $anz = count($calendars); + $i = 0; + foreach ($calendars as $calendar) { + $cal = new DBClass_friendica_calendars($calendar); + $i++; + if (($i % 100) == 0) echo "$i / $anz\n"; + $calobjs = q("SELECT * FROM %s%scalendarobjects WHERE `namespace` = %d AND `namespace_id` = %d", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["namespace"]), IntVal($calendar["namespace_id"])); + foreach ($calobjs as $calobj) { + $obj = new DBClass_friendica_calendarobjects($calobj); + renderCalDavEntry_data($cal, $obj); + } + } +} + + +/** + * @param string $uri + * @return bool + */ +function renderCalDavEntry_uri($uri) +{ + q("DELETE FROM %s%sjqcalendar WHERE `ical_uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri)); + q("DELETE FROM %s%snotifications WHERE `ical_uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri)); + + $calobj = q("SELECT * FROM %s%scalendarobjects WHERE `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri)); + if (count($calobj) == 0) return false; + $cal = new DBClass_friendica_calendarobjects($calobj[0]); + $calendars = q("SELECT * FROM %s%scalendars WHERE `namespace`=%d AND `namespace_id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($cal->namespace), IntVal($cal->namespace_id)); + $calendar = new DBClass_friendica_calendars($calendars[0]); + renderCalDavEntry_data($calendar, $cal); + return true; +} + + +/** + * @param $user_id + * @return array|DBClass_friendica_calendars[] + */ +function dav_getMyCals($user_id) +{ + $d = q("SELECT * FROM %s%scalendars WHERE `uid` = %d ORDER BY `calendarorder` ASC", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($user_id), CALDAV_NAMESPACE_PRIVATE + ); + $cals = array(); + foreach ($d as $e) $cals[] = new DBClass_friendica_calendars($e); + return $cals; +} + + +/** + * @param mixed $obj + * @return string + */ +function wdcal_jsonp_encode($obj) +{ + $str = json_encode($obj); + if (isset($_REQUEST["callback"])) { + $str = $_REQUEST["callback"] . "(" . $str . ")"; + } + return $str; +} + + +/** + * @param string $day + * @param int $weekstartday + * @param int $num_days + * @param string $type + * @return array + */ +function wdcal_get_list_range_params($day, $weekstartday, $num_days, $type) +{ + $phpTime = IntVal($day); + switch ($type) { + case "month": + $st = mktime(0, 0, 0, date("m", $phpTime), 1, date("Y", $phpTime)); + $et = mktime(0, 0, -1, date("m", $phpTime) + 1, 1, date("Y", $phpTime)); + break; + case "week": + //suppose first day of a week is monday + $monday = date("d", $phpTime) - date('N', $phpTime) + 1; + //echo date('N', $phpTime); + $st = mktime(0, 0, 0, date("m", $phpTime), $monday, date("Y", $phpTime)); + $et = mktime(0, 0, -1, date("m", $phpTime), $monday + 7, date("Y", $phpTime)); + break; + case "multi_days": + //suppose first day of a week is monday + $monday = date("d", $phpTime) - date('N', $phpTime) + $weekstartday; + //echo date('N', $phpTime); + $st = mktime(0, 0, 0, date("m", $phpTime), $monday, date("Y", $phpTime)); + $et = mktime(0, 0, -1, date("m", $phpTime), $monday + $num_days, date("Y", $phpTime)); + break; + case "day": + $st = mktime(0, 0, 0, date("m", $phpTime), date("d", $phpTime), date("Y", $phpTime)); + $et = mktime(0, 0, -1, date("m", $phpTime), date("d", $phpTime) + 1, date("Y", $phpTime)); + break; + default: + return array(0, 0); + } + return array($st, $et); +} + + +/** + * + */ +function wdcal_print_feed($base_path = "") +{ + $user_id = dav_compat_get_curr_user_id(); + $cals = array(); + if (isset($_REQUEST["cal"])) foreach ($_REQUEST["cal"] as $c) { + $x = explode("-", $c); + $calendarSource = wdcal_calendar_factory($user_id, $x[0], $x[1]); + $calp = $calendarSource->getPermissionsCalendar($user_id); + if ($calp["read"]) $cals[] = $calendarSource; + } + + $ret = null; + /** @var $cals array|AnimexxCalSource[] */ + + $method = $_GET["method"]; + switch ($method) { + case "add": + $cs = null; + foreach ($cals as $c) if ($cs == null) { + $x = $c->getPermissionsCalendar($user_id); + if ($x["read"]) $cs = $c; + } + if ($cs == null) { + echo wdcal_jsonp_encode(array('IsSuccess' => false, + 'Msg' => t('No access'))); + killme(); + } + try { + $start = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarStartTime"])); + $end = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarEndTime"])); + $newuri = $cs->addItem($start, $end, $_REQUEST["CalendarTitle"], $_REQUEST["IsAllDayEvent"]); + $ret = array( + 'IsSuccess' => true, + 'Msg' => 'add success', + 'Data' => $newuri, + ); + + } catch (Exception $e) { + $ret = array( + 'IsSuccess' => false, + 'Msg' => $e->__toString(), + ); + } + break; + case "list": + $weekstartday = (isset($_REQUEST["weekstartday"]) ? IntVal($_REQUEST["weekstartday"]) : 1); // 1 = Monday + $num_days = (isset($_REQUEST["num_days"]) ? IntVal($_REQUEST["num_days"]) : 7); + $ret = null; + + $date = wdcal_get_list_range_params($_REQUEST["showdate"], $weekstartday, $num_days, $_REQUEST["viewtype"]); + $ret = array(); + $ret['events'] = array(); + $ret["issort"] = true; + $ret["start"] = $date[0]; + $ret["end"] = $date[1]; + $ret['error'] = null; + + foreach ($cals as $c) { + $events = $c->listItemsByRange($date[0], $date[1], $base_path); + $ret["events"] = array_merge($ret["events"], $events); + } + + $tmpev = array(); + foreach ($ret["events"] as $e) { + if (!isset($tmpev[$e["start"]])) $tmpev[$e["start"]] = array(); + $tmpev[$e["start"]][] = $e; + } + ksort($tmpev); + $ret["events"] = array(); + foreach ($tmpev as $e) foreach ($e as $f) $ret["events"][] = $f; + + break; + case "update": + $found = false; + $start = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarStartTime"])); + $end = wdcal_mySql2icalTime(wdcal_php2MySqlTime($_REQUEST["CalendarEndTime"])); + foreach ($cals as $c) try { + $permissions_item = $c->getPermissionsItem($user_id, $_REQUEST["calendarId"], ""); + if ($permissions_item["write"]) { + $c->updateItem($_REQUEST["calendarId"], $start, $end); + $found = true; + } + } catch (Exception $e) { + } + ; + + if ($found) { + $ret = array( + 'IsSuccess' => true, + 'Msg' => 'Succefully', + ); + } else { + echo wdcal_jsonp_encode(array('IsSuccess' => false, + 'Msg' => t('No access'))); + killme(); + } + + try { + } catch (Exception $e) { + $ret = array( + 'IsSuccess' => false, + 'Msg' => $e->__toString(), + ); + } + break; + case "remove": + $found = false; + foreach ($cals as $c) try { + $permissions_item = $c->getPermissionsItem($user_id, $_REQUEST["calendarId"], ""); + if ($permissions_item["write"]) $c->removeItem($_REQUEST["calendarId"]); + } catch (Exception $e) { + } + + if ($found) { + $ret = array( + 'IsSuccess' => true, + 'Msg' => 'Succefully', + ); + } else { + echo wdcal_jsonp_encode(array('IsSuccess' => false, + 'Msg' => t('No access'))); + killme(); + } + break; + } + echo wdcal_jsonp_encode($ret); + killme(); +} + diff --git a/dav/common/dav_caldav_backend.inc.php b/dav/common/dav_caldav_backend.inc.php new file mode 100644 index 000000000..c09d48d90 --- /dev/null +++ b/dav/common/dav_caldav_backend.inc.php @@ -0,0 +1,174 @@ + IntVal($obj["id"]), + "calendardata" => $obj["calendardata"], + "uri" => $obj["uri"], + "lastmodified" => $obj["lastmodified"], + "calendarid" => $calendarId, + "etag" => $obj["etag"], + "size" => IntVal($obj["size"]), + ); + } + return $ret; + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param string $calendarId + * @param string $objectUri + * @throws Sabre_DAV_Exception_FileNotFound + * @return array + */ + function getCalendarObject($calendarId, $objectUri) + { + $x = explode("-", $calendarId); + + $o = q("SELECT * FROM %s%scalendarobjects WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($objectUri)); + if (count($o) > 0) { + $o[0]["calendarid"] = $calendarId; + $o[0]["calendardata"] = str_ireplace("Europe/Belgrade", "Europe/Berlin", $o[0]["calendardata"]); + return $o[0]; + } else throw new Sabre_DAV_Exception_FileNotFound($calendarId . " / " . $objectUri); + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return null|string|void + */ + function createCalendarObject($calendarId, $objectUri, $calendarData) + { + $x = explode("-", $calendarId); + + q("INSERT INTO %s%scalendarobjects (`namespace`, `namespace_id`, `uri`, `calendardata`, `lastmodified`, `etag`, `size`) VALUES (%d, %d, '%s', '%s', NOW(), '%s', %d)", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, + IntVal($x[0]), IntVal($x[1]), dbesc($objectUri), addslashes($calendarData), md5($calendarData), strlen($calendarData) + ); + + $this->increaseCalendarCtag($x[0], $x[1]); + renderCalDavEntry_uri($objectUri); + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return null|string|void + */ + function updateCalendarObject($calendarId, $objectUri, $calendarData) + { + $x = explode("-", $calendarId); + + q("UPDATE %s%scalendarobjects SET `calendardata` = '%s', `lastmodified` = NOW(), `etag` = '%s', `size` = %d WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($calendarData), md5($calendarData), strlen($calendarData), IntVal($x[0]), IntVal($x[1]), dbesc($objectUri)); + + $this->increaseCalendarCtag($x[0], $x[1]); + renderCalDavEntry_uri($objectUri); + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + function deleteCalendarObject($calendarId, $objectUri) + { + $x = explode("-", $calendarId); + + q("DELETE FROM %s%scalendarobjects WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($objectUri) + ); + + $this->increaseCalendarCtag($x[0], $x[1]); + renderCalDavEntry_uri($objectUri); + } +} diff --git a/dav/common/dav_caldav_backend_common.inc.php b/dav/common/dav_caldav_backend_common.inc.php new file mode 100644 index 000000000..c1152dc01 --- /dev/null +++ b/dav/common/dav_caldav_backend_common.inc.php @@ -0,0 +1,170 @@ + 'displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', + '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', + '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + ); + + + abstract public function getNamespace(); + abstract public function getCalUrlPrefix(); + + /** + * @param int $namespace + * @param int $namespace_id + */ + protected function increaseCalendarCtag($namespace, $namespace_id) { + $namespace = IntVal($namespace); + $namespace_id = IntVal($namespace_id); + + q("UPDATE %s%scalendars SET `ctag` = `ctag` + 1 WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $namespace, $namespace_id); + } + + + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principaluri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + public function getCalendarsForUser($principalUri) + { + list(,$name) = Sabre_DAV_URLUtil::splitPath($principalUri); + $user_id = dav_compat_username2id($name); + + $cals = q("SELECT * FROM %s%scalendars WHERE `uid`=%d AND `namespace` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $user_id, $this->getNamespace()); + $ret = array(); + foreach ($cals as $cal) { + $dat = array( + "id" => $cal["namespace"] . "-" . $cal["namespace_id"], + "uri" => $this->getCalUrlPrefix() . "-" . $cal["namespace_id"], + "principaluri" => $principalUri, + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $cal['ctag']?$cal['ctag']:'0', + "calendar_class" => "Sabre_CalDAV_Calendar", + ); + foreach ($this->propertyMap as $key=>$field) $dat[$key] = $cal[$field]; + + $ret[] = $dat; + } + + return $ret; + } + + /** + * Updates properties for a calendar. + * + * The mutations array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existent property is always successful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param mixed $calendarId + * @param array $mutations + * @return bool|array + */ + public function updateCalendar($calendarId, array $mutations) { + + $newValues = array(); + $result = array( + 200 => array(), // Ok + 403 => array(), // Forbidden + 424 => array(), // Failed Dependency + ); + + $hasError = false; + + foreach($mutations as $propertyName=>$propertyValue) { + + // We don't know about this property. + if (!isset($this->propertyMap[$propertyName])) { + $hasError = true; + $result[403][$propertyName] = null; + unset($mutations[$propertyName]); + continue; + } + + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + + } + + // If there were any errors we need to fail the request + if ($hasError) { + // Properties has the remaining properties + foreach($mutations as $propertyName=>$propertyValue) { + $result[424][$propertyName] = null; + } + + // Removing unused statuscodes for cleanliness + foreach($result as $status=>$properties) { + if (is_array($properties) && count($properties)===0) unset($result[$status]); + } + + return $result; + + } + + $x = explode("-", $calendarId); + + $this->increaseCalendarCtag($x[0], $x[1]); + + $valuesSql = array(); + foreach($newValues as $fieldName=>$value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'"; + if (count($valuesSql) > 0) { + q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `namespace` = %d AND `namespace_id` = %d", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]) + ); + } + + return true; + + } + +} \ No newline at end of file diff --git a/dav/common/dav_caldav_root.inc.php b/dav/common/dav_caldav_root.inc.php new file mode 100644 index 000000000..fe87900d1 --- /dev/null +++ b/dav/common/dav_caldav_root.inc.php @@ -0,0 +1,69 @@ +caldavBackends = $caldavBackends; + + } + + /** + * Returns the nodename + * + * We're overriding this, because the default will be the 'principalPrefix', + * and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT + * + * @return string + */ + public function getName() { + + return Sabre_CalDAV_Plugin::CALENDAR_ROOT; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return \Sabre_CalDAV_AnimexxUserCalendars|\Sabre_DAVACL_IPrincipal + */ + public function getChildForPrincipal(array $principal) { + + return new Sabre_CalDAV_AnimexxUserCalendars($this->principalBackend, $this->caldavBackends, $principal['uri']); + + } + +} diff --git a/dav/common/dav_carddav_backend_std.inc.php b/dav/common/dav_carddav_backend_std.inc.php new file mode 100644 index 000000000..c63a74f30 --- /dev/null +++ b/dav/common/dav_carddav_backend_std.inc.php @@ -0,0 +1,313 @@ + CARDDAV_NAMESPACE_PHONECONTACTS . "-" . $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{DAV:}displayname' => $row['displayname'], + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], + '{http://calendarserver.org/ns/}getctag' => $row['ctag'], + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => + new Sabre_CardDAV_Property_SupportedAddressData(), + ); + } + + return $addressBooks; + + } + + + /** + * Updates an addressbook's properties + * + * See Sabre_DAV_IProperties for a description of the mutations array, as + * well as the return value. + * + * @param mixed $addressBookId + * @param array $mutations + * @throws Sabre_DAV_Exception_Forbidden + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateAddressBook($addressBookId, array $mutations) + { + $x = explode("-", $addressBookId); + + $updates = array(); + + foreach ($mutations as $property=> $newValue) { + + switch ($property) { + case '{DAV:}displayname' : + $updates['displayname'] = $newValue; + break; + case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' : + $updates['description'] = $newValue; + break; + default : + // If any unsupported values were being updated, we must + // let the entire request fail. + return false; + } + + } + + // No values are being updated? + if (!$updates) { + return false; + } + + $query = 'UPDATE ' . CALDAV_SQL_DB . CALDAV_SQL_PREFIX . 'addressbooks_phone SET ctag = ctag + 1 '; + foreach ($updates as $key=> $value) { + $query .= ', `' . dbesc($key) . '` = ' . dbesc($key) . ' '; + } + $query .= ' WHERE id = ' . IntVal($x[1]); + q($query); + + return true; + + } + + /** + * Creates a new address book + * + * @param string $principalUri + * @param string $url Just the 'basename' of the url. + * @param array $properties + * @throws Sabre_DAV_Exception_BadRequest + * @return void + */ + public function createAddressBook($principalUri, $url, array $properties) + { + $values = array( + 'displayname' => null, + 'description' => null, + 'principaluri' => $principalUri, + 'uri' => $url, + ); + + foreach ($properties as $property=> $newValue) { + + switch ($property) { + case '{DAV:}displayname' : + $values['displayname'] = $newValue; + break; + case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' : + $values['description'] = $newValue; + break; + default : + throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property); + } + + } + + q("INSERT INTO %s%saddressbooks_phone (uri, displayname, description, principaluri, ctag) VALUES ('%s', '%s', '%s', '%s', 1)", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($values["uri"]), dbesc($values["displayname"]), dbesc($values["description"]), dbesc($values["principaluri"]) + ); + } + + /** + * Deletes an entire addressbook and all its contents + * + * @param int $addressBookId + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function deleteAddressBook($addressBookId) + { + $x = explode("-", $addressBookId); + q("DELETE FROM %s%scards WHERE namespace = %d AND namespace_id = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1])); + q("DELETE FROM %s%saddressbooks_phone WHERE id = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); + } + + /** + * Returns all cards for a specific addressbook id. + * + * This method should return the following properties for each card: + * * carddata - raw vcard data + * * uri - Some unique url + * * lastmodified - A unix timestamp + * + * It's recommended to also return the following properties: + * * etag - A unique etag. This must change every time the card changes. + * * size - The size of the card in bytes. + * + * If these last two properties are provided, less time will be spent + * calculating them. If they are specified, you can also ommit carddata. + * This may speed up certain requests, especially with large cards. + * + * @param string $addressbookId + * @return array + */ + public function getCards($addressbookId) + { + $x = explode("-", $addressbookId); + + $r = q('SELECT id, carddata, uri, lastmodified, etag, size, contact FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND manually_deleted = 0', + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]) + ); + if ($r) return $r; + return array(); + } + + /** + * Returns a specfic card. + * + * The same set of properties must be returned as with getCards. The only + * exception is that 'carddata' is absolutely required. + * + * @param mixed $addressBookId + * @param string $cardUri + * @throws Sabre_DAV_Exception_NotFound + * @return array + */ + public function getCard($addressBookId, $cardUri) + { + $x = explode("-", $addressBookId); + $x = q("SELECT id, carddata, uri, lastmodified, etag, size FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND uri = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri)); + if (count($x) == 0) throw new Sabre_DAV_Exception_NotFound(); + return $x[0]; + } + + /** + * Creates a new card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressbooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag is for the + * newly created resource, and must be enclosed with double quotes (that + * is, the string itself must contain the double quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param string $addressBookId + * @param string $cardUri + * @param string $cardData + * @throws Sabre_DAV_Exception_Forbidden + * @return string + */ + public function createCard($addressBookId, $cardUri, $cardData) + { + $x = explode("-", $addressBookId); + + $etag = md5($cardData); + q("INSERT INTO %s%scards (carddata, uri, lastmodified, namespace, namespace_id, etag, size) VALUES ('%s', '%s', %d, %d, '%s', %d)", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), dbesc($cardUri), time(), IntVal($x[0]), IntVal($x[1]), $etag, strlen($cardData) + ); + + q('UPDATE %s%saddressbooks_phone SET ctag = ctag + 1 WHERE id = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); + + return '"' . $etag . '"'; + + } + + /** + * Updates a card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressbooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag should + * match that of the updated resource, and must be enclosed with double + * quotes (that is: the string itself must contain the actual quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param string $addressBookId + * @param string $cardUri + * @param string $cardData + * @throws Sabre_DAV_Exception_Forbidden + * @return string|null + */ + public function updateCard($addressBookId, $cardUri, $cardData) + { + $x = explode("-", $addressBookId); + + $etag = md5($cardData); + q("UPDATE %s%scards SET carddata = '%s', lastmodified = %d, etag = '%s', size = %d, manually_edited = 1 WHERE uri = '%s' AND namespace = %d AND namespace_id =%d", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), time(), $etag, strlen($cardData), dbesc($cardUri), IntVal($x[10]), IntVal($x[1]) + ); + + q('UPDATE %s%saddressbooks_phone SET ctag = ctag + 1 WHERE id = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); + + return '"' . $etag . '"'; + } + + /** + * Deletes a card + * + * @param string $addressBookId + * @param string $cardUri + * @throws Sabre_DAV_Exception_Forbidden + * @return bool + */ + public function deleteCard($addressBookId, $cardUri) + { + $x = explode("-", $addressBookId); + + q("DELETE FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND uri = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri)); + q('UPDATE %s%saddressbooks_phone SET ctag = ctag + 1 WHERE id = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); + + return true; + } +} diff --git a/dav/common/dav_carddav_root.inc.php b/dav/common/dav_carddav_root.inc.php new file mode 100644 index 000000000..b896afd31 --- /dev/null +++ b/dav/common/dav_carddav_root.inc.php @@ -0,0 +1,64 @@ +carddavBackends = $carddavBackends; + parent::__construct($principalBackend, $principalPrefix); + + } + + /** + * Returns the name of the node + * + * @return string + */ + public function getName() { + return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT; + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return Sabre_DAVACL_IPrincipal + */ + public function getChildForPrincipal(array $principal) { + return new Sabre_CardDAV_UserAddressBooksMultiBackend($this->carddavBackends, $principal['uri']); + + } + +} diff --git a/dav/common/dav_user_addressbooks.inc.php b/dav/common/dav_user_addressbooks.inc.php new file mode 100644 index 000000000..d430274cc --- /dev/null +++ b/dav/common/dav_user_addressbooks.inc.php @@ -0,0 +1,264 @@ +carddavBackends = $carddavBackends; + $this->principalUri = $principalUri; + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_MethodNotAllowed(); + + } + + /** + * Deletes this object + * + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_MethodNotAllowed(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + public function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return null|string|void + */ + public function createFile($filename, $data=null) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return void + */ + public function createDirectory($filename) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @throws Sabre_DAV_Exception_NotFound + * @todo needs optimizing + * @return \Sabre_CardDAV_AddressBook|\Sabre_DAV_INode + */ + public function getChild($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return $child; + + } + throw new Sabre_DAV_Exception_NotFound('Addressbook with name \'' . $name . '\' could not be found'); + + } + + /** + * Returns a list of addressbooks + * + * @return array|Sabre_DAV_INode[] + */ + public function getChildren() { + + $objs = array(); + foreach ($this->carddavBackends as $backend) { + $addressbooks = $backend->getAddressbooksForUser($this->principalUri); + foreach($addressbooks as $addressbook) { + $objs[] = new Sabre_CardDAV_AddressBook($backend, $addressbook); + } + } + return $objs; + + } + + /** + * Creates a new addressbook + * + * @param string $name + * @param array $resourceType + * @param array $properties + * @throws Sabre_DAV_Exception_InvalidResourceType + * @return void + */ + public function createExtendedCollection($name, array $resourceType, array $properties) { + + if (!in_array('{'.Sabre_CardDAV_Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) { + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection'); + } + $this->carddavBackends[0]->createAddressBook($this->principalUri, $name, $properties); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalUri; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalUri, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalUri, + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/dav/common/dav_user_calendars.inc.php b/dav/common/dav_user_calendars.inc.php new file mode 100644 index 000000000..8ad8da73e --- /dev/null +++ b/dav/common/dav_user_calendars.inc.php @@ -0,0 +1,305 @@ +principalBackend = $principalBackend; + $this->caldavBackends = $caldavBackends; + $this->principalInfo = $principalBackend->getPrincipalByPath($userUri); + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Deletes this object + * + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + public function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return null|string|void + */ + public function createFile($filename, $data=null) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return void + */ + public function createDirectory($filename) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @throws Sabre_DAV_Exception_FileNotFound + * @todo needs optimizing + * @return \Sabre_CalDAV_Calendar|\Sabre_DAV_INode + */ + public function getChild($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return $child; + + } + throw new Sabre_DAV_Exception_FileNotFound('Calendar with name \'' . $name . '\' could not be found'); + + } + + /** + * Checks if a calendar exists. + * + * @param string $name + * @todo needs optimizing + * @return bool + */ + public function childExists($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return true; + + } + return false; + + } + + /** + * Returns a list of calendars + * + * @return array|\Sabre_DAV_INode[] + */ + + public function getChildren() { + $objs = array(); + foreach ($this->caldavBackends as $backend) { + $calendars = $backend->getCalendarsForUser($this->principalInfo["uri"]); + foreach($calendars as $calendar) { + $objs[] = new $calendar["calendar_class"]($this->principalBackend, $backend, $calendar); + } + } + //$objs[] = new Sabre_CalDAV_AnimexxUserZirkelCalendars($this->principalBackend, $this->caldavBackend, $this->username); + return $objs; + + } + + /** + * Creates a new calendar + * + * @param string $name + * @param array $resourceType + * @param array $properties + * @throws Sabre_DAV_Exception_InvalidResourceType + * @return void + */ + public function createExtendedCollection($name, array $resourceType, array $properties) { + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) { + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection'); + } + $this->caldavBackends[0]->createCalendar($this->principalInfo['uri'], $name, $properties); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalInfo['uri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + function getSupportedPrivilegeSet() + { + return null; + } +} diff --git a/dav/common/dbclasses/dbclass.friendica.calendarobjects.class.php b/dav/common/dbclasses/dbclass.friendica.calendarobjects.class.php new file mode 100644 index 000000000..509938b8a --- /dev/null +++ b/dav/common/dbclasses/dbclass.friendica.calendarobjects.class.php @@ -0,0 +1,28 @@ +PRIMARY_KEY) == 1) { + $dbarray_or_id = q("SELECT * FROM %s%s%s WHERE %s=%d", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->SRC_TABLE, $this->PRIMARY_KEY[0], IntVal($dbarray_or_id) + ); + if (count($dbarray_or_id) == 0) throw new Exception("Not found"); + $dbarray_or_id = $dbarray_or_id[0]; + } + if (is_array($dbarray_or_id)) { + foreach ($this->_string_fields as $field) { + $this->$field = $dbarray_or_id[$field]; + } + foreach ($this->_int_fields as $field) { + $this->$field = IntVal($dbarray_or_id[$field]); + } + foreach ($this->_float_fields as $field) { + $this->$field = FloatVal($dbarray_or_id[$field]); + } + } else throw new Exception("Not found"); + } + + /** + * @return array + */ + function toArray() + { + $arr = array(); + foreach ($this->_string_fields as $field) $arr[$field] = $this->$field; + foreach ($this->_int_fields as $field) $arr[$field] = $this->$field; + foreach ($this->_float_fields as $field) $arr[$field] = $this->$field; + return $arr; + } +} diff --git a/dav/common/virtual_cal_source_backend.inc.php b/dav/common/virtual_cal_source_backend.inc.php new file mode 100644 index 000000000..5549a6af8 --- /dev/null +++ b/dav/common/virtual_cal_source_backend.inc.php @@ -0,0 +1,63 @@ +
").appendTo(document.body); + } + var $gridcontainer = $(this); + option = $.extend(def, option); + + //no quickUpdateUrl, dragging disabled. + if (option.quickUpdateUrl == null || option.quickUpdateUrl == "") { + option.enableDrag = false; + } + //template for month and date + var __SCOLLEVENTTEMP = "
" + + "
 
" + + "
{starttime} - {endtime} {icon}
" + + "
{content}
 
" + + "
 
" + + "
 
"; + var __ALLDAYEVENTTEMP = '
' + + '
{extendHTML}
{content}
'; + var __MonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + var __LASSOTEMP = "
 
"; + //for dragging var + var _dragdata; + var _dragevent; + + //clear DOM + clearcontainer(); + + //no height specified in options, we get page height. + if (!option.height) { + option.height = document.documentElement.clientHeight; + } + // + $gridcontainer.css("overflow-y", "visible").height(option.height - 8); + + //populate events data for first display. + if (option.url && option.autoload) { + populate(); + } + else { + //contruct HTML + render(); + } + + //clear DOM + function clearcontainer() { + $gridcontainer.empty(); + } + + + //contruct DOM + function render() { + //params needed + //viewType, showday, events, config + var showday = new Date(option.showday.getFullYear(), option.showday.getMonth(), option.showday.getDate()); + var events = option.eventItems; + var config = { view:option.view, weekstartday:option.weekstartday, color:option.std_color }; + if (option.view == "day" || option.view == "week" || option.view == "multi_days") { + var $dvtec = $gridcontainer.find(".scrolltimeevent"); + if ($dvtec.length > 0) { + option.scroll = $dvtec.scrollTop(); //get scroll bar position + } + } + switch (option.view) { + case "day": + BuildDaysAndWeekView(showday, 1, events, config); + break; + case "week": + BuildDaysAndWeekView(showday, 7, events, config); + break; + case "multi_days": + BuildDaysAndWeekView(showday, option.num_days, events, config); + break; + case "month": + BuildMonthView(showday, events, config); + break; + default: + alert(i18n.xgcalendar["no_implement"]); + break; + } + initevents(option.view); + ResizeView(); + } + + //build day view + function BuildDaysAndWeekView(startday, l, events, config) { + var days = [], + show; + if (l == 1) { + show = dateFormat.call(startday, option.date_format_dm1); + days.push({ display:show, date:startday, day:startday.getDate(), year:startday.getFullYear(), month:startday.getMonth() + 1 }); + option.datestrshow = CalDateShow(days[0].date); + option.vstart = days[0].date; + option.vend = days[0].date; + } + else { + var w = 0; + if (l == 7) { + w = config.weekstartday - startday.getDay(); + if (w > 0) w = w - 7; + } + var ndate; + for (var i = w, j = 0; j < l; i = i + 1, j++) { + ndate = DateAdd("d", i, startday); + show = dateFormat.call(ndate, option.date_format_dm1); + days.push({ display:show, date:ndate, day:ndate.getDate(), year:ndate.getFullYear(), month:ndate.getMonth() + 1 }); + } + option.vstart = days[0].date; + option.vend = days[l - 1].date; + option.datestrshow = CalDateShow(days[0].date, days[l - 1].date); + } + + var allDayEvents = []; + var scrollDayEvents = []; + //get number of all-day events, including more-than-one-day events. + var dM = PrepareEvents(days, events, allDayEvents, scrollDayEvents); + + var $html = $("
"); + $html.append(BuildWT(days, allDayEvents, dM)); + + $gridcontainer.html("").append($html); + + $html = $("
"); + $html.append(BuildDayScollEventContainer(days, scrollDayEvents)); + $gridcontainer.append($html); + + //TODO event handlers + //$gridcontainer.find(".weekViewAllDaywk").click(RowHandler); + } + + //build month view + function BuildMonthView(showday, events, config) { + $gridcontainer.find("*").remove(); + $gridcontainer.append("
"); + var html = []; + //build header + html.push("
"); + html.push(""); + for (var i = config.weekstartday, j = 0; j < 7; i++, j++) { + if (i > 6) i = 0; + html.push("
", __WDAY[i], ""); + } + html.push("
"); + html.push("
"); + $gridcontainer.append(html.join("")); + + var bH = GetMonthViewBodyHeight() - GetMonthViewHeaderHeight(); + var $container = $("
"); + var $body = BuilderMonthBody(showday, config.weekstartday, events, bH); + $container.append($body); + $gridcontainer.append($container); + + $gridcontainer.find(".cal-month-closebtn").click(closeCc); + } + + function closeCc() { + $gridcontainer.find(".cal-month-cc").css("visibility", "hidden"); + } + + //all-day event, including more-than-one-day events + function PrepareEvents(dayarrs, events, allDayEvents, scrolLDayEvents) { + var i, j, k, de, x, y, La, H, D, Ia, + tmp_allday = allDayEvents, + tmp_scrollevents = scrolLDayEvents, + l = dayarrs.length, + el = events.length, + fE = []; + for (j = 0; j < el; j++) { + var sD = events[j]["start"]; + var eD = events[j]["end"]; + var s = {}; + s.event = events[j]; + s.day = sD.getDate(); + s.year = sD.getFullYear(); + s.month = sD.getMonth() + 1; + s.allday = events[j]["is_allday"] == 1; + s.crossday = events[j]["is_moredays"] == 1; + s.reevent = events[j]["is_recurring"] == 1; //Recurring event + s.daystr = [s.year, s.month, s.day].join("/"); + s.st = {}; + s.st.hour = sD.getHours(); + s.st.minute = sD.getMinutes(); + s.st.p = s.st.hour * 60 + s.st.minute; // start time + s.et = {}; + s.et.hour = eD.getHours(); + s.et.minute = eD.getMinutes(); + s.et.p = s.et.hour * 60 + s.et.minute; // end time + fE.push(s); + } + var dMax = 0; + for (i = 0; i < l; i++) { + var da = dayarrs[i]; + tmp_scrollevents[i] = []; + tmp_allday[i] = []; + da.daystr = da.year + "/" + da.month + "/" + da.day; + for (j = 0; j < fE.length; j++) { + if (!fE[j].crossday && !fE[j].allday) { + if (da.daystr == fE[j].daystr) + tmp_scrollevents[i].push(fE[j]); + } + else { + if (da.daystr == fE[j].daystr) { + tmp_allday[i].push(fE[j]); + dMax++; + } + else { + if (i == 0 && da.date >= fE[j].event["start"] && da.date <= fE[j].event["end"])//first more-than-one-day event + { + tmp_allday[i].push(fE[j]); + dMax++; + } + } + } + } + } + var lrdate = dayarrs[l - 1].date; + for (i = 0; i < l; i++) { //to deal with more-than-one-day event + de = tmp_allday[i]; + if (de.length > 0) { // + for (j = 0; j < de.length; j++) { + var end = DateDiff("d", lrdate, de[j].event["end"]) > 0 ? lrdate : de[j].event["end"]; + de[j].colSpan = DateDiff("d", dayarrs[i].date, end) + 1 + } + } + de = null; + } + //for all-day events + for (i = 0; i < l; i++) { + de = tmp_scrollevents[i]; + if (de.length > 0) { + x = []; + y = []; + D = []; + var dl = de.length; + for (j = 0; j < dl; ++j) { + var ge = de[j]; + for (La = ge.st.p, Ia = 0; y[Ia] > La;) Ia++; + ge.PO = Ia; + ge.ne = []; //PO is how many events before this one + y[Ia] = ge.et.p || 1440; + x[Ia] = ge; + if (!D[Ia]) { + D[Ia] = []; + } + D[Ia].push(ge); + if (Ia != 0) { + ge.pe = [x[Ia - 1]]; //previous event + x[Ia - 1].ne.push(ge); //next event + } + for (Ia = Ia + 1; y[Ia] <= La;) Ia++; + if (x[Ia]) { + k = x[Ia]; + ge.ne.push(k); + k.pe.push(ge); + } + ge.width = 1 / (ge.PO + 1); + ge.left = 1 - ge.width; + } + k = Array.prototype.concat.apply([], D); + x = y = D = null; + var t = k.length; + for (y = t; y--;) { + H = 1; + La = 0; + x = k[y]; + for (D = x.ne.length; D--;) { + Ia = x.ne[D]; + La = Math.max(La, Ia.VL); + H = Math.min(H, Ia.left) + } + x.VL = La + 1; + x.width = H / (x.PO + 1); + x.left = H - x.width; + } + for (y = 0; y < t; y++) { + x = k[y]; + x.left = 0; + if (x.pe) for (D = x.pe.length; D--;) { + H = x.pe[D]; + x.left = Math.max(x.left, H.left + H.width); + } + var p = (1 - x.left) / x.VL; + x.width = Math.max(x.width, p); + x.aQ = Math.min(1 - x.left, x.width + 0.7 * p); //width offset + } + de = null; + tmp_scrollevents[i] = k; + } + } + return dMax; + } + + + // Week view: top row (full-day events) + function BuildWT(dayarrs, events, dMax) { + //1: + var i, j, h, e, el, x, l; + var html = ""; + html += ""; + for (i = 0; i < dayarrs.length; i++) { + var ev, title, cl; + if (dayarrs.length == 1) { + ev = ""; + title = ""; + cl = ""; + } + else { + ev = ""; // "onclick=\"javascript:FunProxy('week2day',event,this);\""; + title = i18n.xgcalendar.to_date_view; + cl = "wk-daylink"; + } + html += ""; + + } + html += ""; + html += ""; //end tr1; + //2: + html += ""; + html += ""; // stgrid end //wvAd end //td2 end //tr2 end + //3: + html += ""; + + html += ""; + html += ""; + html += "
 "; + html += "
" + dayarrs[i].display + "
 
1) { + html += " colSpan='" + dayarrs.length + "'"; + } + //onclick=\"javascript:FunProxy('rowhandler',event,this);\" + html += ">
"; + + if (dMax == 0) { + html += ""; + for (i = 0; i < dayarrs.length; i++) { + html += ""; + } + html += ""; + } + else { + l = events.length; + el = 0; + x = []; + for (j = 0; j < l; j++) { + x.push(0); + } + //var c = tc(); + for (j = 0; el < dMax; j++) { + html += ""; + for (h = 0; h < l;) { + e = events[h][x[h]]; + html += ""; + } + html += ""; + } + html += ""; + for (h = 0; h < l; h++) { + html += ""; + } + html += ""; + } + html += "
 
1) { + html += " colSpan='" + e.colSpan + "'"; + h += e.colSpan; + } + else { + h++; + } + html += " ch='show'>"; + el++; + } + else { + html += " st-s' data-ch='qkadd' data-abbr='" + dayarrs[h].date.getTime() + "' data-axis='00:00'> "; + h++; + } + html += "
 
1) { + html += " colSpan='" + dayarrs.length + "'"; + } + html += ">
"; + var $el = $(html); + + if (dMax > 0) { + l = events.length; + el = 0; + x = []; + for (j = 0; j < l; j++) { + x.push(0); + } + for (j = 0; el < dMax; j++) { + for (h = 0; h < l;) { + e = events[h][x[h]]; + if (e) { //if exists + x[h] = x[h] + 1; + var $t = BuildMonthDayEvent(e, dayarrs[h].date, l - h); + $el.find(".row" + j + " .col" + h).append($t); + if (e.colSpan > 1) { + h += e.colSpan; + } + else { + h++; + } + el++; + } + else { + h++; + } + } + } + } + + return $el; + } + + function BuildDayScollEventContainer(dayarrs, events) { + //1: + var i, c; + + var html = "
"; + html += " 1) { + html += " colSpan='" + dayarrs.length + "'"; + } + html += ">
"; + for (i = 0; i < 24; i++) { + html += "
"; + } + html += "
"; + + //2: + html += ""; + html += ""; + + var l = dayarrs.length; + var hh24 = option.hour_height * 24; + for (i = 0; i < l; i++) { + html += ""; + } + html += ""; + + html += "
"; + + //get current time + var now = new Date(); + var h = now.getHours(); + var m = now.getMinutes(); + var mHg = gP(h, m) - 4; //make middle alignment vertically + html += "
"; + for (i = 0; i < 24; i++) html += "
" + fomartTimeShow(i) + "
"; + html += "
"; + var istoday = formatDate(dayarrs[i].date) == formatDate(new Date()); + // Today + if (istoday) { + html += "
 
"; + } + //var eventC = $(eventWrap); + //onclick=\"javascript:FunProxy('rowhandler',event,this);\" + html += "
"; + + html += "
"; + if (istoday) { + var mhh = mHg + 4; + html += "
"; + } + html += "
"; + html += "
"; + var $container = $(html); + + for (i = 0; i < l; i++) { + var $col = $container.find(".tgCol" + i); + for (var j = 0; j < events[i].length; j++) { + if (events[i][j].event["color"] && events[i][j].event["color"].match(/^#[0-9a-f]{6}$/i)) { + c = events[i][j].event["color"]; + } + else { + c = option.std_color; + } + var $tt = BuildDayEvent(c, events[i][j], j); + $col.append($tt); + } + } + + return $container; + } + + + function getTitle(event) { + var timeshow, eventshow; + var showtime = event["is_allday"] != 1; + eventshow = event["subject"]; + var startformat = getymformat(event["start"], null, showtime, true); + var endformat = getymformat(event["end"], event["start"], showtime, true); + timeshow = dateFormat.call(event["start"], startformat) + " - " + dateFormat.call(event["end"], endformat); + //var linebreak = ($.browser.mozilla?"":"\r\n"); + var linebreak = "\r\n"; + var ret = []; + if (event["is_allday"] == 1) { + //ret.push("[" + i18n.xgcalendar.allday_event + "]", linebreak ); + } + else { + if (event["is_recurring"] == 1) { + ret.push("[" + i18n.xgcalendar.repeat_event + "]", linebreak); + } + } + ret.push(i18n.xgcalendar.time + ": ", timeshow, linebreak, i18n.xgcalendar.event + ": ", eventshow); + + if (event["location"] != undefined && event["location"] != "") { + ret.push(linebreak, i18n.xgcalendar.location + ": ", event["location"]); + } + + if (event["attendees"] != undefined && event["attendees"] != "") { + ret.push(linebreak, i18n.xgcalendar.participant + ": ", event["attendees"]); + } + return ret.join(""); + } + + function BuildDayEvent(color, e, index) { + var theme = ColorCalcValues(color); + var p = { bdcolor:theme[0], bgcolor2:theme[0], bgcolor1:theme[2], width:"70%", icon:"", title:"", data:"" }; + p.starttime = pZero(e.st.hour) + ":" + pZero(e.st.minute); + p.endtime = pZero(e.et.hour) + ":" + pZero(e.et.minute); + p.content = e.event["subject"]; + p.title = getTitle(e.event); + var icons = []; + if (e.event["has_notification"] == 1) icons.push(" "); + if (e.reevent) { + icons.push(" "); + } + p.icon = icons.join(""); + var sP = gP(e.st.hour, e.st.minute); + var eP = gP(e.et.hour, e.et.minute); + p.top = sP + "px"; + p.left = (e.left * 100) + "%"; + p.width = (e.aQ * 100) + "%"; + p.height = (eP - sP - 4); + p.i = index; + if (option.enableDrag && e.event["is_editable_quick"] == 1) { + p.drag = "drag"; + p.redisplay = "block"; + } + else { + p.drag = ""; + p.redisplay = "none"; + } + + p.addclasses = (e.event["is_editable_quick"] ? "editable" : "not_editable"); + + var $newtemp = $(Tp(__SCOLLEVENTTEMP, p)); + $newtemp.data("eventdata", $.extend(true, {}, e.event)); + + return $newtemp; + } + + //get body height in month view + function GetMonthViewBodyHeight() { + return option.height; + } + + function GetMonthViewHeaderHeight() { + return 21; + } + + function BuilderMonthBody(showday, startday, events, bodyHeight) { + var i, j, k, b, day; + + var htb = []; + var firstdate = new Date(showday.getFullYear(), showday.getMonth(), 1); + var diffday = startday - firstdate.getDay(); + var showmonth = showday.getMonth(); + if (diffday > 0) { + diffday -= 7; + } + var startdate = DateAdd("d", diffday, firstdate); + var enddate = DateAdd("d", 34, startdate); + var rc = 5; + + if (enddate.getFullYear() == showday.getFullYear() && enddate.getMonth() == showday.getMonth() && enddate.getDate() < __MonthDays[showmonth]) { + enddate = DateAdd("d", 7, enddate); + rc = 6; + } + option.vstart = startdate; + option.vend = enddate; + option.datestrshow = CalDateShow(startdate, enddate); + bodyHeight = bodyHeight - 18 * rc; + var rowheight = bodyHeight / rc; + var roweventcount = parseInt(rowheight / 21); + if (rowheight % 21 > 15) { + roweventcount++; + } + var p = 100 / rc; + var formatevents = []; + var hastdata = formartEventsInHashtable(events, startday, 7, startdate, enddate); + var B = []; + var C = []; + for (j = 0; j < rc; j++) { + k = 0; + formatevents[j] = b = []; + for (i = 0; i < 7; i++) { + var newkeyDate = DateAdd("d", j * 7 + i, startdate); + C[j * 7 + i] = newkeyDate; + var newkey = dateFormat.call(newkeyDate, i18n.xgcalendar.dateformat.fulldaykey); + b[i] = hastdata[newkey]; + if (b[i] && b[i].length > 0) { + k += b[i].length; + } + } + B[j] = k; + } + eventDiv.data("mvdata", formatevents); + for (j = 0; j < rc; j++) { + //onclick=\"javascript:FunProxy('rowhandler',event,this);\" + htb.push("
"); + htb.push(""); + + for (i = 0; i < 7; i++) { + day = C[j * 7 + i]; + htb.push(""); + } + //bgtable + htb.push("
"); + } else if (day.getMonth() != showmonth) { + htb.push(" class=\"st-bg st-bg-nonmonth\">"); + } else { + htb.push(" class=\"st-bg\">"); + } + htb.push(" 
"); + + //stgrid + htb.push(""); + + //title tr + htb.push(""); + var titletemp = ""; + + for (i = 0; i < 7; i++) { + var o = { titleClass:"", dayshow:"" }; + day = C[j * 7 + i]; + if (formatDate(day) == formatDate(new Date())) { + o.titleClass = " st-dtitle-today"; + } + if (day.getMonth() != showmonth) { + o.titleClass = " st-dtitle-nonmonth"; + } + o.title = formatDate(day); + if (day.getDate() == 1) { + if (day.getMonth == 0) { + o.dayshow = formatDate(day); + } + else { + o.dayshow = dateFormat.call(day, option.date_format_dm2).toString(); + } + } + else { + o.dayshow = day.getDate(); + } + o.abbr = day.getTime(); + htb.push(Tp(titletemp, o)); + } + htb.push(""); + htb.push("
{dayshow}
"); + //month-row + htb.push("
"); + } + var $ret = $(htb.join("")); + + for (j = 0; j < rc; j++) { + var sfirstday = C[j * 7]; + var dMax = B[j]; + var obs = BuildMonthRow(formatevents[j], dMax, roweventcount, sfirstday); + for (i = 0; i < obs.length; i++) $ret.find(".row" + j).append(obs[i]); + //htb=htb.concat(rowHtml); rowHtml = null; + + } + return $ret; + } + + //formate datetime + function formartEventsInHashtable(events, startday, daylength, rbdate, redate) { + var key; + var hast = new Object(); + var l = events.length; + for (var i = 0; i < l; i++) { + var sD = events[i]["start"]; + var eD = events[i]["end"]; + var diff = DateDiff("d", sD, eD); + var s = {}; + s.event = events[i]; + s.day = sD.getDate(); + s.year = sD.getFullYear(); + s.month = sD.getMonth() + 1; + s.allday = events[i]["is_allday"] == 1; + s.crossday = events[i]["is_moredays"] == 1; + s.reevent = events[i]["is_recurring"] == 1; //Recurring event + s.daystr = s.year + "/" + s.month + "/" + s.day; + s.st = {}; + s.st.hour = sD.getHours(); + s.st.minute = sD.getMinutes(); + s.st.p = s.st.hour * 60 + s.st.minute; // start time position + s.et = {}; + s.et.hour = eD.getHours(); + s.et.minute = eD.getMinutes(); + s.et.p = s.et.hour * 60 + s.et.minute; // end time postition + + if (diff > 0) { + if (sD < rbdate) { //start date out of range + sD = rbdate; + } + if (eD > redate) { //end date out of range + eD = redate; + } + var f = startday - sD.getDay(); + if (f > 0) { + f -= daylength; + } + var sdtemp = DateAdd("d", f, sD); + for (; sdtemp <= eD; sD = sdtemp = DateAdd("d", daylength, sdtemp)) { + var d = $.extend(s, {}); + key = dateFormat.call(sD, i18n.xgcalendar.dateformat.fulldaykey); + var x = DateDiff("d", sdtemp, eD); + if (hast[key] == null) { + hast[key] = []; + } + d.colSpan = (x >= daylength) ? daylength - DateDiff("d", sdtemp, sD) : DateDiff("d", sD, eD) + 1; + hast[key].push(d); + d = null; + } + } + else { + key = dateFormat.call(events[i]["start"], i18n.xgcalendar.dateformat.fulldaykey); + if (hast[key] == null) { + hast[key] = []; + } + s.colSpan = 1; + hast[key].push(s); + } + s = null; + } + return hast; + } + + function BuildMonthRow(events, dMax, sc, day) { + var j, e, m, + x = [], + y = [], + z = [], + cday = []; + var l = events.length; + var el = 0; + var ret = []; + for (j = 0; j < l; j++) { + x.push(0); + y.push(0); + z.push(0); + cday.push(DateAdd("d", j, day)); + } + for (j = 0; j < l; j++) { + var ec = events[j] ? events[j].length : 0; + y[j] += ec; + for (var k = 0; k < ec; k++) { + e = events[j][k]; + if (e && e.colSpan > 1) { + for (m = 1; m < e.colSpan; m++) { + y[j + m]++; + } + } + } + } + + var tdtemp = "{html}"; + for (j = 0; j < sc && el < dMax; j++) { + var $row = $(""); + //var gridtr = $(__TRTEMP); + for (var h = 0; h < l;) { + e = events[h] ? events[h][x[h]] : undefined; + var $ev = null; + var tempdata = { "class":"", axis:"", ch:"", title:"", abbr:"", html:"", otherAttr:"", click:"javascript:void(0);" }; + var tempCss = ["st-c"]; + + if (e) { + x[h] = x[h] + 1; + //last event of the day + var bs = false; + if (z[h] + 1 == y[h] && e.colSpan == 1) { + bs = true; + } + if (!bs && j == (sc - 1) && z[h] < y[h]) { + el++; + $.extend(tempdata, { "axis":h, ch:"more", "abbr":cday[h].getTime(), html:i18n.xgcalendar.others + (y[h] - z[h]) + i18n.xgcalendar.item, click:"javascript:alert('more event');" }); + tempCss.push("st-more st-moreul"); + h++; + } + else { + tempdata.html = ""; + $ev = BuildMonthDayEvent(e, cday[h], l - h); + tempdata.ch = "show"; + if (e.colSpan > 1) { + tempdata.otherAttr = " colSpan='" + e.colSpan + "'"; + for (m = 0; m < e.colSpan; m++) { + z[h + m] = z[h + m] + 1; + } + h += e.colSpan; + + } + else { + z[h] = z[h] + 1; + h++; + } + el++; + } + } + else { + if (j == (sc - 1) && z[h] < y[h] && y[h] > 0) { + $.extend(tempdata, { "axis":h, ch:"more", "abbr":cday[h].getTime(), html:i18n.xgcalendar.others + (y[h] - z[h]) + i18n.xgcalendar.item, click:"javascript:alert('more event');" }); + tempCss.push("st-more st-moreul"); + h++; + } + else { + $.extend(tempdata, { html:" ", ch:"qkadd", "axis":"00:00", "abbr":cday[h].getTime(), title:"" }); + tempCss.push("st-s"); + h++; + } + } + tempdata.cssclass = tempCss.join(" "); + tempCss = null; + var $z = $(Tp(tdtemp, tempdata)); + if ($ev != null) $z.append($ev); + $row.append($z); + tempdata = null; + } + ret.push($row); + } + return ret; + } + + function BuildMonthDayEvent(e, cday, length) { + var theme; + if (e.event["color"] && e.event["color"].match(/^#[0-9a-f]{6}$/i)) { + theme = ColorCalcValues(e.event["color"]); + } + else { + theme = ColorCalcValues(option.std_color); + } + var p = { color:theme[2], title:"", extendClass:"", extendHTML:"", data:"" }; + + p.title = getTitle(e.event); + p.id = "bbit_cal_event_" + e.event["uri"]; + if (option.enableDrag && e.event["is_editable_quick"] == 1) { + p.eclass = "drag"; + } + else { + p.eclass = "cal_" + e.event["uri"]; + } + p.eclass += " " + (e.event["is_editable"] ? "editable" : "not_editable"); + var sp = "{content}"; + var i = " "; + var i2 = " "; + var ml = "
"; + var mr = "
"; + var arrm = []; + var sf = e.event["start"] < cday; + var ef = DateDiff("d", cday, e.event["end"]) >= length; //e.event["end"] >= DateAdd("d", 1, cday); + if (sf || ef) { + if (sf) { + arrm.push(ml); + p.extendClass = "st-ad-mpad "; + } + if (ef) { + arrm.push(mr); + } + p.extendHTML = arrm.join(""); + + } + var cen; + if (!e.allday && !sf) { + cen = pZero(e.st.hour) + ":" + pZero(e.st.minute) + " " + e.event["subject"]; + } + else { + cen = e.event["subject"]; + } + var content = []; + if (cen.indexOf("Geburtstag:") == 0) { + content.push("Geburtstag: "); + cen = cen.replace(/Geburtstag: /, ""); + } + content.push(Tp(sp, { content:cen })); + if (e.event["has_notification"] == 1) content.push(i); + if (e.reevent) { + content.push(i2); + } + p.content = content.join(""); + var $newel = $(Tp(__ALLDAYEVENTTEMP, p)); + $newel.data("eventdata", e.event); + return $newel; + } + + //to populate the data + function populate() { + if (option.isloading) { + return true; + } + if (option.url && option.url != "") { + option.isloading = true; + //clearcontainer(); + if (option.onBeforeRequestData && $.isFunction(option.onBeforeRequestData)) { + option.onBeforeRequestData(1); + } + var param = [ + { name:"showdate", value: Math.floor(option.showday.getTime() / 1000) }, + { name:"viewtype", value:option.view }, + { name:"weekstartday", value:option.weekstartday } + ]; + if (option.view == "multi_days") { + param.push({ name:"num_days", value:option.num_days }); + } + if (option.extParam) { + for (var pi = 0; pi < option.extParam.length; pi++) { + param[param.length] = option.extParam[pi]; + } + } + + $.ajax({ + type:option.method, // + url:option.url + option.url_add, + data:param, + dataType:"json", + dataFilter:function (data) { + //return data.replace(/"\\\/(Date\([0-9-]+\))\\\/"/gi, "new $1"); + + return data; + }, + success:function (data) {//function(datastr) { + //datastr =datastr.replace(/"\\\/(Date\([0-9-]+\))\\\/"/gi, 'new $1'); + //var data = (new Function("return " + datastr))(); + if (data != null && data.error != null) { + if (option.onRequestDataError) { + option.onRequestDataError(1, data); + } + } + else { + data["start"] = parseDate(data["start"]); + data["end"] = parseDate(data["end"]); + $.each(data.events, function (index, value) { + value["start"] = new Date(value["start"] * 1000); + value["end"] = new Date(value["end"] * 1000); + }); + responseData(data, data.start, data.end); + } + if (option.onAfterRequestData && $.isFunction(option.onAfterRequestData)) { + option.onAfterRequestData(1); + } + option.isloading = false; + }, + error:function (data) { + try { + if (option.onRequestDataError) { + option.onRequestDataError(1, data); + } else { + alert(i18n.xgcalendar.get_data_exception); + } + if (option.onAfterRequestData && $.isFunction(option.onAfterRequestData)) { + option.onAfterRequestData(1); + } + option.isloading = false; + } catch (e) { + } + } + }); + } + else { + alert("url" + i18n.xgcalendar.i_undefined); + } + return true; + } + + function responseData(data, start, end) { + var events = data.events; + ConcatEvents(events, start, end); + render(); + + } + + function clearrepeat(events, start) { + var jl = events.length; + if (jl > 0) { + var es = events[0]["start"]; + var el = events[jl - 1]["start"]; + for (var i = 0, l = option.eventItems.length; i < l; i++) { + + if (option.eventItems[i]["sart"] > el || jl == 0) { + break; + } + if (option.eventItems[i]["start"] >= es) { + for (var j = 0; j < jl; j++) { + if (option.eventItems[i]["uri"] == events[j]["uri"] && option.eventItems[i]["start"] < start) { + events.splice(j, 1); //for duplicated event + jl--; + break; + } + } + } + } + } + } + + function ConcatEvents(events, start, end) { + var e, s; + if (!events) { + events = []; + } + if (events) { + if (option.eventItems.length == 0) { + option.eventItems = events; + } + else { + //remove duplicated one + clearrepeat(events, start); + var sl = option.eventItems.length; + var sI = -1; + var eI = sl; + s = start; + e = end; + if (option.eventItems[0]["start"] > e) { + option.eventItems = events.concat(option.eventItems); + return; + } + if (option.eventItems[sl - 1]["start"] < s) { + option.eventItems = option.eventItems.concat(events); + return; + } + for (var i = 0; i < sl; i++) { + if (option.eventItems[i]["start"] >= s && sI < 0) { + sI = i; + continue; + } + if (option.eventItems[i]["start"] > e) { + eI = i; + break; + } + } + + var e1 = sI <= 0 ? [] : option.eventItems.slice(0, sI); + var e2 = eI == sl ? [] : option.eventItems.slice(eI); + option.eventItems = [].concat(e1, events, e2); + } + } + } + + //utils goes here + function weekormonthtoday(e) { + var th = $(this); + option.showday = new Date(parseInt(th.data("abbr"))); + option.view = "day"; + render(); + if (option.onWeekOrMonthToDay) { + option.onWeekOrMonthToDay(option); + } + e.stopPropagation(); + e.preventDefault(); + } + + function parseDate(str) { + return new Date(Date.parse(str)); + } + + function gP(h, m) { + return h * option.hour_height + parseInt(m / 60 * option.hour_height); + } + + function gW(ts1, ts2) { + var t1 = ts1 / option.hour_height; + var t2 = parseInt(t1); + var t3 = t1 - t2 >= 0.5 ? 30 : 0; + var t4 = ts2 / option.hour_height; + var t5 = parseInt(t4); + var t6 = t4 - t5 >= 0.5 ? 30 : 0; + return { sh:t2, sm:t3, eh:t5, em:t6, h:ts2 - ts1 }; + } + + function gH(y1, y2, pt) { + var sy1 = Math.min(y1, y2); + var sy2 = Math.max(y1, y2); + var t1 = (sy1 - pt) / option.hour_height; + var t2 = parseInt(t1); + var t3 = t1 - t2 >= 0.5 ? 30 : 0; + var t4 = (sy2 - pt) / option.hour_height; + var t5 = parseInt(t4); + var t6 = t4 - t5 >= 0.5 ? 30 : 0; + return { sh:t2, sm:t3, eh:t5, em:t6, h:sy2 - sy1 }; + } + + function pZero(n) { + return n < 10 ? "0" + n : "" + n; + } + + function Tp(temp, dataarry) { + return temp.replace(/\{([\w]+)\}/g, function (s1, s2) { + var s = dataarry[s2]; + if (typeof (s) != "undefined") { + return s; + } else { + return s1; + } + }); + } + + function fomartTimeShow(h) { + return h < 10 ? "0" + h + ":00" : h + ":00"; + } + + function getymformat(date, comparedate, isshowtime, isshowweek) { + var showyear = isshowtime != undefined ? (date.getFullYear() != new Date().getFullYear()) : true; + var showmonth = true; + var showday = true; + var showtime = isshowtime || false; + var showweek = isshowweek || false; + if (comparedate) { + showyear = comparedate.getFullYear() != date.getFullYear(); + //showmonth = comparedate.getFullYear() != date.getFullYear() || date.getMonth() != comparedate.getMonth(); + if (comparedate.getFullYear() == date.getFullYear() && + date.getMonth() == comparedate.getMonth() && + date.getDate() == comparedate.getDate() + ) { + showyear = showmonth = showday = showweek = false; + } + } + + var a = []; + if (showyear) { + a.push(option.date_format_dm3) + } else if (showmonth) { + a.push(option.date_format_dm2) + } else if (showday) { + a.push(i18n.xgcalendar.dateformat.day); + } + a.push(showweek ? " (W)" : "", showtime ? " HH:mm" : ""); + return a.join(""); + } + + function CalDateShow(startday, endday, isshowtime, isshowweek) { + if (!endday) { + return dateFormat.call(startday, getymformat(startday, null, isshowtime)); + } else { + var strstart = dateFormat.call(startday, getymformat(startday, null, isshowtime, isshowweek)); + var strend = dateFormat.call(endday, getymformat(endday, startday, isshowtime, isshowweek)); + var join = (strend != "" ? " - " : ""); + return [strstart, strend].join(join); + } + } + + function buildtempdayevent(sh, sm, eh, em, h, title, w, resize, color) { + if (!color.match(/^#[0-9a-f]{6}$/i)) color = option.std_color; + var t = ColorCalcValues(color); + return Tp(__SCOLLEVENTTEMP, { + bdcolor:t[0], + bgcolor2:t[1], + bgcolor1:t[2], + data:"", + starttime:[pZero(sh), pZero(sm)].join(":"), + endtime:[pZero(eh), pZero(em)].join(":"), + content:title ? title : i18n.xgcalendar.new_event, + title:title ? title : i18n.xgcalendar.new_event, + icon:" ", + top:"0px", + left:"", + width:w ? w : "100%", + height:h - 4, + i:"-1", + drag:"drag-chip", + redisplay:resize ? "block" : "none" + }); + } + + function quickd(type) { + $("#bbit-cs-buddle").css("visibility", "hidden"); + var calid = $("#bbit-cs-id").val(); + var param = [ + { "name":"calendarId", value:calid }, + { "name":"type", value:type} + ]; + var de = rebyKey(calid, true); + option.onBeforeRequestData && option.onBeforeRequestData(3); + $.post(option.quickDeleteUrl, param, function (data) { + if (data) { + $(document).trigger("wdcal:updated"); + if (data["IsSuccess"]) { + de = null; + populate(); + option.onAfterRequestData && option.onAfterRequestData(3); + } + else { + option.onRequestDataError && option.onRequestDataError(3, data); + Ind(de); + render(); + option.onAfterRequestData && option.onAfterRequestData(3); + } + } + }, "json"); + } + + function getbuddlepos(x, y) { + var tleft = x - 110; + var ttop = y - 217; + var maxLeft = document.documentElement.clientWidth; + var maxTop = document.documentElement.clientHeight; + var ishide = false; + if (tleft <= 0 || ttop <= 0 || tleft + 400 > maxLeft) { + tleft = x - 200 <= 0 ? 10 : x - 200; + ttop = y - 159 <= 0 ? 10 : y - 159; + if (tleft + 400 >= maxLeft) { + tleft = maxLeft - 410; + } + if (ttop + 164 >= maxTop) { + ttop = maxTop - 165; + } + ishide = true; + } + return { left:tleft, top:ttop, hide:ishide }; + } + + function dayshow(e, data) { + var $t = $(e.target); + if ($t.hasClass("axx_username") || $t.parents(".axx_username").length > 0 || $t.hasClass("cal_nojs") || $t.parents(".cal_nojs").length > 0) return false; + + if (data == undefined) { + if ($t.hasClass("chip") || $t.hasClass("rb-o")) data = $t.data("eventdata"); + else data = $t.parents(".chip, .rb-o").data("eventdata"); + } + + if (data != null) { + var editable = false; + if (option.quickDeleteUrl != "" && data["is_editable"] == 1 && option.readonly != true) editable = true; + var csbuddle = ''; + var $bud = $("#bbit-cs-buddle"); + if ($bud.length == 0) { + $bud = $(csbuddle).appendTo(document.body); + var calbutton = $("#bbit-cs-delete"); + $("#bubbleClose2").on("click", function () { + $("#bbit-cs-buddle").css("visibility", "hidden"); + }); + calbutton.on("click", function () { + var data = $("#bbit-cs-buddle").data("cdata"); + if (option.quickDeleteHandler && $.isFunction(option.quickDeleteHandler)) { + option.quickDeleteHandler.call(this, data, quickd); + } + else { + if (confirm(i18n.xgcalendar.confirm_delete_event)) { + var s = 0; //0 single event , 1 for Recurring event + if (data["is_recurring"] == 1) { + if (confirm(i18n.xgcalendar.confrim_delete_event_or_all)) { + s = 0; + } + else { + s = 1; + } + } + else { + s = 0; + } + quickd(s); + } + } + }); + } + + if (editable) { + $("#bbit-cs-delete").parents(".bbit-cs-split").show(); + $bud.find(".bbit-cs-editLink").attr("href", data["url_edit"]).show(); + } + else { + $("#bbit-cs-delete").parents(".bbit-cs-split").hide(); + $bud.find(".bbit-cs-editLink").hide(); + } + + var pos = getbuddlepos(e.pageX, e.pageY); + if (pos.hide) { + $("#prong1").hide() + } + else { + $("#prong1").show() + } + var ss = []; + var iscos = DateDiff("d", data["start"], data["end"]) != 0; + ss.push(dateFormat.call(data["start"], option.date_format_dm2), " (", __WDAY[data["start"].getDay()], ")"); + if (data["is_allday"] != 1) { + ss.push(",", dateFormat.call(data["start"], "HH:mm")); + } + + if (iscos) { + ss.push(" - ", dateFormat.call(data["end"], option.date_format_dm2), " (", __WDAY[data["end"].getDay()], ")"); + if (data["is_allday"] != 1) { + ss.push(",", dateFormat.call(data["end"], "HH:mm")); + } + } + var location = ""; + if (data["location"] != "") location = data["location"] + ", "; + $("#bbit-cs-buddle-timeshow").html(location + ss.join("")); + $bud.find(".bbit-cs-what").html(data["subject"]).attr("href", data["url_detail"]); + $("#bbit-cs-id").val(data["uri"]); + $bud.data("cdata", data); + $bud.css({ "visibility":"visible", left:pos.left, top:pos.top }); + + $(document).one("click", function () { + $("#bbit-cs-buddle").css("visibility", "hidden"); + }); + } + else { + alert(i18n.xgcalendar.data_format_error); + } + return false; + } + + function moreshow(mv) { + var $me = $(this); + var $pdiv = $(mv); + var divIndex = parseInt($pdiv.data("row")); + var offsetMe = $me.position(); + var offsetP = $pdiv.position(); + var width = ($me.width() + 2) * 1.5; + var top = offsetP.top + 15; + var left = offsetMe.left; + + var day = new Date(parseInt($me.data("abbr"))); + var cc = $gridcontainer.find(".cal-month-cc"); + var ccontent = $gridcontainer.find(".cal-month-cc-content table tbody"); + var ctitle = $gridcontainer.find(".cal-month-cc-title"); + ctitle.html(formatDate(day)); + ccontent.empty(); + var edata = $("#gridEvent").data("mvdata"); + var events = edata[divIndex]; + var index = parseInt($me.data("axis")); + ccontent.find("*").remove(); + for (var i = 0; i <= index; i++) { + var ec = events[i] ? events[i].length : 0; + for (var j = 0; j < ec; j++) { + var e = events[i][j]; + if (e) { + if ((e.colSpan + i - 1) >= index) { + var $x = $(""); + var $y = BuildMonthDayEvent(e, day, 1); + $x.find(".st-c").append($y); + ccontent.append($x); + } + } + } + } + //click + ccontent.find("div.rb-o").each(function () { + $(this).click(dayshow); + }); + + var height = cc.height(); + var maxleft = document.documentElement.clientWidth; + var maxtop = document.documentElement.clientHeight; + if (left + width >= maxleft) { + left = offsetMe.left - ($me.width() + 2) * 0.5; + } + if (top + height >= maxtop) { + top = maxtop - height - 2; + } + var newOff = { left:left, top:top, "z-index":180, width:width, "visibility":"visible" }; + cc.css(newOff); + $(document).on("click", closeCc); + return false; + } + + function dayupdate(data, start, end) { + if (option.quickUpdateUrl != "" && data["is_editable_quick"] == 1 && option.readonly != true) { + if (option.isloading) { + return false; + } + option.isloading = true; + var id = data["uri"]; + var os = data["start"]; + var od = data["end"]; + var param = [ + { "name":"calendarId", value:id }, + { "name":"CalendarStartTime", value:Math.floor(start.getTime() / 1000) }, + { "name":"CalendarEndTime", value:Math.floor(end.getTime() / 1000) } + ]; + var d; + if (option.quickUpdateHandler && $.isFunction(option.quickUpdateHandler)) { + option.quickUpdateHandler.call(this, param); + } + else { + option.onBeforeRequestData && option.onBeforeRequestData(4); + $.post(option.quickUpdateUrl, param, function (data) { + if (data) { + $(document).trigger("wdcal:updated"); + if (data["IsSuccess"] == true) { + option.isloading = false; + option.onAfterRequestData && option.onAfterRequestData(4); + } + else { + option.onRequestDataError && option.onRequestDataError(4, data); + option.isloading = false; + d = rebyKey(id, true); + d["start"] = os; + d["end"] = od; + Ind(d); + render(); + d = null; + option.onAfterRequestData && option.onAfterRequestData(4); + } + } + }, "json"); + d = rebyKey(id, true); + if (d) { + d["start"] = start; + d["end"] = end; + } + Ind(d); + render(); + } + } + return false; + } + + function quickadd(start, end, isallday, pos) { + if ((!option.quickAddHandler && option.quickAddUrl == "") || option.readonly) { + return false; + } + var buddle = $("#bbit-cal-buddle"); + if (buddle.length == 0) { + var temparr = []; + temparr.push('
'); + temparr.push(''); + temparr.push('
'); + var tempquickAddHanler = temparr.join(""); + temparr = null; + $(document.body).append(tempquickAddHanler); + buddle = $("#bbit-cal-buddle"); + $("#bubbleClose1").click(function () { + $("#bbit-cal-buddle").css("visibility", "hidden"); + releasedragevent(); + }); + $("#bbit-cal-submitFORM").keyup(function (e) { + if (e.which == 27) $("#bubbleClose1").click(); + }); + $("#bbit-cal-submitFORM").submit(function (e) { + e.stopPropagation(); + e.preventDefault(); + if (option.isloading) { + return false; + } + option.isloading = true; + var what = $("#bbit-cal-what").val(); + var datestart = $("#bbit-cal-start").val(); + var dateend = $("#bbit-cal-end").val(); + var allday = $("#bbit-cal-allday").val(); + var f = /^[^\$<>]+$/.test(what); + if (!f) { + alert(i18n.xgcalendar.invalid_title); + $("#bbit-cal-what").focus(); + option.isloading = false; + return false; + } + var param = [ + { "name":"CalendarTitle", value:what }, + { "name":"CalendarStartTime", value: Math.floor(datestart / 1000)}, + { "name":"CalendarEndTime", value: Math.floor(dateend / 1000)}, + { "name":"IsAllDayEvent", value:allday } + ]; + + if (option.extParam) { + for (var pi = 0; pi < option.extParam.length; pi++) { + param[param.length] = option.extParam[pi]; + } + } + + if (option.quickAddHandler && $.isFunction(option.quickAddHandler)) { + option.quickAddHandler.call(this, param); + $("#bbit-cal-buddle").css("visibility", "hidden"); + releasedragevent(); + } + else { + $("#bbit-cal-buddle").css("visibility", "hidden"); + var tId = -1; + option.onBeforeRequestData && option.onBeforeRequestData(2); + + var sd = new Date(datestart), + ed = new Date(dateend), + diff = DateDiff("d", sd, ed); + var newdata = { + "uri":"", + "subject":what, + "start":sd, + "end":ed, + "is_allday":(allday == "1" ? 1 : 0), + "is_moredays":(diff > 0 ? 1 : 0), + "is_recurring":0, + "color":option.std_color, + "is_editable":0, + "is_editable_quick":0, + "location":"", + "attendees":"" + }; + tId = Ind(newdata); + releasedragevent(); + render(); + + $.post(option.quickAddUrl, param, function (data) { + option.isloading = false; + if (data) { + if (data["IsSuccess"] == true) { + populate(); + option.onAfterRequestData && option.onAfterRequestData(2); + } + else { + option.onRequestDataError && option.onRequestDataError(2, data); + option.onAfterRequestData && option.onAfterRequestData(2); + } + $(document).trigger("wdcal:updated"); + } + + }, "json"); + } + return false; + }); + buddle.mousedown(function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + } + + var dateshow = CalDateShow(start, end, !isallday, true); + var off = getbuddlepos(pos.left, pos.top); + if (off.hide) { + $("#prong2").hide() + } + else { + $("#prong2").show() + } + $("#bbit-cal-buddle-timeshow").html(dateshow); + var calwhat = $("#bbit-cal-what").val(""); + $("#bbit-cal-allday").val(isallday ? "1" : "0"); + $("#bbit-cal-start").val(start.getTime()); + $("#bbit-cal-end").val(end.getTime()); + buddle.css({ "visibility":"visible", left:off.left, top:off.top }); + calwhat.blur().focus(); //add 2010-01-26 blur() fixed chrome + $(document).one("mousedown", function () { + $("#bbit-cal-buddle").css("visibility", "hidden"); + releasedragevent(); + }); + return false; + } + + function formatDate(time, format) { + if (typeof(format) == "undefined") return $.datepicker.formatDate(option.date_format_full,time); + var time2 = $.datepicker.formatDate(format, time); + var h = time.getHours(); + var i = time.getMinutes(); + time2 = time2.replace("HH", (h > 10 ? "" : "0") + h); + time2 = time2.replace("ii", (i > 10 ? "" : "0") + i); + return time2; + } + + function rebyKey(key, remove) { + if (option.eventItems && option.eventItems.length > 0) { + var sl = option.eventItems.length; + var i = -1; + for (var j = 0; j < sl; j++) { + if (option.eventItems[j]["uri"] == key) { + i = j; + break; + } + } + if (i >= 0) { + var t = option.eventItems[i]; + if (remove) { + option.eventItems.splice(i, 1); + } + return t; + } + } + return null; + } + + function Ind(event, i) { + var d = 0; + var j; + if (!i) { + if (option.eventItems && option.eventItems.length > 0) { + var sl = option.eventItems.length; + var s = event["start"]; + var d1 = s.getTime() - option.eventItems[0]["start"].getTime(); + var d2 = option.eventItems[sl - 1]["start"].getTime() - s.getTime(); + var diff = d1 - d2; + if (d1 < 0 || diff < 0) { + for (j = 0; j < sl; j++) { + if (option.eventItems[j]["start"] >= s) { + i = j; + break; + } + } + } + else if (d2 < 0) { + i = sl; + } + else { + for (j = sl - 1; j >= 0; j--) { + if (option.eventItems[j]["start"] < s) { + i = j + 1; + break; + } + } + } + } + else { + i = 0; + } + } + else { + d = 1; + } + if (option.eventItems && option.eventItems.length > 0) { + if (i == option.eventItems.length) { + option.eventItems.push(event); + } + else { + option.eventItems.splice(i, d, event); + } + } + else { + option.eventItems = [event]; + } + return i; + } + + + function ResizeView() { + var _viewType = option.view; + if (_viewType == "day" || _viewType == "week" || _viewType == "multi_days") { + var $dvwkcontaienr = $gridcontainer.find(".wktopcontainer"); + var $dvtec = $gridcontainer.find(".scrolltimeevent"); + if ($dvwkcontaienr.length == 0 || $dvtec.length == 0) { + alert(i18n.xgcalendar.view_no_ready); + return; + } + var dvwkH = $dvwkcontaienr.height() + 2; + var calH = option.height - 8 - dvwkH; + $dvtec.height(calH); + if (typeof (option.scroll) == "undefined") { + var currentday = new Date(); + var h = currentday.getHours(); + var m = currentday.getMinutes(); + var th = gP(h, m); + //var ch = $dvtec.attr("clientHeight"); + var ch = $dvtec.height(); + var sh = th - 0.5 * ch; + //var ph = $dvtec.attr("scrollHeight"); + var ph = $dvtec.children().height(); + if (sh < 0) sh = 0; + if (sh > ph - ch) sh = ph - ch - 10 * (23 - h); + //$dvtec.attr("scrollTop", sh); + $dvtec.scrollTop(sh); + } + else { + $dvtec.scrollTop(option.scroll); + } + } + else if (_viewType == "month") { + //Resize GridContainer + } + } + + function returnfalse() { + return false; + } + + function initevents(viewtype) { + if (viewtype == "week" || viewtype == "day" || viewtype == "multi_days") { + $("div.chip", $gridcontainer).each(function () { + var chip = $(this); + chip.click(dayshow); + if (chip.hasClass("drag")) { + chip.mousedown(function (e) { + dragStart.call(this, "std_item_move", e); + e.stopPropagation(); + e.preventDefault(); + }); + //resize + chip.find("div.resizer").mousedown(function (e) { + dragStart.call($(this).parent().parent(), "std_item_resize", e); + e.stopPropagation(); + e.preventDefault(); + }); + } + else { + chip.mousedown(returnfalse) + } + }); + $("div.rb-o", $gridcontainer).each(function () { + var chip = $(this); + chip.click(dayshow); + if (chip.hasClass("drag") && (viewtype == "week" || viewtype == "multi_days")) { + //drag; + chip.mousedown(function (e) { + dragStart.call(this, "fullday_item_move", e); + e.stopPropagation(); + e.preventDefault(); + }); + } + else { + chip.mousedown(returnfalse) + } + }); + if (option.readonly == false) { + $("td.tg-col", $gridcontainer).each(function () { + $(this).mousedown(function (e) { + dragStart.call(this, "std_empty_drag", e); + e.stopPropagation(); + e.preventDefault(); + }); + }); + $gridcontainer.find(".weekViewAllDaywk").mousedown(function (e) { + dragStart.call(this, "fullday_empty_drag", e); + e.stopPropagation(); + e.preventDefault(); + }); + } + + if (viewtype == "week" || viewtype == "multi_days") { + $gridcontainer.find(".wktopcontainer th.gcweekname").each(function () { + $(this).click(weekormonthtoday); + }); + } + + + } + else if (viewtype = "month") { + $("div.rb-o", $gridcontainer).each(function () { + var chip = $(this); + chip.click(dayshow); + if (chip.hasClass("drag")) { + //drag; + chip.mousedown(function (e) { + dragStart.call(this, "std_item_month_drag", e); + e.stopPropagation(); + e.preventDefault(); + }); + } + else { + chip.mousedown(returnfalse) + } + }); + $("td.st-more", $gridcontainer).each(function () { + + $(this).on("click", function (e) { + moreshow.call(this, $(this).parent().parent().parent().parent()[0]); + e.stopPropagation(); + e.preventDefault(); + }).on("mousedown", function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + }); + if (option.readonly == false) { + $gridcontainer.find(".mvEventContainer").mousedown(function (e) { + dragStart.call(this, "empty_month_drag", e); + e.stopPropagation(); + e.preventDefault(); + }); + } + } + + } + + function releasedragevent() { + if (_dragevent) { + _dragevent(); + _dragevent = null; + } + } + + function dragStart(type, e) { + var w, h, offset, moffset, left, top, l, py, pw, xa, ya, i, data, fdi, dp, yl; + var $obj = $(this); + releasedragevent(); + switch (type) { + case "std_empty_drag": + _dragdata = { type:"std_empty_drag", target:$obj, sx:e.pageX, sy:e.pageY }; + break; + case "fullday_empty_drag": + w = $obj.width(); + h = $obj.height(); + offset = $obj.offset(); + left = offset.left; + top = offset.top; + l = option.view == "day" ? 1 : 7; + py = w % l; + pw = parseInt(w / l); + if (py > l / 2 + 1) { + pw++; + } + xa = []; + ya = []; + for (i = 0; i < l; i++) { + xa.push({ s:i * pw + left, e:(i + 1) * pw + left }); + } + ya.push({ s:top, e:top + h }); + _dragdata = { type:"fullday_empty_drag", target:$obj, sx:e.pageX, sy:e.pageY, pw:pw, xa:xa, ya:ya, h:h }; + w = left = l = py = pw = xa = null; + break; + case "std_item_move": + var evid = $obj.parent().data("col"); + var p = $obj.parent(); + var pos = p.offset(); + w = p.width() + 10; + h = $obj.height(); + data = $obj.data("eventdata"); + _dragdata = { type:"std_item_move", target:$obj, sx:e.pageX, sy:e.pageY, + pXMin:pos.left, pXMax:pos.left + w, pw:w, h:h, + cdi:parseInt(evid), fdi:parseInt(evid), data:data + }; + break; + case "std_item_resize": + h = $obj.height(); + data = $obj.data("eventdata"); + _dragdata = { type:"std_item_resize", target:$obj, sx:e.pageX, sy:e.pageY, h:h, data:data }; + break; + case "fullday_item_move": + var con = $gridcontainer.find(".weekViewAllDaywk"); + w = con.width(); + h = con.height(); + offset = con.offset(); + moffset = $obj.offset(); + left = offset.left; + top = offset.top; + l = 7; + py = w % l; + pw = parseInt(w / l); + if (py > l / 2 + 1) { + pw++; + } + xa = []; + ya = []; + var di = 0; + for (i = 0; i < l; i++) { + xa.push({ s:i * pw + left, e:(i + 1) * pw + left }); + if (moffset.left >= xa[i].s && moffset.left < xa[i].e) { + di = i; + } + } + fdi = { x:di, y:0, di:di }; + ya.push({ s:top, e:top + h }); + data = $obj.data("eventdata"); + dp = DateDiff("d", data["start"], data["end"]) + 1; + _dragdata = { type:"fullday_item_move", target:$obj, sx:e.pageX, sy:e.pageY, data:data, xa:xa, ya:ya, fdi:fdi, h:h, dp:dp, pw:pw }; + break; + case "empty_month_drag": + w = $obj.width(); + offset = $obj.offset(); + left = offset.left; + top = offset.top; + l = 7; + yl = $obj.children().length; + py = w % l; + pw = parseInt(w / l); + if (py > l / 2 + 1) { + pw++; + } + h = $gridcontainer.find(".mvrow_0").height(); + xa = []; + ya = []; + for (i = 0; i < l; i++) { + xa.push({ s:i * pw + left, e:(i + 1) * pw + left }); + } + xa = []; + ya = []; + for (i = 0; i < l; i++) { + xa.push({ s:i * pw + left, e:(i + 1) * pw + left }); + } + for (i = 0; i < yl; i++) { + ya.push({ s:i * h + top, e:(i + 1) * h + top }); + } + _dragdata = { type:"empty_month_drag", target:$obj, sx:e.pageX, sy:e.pageY, pw:pw, xa:xa, ya:ya, h:h }; + break; + case "std_item_month_drag": + var row0 = $gridcontainer.find(".mvrow_0"); + var row1 = $gridcontainer.find(".mvrow_1"); + w = row0.width(); + offset = row0.offset(); + var diffset = row1.offset(); + moffset = $obj.offset(); + h = diffset.top - offset.top; + left = offset.left; + top = offset.top; + l = 7; + yl = row0.parent().children().length; + py = w % l; + pw = parseInt(w / l); + if (py > l / 2 + 1) { + pw++; + } + xa = []; + ya = []; + var xi = 0; + var yi = 0; + for (i = 0; i < l; i++) { + xa.push({ s:i * pw + left, e:(i + 1) * pw + left }); + if (moffset.left >= xa[i].s && moffset.left < xa[i].e) { + xi = i; + } + } + for (i = 0; i < yl; i++) { + ya.push({ s:i * h + top, e:(i + 1) * h + top }); + if (moffset.top >= ya[i].s && moffset.top < ya[i].e) { + yi = i; + } + } + fdi = { x:xi, y:yi, di:yi * 7 + xi }; + data = $obj.data("eventdata"); + dp = DateDiff("d", data["start"], data["end"]) + 1; + _dragdata = { type:"std_item_month_drag", target:$obj, sx:e.pageX, sy:e.pageY, data:data, xa:xa, ya:ya, fdi:fdi, h:h, dp:dp, pw:pw }; + break; + } + $('body').noSelect(); + } + + function dragMove(e) { + var d, sy, sx, x, y, diffy, gh, ny, tempdata, cpwrap, ndi, evid, nh, cp, w1; + if (_dragdata) { + if (e.pageX < 0 || e.pageY < 0 + || e.pageX > document.documentElement.clientWidth + || e.pageY >= document.documentElement.clientHeight) { + dragEnd(e); + return false; + } + d = _dragdata; + switch (d.type) { + case "std_empty_drag": + sy = d.sy; + y = e.pageY; + diffy = y - sy; + if (diffy > (option.hour_height / 4) || diffy < (-1 * (option.hour_height / 4)) || d.cpwrap) { + if (diffy == 0) { + diffy = Math.ceil(option.hour_height / 2); + } + var dy = diffy % Math.ceil(option.hour_height / 2); + if (dy != 0) { + diffy = dy > 0 ? diffy + Math.ceil(option.hour_height / 2) - dy : diffy - Math.ceil(option.hour_height / 2) - dy; + y = d.sy + diffy; + if (diffy < 0) { + sy = sy + Math.ceil(option.hour_height / 2); + } + } + if (!d.tp) { + d.tp = $(d.target).offset().top; + } + gh = gH(sy, y, d.tp); + ny = gP(gh.sh, gh.sm); + if (!d.cpwrap) { + tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, "", "", "", option.std_color); + cpwrap = $("
").html(tempdata); + $(d.target).find("div.tg-col-overlaywrapper").append(cpwrap); + d.cpwrap = cpwrap; + } + else { + if (d.cgh.sh != gh.sh || d.cgh.eh != gh.eh || d.cgh.sm != gh.sm || d.cgh.em != gh.em) { + tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, "", "", "", option.std_color); + d.cpwrap.css("top", ny + "px").html(tempdata); + } + } + d.cgh = gh; + } + break; + case "fullday_empty_drag": + sx = d.sx; + x = e.pageX; + diffx = x - sx; + if (diffx > 5 || diffx < -5 || d.lasso) { + if (!d.lasso) { + d.lasso = $("
"); + $(document.body).append(d.lasso); + } + if (!d.sdi) { + d.sdi = getdi(d.xa, d.ya, sx, d.sy); + } + ndi = getdi(d.xa, d.ya, x, e.pageY); + if (!d.fdi || d.fdi.di != ndi.di) { + addlasso(d.lasso, d.sdi, ndi, d.xa, d.ya, d.h); + } + d.fdi = ndi; + } + break; + case "empty_month_drag": + sx = d.sx; + x = e.pageX; + sy = d.sy; + y = e.pageY; + diffx = x - sx; + diffy = y - sy; + if (diffx > 5 || diffx < -5 || diffy < -5 || diffy > 5 || d.lasso) { + if (!d.lasso) { + d.lasso = $("
"); + $(document.body).append(d.lasso); + } + if (!d.sdi) { + d.sdi = getdi(d.xa, d.ya, sx, sy); + } + ndi = getdi(d.xa, d.ya, x, y); + if (!d.fdi || d.fdi.di != ndi.di) { + addlasso(d.lasso, d.sdi, ndi, d.xa, d.ya, d.h); + } + d.fdi = ndi; + } + break; + case "std_item_move": + data = d.data; + if (data != null && data["is_editable_quick"] == 1) { + sx = d.sx; + x = e.pageX; + sy = d.sy; + y = e.pageY; + diffx = x - sx; + diffy = y - sy; + if (diffx > 5 || diffx < -5 || diffy > 5 || diffy < -5 || d.cpwrap) { + if (!d.cpwrap) { + gh = { sh:data["start"].getHours(), + sm:data["start"].getMinutes(), + eh:data["end"].getHours(), + em:data["end"].getMinutes(), + h:d.h + }; + d.target.hide(); + ny = gP(gh.sh, gh.sm); + d.top = ny; + tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], false, false, data["color"]); + cpwrap = $("
").html(tempdata); + evid = ".tgOver" + d.target.parent().data("col"); + $gridcontainer.find(evid).append(cpwrap); + d.cpwrap = cpwrap; + d.ny = ny; + } + else { + var pd = 0; + if (x < d.pXMin) { + pd = -1; + } + else if (x > d.pXMax) { + pd = 1; + } + if (pd != 0) { + + d.cdi = d.cdi + pd; + var ov = $gridcontainer.find(".tgOver" + d.cdi); + if (ov.length == 1) { + d.pXMin = d.pXMin + d.pw * pd; + d.pXMax = d.pXMax + d.pw * pd; + ov.append(d.cpwrap); + } + else { + d.cdi = d.cdi - pd; + } + } + ny = d.top + diffy; + var pny = ny % Math.ceil(option.hour_height / 2); + if (pny != 0) { + ny = ny - pny; + } + if (d.ny != ny) { + //log.info("ny=" + ny); + gh = gW(ny, ny + d.h); + //log.info("sh=" + gh.sh + ",sm=" + gh.sm); + tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], false, false, data["color"]); + d.cpwrap.css("top", ny + "px").html(tempdata); + } + d.ny = ny; + } + } + } + + break; + case "std_item_resize": + var data = d.data; + if (data != null && data["is_editable_quick"] == 1) { + sy = d.sy; + y = e.pageY; + diffy = y - sy; + if (diffy != 0 || d.cpwrap) { + if (!d.cpwrap) { + gh = { sh:data["start"].getHours(), + sm:data["start"].getMinutes(), + eh:data["end"].getHours(), + em:data["end"].getMinutes(), + h:d.h + }; + d.target.hide(); + ny = gP(gh.sh, gh.sm); + d.top = ny; + tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], "100%", true, data["color"]); + cpwrap = $("
").html(tempdata); + evid = ".tgOver" + d.target.parent().data("col"); + $gridcontainer.find(evid).append(cpwrap); + d.cpwrap = cpwrap; + } + else { + nh = d.h + diffy; + var pnh = nh % Math.ceil(option.hour_height / 2); + nh = pnh > 1 ? nh - pnh + Math.ceil(option.hour_height / 2) : nh - pnh; + if (d.nh != nh) { + gh = gW(d.top, d.top + nh); + tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, data["subject"], "100%", true, data["color"]); + d.cpwrap.html(tempdata); + } + d.nh = nh; + } + } + } + break; + case "fullday_item_move": + sx = d.sx; + x = e.pageX; + y = e.pageY; + diffx = x - sx; + if (diffx > 5 || diffx < -5 || d.lasso) { + if (!d.lasso) { + w1 = d.dp > 1 ? (d.pw - 4) * 1.5 : (d.pw - 4); + cp = d.target.clone(); + if (d.dp > 1) { + cp.find("div.rb-i>span").prepend("(" + d.dp + " " + i18n.xgcalendar.day_plural + ") "); + } + cpwrap = $("
").append(cp).appendTo(document.body); + d.cpwrap = cpwrap; + d.lasso = $("
"); + $(document.body).append(d.lasso); + cp = cpwrap = null; + } + fixcppostion(d.cpwrap, e, d.xa, d.ya); + ndi = getdi(d.xa, d.ya, x, e.pageY); + if (!d.cdi || d.cdi.di != ndi.di) { + addlasso(d.lasso, ndi, { x:ndi.x, y:ndi.y, di:ndi.di + d.dp - 1 }, d.xa, d.ya, d.h); + } + d.cdi = ndi; + } + break; + case "std_item_month_drag": + sx = d.sx; + sy = d.sy; + x = e.pageX; + y = e.pageY; + var diffx = x - sx; + diffy = y - sy; + if (diffx > 5 || diffx < -5 || diffy > 5 || diffy < -5 || d.lasso) { + if (!d.lasso) { + w1 = d.dp > 1 ? (d.pw - 4) * 1.5 : (d.pw - 4); + cp = d.target.clone(); + if (d.dp > 1) { + cp.find("div.rb-i>span").prepend("(" + d.dp + " " + i18n.xgcalendar.day_plural + ") "); + } + cpwrap = $("
").append(cp).appendTo(document.body); + d.cpwrap = cpwrap; + d.lasso = $("
"); + $(document.body).append(d.lasso); + cp = cpwrap = null; + } + fixcppostion(d.cpwrap, e, d.xa, d.ya); + ndi = getdi(d.xa, d.ya, x, e.pageY); + if (!d.cdi || d.cdi.di != ndi.di) { + addlasso(d.lasso, ndi, { x:ndi.x, y:ndi.y, di:ndi.di + d.dp - 1 }, d.xa, d.ya, d.h); + } + d.cdi = ndi; + } + break; + } + } + return false; + } + + function dragEnd(e) { + if (_dragdata) { + var d = _dragdata; + var tp, start, end, gh; + switch (d.type) { + case "std_empty_drag": //day view + var wrapid = new Date().getTime(); + tp = d.target.offset().top; + if (!d.cpwrap) { + gh = gH(d.sy, d.sy + option.hour_height, tp); + var ny = gP(gh.sh, gh.sm); + var tempdata = buildtempdayevent(gh.sh, gh.sm, gh.eh, gh.em, gh.h, "", "", "", option.std_color); + d.cpwrap = $("
").html(tempdata); + $(d.target).find("div.tg-col-overlaywrapper").append(d.cpwrap); + d.cgh = gh; + } + var pos = d.cpwrap.offset(); + pos.left = pos.left + 30; + d.cpwrap.attr("id", wrapid); + start = new Date(parseInt(d.target.data("abbr")) + (d.cgh.sh * 3600 + d.cgh.sm * 60) * 1000); + end = new Date(parseInt(d.target.data("abbr")) + (d.cgh.eh * 3600 + d.cgh.em * 60) * 1000); + _dragevent = function () { + $("#" + wrapid).remove(); + $("#bbit-cal-buddle").css("visibility", "hidden"); + }; + quickadd(start, end, false, pos); + break; + case "fullday_empty_drag": //week view + case "empty_month_drag": //month view + var source = e.srcElement || e.target; + var lassoid = new Date().getTime(); + if (!d.lasso) { + if ($(source).hasClass("monthdayshow")) { + weekormonthtoday.call($(source).parent()[0], e); + break; + } + d.fdi = d.sdi = getdi(d.xa, d.ya, d.sx, d.sy); + d.lasso = $("
"); + $(document.body).append(d.lasso); + addlasso(d.lasso, d.sdi, d.fdi, d.xa, d.ya, d.h); + } + d.lasso.attr("id", lassoid); + var si = Math.min(d.fdi.di, d.sdi.di); + var ei = Math.max(d.fdi.di, d.sdi.di); + var firstday = option.vstart; + start = DateAdd("d", si, firstday); + end = DateAdd("d", ei, firstday); + _dragevent = function () { + $("#" + lassoid).remove(); + }; + quickadd(start, end, true, { left:e.pageX, top:e.pageY }); + break; + case "std_item_move": // event moving + if (d.cpwrap) { + start = DateAdd("d", d.cdi, option.vstart); + end = DateAdd("d", d.cdi, option.vstart); + gh = gW(d.ny, d.ny + d.h); + start.setHours(gh.sh, gh.sm); + end.setHours(gh.eh, gh.em); + if (start.getTime() == d.data["start"].getTime() && end.getTime() == d.data["end"].getTime()) { + d.cpwrap.remove(); + d.target.show(); + } + else { + dayupdate(d.data, start, end); + } + } + break; + case "std_item_resize": //Resize + if (d.cpwrap) { + start = new Date(d.data["start"].toString()); + end = new Date(d.data["end"].toString()); + gh = gW(d.top, d.top + d.nh); + start.setHours(gh.sh, gh.sm); + end.setHours(gh.eh, gh.em); + if (start.getTime() == d.data["start"].getTime() && end.getTime() == d.data["end"].getTime()) { + d.cpwrap.remove(); + d.target.show(); + } + else { + dayupdate(d.data, start, end); + } + } + break; + case "fullday_item_move": + case "std_item_month_drag": + if (d.lasso) { + d.cpwrap.remove(); + d.lasso.remove(); + start = new Date(d.data["start"].toString()); + end = new Date(d.data["end"].toString()); + var currrentdate = DateAdd("d", d.cdi.di, option.vstart); + var diff = DateDiff("d", start, currrentdate); + start = DateAdd("d", diff, start); + end = DateAdd("d", diff, end); + if (start.getTime() != d.data["start"].getTime() || end.getTime() != d.data["end"].getTime()) { + dayupdate(d.data, start, end); + } + } + break; + } + d = _dragdata = null; + $('body').noSelect(false); + return false; + } + return false; + } + + function getdi(xa, ya, x, y) { + var ty = 0; + var tx = 0; + var lx = 0; + var ly = 0; + if (xa && xa.length != 0) { + lx = xa.length; + if (x >= xa[lx - 1].e) { + tx = lx - 1; + } + else { + for (var i = 0; i < lx; i++) { + if (x > xa[i].s && x <= xa[i].e) { + tx = i; + break; + } + } + } + } + if (ya && ya.length != 0) { + ly = ya.length; + if (y >= ya[ly - 1].e) { + ty = ly - 1; + } + else { + for (var j = 0; j < ly; j++) { + if (y > ya[j].s && y <= ya[j].e) { + ty = j; + break; + } + } + } + } + return { x:tx, y:ty, di:ty * lx + tx }; + } + + function addlasso(lasso, sdi, edi, xa, ya, height) { + var diff = sdi.di > edi.di ? sdi.di - edi.di : edi.di - sdi.di; + diff++; + var sp = sdi.di > edi.di ? edi : sdi; + var l = xa.length > 0 ? xa.length : 1; + var h = ya.length > 0 ? ya.length : 1; + var play = []; + var width = xa[0].e - xa[0].s; + var i = sp.x; + var j = sp.y; + var max = Math.min(document.documentElement.clientWidth, xa[l - 1].e) - 2; + + while (j < h && diff > 0) { + var left = xa[i].s; + var d = i + diff > l ? l - i : diff; + var wid = width * d; + while (left + wid >= max) { + wid--; + } + play.push(Tp(__LASSOTEMP, { left:left, top:ya[j].s, height:height, width:wid })); + i = 0; + diff = diff - d; + j++; + } + lasso.html(play.join("")); + } + + function fixcppostion(cpwrap, e, xa, ya) { + var x = e.pageX - 6; + var y = e.pageY - 4; + var w = cpwrap.width(); + var h = 21; + var lmin = xa[0].s + 6; + var tmin = ya[0].s + 4; + var lmax = xa[xa.length - 1].e - w - 2; + var tmax = ya[ya.length - 1].e - h - 2; + if (x > lmax) { + x = lmax; + } + if (x <= lmin) { + x = lmin + 1; + } + if (y <= tmin) { + y = tmin + 1; + } + if (y > tmax) { + y = tmax; + } + cpwrap.css({ left:x, top:y }); + } + + $(document) + .mousemove(dragMove) + .mouseup(dragEnd); + //.mouseout(dragEnd); + + this[0].bcal = { + sv:function (view) { //switch view + if (view == option.view) { + return; + } + clearcontainer(); + option.view = view; + render(); + populate(); + }, + rf:function () { + populate(); + }, + gt:function (d) { + if (!d) { + d = new Date(); + } + option.showday = d; + render(); + populate(); + }, + + pv:function () { + switch (option.view) { + case "day": + option.showday = DateAdd("d", -1, option.showday); + break; + case "week": + option.showday = DateAdd("w", -1, option.showday); + break; + case "multi_days": + option.showday = DateAdd("w", -1, option.showday); + break; + case "month": + option.showday = DateAdd("m", -1, option.showday); + break; + } + render(); + populate(); + }, + nt:function () { + switch (option.view) { + case "day": + option.showday = DateAdd("d", 1, option.showday); + break; + case "week": + option.showday = DateAdd("w", 1, option.showday); + break; + case "multi_days": + option.showday = DateAdd("w", 1, option.showday); + break; + case "month": + var od = option.showday.getDate(); + option.showday = DateAdd("m", 1, option.showday); + var nd = option.showday.getDate(); + if (od != nd) //we go to the next month + { + option.showday = DateAdd("d", 0 - nd, option.showday); //last day of last month + } + break; + } + render(); + populate(); + }, + go:function () { + return option; + }, + so:function (p) { + option = $.extend(option, p); + } + }; + + return this; + }; + + /** + * @description {Method} switchView To switch to another view. + * @param {String} view View name, one of 'day', 'week', 'multi_days', 'month'. + */ + $.fn.switchView = function (view) { + return this.each(function () { + if (this.bcal) { + this.bcal.sv(view); + } + }) + }; + + /** + * @description {Method} reload To reload event of current time range. + */ + $.fn.reload = function () { + return this.each(function () { + if (this.bcal) { + this.bcal.rf(); + } + }) + }; + + /** + * @description {Method} gotoDate To go to a range containing date. + * If view is week, it will go to a week containing date. + * If view is month, it will got to a month containing date. + * @param {Date} d. Date to go. + */ + $.fn.gotoDate = function (d) { + return this.each(function () { + if (this.bcal) { + this.bcal.gt(d); + } + }) + }; + + /** + * @description {Method} previousRange To go to previous date range. + * If view is week, it will go to previous week. + * If view is month, it will got to previous month. + */ + $.fn.previousRange = function () { + return this.each(function () { + if (this.bcal) { + this.bcal.pv(); + } + }) + }; + + /** + * @description {Method} nextRange To go to next date range. + * If view is week, it will go to next week. + * If view is month, it will got to next month. + */ + $.fn.nextRange = function () { + return this.each(function () { + if (this.bcal) { + this.bcal.nt(); + } + }) + }; + + + $.fn.BcalGetOp = function () { + if (this[0].bcal) { + return this[0].bcal.go(); + } + return null; + }; + + + $.fn.BcalSetOp = function (p) { + if (this[0].bcal) { + return this[0].bcal.so(p); + } + }; + +})(jQuery); \ No newline at end of file diff --git a/dav/common/wdcal/js/main.js b/dav/common/wdcal/js/main.js new file mode 100644 index 000000000..a238f010b --- /dev/null +++ b/dav/common/wdcal/js/main.js @@ -0,0 +1,210 @@ +$(function () { + "use strict"; + + $.fn.animexxCalendar = function (option) { + //(wdcal_view, std_theme, data_feed_url, readonly, height_diff) { + + var url_cal_add = "?"; + $(this).find(".calselect input[type=checkbox]").each(function() { + if ($(this).prop("checked")) url_cal_add += "cal[]=" + $(this).val() + "&"; + }); + + var def = { + calendars:[], + calendars_show:[], + view:"week", + theme:0, + onWeekOrMonthToDay:wtd, + onBeforeRequestData:cal_beforerequest, + onAfterRequestData:cal_afterrequest, + onRequestDataError:cal_onerror, + autoload:true, + data_feed_url:"", + url:option.data_feed_url + url_cal_add + "method=list", + quickAddUrl:option.data_feed_url + url_cal_add + "method=add", + quickUpdateUrl:option.data_feed_url + url_cal_add + "method=update", + quickDeleteUrl:option.data_feed_url + url_cal_add + "method=remove" + }; + + option = $.extend(def, option); + + var $animexxcal = $(this), + $gridcontainer = $animexxcal.find(".gridcontainer"), + $dv = $animexxcal.find(".calhead"), + $caltoolbar = $animexxcal.find(".ctoolbar"), + $txtdatetimeshow = $animexxcal.find(".txtdatetimeshow"), + $loadingpanel = $animexxcal.find(".loadingpanel"), + $loaderrpanel = $animexxcal.find(".loaderror"); + + var _MH = document.documentElement.clientHeight; + var dvH = $dv.height() + 2; + + option.height = _MH - dvH - option.height_diff; + if (option.height < 300) option.height = 300; + option.eventItems = []; + + $animexxcal.find(".hdtxtshow").datepicker({ + changeMonth: true, + changeYear: true, + onSelect: function(dateText, inst) { + var r = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay); + var p = $gridcontainer.gotoDate(r).BcalGetOp(); + if (p && p.datestrshow) { + $animexxcal.find(".txtdatetimeshow").text(p.datestrshow); + } + } + }); + $animexxcal.find(".txtdatetimeshow").css("cursor", "pointer").bind("click", function() { + $animexxcal.find(".hdtxtshow").datepicker("show"); + }); + + var p = $gridcontainer.bcalendar(option).BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + + $caltoolbar.noSelect(); + + function cal_beforerequest(type) { + var t = "Lade Daten..."; + switch (type) { + case 1: + t = "Lade Daten..."; + break; + case 2: + case 3: + case 4: + t = "Wird bearbeitete ..."; + break; + } + $loaderrpanel.hide(); + $loadingpanel.html(t).show(); + } + + function cal_afterrequest(type) { + var p = $gridcontainer.BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + + switch (type) { + case 1: + $loadingpanel.hide(); + break; + case 2: + case 3: + case 4: + $loadingpanel.html("Erfolg!"); + $gridcontainer.reload(); + window.setTimeout(function () { + $loadingpanel.hide(); + }, 2000); + break; + } + + } + + function cal_onerror(type, data) { + $loaderrpanel.show(); + } + + function wtd(p) { + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + $caltoolbar.find("div.fcurrent").removeClass("fcurrent"); + $animexxcal.find(".showdaybtn").addClass("fcurrent"); + } + + //to show day view + $animexxcal.find(".showdaybtn").on("click", function (e) { + //document.location.href="#day"; + $caltoolbar.find("div.fcurrent").removeClass("fcurrent"); + $(this).addClass("fcurrent"); + var p = $gridcontainer.switchView("day").BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + e.preventDefault(); + }); + //to show week view + $animexxcal.find(".showweekbtn").on("click", function (e) { + //document.location.href="#week"; + $caltoolbar.find("div.fcurrent").removeClass("fcurrent"); + $(this).addClass("fcurrent"); + var p = $gridcontainer.switchView("week").BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + e.preventDefault(); + }); + //to show month view + $animexxcal.find(".showmonthbtn").on("click", function (e) { + //document.location.href="#month"; + $caltoolbar.find("div.fcurrent").removeClass("fcurrent"); + $(this).addClass("fcurrent"); + var p = $gridcontainer.switchView("month").BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + e.preventDefault(); + }); + + $animexxcal.find(".showreflashbtn").on("click", function (e) { + $gridcontainer.reload(); + e.preventDefault(); + }); + + //go to today + $animexxcal.find(".showtodaybtn").on("click", function (e) { + var p = $gridcontainer.gotoDate().BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + e.preventDefault(); + + }); + //previous date range + $animexxcal.find(".sfprevbtn").on("click", function (e) { + var p = $gridcontainer.previousRange().BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + e.preventDefault(); + }); + //next date range + $animexxcal.find(".sfnextbtn").on("click", function (e) { + var p = $gridcontainer.nextRange().BcalGetOp(); + if (p && p.datestrshow) { + $txtdatetimeshow.text(p.datestrshow); + } + e.preventDefault(); + }); + + $animexxcal.find(".calselect input[type=checkbox]").on("click change", function() { + var url_cal_add = option.data_feed_url + "?"; + $animexxcal.find(".calselect input[type=checkbox]").each(function() { + if ($(this).prop("checked")) url_cal_add += "cal[]=" + $(this).val() + "&"; + }); +/* + url:option.data_feed_url + url_cal_add + "method=list", + quickAddUrl:option.data_feed_url + url_cal_add + "method=add", + quickUpdateUrl:option.data_feed_url + url_cal_add + "method=update", + quickDeleteUrl:option.data_feed_url + url_cal_add + "method=remove" + + */ + var url = url_cal_add + "method=list"; + var p = $gridcontainer.BcalGetOp(); + if (p.url != url) { + $gridcontainer.BcalSetOp({ + "url": url_cal_add + "method=list", + "quickAddUrl": url_cal_add + "method=add", + "quickUpdateUrl": url_cal_add + "method=update", + "quickDeleteUrl": url_cal_add + "method=remove" + }); + $gridcontainer.reload(); + } + }); + } + +}); \ No newline at end of file diff --git a/dav/common/wdcal/js/wdCalendar_lang_DE.js b/dav/common/wdcal/js/wdCalendar_lang_DE.js new file mode 100644 index 000000000..261a7fb76 --- /dev/null +++ b/dav/common/wdcal/js/wdCalendar_lang_DE.js @@ -0,0 +1,57 @@ +var i18n = $.extend({}, i18n || {}, { + xgcalendar: { + dateformat: { + "fulldaykey": "ddMMyyyy", + "fulldayshow": "d L yyyy", + "separator": ".", + "year_index": 2, + "month_index": 1, + "day_index": 0, + "day": "d", + "sun": "So", + "mon": "Mo", + "tue": "Di", + "wed": "Mi", + "thu": "Do", + "fri": "Fr", + "sat": "Sa", + "jan": "Jan", + "feb": "Feb", + "mar": "Mär", + "apr": "Apr", + "may": "Mai", + "jun": "Jun", + "jul": "Jul", + "aug": "Aug", + "sep": "Sep", + "oct": "Okt", + "nov": "Nov", + "dec": "Dez" + }, + "no_implemented": "Nicht eingebaut", + "to_date_view": "Zum aktuellen Datum gehen", + "i_undefined": "Undefined", + "allday_event": "Ganztages-Termin", + "repeat_event": "Wiederholter Termin", + "time": "Zeit", + "event": "Termin", + "location": "Ort", + "participant": "Teilnehmer", + "get_data_exception": "Exception when getting data", + "new_event": "Neuer Termin", + "confirm_delete_event": "Diesen Termin wirklich löschen? ", + "confrim_delete_event_or_all": "Nur diesen einen Termin löschen, oder alle Wiederholungen? \r\n[OK] für diesen einen, [Abbrechen] für alle.", + "data_format_error": "Data format error! ", + "invalid_title": "Der Titel des Termins darf nicht leer sein und kein ($<>) enthalten.", + "view_no_ready": "View is not ready", + "example": "e.g., Treffen in Raum 23", + "content": "Was", + "create_event": "Termin anlegen", + "update_detail": "Details bearbeiten", + "click_to_detail": "Details anzeigen", + "i_delete": "Löschen", + "day_plural": "Tage", + "others": "Weitere: ", + "item": "" + } +}); diff --git a/dav/common/wdcal/js/wdCalendar_lang_EN.js b/dav/common/wdcal/js/wdCalendar_lang_EN.js new file mode 100644 index 000000000..3f305a736 --- /dev/null +++ b/dav/common/wdcal/js/wdCalendar_lang_EN.js @@ -0,0 +1,57 @@ +var i18n = $.extend({}, i18n || {}, { + xgcalendar: { + dateformat: { + "fulldaykey": "ddMMyyyy", + "fulldayshow": "d L yyyy", + "separator": ".", + "year_index": 2, + "month_index": 1, + "day_index": 0, + "day": "d", + "sun": "Su", + "mon": "Mo", + "tue": "Tu", + "wed": "Mi", + "thu": "Th", + "fri": "Fr", + "sat": "Sa", + "jan": "Jan", + "feb": "Feb", + "mar": "Mar", + "apr": "Apr", + "may": "May", + "jun": "Jun", + "jul": "Jul", + "aug": "Aug", + "sep": "Sep", + "oct": "Oct", + "nov": "Nov", + "dec": "Dec" + }, + "no_implemented": "Not implemented", + "to_date_view": "Go to today", + "i_undefined": "Undefined", + "allday_event": "All-day event", + "repeat_event": "Recurring event", + "time": "Time", + "event": "Event", + "location": "Loaction", + "participant": "Attendees", + "get_data_exception": "Exception when getting data", + "new_event": "New event", + "confirm_delete_event": "Do you really want to delete this event?", + "confrim_delete_event_or_all": "Do you want to delete this event alone, or all recurrences? \r\n[OK] for this single item, [Abort] for all.", + "data_format_error": "Data format error!", + "invalid_title": "The title of an event must not be empty and must not contain ($<>).", + "view_no_ready": "View is not ready", + "example": "e.g., Meeting in room 23", + "content": "Title", + "create_event": "Create event", + "update_detail": "Edit", + "click_to_detail": "Show details", + "i_delete": "Delete", + "day_plural": "days", + "others": "More: ", + "item": "" + } +}); diff --git a/dav/common/wdcal_cal_source.inc.php b/dav/common/wdcal_cal_source.inc.php new file mode 100644 index 000000000..9db340221 --- /dev/null +++ b/dav/common/wdcal_cal_source.inc.php @@ -0,0 +1,138 @@ +namespace_id = IntVal($namespace_id); + $this->user_id = IntVal($user_id); + + $x = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uid` = %d", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), $this->namespace_id, $this->user_id + ); + + if (count($x) != 1) throw new Sabre_DAV_Exception_NotFound("Not found"); + + try { + $this->calendarDb = new DBClass_friendica_calendars($x[0]); + } catch (Exception $e) { + throw new Sabre_DAV_Exception_NotFound("Not found"); + } + } + + /** + * @abstract + * @return int + */ + public static abstract function getNamespace(); + + /** + * @abstract + * @param int $user + * @return array + */ + public abstract function getPermissionsCalendar($user); + + /** + * @abstract + * @param int $user + * @param string $item_uri + * @param string $recurrence_uri + * @param array|null $item_arr + * @return array + */ + public abstract function getPermissionsItem($user, $item_uri, $recurrence_uri, $item_arr = null); + + /** + * @param string $uri + * @param array $start + * @param array $end + * @param string $subject + * @param bool $allday + * @param string $description + * @param string $location + * @param null $color + * @param string $timezone + * @param bool $notification + * @param null $notification_type + * @param null $notification_value + */ + public abstract function updateItem($uri, $start, $end, $subject = "", $allday = false, $description = "", $location = "", $color = null, + $timezone = "", $notification = true, $notification_type = null, $notification_value = null); + + + /** + * @abstract + * @param array $start + * @param array $end + * @param string $subject + * @param bool $allday + * @param string $description + * @param string $location + * @param null $color + * @param string $timezone + * @param bool $notification + * @param null $notification_type + * @param null $notification_value + * @return array + */ + public abstract function addItem($start, $end, $subject, $allday = false, $description = "", $location = "", $color = null, + $timezone = "", $notification = true, $notification_type = null, $notification_value = null); + + + /** + * @param string $uri + */ + public abstract function removeItem($uri); + + + /** + * @abstract + * @param string $sd + * @param string $ed + * @param string $base_path + * @return array + */ + public abstract function listItemsByRange($sd, $ed, $base_path); + + + /** + * @abstract + * @param string $uri + * @return array + */ + public abstract function getItemByUri($uri); + + + /** + * @param string $uri + * @return null|string + */ + public function getItemDetailRedirect($uri) { + return null; + } + +} diff --git a/dav/common/wdcal_cal_source_private.inc.php b/dav/common/wdcal_cal_source_private.inc.php new file mode 100644 index 000000000..cb5918e18 --- /dev/null +++ b/dav/common/wdcal_cal_source_private.inc.php @@ -0,0 +1,332 @@ +calendarDb->uid) return array("read"=> true, "write"=> true); + return array("read"=> false, "write"=> false); + } + + /** + * @param int $user + * @param string $item_uri + * @param string $recurrence_uri + * @param null|array $item_arr + * @return array + */ + public function getPermissionsItem($user, $item_uri, $recurrence_uri, $item_arr = null) + { + $cal_perm = $this->getPermissionsCalendar($user); + if (!$cal_perm["read"]) return array("read"=> false, "write"=> false); + if (!$cal_perm["write"]) array("read"=> true, "write"=> false); + + if ($item_arr === null) { + $x = q("SELECT `permission_edit` FROM %s%sjqcalendar WHERE `namespace` = %d AND `namespace_id` = %d AND `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), $this->namespace_id, dbesc($item_uri), dbesc($recurrence_uri) + ); + if (!$x || count($x) == 0) return array("read"=> false, "write"=> false); + return array("read"=> true, "write"=> ($x[0]["permission_edit"])); + } else { + return array("read"=> true, "write"=> ($item_arr["permission_edit"])); + } + + } + + /** + * @param string $uri + * @throws Sabre_DAV_Exception_NotFound + */ + public function removeItem($uri){ + $obj_alt = q("SELECT * FROM %s%sjqcalendar WHERE namespace = %d AND namespace_id = %d AND ical_uri = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), $this->namespace_id, dbesc($uri)); + + if (count($obj_alt) == 0) throw new Sabre_DAV_Exception_NotFound("Not found"); + + $calendarBackend = new Sabre_CalDAV_Backend_Std(); + $calendarBackend->deleteCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $obj_alt[0]["ical_uri"]); + } + + /** + * @param string $uri + * @param array $start + * @param array $end + * @param string $subject + * @param bool $allday + * @param string $description + * @param string $location + * @param null $color + * @param string $timezone + * @param bool $notification + * @param null $notification_type + * @param null $notification_value + * @throws Sabre_DAV_Exception_NotFound + * @throws Sabre_DAV_Exception_Conflict + */ + public function updateItem($uri, $start, $end, $subject = "", $allday = false, $description = "", $location = "", $color = null, $timezone = "", $notification = true, $notification_type = null, $notification_value = null) + { + $a = get_app(); + + $usr_id = IntVal($this->calendarDb->uid); + + $old = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `namespace` = %d AND `namespace_id` = %d AND `ical_uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $usr_id, $this->getNamespace(), $this->namespace_id, dbesc($uri)); + if (count($old) == 0) throw new Sabre_DAV_Exception_NotFound("Not Found 1"); + $old_obj = new DBClass_friendica_jqcalendar($old[0]); + + $calendarBackend = new Sabre_CalDAV_Backend_Std(); + $obj = $calendarBackend->getCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $old_obj->ical_uri); + if (!$obj) throw new Sabre_DAV_Exception_NotFound("Not Found 2"); + + $v = new vcalendar(); + $v->setConfig('unique_id', $a->get_hostname()); + + $v->setMethod('PUBLISH'); + $v->setProperty("x-wr-calname", "AnimexxCal"); + $v->setProperty("X-WR-CALDESC", "Animexx Calendar"); + $v->setProperty("X-WR-TIMEZONE", $a->timezone); + + $obj["calendardata"] = icalendar_sanitize_string($obj["calendardata"]); + + $v->parse($obj["calendardata"]); + /** @var $vevent vevent */ + $vevent = $v->getComponent('vevent'); + + if (trim($vevent->getProperty('uid')) . ".ics" != $old_obj->ical_uri) + throw new Sabre_DAV_Exception_Conflict("URI != URI: " . $old_obj->ical_uri . " vs. " . trim($vevent->getProperty("uid"))); + + if ($end["year"] < $start["year"] || + ($end["year"] == $start["year"] && $end["month"] < $start["month"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] < $start["day"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] < $start["hour"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] == $start["hour"] && $end["minute"] < $start["minute"]) || + ($end["year"] == $start["year"] && $end["month"] == $start["month"] && $end["day"] == $start["day"] && $end["hour"] == $start["hour"] && $end["minute"] == $start["minute"] && $end["second"] < $start["second"]) + ) { + $end = $start; + if ($end["hour"] < 23) $end["hour"]++; + } // DTEND muss <= DTSTART + + if ($start["hour"] == 0 && $start["minute"] == 0 && $end["hour"] == 23 && $end["minute"] == 59) { + $allday = true; + } + + if ($allday) { + $vevent->setDtstart($start["year"], $start["month"], $start["day"], FALSE, FALSE, FALSE, FALSE, array("VALUE"=> "DATE")); + $end = mktime(0, 0, 0, $end["month"], $end["day"], $end["year"]) + 3600 * 24; + + // If a DST change occurs on the current day + $end += date("Z", ($end - 3600*24)) - date("Z", $end); + + $vevent->setDtend(date("Y", $end), date("m", $end), date("d", $end), FALSE, FALSE, FALSE, FALSE, array("VALUE"=> "DATE")); + } else { + $vevent->setDtstart($start["year"], $start["month"], $start["day"], $start["hour"], $start["minute"], $start["second"], FALSE, array("VALUE"=> "DATE-TIME")); + $vevent->setDtend($end["year"], $end["month"], $end["day"], $end["hour"], $end["minute"], $end["second"], FALSE, array("VALUE"=> "DATE-TIME")); + } + + if ($subject != "") { + $vevent->setProperty('LOCATION', $location); + $vevent->setProperty('summary', $subject); + $vevent->setProperty('description', $description); + } + if (!is_null($color) && $color >= 0) $vevent->setProperty("X-ANIMEXX-COLOR", $color); + + if (!$notification || $notification_type != null) { + $vevent->deleteComponent("VALARM"); + + if ($notification) { + $valarm = new valarm(); + + $valarm->setTrigger( + ($notification_type == "year" ? $notification_value : 0), + ($notification_type == "month" ? $notification_value : 0), + ($notification_type == "day" ? $notification_value : 0), + ($notification_type == "week" ? $notification_value : 0), + ($notification_type == "hour" ? $notification_value : 0), + ($notification_type == "minute" ? $notification_value : 0), + ($notification_type == "minute" ? $notification_value : 0), + true, + ($notification_value > 0) + ); + $valarm->setProperty("ACTION", "DISPLAY"); + $valarm->setProperty("DESCRIPTION", $subject); + + $vevent->setComponent($valarm); + } + } + + + $v->deleteComponent("vevent"); + $v->setComponent($vevent, trim($vevent->getProperty("uid"))); + $ical = $v->createCalendar(); + + $calendarBackend->updateCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $old_obj->ical_uri, $ical); + } + + /** + * @param array $start + * @param array $end + * @param string $subject + * @param bool $allday + * @param string $description + * @param string $location + * @param null $color + * @param string $timezone + * @param bool $notification + * @param null $notification_type + * @param null $notification_value + * @return array|string + */ + public function addItem($start, $end, $subject, $allday = false, $description = "", $location = "", $color = null, + $timezone = "", $notification = true, $notification_type = null, $notification_value = null) + { + $a = get_app(); + + $v = new vcalendar(); + $v->setConfig('unique_id', $a->get_hostname()); + + $v->setProperty('method', 'PUBLISH'); + $v->setProperty("x-wr-calname", "AnimexxCal"); + $v->setProperty("X-WR-CALDESC", "Animexx Calendar"); + $v->setProperty("X-WR-TIMEZONE", $a->timezone); + + $vevent = dav_create_vevent($start, $end, $allday); + $vevent->setLocation(icalendar_sanitize_string($location)); + $vevent->setSummary(icalendar_sanitize_string($subject)); + $vevent->setDescription(icalendar_sanitize_string($description)); + + if (!is_null($color) && $color >= 0) $vevent->setProperty("X-ANIMEXX-COLOR", $color); + + if ($notification && $notification_type == null) { + if ($allday) { + $notification_type = "hour"; + $notification_value = 24; + } else { + $notification_type = "minute"; + $notification_value = 60; + } + } + if ($notification) { + $valarm = new valarm(); + + $valarm->setTrigger( + ($notification_type == "year" ? $notification_value : 0), + ($notification_type == "month" ? $notification_value : 0), + ($notification_type == "day" ? $notification_value : 0), + ($notification_type == "week" ? $notification_value : 0), + ($notification_type == "hour" ? $notification_value : 0), + ($notification_type == "minute" ? $notification_value : 0), + ($notification_type == "second" ? $notification_value : 0), + true, + ($notification_value > 0) + ); + $valarm->setAction("DISPLAY"); + $valarm->setDescription($subject); + + $vevent->setComponent($valarm); + + } + + $v->setComponent($vevent); + $ical = $v->createCalendar(); + $obj_id = trim($vevent->getProperty("UID")); + + $calendarBackend = new Sabre_CalDAV_Backend_Std(); + $calendarBackend->createCalendarObject($this->getNamespace() . "-" . $this->namespace_id, $obj_id . ".ics", $ical); + + return $obj_id . ".ics"; + } + + private function jqcal2wdcal($row, $usr_id, $base_path) { + $evo = new DBClass_friendica_jqcalendar($row); + $not = q("SELECT COUNT(*) num FROM %s%snotifications WHERE `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($row["ical_uri"]), $row["ical_recurr_uri"] + ); + $editable = $this->getPermissionsItem($usr_id, $row["ical_uri"], $row["ical_recurr_uri"], $row); + $recurring = (is_null($evo->RecurringRule) || $evo->RecurringRule == "" || $evo->RecurringRule == "NULL" ? 0 : 1); + + $end = wdcal_mySql2PhpTime($evo->EndTime); + if ($evo->IsAllDayEvent) $end -= 1; + + $arr = array( + "uri" => $evo->ical_uri, + "subject" => escape_tags($evo->Subject), + "start" => wdcal_mySql2PhpTime($evo->StartTime), + "end" => $end, + "is_allday" => $evo->IsAllDayEvent, + "is_moredays" => 0, + "is_recurring" => $recurring, + "color" => (is_null($evo->Color) || $evo->Color == "" ? $this->calendarDb->calendarcolor : $evo->Color), + "is_editable" => ($editable ? 1 : 0), + "is_editable_quick" => ($editable && !$recurring ? 1 : 0), + "location" => $evo->Location, + "attendees" => '', + "has_notification" => ($not[0]["num"] > 0 ? 1 : 0), + "url_detail" => $base_path . $evo->ical_uri . "/", + "url_edit" => $base_path . $evo->ical_uri . "/edit/", + "special_type" => "", + ); + return $arr; + } + + /** + * @param string $sd + * @param string $ed + * @param string $base_path + * @return array + */ + public function listItemsByRange($sd, $ed, $base_path) + { + + $usr_id = IntVal($this->calendarDb->uid); + + $von = wdcal_php2MySqlTime($sd); + $bis = wdcal_php2MySqlTime($ed); + + // @TODO Events, die früher angefangen haben, aber noch andauern + $evs = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `namespace` = %d AND `namespace_id` = %d AND `starttime` between '%s' and '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, + $usr_id, $this->getNamespace(), $this->namespace_id, dbesc($von), dbesc($bis)); + + $events = array(); + foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $usr_id, $base_path); + + return $events; + } + + /** + * @param string $uri + * @throws Sabre_DAV_Exception_NotFound + * @return array + */ + public function getItemByUri($uri) + { + $usr_id = IntVal($this->calendarDb->uid); + $evs = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `namespace` = %d AND `namespace_id` = %d AND `ical_uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, + $usr_id, $this->getNamespace(), $this->namespace_id, dbesc($uri)); + if (count($evs) == 0) throw new Sabre_DAV_Exception_NotFound(); + return $this->jqcal2wdcal($evs[0], $usr_id); + } + + + /** + * @param string $uri + * @return string + */ + public function getItemDetailRedirect($uri) { + return "/dav/wdcal/$uri/edit/"; + } +} diff --git a/dav/common/wdcal_configuration.php b/dav/common/wdcal_configuration.php new file mode 100644 index 000000000..8d53edee9 --- /dev/null +++ b/dav/common/wdcal_configuration.php @@ -0,0 +1,277 @@ + "wdcal_local_us", + self::LOCAL_DE => "wdcal_local_de", + ); + } + + /** + * @static + * @param int $config + * @return null|wdcal_local + */ + static function getInstance($config = 0) { + $classes = self::getInstanceClasses(); + if (isset($classes[$config])) return new $classes[$config]; + return null; + } + + /** + * @static + * @param int $uid + * @return wdcal_local + */ + static function getInstanceByUser($uid = 0) { + $dateformat = get_pconfig($uid, "dav", "dateformat"); + $format = self::getInstance($dateformat); + if ($format == null) $format = self::getInstance(self::LOCAL_US); + return $format; + } + + /** + * @abstract + * @static + * @return string + */ + abstract static function getName(); + + /** + * @static + * @abstract + * @return int + */ + abstract static function getID(); + + /** + * @param string $str + * @return int + */ + function date_local2timestamp($str) { + $x = $this->date_parseLocal($str); + return mktime($x["hour"], $x["minute"], $x["second"], $x["month"], $x["day"], $x["year"]); + } + + /** + * @abstract + * @param string $str + * @return array + */ + abstract function date_parseLocal($str); + + /** + * @abstract + * @param int $ts + * @return string + */ + abstract function date_timestamp2local($ts); + + /** + * @abstract + * @return int + */ + abstract function getFirstDayOfWeek(); + + /** + * @abstract + * @return string + */ + abstract function dateformat_js_dm1(); + /** + * @abstract + * @return string + */ + abstract function dateformat_js_dm2(); + + /** + * @abstract + * @return string + */ + abstract function dateformat_js_dm3(); + + /** + * @abstract + * @return string + */ + abstract function dateformat_datepicker_js(); + + /** + * @abstract + * @param int $ts + * @return string + */ + abstract function dateformat_datepicker_php($ts = 0); + +} + + + +class wdcal_local_us extends wdcal_local { + + /** + * @return string + */ + static function getName() { + return t("U.S. Time Format (mm/dd/YYYY)"); + } + + /** + * @static + * @return int + */ + static function getID() { + return wdcal_local::LOCAL_US; + } + + /** + * @param string $str + * @return array + */ + function date_parseLocal($str) { + return date_parse_from_format("m/d/Y H:i", $str); + } + + + /** + * @param int $ts + * @return string + */ + function date_timestamp2local($ts) + { + return date("m/d/Y H:i", $ts); + } + + /** + * @return int + */ + function getFirstDayOfWeek() { + return 0; + } + + /** + * @return string + */ + function dateformat_js_dm1() { + return "W, M/d"; + } + + /** + * @return string + */ + function dateformat_js_dm2() { + return "d. L"; + } + + /** + * @return string + */ + function dateformat_js_dm3() { + return "d L yyyy"; + } + + /** + * @return string + */ + function dateformat_datepicker_js() { + return "mm/dd/yy"; + } + + /** + * @param int $ts + * @return string + */ + function dateformat_datepicker_php($ts = 0) { + return date("m/d/Y", $ts); + } +} + +class wdcal_local_de extends wdcal_local { + + /** + * @return string + */ + static function getName() { + return t("German Time Format (dd.mm.YYYY)"); + } + + /** + * @static + * @return int + */ + static function getID() { + return wdcal_local::LOCAL_DE; + } + + /** + * @param string $str + * @return array + */ + function date_parseLocal($str) + { + return date_parse_from_format("d.m.Y H:i", $str); + } + + /** + * @param int $ts + * @return string + */ + function date_timestamp2local($ts) + { + return date("d.m.Y H:i", $ts); + } + + /** + * @return int + */ + function getFirstDayOfWeek() { + return 1; + } + + /** + * @return string + */ + function dateformat_js_dm1() { + return "W, d.M"; + } + + /** + * @return string + */ + function dateformat_js_dm2() { + return "d. L"; + } + + /** + * @return string + */ + function dateformat_js_dm3() { + return "d L yyyy"; + } + + /** + * @return string + */ + function dateformat_datepicker_js() { + return "dd.mm.yy"; + } + + /** + * @param int $ts + * @return string + */ + function dateformat_datepicker_php($ts = 0) { + return date("d.m.Y", $ts); + } +} + diff --git a/dav/database-init.inc.php b/dav/database-init.inc.php new file mode 100644 index 000000000..06e3abc52 --- /dev/null +++ b/dav/database-init.inc.php @@ -0,0 +1,199 @@ +q($st); + if ($db->error) $errors[] = $db->error; + } + + if (count($errors) == 0) set_config("dav", "db_version", CALDAV_DB_VERSION); + + return $errors; +} \ No newline at end of file diff --git a/dav/dav.php b/dav/dav.php new file mode 100644 index 000000000..8c29e385d --- /dev/null +++ b/dav/dav.php @@ -0,0 +1,12 @@ += 5.3. + * Version: 0.1 + * Author: Tobias Hößl + */ + +$_v = explode(".", phpversion()); +if ($_v[0] > 5 || ($_v[0] == 5 && $_v[1] >= 3)) { + require(__DIR__ . "/main.php"); +} \ No newline at end of file diff --git a/dav/dav_caldav_backend_friendica.inc.php b/dav/dav_caldav_backend_friendica.inc.php new file mode 100644 index 000000000..11b27ea6f --- /dev/null +++ b/dav/dav_caldav_backend_friendica.inc.php @@ -0,0 +1,141 @@ +user["uid"]; + $x = explode("-", $calendarId); + + $ret = array(); + $objs = FriendicaVirtualCalSourceBackend::getItemsByTime($user_id, $x[1]); + foreach ($objs as $obj) { + $ret[] = array( + "id" => IntVal($obj["data_uri"]), + "calendardata" => $obj["ical"], + "uri" => $obj["data_uri"], + "lastmodified" => $obj["date"], + "calendarid" => $calendarId, + "etag" => $obj["ical_etag"], + "size" => IntVal($obj["ical_size"]), + ); + } + + return $ret; + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param string $calendarId + * @param string $objectUri + * @throws Sabre_DAV_Exception_FileNotFound + * @return array + */ + function getCalendarObject($calendarId, $objectUri) + { + $a = get_app(); + $user_id = $a->user["uid"]; + $obj = FriendicaVirtualCalSourceBackend::getItemsByUri($user_id, $objectUri); + + return array( + "id" => IntVal($obj["data_uri"]), + "calendardata" => $obj["ical"], + "uri" => $obj["data_uri"], + "lastmodified" => $obj["date"], + "calendarid" => $calendarId, + "etag" => $obj["ical_etag"], + "size" => IntVal($obj["ical_size"]), + ); + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @throws Sabre_DAV_Exception_Forbidden + * @return null|string|void + */ + function createCalendarObject($calendarId, $objectUri, $calendarData) + { + throw new Sabre_DAV_Exception_Forbidden(); + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @throws Sabre_DAV_Exception_Forbidden + * @return null|string|void + */ + function updateCalendarObject($calendarId, $objectUri, $calendarData) + { + throw new Sabre_DAV_Exception_Forbidden(); + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + function deleteCalendarObject($calendarId, $objectUri) + { + throw new Sabre_DAV_Exception_Forbidden(); + } +} diff --git a/dav/dav_carddav_backend_friendica_community.inc.php b/dav/dav_carddav_backend_friendica_community.inc.php new file mode 100644 index 000000000..cf8b2ff00 --- /dev/null +++ b/dav/dav_carddav_backend_friendica_community.inc.php @@ -0,0 +1,320 @@ + CARDDAV_NAMESPACE_COMMUNITYCONTACTS . "-" . $uid, + 'uri' => "friendica", + 'principaluri' => $principalUri, + '{DAV:}displayname' => t("Friendica-Contacts"), + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => t("Your Friendica-Contacts"), + '{http://calendarserver.org/ns/}getctag' => $ctag, + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => + new Sabre_CardDAV_Property_SupportedAddressData(), + ); + + return $addressBooks; + + } + + + /** + * Updates an addressbook's properties + * + * See Sabre_DAV_IProperties for a description of the mutations array, as + * well as the return value. + * + * @param string $addressBookId + * @param array $mutations + * @throws Sabre_DAV_Exception_Forbidden + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateAddressBook($addressBookId, array $mutations) + { + throw new Sabre_DAV_Exception_Forbidden(); + } + + /** + * Creates a new address book + * + * @param string $principalUri + * @param string $url Just the 'basename' of the url. + * @param array $properties + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createAddressBook($principalUri, $url, array $properties) + { + throw new Sabre_DAV_Exception_Forbidden(); + } + + /** + * Deletes an entire addressbook and all its contents + * + * @param int $addressBookId + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function deleteAddressBook($addressBookId) + { + throw new Sabre_DAV_Exception_Forbidden(); + } + + + /** + * @param array $contact + * @return array + */ + private function dav_contactarr2vcardsource($contact) + { + $name = explode(" ", $contact["name"]); + $first_name = $last_name = ""; + $middle_name = array(); + $num = count($name); + for ($i = 0; $i < $num && $first_name == ""; $i++) if ($name[$i] != "") { + $first_name = $name[$i]; + unset($name[$i]); + } + for ($i = $num - 1; $i >= 0 && $last_name == ""; $i--) if (isset($name[$i]) && $name[$i] != "") { + $last_name = $name[$i]; + unset($name[$i]); + } + foreach ($name as $n) if ($n != "") $middle_name[] = $n; + $vcarddata = new vcard_source_data($first_name, implode(" ", $middle_name), $last_name); + $vcarddata->homepages[] = new vcard_source_data_homepage("pref", $contact["url"]); + $vcarddata->last_update = ($contact["last-update"] > 0 ? $contact["last-update"] : $contact["created"]); + + $photo = q("SELECT * FROM photo WHERE `contact-id` = %d ORDER BY scale DESC", $contact["id"]); //prefer size 80x80 + if ($photo && count($photo) > 0) { + $photodata = new vcard_source_data_photo(); + $photodata->width = $photo[0]["width"]; + $photodata->height = $photo[0]["height"]; + $photodata->type = "JPEG"; + $photodata->binarydata = $photo[0]["data"]; + $vcarddata->photo = $photodata; + } + + switch ($contact["network"]) { + case "face": + $vcarddata->socialnetworks[] = new vcard_source_data_socialnetwork("facebook", $contact["notify"], "http://www.facebook.com/" . $contact["notify"]); + break; + case "dfrn": + $vcarddata->socialnetworks[] = new vcard_source_data_socialnetwork("dfrn", $contact["nick"], $contact["url"]); + break; + case "twitter": + $vcarddata->socialnetworks[] = new vcard_source_data_socialnetwork("twitter", $contact["nick"], "http://twitter.com/" . $contact["nick"]); // @TODO Stimmt das? + break; + } + + $vcard = vcard_source_compile($vcarddata); + return array( + "id" => $contact["id"], + "carddata" => $vcard, + "uri" => $contact["id"] . ".vcf", + "lastmodified" => wdcal_mySql2PhpTime($vcarddata->last_update), + "etag" => md5($vcard), + "size" => strlen($vcard), + ); + + } + + /** + * @param int $uid + * @param array|int[] $exclude_ids + * @return array + */ + private function dav_getCommunityContactsVCards($uid = 0, $exclude_ids = array()) + { + $notin = (count($exclude_ids) > 0 ? " AND id NOT IN (" . implode(", ", $exclude_ids) . ") " : ""); + $uid = IntVal($uid); + $contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 $notin ORDER BY `name` ASC", $uid); + + $retdata = array(); + foreach ($contacts as $contact) { + $x = $this->dav_contactarr2vcardsource($contact); + $x["contact"] = $contact["id"]; + $retdata[] = $x; + } + return $retdata; + } + + + /** + * Returns all cards for a specific addressbook id. + * + * This method should return the following properties for each card: + * * carddata - raw vcard data + * * uri - Some unique url + * * lastmodified - A unix timestamp + * + * It's recommended to also return the following properties: + * * etag - A unique etag. This must change every time the card changes. + * * size - The size of the card in bytes. + * + * If these last two properties are provided, less time will be spent + * calculating them. If they are specified, you can also ommit carddata. + * This may speed up certain requests, especially with large cards. + * + * @param string $addressbookId + * @return array + */ + public function getCards($addressbookId) + { + $add = explode("-", $addressbookId); + + $indb = q('SELECT id, carddata, uri, lastmodified, etag, size, contact, manually_deleted FROM %s%scards WHERE namespace = %d AND namespace_id = %d', + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($add[0]), IntVal($add[1]) + ); + $found_contacts = array(); + $contacts = array(); + foreach ($indb as $x) { + if ($x["manually_deleted"] == 0) $contacts[] = $x; + $found_contacts[] = IntVal($x["contact"]); + } + $new_found = $this->dav_getCommunityContactsVCards($add[1], $found_contacts); + foreach ($new_found as $new) { + q("INSERT INTO %s%scards (namespace, namespace_id, contact, carddata, uri, lastmodified, manually_edited, manually_deleted, etag, size) + VALUES (%d, %d, %d, '%s', '%s', %d, 0, 0, '%s', %d)", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, + IntVal($add[0]), IntVal($add[1]), IntVal($new["contact"]), dbesc($new["carddata"]), dbesc($new["uri"]), time(), md5($new["carddata"]), strlen($new["carddata"]) + ); + } + return array_merge($contacts, $new_found); + } + + /** + * Returns a specfic card. + * + * The same set of properties must be returned as with getCards. The only + * exception is that 'carddata' is absolutely required. + * + * @param mixed $addressBookId + * @param string $cardUri + * @throws Sabre_DAV_Exception_NotFound + * @return array + */ + public function getCard($addressBookId, $cardUri) + { + $x = explode("-", $addressBookId); + $x = q("SELECT id, carddata, uri, lastmodified, etag, size FROM %s%scards WHERE namespace = %d AND namespace_id = %d AND uri = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri)); + if (count($x) == 0) throw new Sabre_DAV_Exception_NotFound(); + return $x[0]; + } + + /** + * Creates a new card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressbooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag is for the + * newly created resource, and must be enclosed with double quotes (that + * is, the string itself must contain the double quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param string $addressBookId + * @param string $cardUri + * @param string $cardData + * @throws Sabre_DAV_Exception_Forbidden + * @return string + */ + public function createCard($addressBookId, $cardUri, $cardData) + { + throw new Sabre_DAV_Exception_Forbidden(); + } + + /** + * Updates a card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressbooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag should + * match that of the updated resource, and must be enclosed with double + * quotes (that is: the string itself must contain the actual quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param string $addressBookId + * @param string $cardUri + * @param string $cardData + * @throws Sabre_DAV_Exception_Forbidden + * @return string|null + */ + public function updateCard($addressBookId, $cardUri, $cardData) + { + $x = explode("-", $addressBookId); + + $etag = md5($cardData); + q("UPDATE %s%scards SET carddata = '%s', lastmodified = %d, etag = '%s', size = %d, manually_edited = 1 WHERE uri = '%s' AND namespace = %d AND namespace_id =%d", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), time(), $etag, strlen($cardData), dbesc($cardUri), IntVal($x[10]), IntVal($x[1]) + ); + q('UPDATE %s%saddressbooks_community SET ctag = ctag + 1 WHERE uid = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); + + return '"' . $etag . '"'; + } + + /** + * Deletes a card + * + * @param string $addressBookId + * @param string $cardUri + * @throws Sabre_DAV_Exception_Forbidden + * @return bool + */ + public function deleteCard($addressBookId, $cardUri) + { + $x = explode("-", $addressBookId); + + q("UPDATE %s%scards SET manually_deleted = 1 WHERE namespace = %d AND namespace_id = %d AND uri = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[0]), IntVal($x[1]), dbesc($cardUri)); + q('UPDATE %s%saddressbooks_community SET ctag = ctag + 1 WHERE uid = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1])); + + return true; + } +} diff --git a/dav/dav_friendica_auth.inc.php b/dav/dav_friendica_auth.inc.php new file mode 100644 index 000000000..5082a2f95 --- /dev/null +++ b/dav/dav_friendica_auth.inc.php @@ -0,0 +1,58 @@ +currentUser); + } + + public function getCurrentUser() { + return $this->currentUser; + } + + /** + * Authenticates the user based on the current request. + * + * If authentication is successful, true must be returned. + * If authentication fails, an exception must be thrown. + * + * @param Sabre_DAV_Server $server + * @param string $realm + * @throws Sabre_DAV_Exception_NotAuthenticated + * @return bool + */ + public function authenticate(Sabre_DAV_Server $server, $realm) { + + $auth = new Sabre_HTTP_BasicAuth(); + $auth->setHTTPRequest($server->httpRequest); + $auth->setHTTPResponse($server->httpResponse); + $auth->setRealm($realm); + $userpass = $auth->getUserPass(); + if (!$userpass) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found'); + } + + // Authenticates the user + if (!$this->validateUserPass($userpass[0],$userpass[1])) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('Username or password does not match'); + } + $this->currentUser = strtolower($userpass[0]); + return true; + } + + + protected function validateUserPass($username, $password) { + + $user = array( + 'uri' => "/" . 'principals/users/' . strtolower($username), + ); + return $user; + } + +} diff --git a/dav/dav_friendica_principal.inc.php b/dav/dav_friendica_principal.inc.php new file mode 100644 index 000000000..eba31c288 --- /dev/null +++ b/dav/dav_friendica_principal.inc.php @@ -0,0 +1,201 @@ +authBackend = &$authBackend; + + } + + + /** + * Returns a list of principals based on a prefix. + * + * This prefix will often contain something like 'principals'. You are only + * expected to return principals that are in this base path. + * + * You are expected to return at least a 'uri' for every user, you can + * return any additional properties if you wish so. Common properties are: + * {DAV:}displayname + * {http://sabredav.org/ns}email-address - This is a custom SabreDAV + * field that's actualy injected in a number of other properties. If + * you have an email address, use this property. + * + * @param string $prefixPath + * @return array + */ + public function getPrincipalsByPrefix($prefixPath) + { + + // This backend only support principals in one collection + if ($prefixPath !== $this->prefix) return array(); + + $users = array(); + + $r = q("SELECT `nickname` FROM `user` WHERE `nickname` = '%s'", escape_tags($this->authBackend->getCurrentUser()) ); + foreach ($r as $t) { + $users[] = array( + 'uri' => $this->prefix . '/' . strtolower($t['nickname']), + '{DAV:}displayname' => $t['nickname'], + ); + } + + return $users; + + } + + /** + * Returns a specific principal, specified by it's path. + * The returned structure should be the exact same as from + * getPrincipalsByPrefix. + * + * @param string $path + * @return array + */ + public function getPrincipalByPath($path) + { + + list($prefixPath, $userName) = Sabre_DAV_URLUtil::splitPath($path); + + // This backend only support principals in one collection + if ($prefixPath !== $this->prefix) return null; + + $r = q("SELECT `nickname` FROM `user` WHERE `nickname` = '%s'", escape_tags($userName) ); + if (count($r) == 0) return array(); + + return array( + 'uri' => $this->prefix . '/' . strtolower($r[0]['nickname']), + '{DAV:}displayname' => $r[0]['nickname'], + ); + + } + + + function getGroupMemberSet($principal) + { + return array(); + } + + function getGroupMembership($principal) + { + return array(); + } + + + /** + * Updates the list of group members for a group principal. + * + * The principals should be passed as a list of uri's. + * + * @param string $principal + * @param array $members + * @throws Sabre_DAV_Exception + * @return void + */ + public function setGroupMemberSet($principal, array $members) + { + throw new Sabre_DAV_Exception('Operation not supported'); + } + + /** + * Updates one ore more webdav properties on a principal. + * + * The list of mutations is supplied as an array. Each key in the array is + * a propertyname, such as {DAV:}displayname. + * + * Each value is the actual value to be updated. If a value is null, it + * must be deleted. + * + * This method should be atomic. It must either completely succeed, or + * completely fail. Success and failure can simply be returned as 'true' or + * 'false'. + * + * It is also possible to return detailed failure information. In that case + * an array such as this should be returned: + * + * array( + * 200 => array( + * '{DAV:}prop1' => null, + * ), + * 201 => array( + * '{DAV:}prop2' => null, + * ), + * 403 => array( + * '{DAV:}prop3' => null, + * ), + * 424 => array( + * '{DAV:}prop4' => null, + * ), + * ); + * + * In this previous example prop1 was successfully updated or deleted, and + * prop2 was succesfully created. + * + * prop3 failed to update due to '403 Forbidden' and because of this prop4 + * also could not be updated with '424 Failed dependency'. + * + * This last example was actually incorrect. While 200 and 201 could appear + * in 1 response, if there's any error (403) the other properties should + * always fail with 423 (failed dependency). + * + * But anyway, if you don't want to scratch your head over this, just + * return true or false. + * + * @param string $path + * @param array $mutations + * @return array|bool + */ + function updatePrincipal($path, $mutations) + { + // TODO: Implement updatePrincipal() method. + } + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * If multiple properties are being searched on, the search should be + * AND'ed. + * + * This method should simply return an array with full principal uri's. + * + * If somebody attempted to search on a property the backend does not + * support, you should simply return 0 results. + * + * You can also just return 0 results if you choose to not support + * searching at all, but keep in mind that this may stop certain features + * from working. + * + * @param string $prefixPath + * @param array $searchProperties + * @return array + */ + function searchPrincipals($prefixPath, array $searchProperties) + { + // TODO: Implement searchPrincipals() method. + } +} diff --git a/dav/iCalcreator/iCalcreator.class.php b/dav/iCalcreator/iCalcreator.class.php new file mode 100755 index 000000000..d3b847633 --- /dev/null +++ b/dav/iCalcreator/iCalcreator.class.php @@ -0,0 +1,10181 @@ += '5.1' ) + // && ( 'UTC' == date_default_timezone_get())) + date_default_timezone_set( 'Europe/Stockholm' ); +/*********************************************************************************/ +/* version, do NOT remove!! */ +define( 'ICALCREATOR_VERSION', 'iCalcreator 2.12' ); +/*********************************************************************************/ +/*********************************************************************************/ +/** + * vcalendar class + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-14 + */ +class vcalendar { + // calendar property variables + var $calscale; + var $method; + var $prodid; + var $version; + var $xprop; + // container for calendar components + var $components; + // component config variables + var $allowEmpty; + var $unique_id; + var $language; + var $directory; + var $filename; + var $url; + var $delimiter; + var $nl; + var $format; + var $dtzid; + // component internal variables + var $attributeDelimiter; + var $valueInit; + // component xCal declaration container + var $xcaldecl; +/** + * constructor for calendar object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-14 + * @param array $config + * @return void + */ + function vcalendar ( $config = array()) { + $this->_makeVersion(); + $this->calscale = null; + $this->method = null; + $this->_makeUnique_id(); + $this->prodid = null; + $this->xprop = array(); + $this->language = null; + $this->directory = null; + $this->filename = null; + $this->url = null; + $this->dtzid = null; +/** + * language = + */ + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + + $this->xcaldecl = array(); + $this->components = array(); + } +/*********************************************************************************/ +/** + * Property Name: CALSCALE + */ +/** + * creates formatted output for calendar property calscale + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @return string + */ + function createCalscale() { + if( empty( $this->calscale )) return FALSE; + switch( $this->format ) { + case 'xcal': + return $this->nl.' calscale="'.$this->calscale.'"'; + break; + default: + return 'CALSCALE:'.$this->calscale.$this->nl; + break; + } + } +/** + * set calendar property calscale + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @param string $value + * @return void + */ + function setCalscale( $value ) { + if( empty( $value )) return FALSE; + $this->calscale = $value; + } +/*********************************************************************************/ +/** + * Property Name: METHOD + */ +/** + * creates formatted output for calendar property method + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @return string + */ + function createMethod() { + if( empty( $this->method )) return FALSE; + switch( $this->format ) { + case 'xcal': + return $this->nl.' method="'.$this->method.'"'; + break; + default: + return 'METHOD:'.$this->method.$this->nl; + break; + } + } +/** + * set calendar property method + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-20-23 + * @param string $value + * @return bool + */ + function setMethod( $value ) { + if( empty( $value )) return FALSE; + $this->method = $value; + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: PRODID + * + * The identifier is RECOMMENDED to be the identical syntax to the + * [RFC 822] addr-spec. A good method to assure uniqueness is to put the + * domain name or a domain literal IP address of the host on which.. . + */ +/** + * creates formatted output for calendar property prodid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @return string + */ + function createProdid() { + if( !isset( $this->prodid )) + $this->_makeProdid(); + switch( $this->format ) { + case 'xcal': + return $this->nl.' prodid="'.$this->prodid.'"'; + break; + default: + return 'PRODID:'.$this->prodid.$this->nl; + break; + } + } +/** + * make default value for calendar prodid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.8 - 2009-12-30 + * @return void + */ + function _makeProdid() { + $this->prodid = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language ); + } +/** + * Conformance: The property MUST be specified once in an iCalendar object. + * Description: The vendor of the implementation SHOULD assure that this + * is a globally unique identifier; using some technique such as an FPI + * value, as defined in [ISO 9070]. + */ +/** + * make default unique_id for calendar prodid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 0.3.0 - 2006-08-10 + * @return void + */ + function _makeUnique_id() { + $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; + } +/*********************************************************************************/ +/** + * Property Name: VERSION + * + * Description: A value of "2.0" corresponds to this memo. + */ +/** + * creates formatted output for calendar property version + + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @return string + */ + function createVersion() { + if( empty( $this->version )) + $this->_makeVersion(); + switch( $this->format ) { + case 'xcal': + return $this->nl.' version="'.$this->version.'"'; + break; + default: + return 'VERSION:'.$this->version.$this->nl; + break; + } + } +/** + * set default calendar version + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 0.3.0 - 2006-08-10 + * @return void + */ + function _makeVersion() { + $this->version = '2.0'; + } +/** + * set calendar version + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @param string $value + * @return void + */ + function setVersion( $value ) { + if( empty( $value )) return FALSE; + $this->version = $value; + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: x-prop + */ +/** + * creates formatted output for calendar property x-prop, iCal format only + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-11-01 + * @return string + */ + function createXprop() { + if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE; + $output = null; + $toolbox = new calendarComponent(); + $toolbox->setConfig( $this->getConfig()); + foreach( $this->xprop as $label => $xpropPart ) { + if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) { + $output .= $toolbox->_createElement( $label ); + continue; + } + $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' )); + if( is_array( $xpropPart['value'] )) { + foreach( $xpropPart['value'] as $pix => $theXpart ) + $xpropPart['value'][$pix] = $toolbox->_strrep( $theXpart ); + $xpropPart['value'] = implode( ',', $xpropPart['value'] ); + } + else + $xpropPart['value'] = $toolbox->_strrep( $xpropPart['value'] ); + $output .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] ); + if( is_array( $toolbox->xcaldecl ) && ( 0 < count( $toolbox->xcaldecl ))) { + foreach( $toolbox->xcaldecl as $localxcaldecl ) + $this->xcaldecl[] = $localxcaldecl; + } + } + return $output; + } +/** + * set calendar property x-prop + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.9 - 2012-01-16 + * @param string $label + * @param string $value + * @param array $params optional + * @return bool + */ + function setXprop( $label, $value, $params=FALSE ) { + if( empty( $label )) + return FALSE; + if( 'X-' != strtoupper( substr( $label, 0, 2 ))) + return FALSE; + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $xprop = array( 'value' => $value ); + $xprop['params'] = iCalUtilityFunctions::_setParams( $params ); + if( !is_array( $this->xprop )) $this->xprop = array(); + $this->xprop[strtoupper( $label )] = $xprop; + return TRUE; + } +/*********************************************************************************/ +/** + * delete calendar property value + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $propName, bool FALSE => X-property + * @param int $propix, optional, if specific property is wanted in case of multiply occurences + * @return bool, if successfull delete + */ + function deleteProperty( $propName=FALSE, $propix=FALSE ) { + $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; + if( !$propix ) + $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; + $this->propdelix[$propName] = --$propix; + $return = FALSE; + switch( $propName ) { + case 'CALSCALE': + if( isset( $this->calscale )) { + $this->calscale = null; + $return = TRUE; + } + break; + case 'METHOD': + if( isset( $this->method )) { + $this->method = null; + $return = TRUE; + } + break; + default: + $reduced = array(); + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; } + foreach( $this->xprop as $k => $a ) { + if(( $k != $propName ) && !empty( $a )) + $reduced[$k] = $a; + } + } + else { + if( count( $this->xprop ) <= $propix ) return FALSE; + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix != $xpropno ) + $reduced[$xpropkey] = $xpropvalue; + $xpropno++; + } + } + $this->xprop = $reduced; + if( empty( $this->xprop )) { + unset( $this->propdelix[$propName] ); + return FALSE; + } + return TRUE; + } + return $return; + } +/** + * get calendar property value/params + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-04-16 + * @param string $propName, optional + * @param int $propix, optional, if specific property is wanted in case of multiply occurences + * @param bool $inclParam=FALSE + * @return mixed + */ + function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) { + $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; + if( 'X-PROP' == $propName ) { + if( !$propix ) + $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; + $this->propix[$propName] = --$propix; + } + switch( $propName ) { + case 'ATTENDEE': + case 'CATEGORIES': + case 'DTSTART': + case 'LOCATION': + case 'ORGANIZER': + case 'PRIORITY': + case 'RESOURCES': + case 'STATUS': + case 'SUMMARY': + case 'RECURRENCE-ID-UID': + case 'R-UID': + case 'UID': + $output = array(); + foreach ( $this->components as $cix => $component) { + if( !in_array( $component->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ))) + continue; + if(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) { + $component->_getProperties( $propName, $output ); + continue; + } + elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) { + if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' ))) + $content = $component->getProperty( 'UID' ); + } + elseif( FALSE === ( $content = $component->getProperty( $propName ))) + continue; + if( FALSE === $content ) + continue; + elseif( is_array( $content )) { + if( isset( $content['year'] )) { + $key = sprintf( '%04d%02d%02d', $content['year'], $content['month'], $content['day'] ); + if( !isset( $output[$key] )) + $output[$key] = 1; + else + $output[$key] += 1; + } + else { + foreach( $content as $partValue => $partCount ) { + if( !isset( $output[$partValue] )) + $output[$partValue] = $partCount; + else + $output[$partValue] += $partCount; + } + } + } // end elseif( is_array( $content )) { + elseif( !isset( $output[$content] )) + $output[$content] = 1; + else + $output[$content] += 1; + } // end foreach ( $this->components as $cix => $component) + if( !empty( $output )) + ksort( $output ); + return $output; + break; + + case 'CALSCALE': + return ( !empty( $this->calscale )) ? $this->calscale : FALSE; + break; + case 'METHOD': + return ( !empty( $this->method )) ? $this->method : FALSE; + break; + case 'PRODID': + if( empty( $this->prodid )) + $this->_makeProdid(); + return $this->prodid; + break; + case 'VERSION': + return ( !empty( $this->version )) ? $this->version : FALSE; + break; + default: + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) return FALSE; + return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) + : array( $propName, $this->xprop[$propName]['value'] ); + } + else { + if( empty( $this->xprop )) return FALSE; + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix == $xpropno ) + return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) + : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); + else + $xpropno++; + } + unset( $this->propix[$propName] ); + return FALSE; // not found ?? + } + } + return FALSE; + } +/** + * general vcalendar property setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.2.13 - 2007-11-04 + * @param mixed $args variable number of function arguments, + * first argument is ALWAYS component name, + * second ALWAYS component value! + * @return bool + */ + function setProperty () { + $numargs = func_num_args(); + if( 1 > $numargs ) + return FALSE; + $arglist = func_get_args(); + $arglist[0] = strtoupper( $arglist[0] ); + switch( $arglist[0] ) { + case 'CALSCALE': + return $this->setCalscale( $arglist[1] ); + case 'METHOD': + return $this->setMethod( $arglist[1] ); + case 'VERSION': + return $this->setVersion( $arglist[1] ); + default: + if( !isset( $arglist[1] )) $arglist[1] = null; + if( !isset( $arglist[2] )) $arglist[2] = null; + return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); + } + return FALSE; + } +/*********************************************************************************/ +/** + * get vcalendar config values or * calendar components + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.7 - 2012-01-12 + * @param mixed $config + * @return value + */ + function getConfig( $config = FALSE ) { + if( !$config ) { + $return = array(); + $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); + $return['DELIMITER'] = $this->getConfig( 'DELIMITER' ); + $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' ); + $return['FILENAME'] = $this->getConfig( 'FILENAME' ); + $return['DIRFILE'] = $this->getConfig( 'DIRFILE' ); + $return['FILESIZE'] = $this->getConfig( 'FILESIZE' ); + $return['FORMAT'] = $this->getConfig( 'FORMAT' ); + if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) + $return['LANGUAGE'] = $lang; + $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); + $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); + if( FALSE !== ( $url = $this->getConfig( 'URL' ))) + $return['URL'] = $url; + $return['TZID'] = $this->getConfig( 'TZID' ); + return $return; + } + switch( strtoupper( $config )) { + case 'ALLOWEMPTY': + return $this->allowEmpty; + break; + case 'COMPSINFO': + unset( $this->compix ); + $info = array(); + foreach( $this->components as $cix => $component ) { + if( empty( $component )) continue; + $info[$cix]['ordno'] = $cix + 1; + $info[$cix]['type'] = $component->objName; + $info[$cix]['uid'] = $component->getProperty( 'uid' ); + $info[$cix]['props'] = $component->getConfig( 'propinfo' ); + $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); + } + return $info; + break; + case 'DELIMITER': + return $this->delimiter; + break; + case 'DIRECTORY': + if( empty( $this->directory ) && ( '0' != $this->directory )) + $this->directory = '.'; + return $this->directory; + break; + case 'DIRFILE': + return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' ); + break; + case 'FILEINFO': + return array( $this->getConfig( 'directory' ) + , $this->getConfig( 'filename' ) + , $this->getConfig( 'filesize' )); + break; + case 'FILENAME': + if( empty( $this->filename ) && ( '0' != $this->filename )) { + if( 'xcal' == $this->format ) + $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. . + else + $this->filename = date( 'YmdHis' ).'.ics'; + } + return $this->filename; + break; + case 'FILESIZE': + $size = 0; + if( empty( $this->url )) { + $dirfile = $this->getConfig( 'dirfile' ); + if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile )))) + $size = 0; + clearstatcache(); + } + return $size; + break; + case 'FORMAT': + return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal'; + break; + case 'LANGUAGE': + /* get language for calendar component as defined in [RFC 1766] */ + return $this->language; + break; + case 'NL': + case 'NEWLINECHAR': + return $this->nl; + break; + case 'TZID': + return $this->dtzid; + break; + case 'UNIQUE_ID': + return $this->unique_id; + break; + case 'URL': + if( !empty( $this->url )) + return $this->url; + else + return FALSE; + break; + } + } +/** + * general vcalendar config setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.11 - 2011-01-16 + * @param mixed $config + * @param string $value + * @return void + */ + function setConfig( $config, $value = FALSE) { + if( is_array( $config )) { + $ak = array_keys( $config ); + foreach( $ak as $k ) { + if( 'DIRECTORY' == strtoupper( $k )) { + if( FALSE === $this->setConfig( 'DIRECTORY', $config[$k] )) + return FALSE; + unset( $config[$k] ); + } + elseif( 'NEWLINECHAR' == strtoupper( $k )) { + if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] )) + return FALSE; + unset( $config[$k] ); + } + } + foreach( $config as $cKey => $cValue ) { + if( FALSE === $this->setConfig( $cKey, $cValue )) + return FALSE; + } + return TRUE; + } + $res = FALSE; + switch( strtoupper( $config )) { + case 'ALLOWEMPTY': + $this->allowEmpty = $value; + $subcfg = array( 'ALLOWEMPTY' => $value ); + $res = TRUE; + break; + case 'DELIMITER': + $this->delimiter = $value; + return TRUE; + break; + case 'DIRECTORY': + $value = trim( $value ); + $del = $this->getConfig('delimiter'); + if( $del == substr( $value, ( 0 - strlen( $del )))) + $value = substr( $value, 0, ( strlen( $value ) - strlen( $del ))); + if( is_dir( $value )) { + /* local directory */ + clearstatcache(); + $this->directory = $value; + $this->url = null; + return TRUE; + } + else + return FALSE; + break; + case 'FILENAME': + $value = trim( $value ); + if( !empty( $this->url )) { + /* remote directory+file -> URL */ + $this->filename = $value; + return TRUE; + } + $dirfile = $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$value; + if( file_exists( $dirfile )) { + /* local file exists */ + if( is_readable( $dirfile ) || is_writable( $dirfile )) { + clearstatcache(); + $this->filename = $value; + return TRUE; + } + else + return FALSE; + } + elseif( is_readable($this->getConfig( 'directory' ) ) || is_writable( $this->getConfig( 'directory' ) )) { + /* read- or writable directory */ + $this->filename = $value; + return TRUE; + } + else + return FALSE; + break; + case 'FORMAT': + $value = trim( strtolower( $value )); + if( 'xcal' == $value ) { + $this->format = 'xcal'; + $this->attributeDelimiter = $this->nl; + $this->valueInit = null; + } + else { + $this->format = null; + $this->attributeDelimiter = ';'; + $this->valueInit = ':'; + } + $subcfg = array( 'FORMAT' => $value ); + $res = TRUE; + break; + case 'LANGUAGE': + // set language for calendar component as defined in [RFC 1766] + $value = trim( $value ); + $this->language = $value; + $subcfg = array( 'LANGUAGE' => $value ); + $res = TRUE; + break; + case 'NL': + case 'NEWLINECHAR': + $this->nl = $value; + if( 'xcal' == $value ) { + $this->attributeDelimiter = $this->nl; + $this->valueInit = null; + } + else { + $this->attributeDelimiter = ';'; + $this->valueInit = ':'; + } + $subcfg = array( 'NL' => $value ); + $res = TRUE; + break; + case 'TZID': + $this->dtzid = $value; + $subcfg = array( 'TZID' => $value ); + $res = TRUE; + break; + case 'UNIQUE_ID': + $value = trim( $value ); + $this->unique_id = $value; + $this->_makeProdid(); + $subcfg = array( 'UNIQUE_ID' => $value ); + $res = TRUE; + break; + case 'URL': + /* remote file - URL */ + $value = trim( $value ); + $value = str_replace( 'HTTP://', 'http://', $value ); + $value = str_replace( 'WEBCAL://', 'http://', $value ); + $value = str_replace( 'webcal://', 'http://', $value ); + $this->url = $value; + $this->directory = null; + $parts = pathinfo( $value ); + return $this->setConfig( 'filename', $parts['basename'] ); + break; + default: // any unvalid config key.. . + return TRUE; + } + if( !$res ) return FALSE; + if( isset( $subcfg ) && !empty( $this->components )) { + foreach( $subcfg as $cfgkey => $cfgvalue ) { + foreach( $this->components as $cix => $component ) { + $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE ); + if( !$res ) + break 2; + $this->components[$cix] = $component->copy(); // PHP4 compliant + } + } + } + return $res; + } +/*********************************************************************************/ +/** + * add calendar component to container + * + * alias to setComponent + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 1.x.x - 2007-04-24 + * @param object $component calendar component + * @return void + */ + function addComponent( $component ) { + $this->setComponent( $component ); + } +/** + * delete calendar component from container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $arg1 ordno / component type / component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @return void + */ + function deleteComponent( $arg1, $arg2=FALSE ) { + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { + $argType = strtolower( $arg1 ); + $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; + } + $cix1dC = 0; + foreach ( $this->components as $cix => $component) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { + unset( $this->components[$cix] ); + return TRUE; + } + elseif( $argType == $component->objName ) { + if( $index == $cix1dC ) { + unset( $this->components[$cix] ); + return TRUE; + } + $cix1dC++; + } + elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { + unset( $this->components[$cix] ); + return TRUE; + } + } + return FALSE; + } +/** + * get calendar component from container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.1 - 2011-04-16 + * @param mixed $arg1 optional, ordno/component type/ component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @return object + */ + function getComponent( $arg1=FALSE, $arg2=FALSE ) { + $index = $argType = null; + if ( !$arg1 ) { // first or next in component chain + $argType = 'INDEX'; + $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; + } + elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain + $argType = 'INDEX'; + $index = (int) $arg1; + unset( $this->compix ); + } + elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) + $arg2 = implode( '-', array_keys( $arg1 )); + $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1; + $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' ); + $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID' ); + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name + unset( $this->compix['INDEX'] ); + $argType = strtolower( $arg1 ); + if( !$arg2 ) + $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; + elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) + $index = (int) $arg2; + } + elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument + if( !$arg2 ) + $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1; + elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) + $index = (int) $arg2; + } + if( isset( $index )) + $index -= 1; + $ckeys = array_keys( $this->components ); + if( !empty( $index) && ( $index > end( $ckeys ))) + return FALSE; + $cix1gC = 0; + foreach ( $this->components as $cix => $component) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) + return $component->copy(); + elseif( $argType == $component->objName ) { + if( $index == $cix1gC ) + return $component->copy(); + $cix1gC++; + } + elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) + $hit = FALSE; + foreach( $arg1 as $pName => $pValue ) { + $pName = strtoupper( $pName ); + if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps )) + continue; + if(( 'ATTENDEE' == $pName ) || ( 'CATEGORIES' == $pName ) || ( 'RESOURCES' == $pName )) { // multiple ocurrence may occur + $propValues = array(); + $component->_getProperties( $pName, $propValues ); + $propValues = array_keys( $propValues ); + $hit = ( in_array( $pValue, $propValues )) ? TRUE : FALSE; + continue; + } // end if(( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) { // multiple ocurrence may occur + if( FALSE === ( $value = $component->getProperty( $pName ))) { // single ocurrency + $hit = FALSE; // missing property + continue; + } + if( 'SUMMARY' == $pName ) { // exists within (any case) + $hit = ( FALSE !== stripos( $d, $pValue )) ? TRUE : FALSE; + continue; + } + if( in_array( strtoupper( $pName ), $dateProps )) { + $valuedate = sprintf( '%04d%02d%02d', $value['year'], $value['month'], $value['day'] ); + if( 8 < strlen( $pValue )) { + if( isset( $value['hour'] )) { + if( 'T' == substr( $pValue, 8, 1 )) + $pValue = str_replace( 'T', '', $pValue ); + $valuedate .= sprintf( '%02d%02d%02d', $value['hour'], $value['min'], $value['sec'] ); + } + else + $pValue = substr( $pValue, 0, 8 ); + } + $hit = ( $pValue == $valuedate ) ? TRUE : FALSE; + continue; + } + elseif( !is_array( $value )) + $value = array( $value ); + foreach( $value as $part ) { + $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part ); + foreach( $part as $subPart ) { + if( $pValue == $subPart ) { + $hit = TRUE; + continue 2; + } + } + } + $hit = FALSE; // no hit in property + } // end foreach( $arg1 as $pName => $pValue ) + if( $hit ) { + if( $index == $cix1gC ) + return $component->copy(); + $cix1gC++; + } + } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) + elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID + if( $index == $cix1gC ) + return $component->copy(); + $cix1gC++; + } + } // end foreach ( $this->components.. . + /* not found.. . */ + unset( $this->compix ); + return FALSE; + } +/** + * create new calendar component, already included within calendar + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.33 - 2011-01-03 + * @param string $compType component type + * @return object (reference) + */ + function & newComponent( $compType ) { + $config = $this->getConfig(); + $keys = array_keys( $this->components ); + $ix = end( $keys) + 1; + switch( strtoupper( $compType )) { + case 'EVENT': + case 'VEVENT': + $this->components[$ix] = new vevent( $config ); + break; + case 'TODO': + case 'VTODO': + $this->components[$ix] = new vtodo( $config ); + break; + case 'JOURNAL': + case 'VJOURNAL': + $this->components[$ix] = new vjournal( $config ); + break; + case 'FREEBUSY': + case 'VFREEBUSY': + $this->components[$ix] = new vfreebusy( $config ); + break; + case 'TIMEZONE': + case 'VTIMEZONE': + array_unshift( $this->components, new vtimezone( $config )); + $ix = 0; + break; + default: + return FALSE; + } + return $this->components[$ix]; + } +/** + * select components from calendar on date or selectOption basis + * + * Ensure DTSTART is set for every component. + * No date controls occurs. + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.22 - 2012-02-13 + * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions ( *[ => ] ) + * @param int $startM optional, start Month, default current Month + * @param int $startD optional, start Day, default current Day + * @param int $endY optional, end Year, default $startY + * @param int $endY optional, end Month, default $startM + * @param int $endY optional, end Day, default $startD + * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s) + * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][] + * TRUE => output : array[] (ignores split) + * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period + * FALSE - only component(-s) that starts within period + * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the + * period (implies flat=FALSE) + * FALSE - one occurance of component only in output array + * @return array or FALSE + */ + function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) { + /* check if empty calendar */ + if( 0 >= count( $this->components )) return FALSE; + if( is_array( $startY )) + return $this->selectComponents2( $startY ); + /* check default dates */ + if( !$startY ) $startY = date( 'Y' ); + if( !$startM ) $startM = date( 'm' ); + if( !$startD ) $startD = date( 'd' ); + $startDate = mktime( 0, 0, 0, $startM, $startD, $startY ); + if( !$endY ) $endY = $startY; + if( !$endM ) $endM = $startM; + if( !$endD ) $endD = $startD; + $endDate = mktime( 23, 59, 59, $endM, $endD, $endY ); +//echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."
\n"; $tcnt = 0;// test ### + /* check component types */ + $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ); + if( is_array( $cType )) { + foreach( $cType as $cix => $theType ) { + $cType[$cix] = $theType = strtolower( $theType ); + if( !in_array( $theType, $validTypes )) + $cType[$cix] = 'vevent'; + } + $cType = array_unique( $cType ); + } + elseif( !empty( $cType )) { + $cType = strtolower( $cType ); + if( !in_array( $cType, $validTypes )) + $cType = array( 'vevent' ); + else + $cType = array( $cType ); + } + else + $cType = $validTypes; + if( 0 >= count( $cType )) + $cType = $validTypes; + if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination + $split = FALSE; + if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination + $split = FALSE; + /* iterate components */ + $result = array(); + foreach ( $this->components as $cix => $component ) { + if( empty( $component )) continue; + unset( $start ); + /* deselect unvalid type components */ + if( !in_array( $component->objName, $cType )) + continue; + $start = $component->getProperty( 'dtstart' ); + /* select due when dtstart is missing */ + if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' )))) + continue; + if( empty( $start )) + continue; + $dtendExist = $dueExist = $durationExist = $endAllDayEvent = $recurrid = FALSE; + unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat ); // clean up + $startWdate = iCalUtilityFunctions::_date2timestamp( $start ); + $startDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; + /* get end date from dtend/due/duration properties */ + $end = $component->getProperty( 'dtend' ); + if( !empty( $end )) { + $dtendExist = TRUE; + $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; + } + if( empty( $end ) && ( $component->objName == 'vtodo' )) { + $end = $component->getProperty( 'due' ); + if( !empty( $end )) { + $dueExist = TRUE; + $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; + } + } + if( !empty( $end ) && !isset( $end['hour'] )) { + /* a DTEND without time part regards an event that ends the day before, + for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */ + $endAllDayEvent = TRUE; + $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] ); + $end['year'] = date( 'Y', $endWdate ); + $end['month'] = date( 'm', $endWdate ); + $end['day'] = date( 'd', $endWdate ); + $end['hour'] = 23; + $end['min'] = $end['sec'] = 59; + } + if( empty( $end )) { + $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format + if( !empty( $end )) + $durationExist = TRUE; + $endDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; +// if( !empty($end)) echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."
\n"; // test ### + } + if( empty( $end )) { // assume one day duration if missing end date + $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 ); + } +// if( isset($end)) echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."
\n"; // test ### + $endWdate = iCalUtilityFunctions::_date2timestamp( $end ); + if( $endWdate < $startWdate ) { // MUST be after start date!! + $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 ); + $endWdate = iCalUtilityFunctions::_date2timestamp( $end ); + } + $rdurWsecs = $endWdate - $startWdate; // compute event (component) duration in seconds + /* make a list of optional exclude dates for component occurence from exrule and exdate */ + $exdatelist = array(); + $workstart = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6); + $workend = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6); + while( FALSE !== ( $exrule = $component->getProperty( 'exrule' ))) // check exrule + iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend ); + while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) { // check exdate + foreach( $exdate as $theExdate ) { + $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate ); + $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate )); // on a day-basis !!! + if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate )) + $exdatelist[$exWdate] = TRUE; + } // end - foreach( $exdate as $theExdate ) + } // end - check exdate + $compUID = $component->getProperty( 'UID' ); + /* check recurrence-id (with sequence), remove hit with reccurr-id date */ + if(( FALSE !== ( $recurrid = $component->getProperty( 'recurrence-id' ))) && + ( FALSE !== ( $sequence = $component->getProperty( 'sequence' ))) ) { + $recurrid = iCalUtilityFunctions::_date2timestamp( $recurrid ); + $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ), date( 'Y', $recurrid )); // on a day-basis !!! + $endD = $recurrid + $rdurWsecs; + do { + if( date( 'Ymd', $startWdate ) != date( 'Ymd', $recurrid )) + $exdatelist[$recurrid] = TRUE; // exclude all other days than startdate + $wd = getdate( $recurrid ); + if( isset( $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] )) + unset( $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] ); // remove from output, dtstart etc added below + if( $split && ( $recurrid <= $endD )) + $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ) + 1, date( 'Y', $recurrid )); // step one day + else + break; + } while( TRUE ); + } // end recurrence-id test + /* select only components with.. . */ + if(( !$any && ( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) || // (dt)start within the period + ( $any && ( $startWdate < $endDate ) && ( $endWdate >= $startDate ))) { // occurs within the period + /* add the selected component (WITHIN valid dates) to output array */ + if( $flat ) { // any=true/false, ignores split + if( !$recurrid ) + $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id) + } + elseif( $split ) { // split the original component + if( $endWdate > $endDate ) + $endWdate = $endDate; // use period end date + $rstart = $startWdate; + if( $rstart < $startDate ) + $rstart = $startDate; // use period start date + $startYMD = date( 'Ymd', $rstart ); + $endYMD = date( 'Ymd', $endWdate ); + $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! + while( date( 'Ymd', $rstart ) <= $endYMD ) { // iterate + $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! + if( isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist + $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day + continue; + } + if( date( 'Ymd', $rstart ) > $startYMD ) // date after dtstart + $datestring = date( $startDateFormat, mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ))); + else + $datestring = date( $startDateFormat, $rstart ); + if( isset( $start['tz'] )) + $datestring .= ' '.$start['tz']; +// echo "X-CURRENT-DTSTART 3 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."
";$component->setProperty( 'X-CNT', $tcnt ); // test ### + $component->setProperty( 'X-CURRENT-DTSTART', $datestring ); + if( $dtendExist || $dueExist || $durationExist ) { + if( date( 'Ymd', $rstart ) < $endYMD ) // not the last day + $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )); + else + $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! + if( $endAllDayEvent && $dtendExist ) + $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day + $datestring = date( $endDateFormat, $tend ); + if( isset( $end['tz'] )) + $datestring .= ' '.$end['tz']; + $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; + $component->setProperty( $propName, $datestring ); + } // end if( $dtendExist || $dueExist || $durationExist ) + $wd = getdate( $rstart ); + $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output + $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day + } // end while( $rstart <= $endWdate ) + } // end if( $split ) - else use component date + elseif( $recurrid && !$flat && !$any && !$split ) + $continue = TRUE; + else { // !$flat && !$split, i.e. no flat array and DTSTART within period + $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!! + if( !$any || !isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist + $wd = getdate( $startWdate ); + $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output + } + } + } // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) + + /* if 'any' components, check components with reccurrence rules, removing all excluding dates */ + if( TRUE === $any ) { + /* make a list of optional repeating dates for component occurence, rrule, rdate */ + $recurlist = array(); + while( FALSE !== ( $rrule = $component->getProperty( 'rrule' ))) // check rrule + iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend ); + foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp + $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds + while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) { // check rdate + foreach( $rdate as $theRdate ) { + if( is_array( $theRdate ) && ( 2 == count( $theRdate )) && // all days within PERIOD + array_key_exists( '0', $theRdate ) && array_key_exists( '1', $theRdate )) { + $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] ); + if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate )) + continue; + if( isset( $theRdate[1]['year'] )) // date-date period + $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] ); + else { // date-duration period + $rend = iCalUtilityFunctions::_duration2date( $theRdate[0], $theRdate[1] ); + $rend = iCalUtilityFunctions::_date2timestamp( $rend ); + } + while( $rstart < $rend ) { + $recurlist[$rstart] = $rdurWsecs; // set start date for recurrence instance + rdate duration in seconds + $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day + } + } // PERIOD end + else { // single date + $theRdate = iCalUtilityFunctions::_date2timestamp( $theRdate ); + if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate )) + $recurlist[$theRdate] = $rdurWsecs; // set start date for recurrence instance + event duration in seconds + } + } + } // end - check rdate + if( 0 < count( $recurlist )) { + ksort( $recurlist ); + $xRecurrence = 1; + $component2 = $component->copy(); + $compUID = $component2->getProperty( 'UID' ); + foreach( $recurlist as $recurkey => $durvalue ) { +// echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."
\n"; // test ###; + if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period + continue; + $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!! + if( isset( $exdatelist[$checkDate] )) // check excluded dates + continue; + if( $startWdate >= $recurkey ) // exclude component start date + continue; + $rstart = $recurkey; + $rend = $recurkey + $durvalue; + /* add repeating components within valid dates to output array, only start date set */ + if( $flat ) { + if( !isset( $result[$compUID] )) // only one comp + $result[$compUID] = $component2->copy(); // copy to output + } + /* add repeating components within valid dates to output array, one each day */ + elseif( $split ) { + if( $rend > $endDate ) + $rend = $endDate; + $startYMD = date( 'Ymd', $rstart ); + $endYMD = date( 'Ymd', $rend ); +// echo "splitStart=".date( 'Y-m-d H:i:s', $rstart ).' end='.date( 'Y-m-d H:i:s', $rend )."
\n"; // test ###; + while( $rstart <= $rend ) { // iterate.. . + $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! + if( isset( $exdatelist[$checkDate] )) // exclude any recurrence START date, found in exdatelist + break; +// echo "checking date after startdate=".date( 'Y-m-d H:i:s', $rstart ).' mot '.date( 'Y-m-d H:i:s', $startDate )."
"; // test ###; + if( $rstart >= $startDate ) { // date after dtstart + if( date( 'Ymd', $rstart ) > $startYMD ) // date after dtstart + $datestring = date( $startDateFormat, $checkDate ); + else + $datestring = date( $startDateFormat, $rstart ); + if( isset( $start['tz'] )) + $datestring .= ' '.$start['tz']; +//echo "X-CURRENT-DTSTART 1 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."
";$component2->setProperty( 'X-CNT', $tcnt ); // test ### + $component2->setProperty( 'X-CURRENT-DTSTART', $datestring ); + if( $dtendExist || $dueExist || $durationExist ) { + if( date( 'Ymd', $rstart ) < $endYMD ) // not the last day + $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )); + else + $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! + if( $endAllDayEvent && $dtendExist ) + $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day + $datestring = date( $endDateFormat, $tend ); + if( isset( $end['tz'] )) + $datestring .= ' '.$end['tz']; + $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; + $component2->setProperty( $propName, $datestring ); + } // end if( $dtendExist || $dueExist || $durationExist ) + $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); + $wd = getdate( $rstart ); + $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output + } // end if( $checkDate > $startYMD ) { // date after dtstart + $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day + } // end while( $rstart <= $rend ) + $xRecurrence += 1; + } // end elseif( $split ) + elseif( $rstart >= $startDate ) { // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *// + $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! + if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist + $xRecurrence += 1; + $datestring = date( $startDateFormat, $rstart ); + if( isset( $start['tz'] )) + $datestring .= ' '.$start['tz']; +//echo "X-CURRENT-DTSTART 2 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."
";$component2->setProperty( 'X-CNT', $tcnt ); // test ### + $component2->setProperty( 'X-CURRENT-DTSTART', $datestring ); + if( $dtendExist || $dueExist || $durationExist ) { + $tend = $rstart + $rdurWsecs; + if( date( 'Ymd', $tend ) < date( 'Ymd', $endWdate )) + $tend = mktime( 23, 59, 59, date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend )); + else + $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend ) ); // on a day-basis !!! + if( $endAllDayEvent && $dtendExist ) + $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day + $datestring = date( $endDateFormat, $tend ); + if( isset( $end['tz'] )) + $datestring .= ' '.$end['tz']; + $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; + $component2->setProperty( $propName, $datestring ); + } // end if( $dtendExist || $dueExist || $durationExist ) + $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); + $wd = getdate( $rstart ); + $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output + } // end if( !isset( $exdatelist[$checkDate] )) + } // end elseif( $rstart >= $startDate ) + } // end foreach( $recurlist as $recurkey => $durvalue ) + } // end if( 0 < count( $recurlist )) + /* deselect components with startdate/enddate not within period */ + if(( $endWdate < $startDate ) || ( $startWdate > $endDate )) + continue; + } // end if( TRUE === $any ) + } // end foreach ( $this->components as $cix => $component ) + if( 0 >= count( $result )) return FALSE; + elseif( !$flat ) { + foreach( $result as $y => $yeararr ) { + foreach( $yeararr as $m => $montharr ) { + foreach( $montharr as $d => $dayarr ) { + if( empty( $result[$y][$m][$d] )) + unset( $result[$y][$m][$d] ); + else + $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index, hoping they are in hour order.. . + } + if( empty( $result[$y][$m] )) + unset( $result[$y][$m] ); + else + ksort( $result[$y][$m] ); + } + if( empty( $result[$y] )) + unset( $result[$y] ); + else + ksort( $result[$y] ); + } + if( empty( $result )) + unset( $result ); + else + ksort( $result ); + } // end elseif( !$flat ) + if( 0 >= count( $result )) + return FALSE; + return $result; + } +/** + * select components from calendar on based on Categories, Location, Resources and/or Summary + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-05-03 + * @param array $selectOptions, (string) key => (mixed) value, (key=propertyName) + * @return array + */ + function selectComponents2( $selectOptions ) { + $output = array(); + $allowedProperties = array( 'ATTENDEE', 'CATEGORIES', 'LOCATION', 'ORGANIZER', 'RESOURCES', 'PRIORITY', 'STATUS', 'SUMMARY', 'UID' ); + foreach( $this->components as $cix => $component3 ) { + if( !in_array( $component3->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ))) + continue; + $uid = $component3->getProperty( 'UID' ); + foreach( $selectOptions as $propName => $pvalue ) { + $propName = strtoupper( $propName ); + if( !in_array( $propName, $allowedProperties )) + continue; + if( !is_array( $pvalue )) + $pvalue = array( $pvalue ); + if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) { + $output[] = $component3->copy(); + continue; + } + elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) { + $propValues = array(); + $component3->_getProperties( $propName, $propValues ); + $propValues = array_keys( $propValues ); + foreach( $pvalue as $theValue ) { + if( in_array( $theValue, $propValues ) && !isset( $output[$uid] )) { + $output[$uid] = $component3->copy(); + break; + } + } + continue; + } // end elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) + elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single ocurrence + continue; + if( is_array( $d )) { + foreach( $d as $part ) { + if( in_array( $part, $pvalue ) && !isset( $output[$uid] )) + $output[$uid] = $component3->copy(); + } + } + elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) { + foreach( $pvalue as $pval ) { + if( FALSE !== stripos( $d, $pval )) { + $output[$uid] = $component3->copy(); + break; + } + } + } + elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] )) + $output[$uid] = $component3->copy(); + } // end foreach( $selectOptions as $propName => $pvalue ) { + } // end foreach( $this->components as $cix => $component3 ) { + if( !empty( $output )) { + ksort( $output ); + $output = array_values( $output ); + } + return $output; + } +/** + * add calendar component to container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param object $component calendar component + * @param mixed $arg1 optional, ordno/component type/ component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @return void + */ + function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) { + /* make sure dtstamp and uid is set */ + $dummy1 = $component->getProperty( 'dtstamp' ); + $dummy2 = $component->getProperty( 'uid' ); + } + if( !$arg1 ) { // plain insert, last in chain + $this->components[] = $component->copy(); + return TRUE; + } + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { // index insert/replace + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) { + $argType = strtolower( $arg1 ); + $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; + } + // else if arg1 is set, arg1 must be an UID + $cix1sC = 0; + foreach ( $this->components as $cix => $component2) { + if( empty( $component2 )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + elseif( $argType == $component2->objName ) { // component Type index insert/replace + if( $index == $cix1sC ) { + $this->components[$cix] = $component->copy(); + return TRUE; + } + $cix1sC++; + } + elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + } + /* arg1=index and not found.. . insert at index .. .*/ + if( 'INDEX' == $argType ) { + $this->components[$index] = $component->copy(); + ksort( $this->components, SORT_NUMERIC ); + } + else /* not found.. . insert last in chain anyway .. .*/ + $this->components[] = $component->copy(); + return TRUE; + } +/** + * sort iCal compoments + * + * ascending sort on properties (if exist) x-current-dtstart, dtstart, + * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid + * if no arguments, otherwise sorting on argument CATEGORIES, LOCATION, SUMMARY or RESOURCES + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.4 - 2011-06-02 + * @param string $sortArg, optional + * @return void + * + */ + function sort( $sortArg=FALSE ) { + if( is_array( $this->components )) { + if( $sortArg ) { + $sortArg = strtoupper( $sortArg ); + if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', 'DTSTAMP', 'LOCATION', 'ORGANIZER', 'RESOURCES', 'PRIORITY', 'STATUS', 'SUMMARY' ))) + $sortArg = FALSE; + } + /* set sort parameters for each component */ + foreach( $this->components as $cix => & $c ) { + $c->srtk = array( '0', '0', '0', '0' ); + if( 'vtimezone' == $c->objName ) { + if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' ))) + $c->srtk[0] = 0; + continue; + } + elseif( $sortArg ) { + if(( 'ATTENDEE' == $sortArg ) || ( 'CATEGORIES' == $sortArg ) || ( 'RESOURCES' == $sortArg )) { + $propValues = array(); + $c->_getProperties( $sortArg, $propValues ); + $c->srtk[0] = reset( array_keys( $propValues )); + } + elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) + $c->srtk[0] = $d; + continue; + } + if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) { + $c->srtk[0] = iCalUtilityFunctions::_date_time_string( $d[1] ); + unset( $c->srtk[0]['unparsedtext'] ); + } + elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' ))) + $c->srtk[1] = 0; // sortkey 0 : dtstart + if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) { + $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] ); // sortkey 1 : dtend/due(/dtstart+duration) + unset( $c->srtk[1]['unparsedtext'] ); + } + elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) { + if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) { + $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] ); + unset( $c->srtk[1]['unparsedtext'] ); + } + elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' ))) + if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE ))) + $c->srtk[1] = 0; + } + if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp + if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' ))) + $c->srtk[2] = 0; + if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid + $c->srtk[3] = 0; + } // end foreach( $this->components as & $c + /* sort */ + usort( $this->components, array( $this, '_cmpfcn' )); + } + } + function _cmpfcn( $a, $b ) { + if( empty( $a )) return -1; + if( empty( $b )) return 1; + if( 'vtimezone' == $a->objName ) { + if( 'vtimezone' != $b->objName ) return -1; + elseif( $a->srtk[0] <= $b->srtk[0] ) return -1; + else return 1; + } + elseif( 'vtimezone' == $b->objName ) return 1; + $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' ); + for( $k = 0; $k < 4 ; $k++ ) { + if( empty( $a->srtk[$k] )) return -1; + elseif( empty( $b->srtk[$k] )) return 1; + if( is_array( $a->srtk[$k] )) { + if( is_array( $b->srtk[$k] )) { + foreach( $sortkeys as $key ) { + if ( empty( $a->srtk[$k][$key] )) return -1; + elseif( empty( $b->srtk[$k][$key] )) return 1; + if ( $a->srtk[$k][$key] == $b->srtk[$k][$key]) + continue; + if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] )) + return -1; + elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] )) + return 1; + } + } + else return -1; + } + elseif( is_array( $b->srtk[$k] )) return 1; + elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1; + elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1; + } + return 0; + } +/** + * parse iCal text/file into vcalendar, components, properties and parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.10 - 2012-01-31 + * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings + * @return bool FALSE if error occurs during parsing + * + */ + function parse( $unparsedtext=FALSE ) { + $nl = $this->getConfig( 'nl' ); + if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) { + /* directory+filename is set previously via setConfig directory+filename or url */ + if( FALSE === ( $filename = $this->getConfig( 'url' ))) + $filename = $this->getConfig( 'dirfile' ); + /* READ FILE */ + if( FALSE === ( $rows = file_get_contents( $filename ))) + return FALSE; /* err 1 */ + } + elseif( is_array( $unparsedtext )) + $rows = implode( '\n'.$nl, $unparsedtext ); + else + $rows = & $unparsedtext; + /* identify BEGIN:VCALENDAR, MUST be first row */ + if( 'BEGIN:VCALENDAR' != strtoupper( substr( $rows, 0, 15 ))) + return FALSE; /* err 8 */ + /* fix line folding */ + $eolchars = array( "\r\n", "\n\r", "\n", "\r" ); // check all line endings + $EOLmark = FALSE; + foreach( $eolchars as $eolchar ) { + if( !$EOLmark && ( FALSE !== strpos( $rows, $eolchar ))) { + $rows = str_replace( $eolchar." ", '', $rows ); + $rows = str_replace( $eolchar."\t", '', $rows ); + if( $eolchar != $nl ) + $rows = str_replace( $eolchar, $nl, $rows ); + $EOLmark = TRUE; + } + } + $rows = explode( $nl, $rows ); + /* skip trailing empty lines */ + $lix = count( $rows ) - 1; + while( empty( $rows[$lix] ) && ( 0 < $lix )) + $lix -= 1; + /* identify ending END:VCALENDAR row, MUST be last row */ + if( 'END:VCALENDAR' != strtoupper( substr( $rows[$lix], 0, 13 ))) + return FALSE; /* err 9 */ + if( 3 > count( $rows )) + return FALSE; /* err 10 */ + $comp = & $this; + $calsync = 0; + /* identify components and update unparsed data within component */ + $config = $this->getConfig(); + foreach( $rows as $line ) { + if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) { + $calsync++; + continue; + } + elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) { + $calsync--; + break; + } + elseif( 1 != $calsync ) + return FALSE; /* err 20 */ + elseif( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ))) { + $this->components[] = $comp->copy(); + continue; + } + if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) + $comp = new vevent( $config ); + elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) + $comp = new vfreebusy( $config ); + elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) + $comp = new vjournal( $config ); + elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) + $comp = new vtodo( $config ); + elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) + $comp = new vtimezone( $config ); + else { /* update component with unparsed data */ + $comp->unparsed[] = $line; + } + } // end foreach( $rows as $line ) + unset( $config ); + /* parse data for calendar (this) object */ + if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) { + /* concatenate property values spread over several lines */ + $lastix = -1; + $propnames = array( 'calscale','method','prodid','version','x-' ); + $proprows = array(); + foreach( $this->unparsed as $line ) { + $newProp = FALSE; + foreach ( $propnames as $propname ) { + if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) { + $newProp = TRUE; + break; + } + } + if( $newProp ) { + $newProp = FALSE; + $lastix++; + $proprows[$lastix] = $line; + } + else + $proprows[$lastix] .= '!"#¤%&/()=?'.$line; + } + $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' ); + $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ); + $paramProto4 = array( 'crid:', 'news:', 'pres:' ); + foreach( $proprows as $line ) { + $line = str_replace( '!"#¤%&/()=? ', '', $line ); + $line = str_replace( '!"#¤%&/()=?', '', $line ); + if( '\n' == substr( $line, -2 )) + $line = substr( $line, 0, strlen( $line ) - 2 ); + /* get property name */ + $cix = $propname = null; + for( $cix=0, $clen = strlen( $line ); $cix < $clen; $cix++ ) { + if( in_array( $line[$cix], array( ':', ';' ))) + break; + else + $propname .= $line[$cix]; + } + /* ignore version/prodid properties */ + if( in_array( strtoupper( $propname ), array( 'VERSION', 'PRODID' ))) + continue; + $line = substr( $line, $cix); + /* separate attributes from value */ + $attr = array(); + $attrix = -1; + $strlen = strlen( $line ); + $WithinQuotes = FALSE; + for( $cix=0; $cix < $strlen; $cix++ ) { + if( ( ':' == $line[$cix] ) && + ( substr( $line,$cix, 3 ) != '://' ) && + ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) && + ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) && + ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) && + ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) && + !$WithinQuotes ) { + $attrEnd = TRUE; + if(( $cix < ( $strlen - 4 )) && + ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr?? + for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) { + if( '://' == substr( $line, $c2ix - 2, 3 )) { + $attrEnd = FALSE; + break; // an URI with a portnr!! + } + } + } + if( $attrEnd) { + $line = substr( $line, ( $cix + 1 )); + break; + } + } + if( '"' == $line[$cix] ) + $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE; + if( ';' == $line[$cix] ) + $attr[++$attrix] = null; + else + $attr[$attrix] .= $line[$cix]; + } + /* make attributes in array format */ + $propattr = array(); + foreach( $attr as $attribute ) { + $attrsplit = explode( '=', $attribute, 2 ); + if( 1 < count( $attrsplit )) + $propattr[$attrsplit[0]] = $attrsplit[1]; + else + $propattr[] = $attribute; + } + /* update Property */ + if( FALSE !== strpos( $line, ',' )) { + $llen = strlen( $line ); + $content = array( 0 => '' ); + $cix = 0; + for( $lix = 0; $lix < $llen; $lix++ ) { + if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { + $cix++; + $content[$cix] = ''; + } + else + $content[$cix] .= $line[$lix]; + } + if( 1 < count( $content )) { + foreach( $content as $cix => $contentPart ) + $content[$cix] = calendarComponent::_strunrep( $contentPart ); + $this->setProperty( $propname, $content, $propattr ); + continue; + } + else + $line = reset( $content ); + $line = calendarComponent::_strunrep( $line ); + } + $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propattr ); + } // end - foreach( $this->unparsed.. . + } // end - if( is_array( $this->unparsed.. . + unset( $unparsedtext, $rows, $this->unparsed, $proprows ); + /* parse Components */ + if( is_array( $this->components ) && ( 0 < count( $this->components ))) { + $ckeys = array_keys( $this->components ); + foreach( $ckeys as $ckey ) { + if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { + $this->components[$ckey]->parse(); + } + } + } + else + return FALSE; /* err 91 or something.. . */ + return TRUE; + } +/*********************************************************************************/ +/** + * creates formatted output for calendar object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @return string + */ + function createCalendar() { + $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = ''; + switch( $this->format ) { + case 'xcal': + $calendarInit = ''.$this->nl. + 'nl. + '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"'; + $calendarStart = '>'.$this->nl.'nl; + break; + } + $calendarStart .= $this->createVersion(); + $calendarStart .= $this->createProdid(); + $calendarStart .= $this->createCalscale(); + $calendarStart .= $this->createMethod(); + if( 'xcal' == $this->format ) + $calendarStart .= '>'.$this->nl; + $calendar .= $this->createXprop(); + + foreach( $this->components as $component ) { + if( empty( $component )) continue; + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + $calendar .= $component->createComponent( $this->xcaldecl ); + } + if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only + $calendarInit .= ' ['; + $old_xcaldecl = array(); + foreach( $this->xcaldecl as $declix => $declPart ) { + if(( 0 < count( $old_xcaldecl)) && + isset( $declPart['uri'] ) && isset( $declPart['external'] ) && + isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) && + ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) && + ( in_array( $declPart['external'], $old_xcaldecl['external'] ))) + continue; // no duplicate uri and ext. references + if(( 0 < count( $old_xcaldecl)) && + !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) && + isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) && + ( in_array( $declPart['ref'], $old_xcaldecl['ref'] ))) + continue; // no duplicate element declarations + $calendarxCaldecl .= $this->nl.' $declValue ) { + switch( $declKey ) { // index + case 'xmldecl': // no 1 + $calendarxCaldecl .= $declValue.' '; + break; + case 'uri': // no 2 + $calendarxCaldecl .= $declValue.' '; + $old_xcaldecl['uri'][] = $declValue; + break; + case 'ref': // no 3 + $calendarxCaldecl .= $declValue.' '; + $old_xcaldecl['ref'][] = $declValue; + break; + case 'external': // no 4 + $calendarxCaldecl .= '"'.$declValue.'" '; + $old_xcaldecl['external'][] = $declValue; + break; + case 'type': // no 5 + $calendarxCaldecl .= $declValue.' '; + break; + case 'type2': // no 6 + $calendarxCaldecl .= $declValue; + break; + } + } + $calendarxCaldecl .= '>'; + } + $calendarxCaldecl .= $this->nl.']'; + } + switch( $this->format ) { + case 'xcal': + $calendar .= ''.$this->nl; + break; + default: + $calendar .= 'END:VCALENDAR'.$this->nl; + break; + } + return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar; + } +/** + * a HTTP redirect header is sent with created, updated and/or parsed calendar + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.24 - 2011-12-23 + * @param bool $utf8Encode + * @param bool $gzip + * @return redirect + */ + function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE ) { + $filename = $this->getConfig( 'filename' ); + $output = $this->createCalendar(); + if( $utf8Encode ) + $output = utf8_encode( $output ); + if( $gzip ) { + $output = gzencode( $output, 9 ); + header( 'Content-Encoding: gzip' ); + header( 'Vary: *' ); + header( 'Content-Length: '.strlen( $output )); + } + if( 'xcal' == $this->format ) + header( 'Content-Type: application/calendar+xml; charset=utf-8' ); + else + header( 'Content-Type: text/calendar; charset=utf-8' ); + header( 'Content-Disposition: attachment; filename="'.$filename.'"' ); + header( 'Cache-Control: max-age=10' ); + die( $output ); + } +/** + * save content in a file + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.2.12 - 2007-12-30 + * @param string $directory optional + * @param string $filename optional + * @param string $delimiter optional + * @return bool + */ + function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) { + if( $directory ) + $this->setConfig( 'directory', $directory ); + if( $filename ) + $this->setConfig( 'filename', $filename ); + if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR )) + $this->setConfig( 'delimiter', $delimiter ); + if( FALSE === ( $dirfile = $this->getConfig( 'url' ))) + $dirfile = $this->getConfig( 'dirfile' ); + $iCalFile = @fopen( $dirfile, 'w' ); + if( $iCalFile ) { + if( FALSE === fwrite( $iCalFile, $this->createCalendar() )) + return FALSE; + fclose( $iCalFile ); + return TRUE; + } + else + return FALSE; + } +/** + * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent + * else FALSE is returned + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.2.12 - 2007-10-28 + * @param string $directory optional alt. int timeout + * @param string $filename optional + * @param string $delimiter optional + * @param int timeout optional, default 3600 sec + * @return redirect/FALSE + */ + function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) { + if ( $directory && ctype_digit( (string) $directory ) && !$filename ) { + $timeout = (int) $directory; + $directory = FALSE; + } + if( $directory ) + $this->setConfig( 'directory', $directory ); + if( $filename ) + $this->setConfig( 'filename', $filename ); + if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR )) + $this->setConfig( 'delimiter', $delimiter ); + $filesize = $this->getConfig( 'filesize' ); + if( 0 >= $filesize ) + return FALSE; + $dirfile = $this->getConfig( 'dirfile' ); + if( time() - filemtime( $dirfile ) < $timeout) { + clearstatcache(); + $dirfile = $this->getConfig( 'dirfile' ); + $filename = $this->getConfig( 'filename' ); +// if( headers_sent( $filename, $linenum )) +// die( "Headers already sent in $filename on line $linenum\n" ); + if( 'xcal' == $this->format ) + header( 'Content-Type: application/calendar+xml; charset=utf-8' ); + else + header( 'Content-Type: text/calendar; charset=utf-8' ); + header( 'Content-Length: '.$filesize ); + header( 'Content-Disposition: attachment; filename="'.$filename.'"' ); + header( 'Cache-Control: max-age=10' ); + $fp = @fopen( $dirfile, 'r' ); + if( $fp ) { + fpassthru( $fp ); + fclose( $fp ); + } + die(); + } + else + return FALSE; + } +} +/*********************************************************************************/ +/*********************************************************************************/ +/** + * abstract class for calendar components + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-14 + */ +class calendarComponent { + // component property variables + var $uid; + var $dtstamp; + + // component config variables + var $allowEmpty; + var $language; + var $nl; + var $unique_id; + var $format; + var $objName; // created automatically at instance creation + var $dtzid; // default (local) timezone + // component internal variables + var $componentStart1; + var $componentStart2; + var $componentEnd1; + var $componentEnd2; + var $elementStart1; + var $elementStart2; + var $elementEnd1; + var $elementEnd2; + var $intAttrDelimiter; + var $attributeDelimiter; + var $valueInit; + // component xCal declaration container + var $xcaldecl; +/** + * constructor for calendar component object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-17 + */ + function calendarComponent() { + $this->objName = ( isset( $this->timezonetype )) ? + strtolower( $this->timezonetype ) : get_class ( $this ); + $this->uid = array(); + $this->dtstamp = array(); + + $this->language = null; + $this->nl = null; + $this->unique_id = null; + $this->format = null; + $this->dtzid = null; + $this->allowEmpty = TRUE; + $this->xcaldecl = array(); + + $this->_createFormat(); + $this->_makeDtstamp(); + } +/*********************************************************************************/ +/** + * Property Name: ACTION + */ +/** + * creates formatted output for calendar component property action + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createAction() { + if( empty( $this->action )) return FALSE; + if( empty( $this->action['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE; + $attributes = $this->_createParams( $this->action['params'] ); + return $this->_createElement( 'ACTION', $attributes, $this->action['value'] ); + } +/** + * set calendar component property action + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE" + * @param mixed $params + * @return bool + */ + function setAction( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: ATTACH + */ +/** + * creates formatted output for calendar component property attach + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.16 - 2012-02-04 + * @return string + */ + function createAttach() { + if( empty( $this->attach )) return FALSE; + $output = null; + foreach( $this->attach as $attachPart ) { + if( !empty( $attachPart['value'] )) { + $attributes = $this->_createParams( $attachPart['params'] ); + if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) { + $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); + $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value']; + $output = substr( $str, 0, 75 ).$this->nl; + $str = substr( $str, 75 ); + $output .= ' '.chunk_split( $str, 74, $this->nl.' ' ); + if( ' ' == substr( $output, -1 )) + $output = rtrim( $output ); + if( $this->nl != substr( $output, ( 0 - strlen( $this->nl )))) + $output .= $this->nl; + return $output; + } + $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] ); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' ); + } + return $output; + } +/** + * set calendar component property attach + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-06 + * @param string $value + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setAttach( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: ATTENDEE + */ +/** + * creates formatted output for calendar component property attendee + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.12 - 2012-01-31 + * @return string + */ + function createAttendee() { + if( empty( $this->attendee )) return FALSE; + $output = null; + foreach( $this->attendee as $attendeePart ) { // start foreach 1 + if( empty( $attendeePart['value'] )) { + if( $this->getConfig( 'allowEmpty' )) + $output .= $this->_createElement( 'ATTENDEE' ); + continue; + } + $attendee1 = $attendee2 = null; + foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2 + if( 'value' == $paramlabel ) + $attendee2 .= $paramvalue; + elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif + $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ); + foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes + if( is_array( $pValue ) || in_array( $pKey, $mParams )) + continue; + if(( FALSE !== strpos( $pValue, ':' )) || + ( FALSE !== strpos( $pValue, ';' )) || + ( FALSE !== strpos( $pValue, ',' ))) + $paramvalue[$pKey] = '"'.$pValue.'"'; + } + // set attenddee parameters in rfc2445 order + if( isset( $paramvalue['CUTYPE'] )) + $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE']; + if( isset( $paramvalue['MEMBER'] )) { + $attendee1 .= $this->intAttrDelimiter.'MEMBER='; + foreach( $paramvalue['MEMBER'] as $cix => $opv ) + $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; + } + if( isset( $paramvalue['ROLE'] )) + $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE']; + if( isset( $paramvalue['PARTSTAT'] )) + $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT']; + if( isset( $paramvalue['RSVP'] )) + $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP']; + if( isset( $paramvalue['DELEGATED-TO'] )) { + $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO='; + foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv ) + $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; + } + if( isset( $paramvalue['DELEGATED-FROM'] )) { + $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM='; + foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv ) + $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; + } + if( isset( $paramvalue['SENT-BY'] )) + $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY']; + if( isset( $paramvalue['CN'] )) + $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN']; + if( isset( $paramvalue['DIR'] )) { + $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : ''; + $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim; + } + if( isset( $paramvalue['LANGUAGE'] )) + $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE']; + $xparams = array(); + foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3 + if( ctype_digit( (string) $optparamlabel )) { + $xparams[] = $optparamvalue; + continue; + } + if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' ))) + $xparams[$optparamlabel] = $optparamvalue; + } // end foreach 3 + ksort( $xparams, SORT_STRING ); + foreach( $xparams as $paramKey => $paramValue ) { + if( ctype_digit( (string) $paramKey )) + $attendee1 .= $this->intAttrDelimiter.$paramValue; + else + $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue"; + } // end foreach 3 + } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) + } // end foreach 2 + $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 ); + } // end foreach 1 + return $output; + } +/** + * set calendar component property attach + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.17 - 2012-02-03 + * @param string $value + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setAttendee( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params + if( FALSE !== ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) + $value = strtoupper( substr( $value, 0, $pos )).substr( $value, $pos ); + elseif( !empty( $value )) + $value = 'MAILTO:'.$value; + $params2 = array(); + if( is_array($params )) { + $optarrays = array(); + foreach( $params as $optparamlabel => $optparamvalue ) { + $optparamlabel = strtoupper( $optparamlabel ); + switch( $optparamlabel ) { + case 'MEMBER': + case 'DELEGATED-TO': + case 'DELEGATED-FROM': + if( !is_array( $optparamvalue )) + $optparamvalue = array( $optparamvalue ); + foreach( $optparamvalue as $part ) { + $part = trim( $part ); + if(( '"' == substr( $part, 0, 1 )) && + ( '"' == substr( $part, -1 ))) + $part = substr( $part, 1, ( strlen( $part ) - 2 )); + if( 'mailto:' != strtolower( substr( $part, 0, 7 ))) + $part = "MAILTO:$part"; + else + $part = 'MAILTO:'.substr( $part, 7 ); + $optarrays[$optparamlabel][] = $part; + } + break; + default: + if(( '"' == substr( $optparamvalue, 0, 1 )) && + ( '"' == substr( $optparamvalue, -1 ))) + $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 )); + if( 'SENT-BY' == $optparamlabel ) { + if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 ))) + $optparamvalue = "MAILTO:$optparamvalue"; + else + $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 ); + } + $params2[$optparamlabel] = $optparamvalue; + break; + } // end switch( $optparamlabel.. . + } // end foreach( $optparam.. . + foreach( $optarrays as $optparamlabel => $optparams ) + $params2[$optparamlabel] = $optparams; + } + // remove defaults + iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' ); + iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' ); + iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' ); + iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' ); + // check language setting + if( isset( $params2['CN' ] )) { + $lang = $this->getConfig( 'language' ); + if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang )) + $params2['LANGUAGE' ] = $lang; + } + iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CATEGORIES + */ +/** + * creates formatted output for calendar component property categories + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createCategories() { + if( empty( $this->categories )) return FALSE; + $output = null; + foreach( $this->categories as $category ) { + if( empty( $category['value'] )) { + if ( $this->getConfig( 'allowEmpty' )) + $output .= $this->_createElement( 'CATEGORIES' ); + continue; + } + $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' )); + if( is_array( $category['value'] )) { + foreach( $category['value'] as $cix => $categoryPart ) + $category['value'][$cix] = $this->_strrep( $categoryPart ); + $content = implode( ',', $category['value'] ); + } + else + $content = $this->_strrep( $category['value'] ); + $output .= $this->_createElement( 'CATEGORIES', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property categories + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-06 + * @param mixed $value + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setCategories( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CLASS + */ +/** + * creates formatted output for calendar component property class + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 0.9.7 - 2006-11-20 + * @return string + */ + function createClass() { + if( empty( $this->class )) return FALSE; + if( empty( $this->class['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE; + $attributes = $this->_createParams( $this->class['params'] ); + return $this->_createElement( 'CLASS', $attributes, $this->class['value'] ); + } +/** + * set calendar component property class + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name + * @param array $params optional + * @return bool + */ + function setClass( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: COMMENT + */ +/** + * creates formatted output for calendar component property comment + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createComment() { + if( empty( $this->comment )) return FALSE; + $output = null; + foreach( $this->comment as $commentPart ) { + if( empty( $commentPart['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' ); + continue; + } + $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = $this->_strrep( $commentPart['value'] ); + $output .= $this->_createElement( 'COMMENT', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property comment + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-06 + * @param string $value + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setComment( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: COMPLETED + */ +/** + * creates formatted output for calendar component property completed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createCompleted( ) { + if( empty( $this->completed )) return FALSE; + if( !isset( $this->completed['value']['year'] ) && + !isset( $this->completed['value']['month'] ) && + !isset( $this->completed['value']['day'] ) && + !isset( $this->completed['value']['hour'] ) && + !isset( $this->completed['value']['min'] ) && + !isset( $this->completed['value']['sec'] )) + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'COMPLETED' ); + else return FALSE; + $formatted = iCalUtilityFunctions::_format_date_time( $this->completed['value'], 7 ); + $attributes = $this->_createParams( $this->completed['params'] ); + return $this->_createElement( 'COMPLETED', $attributes, $formatted ); + } +/** + * set calendar component property completed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @return bool + */ + function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->completed = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CONTACT + */ +/** + * creates formatted output for calendar component property contact + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @return string + */ + function createContact() { + if( empty( $this->contact )) return FALSE; + $output = null; + foreach( $this->contact as $contact ) { + if( !empty( $contact['value'] )) { + $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = $this->_strrep( $contact['value'] ); + $output .= $this->_createElement( 'CONTACT', $attributes, $content ); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' ); + } + return $output; + } +/** + * set calendar component property contact + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param string $value + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setContact( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CREATED + */ +/** + * creates formatted output for calendar component property created + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createCreated() { + if( empty( $this->created )) return FALSE; + $formatted = iCalUtilityFunctions::_format_date_time( $this->created['value'], 7 ); + $attributes = $this->_createParams( $this->created['params'] ); + return $this->_createElement( 'CREATED', $attributes, $formatted ); + } +/** + * set calendar component property created + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @param mixed $year optional + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param mixed $params optional + * @return bool + */ + function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( !isset( $year )) { + $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' ))); + } + $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DESCRIPTION + */ +/** + * creates formatted output for calendar component property description + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createDescription() { + if( empty( $this->description )) return FALSE; + $output = null; + foreach( $this->description as $description ) { + if( !empty( $description['value'] )) { + $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = $this->_strrep( $description['value'] ); + $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content ); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' ); + } + return $output; + } +/** + * set calendar component property description + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.24 - 2010-11-06 + * @param string $value + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setDescription( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; } + if( 'vjournal' != $this->objName ) + $index = 1; + iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DTEND + */ +/** + * creates formatted output for calendar component property dtend + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-14 + * @return string + */ + function createDtend() { + if( empty( $this->dtend )) return FALSE; + if( !isset( $this->dtend['value']['year'] ) && + !isset( $this->dtend['value']['month'] ) && + !isset( $this->dtend['value']['day'] ) && + !isset( $this->dtend['value']['hour'] ) && + !isset( $this->dtend['value']['min'] ) && + !isset( $this->dtend['value']['sec'] )) + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DTEND' ); + else return FALSE; + $formatted = iCalUtilityFunctions::_format_date_time( $this->dtend['value'] ); + if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && + ( !isset( $this->dtend['params']['VALUE'] ) || ( $this->dtend['params']['VALUE'] != 'DATE' )) && + !isset( $this->dtend['params']['TZID'] )) + $this->dtend['params']['TZID'] = $tzid; + $attributes = $this->_createParams( $this->dtend['params'] ); + return $this->_createElement( 'DTEND', $attributes, $formatted ); + } +/** + * set calendar component property dtend + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-14 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param string $tz optional + * @param array $params optional + * @return bool + */ + function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->dtend = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DTSTAMP + */ +/** + * creates formatted output for calendar component property dtstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.4 - 2008-03-07 + * @return string + */ + function createDtstamp() { + if( !isset( $this->dtstamp['value']['year'] ) && + !isset( $this->dtstamp['value']['month'] ) && + !isset( $this->dtstamp['value']['day'] ) && + !isset( $this->dtstamp['value']['hour'] ) && + !isset( $this->dtstamp['value']['min'] ) && + !isset( $this->dtstamp['value']['sec'] )) + $this->_makeDtstamp(); + $formatted = iCalUtilityFunctions::_format_date_time( $this->dtstamp['value'], 7 ); + $attributes = $this->_createParams( $this->dtstamp['params'] ); + return $this->_createElement( 'DTSTAMP', $attributes, $formatted ); + } +/** + * computes datestamp for calendar component object instance dtstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.9 - 2011-08-10 + * @return void + */ + function _makeDtstamp() { + $d = mktime( date('H'), date('i'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y')); + $this->dtstamp['value'] = array( 'year' => date( 'Y', $d ) + , 'month' => date( 'm', $d ) + , 'day' => date( 'd', $d ) + , 'hour' => date( 'H', $d ) + , 'min' => date( 'i', $d ) + , 'sec' => date( 's', $d )); + $this->dtstamp['params'] = null; + } +/** + * set calendar component property dtstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @return TRUE + */ + function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $year )) + $this->_makeDtstamp(); + else + $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DTSTART + */ +/** + * creates formatted output for calendar component property dtstart + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-15 + * @return string + */ + function createDtstart() { + if( empty( $this->dtstart )) return FALSE; + if( !isset( $this->dtstart['value']['year'] ) && + !isset( $this->dtstart['value']['month'] ) && + !isset( $this->dtstart['value']['day'] ) && + !isset( $this->dtstart['value']['hour'] ) && + !isset( $this->dtstart['value']['min'] ) && + !isset( $this->dtstart['value']['sec'] )) { + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DTSTART' ); + else return FALSE; + } + if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) + unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] ); + elseif(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && + ( !isset( $this->dtstart['params']['VALUE'] ) || ( $this->dtstart['params']['VALUE'] != 'DATE' )) && + !isset( $this->dtstart['params']['TZID'] )) + $this->dtstart['params']['TZID'] = $tzid; + $formatted = iCalUtilityFunctions::_format_date_time( $this->dtstart['value'] ); + $attributes = $this->_createParams( $this->dtstart['params'] ); + return $this->_createElement( 'DTSTART', $attributes, $formatted ); + } +/** + * set calendar component property dtstart + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.22 - 2010-09-22 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param string $tz optional + * @param array $params optional + * @return bool + */ + function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->dtstart = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DUE + */ +/** + * creates formatted output for calendar component property due + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createDue() { + if( empty( $this->due )) return FALSE; + if( !isset( $this->due['value']['year'] ) && + !isset( $this->due['value']['month'] ) && + !isset( $this->due['value']['day'] ) && + !isset( $this->due['value']['hour'] ) && + !isset( $this->due['value']['min'] ) && + !isset( $this->due['value']['sec'] )) { + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DUE' ); + else + return FALSE; + } + $formatted = iCalUtilityFunctions::_format_date_time( $this->due['value'] ); + if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && + ( !isset( $this->due['params']['VALUE'] ) || ( $this->due['params']['VALUE'] != 'DATE' )) && + !isset( $this->due['params']['TZID'] )) + $this->due['params']['TZID'] = $tzid; + $attributes = $this->_createParams( $this->due['params'] ); + return $this->_createElement( 'DUE', $attributes, $formatted ); + } +/** + * set calendar component property due + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @return bool + */ + function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->due = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DURATION + */ +/** + * creates formatted output for calendar component property duration + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createDuration() { + if( empty( $this->duration )) return FALSE; + if( !isset( $this->duration['value']['week'] ) && + !isset( $this->duration['value']['day'] ) && + !isset( $this->duration['value']['hour'] ) && + !isset( $this->duration['value']['min'] ) && + !isset( $this->duration['value']['sec'] )) + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DURATION', array(), null ); + else return FALSE; + $attributes = $this->_createParams( $this->duration['params'] ); + return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_format_duration( $this->duration['value'] )); + } +/** + * set calendar component property duration + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param mixed $week + * @param mixed $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @return bool + */ + function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE; + if( is_array( $week ) && ( 1 <= count( $week ))) + $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); + elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) { + $week = trim( $week ); + if( in_array( substr( $week, 0, 1 ), array( '+', '-' ))) + $week = substr( $week, 1 ); + $this->duration = array( 'value' => iCalUtilityFunctions::_duration_string( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); + } + elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec )) + return FALSE; + else + $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: EXDATE + */ +/** + * creates formatted output for calendar component property exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createExdate() { + if( empty( $this->exdate )) return FALSE; + $output = null; + foreach( $this->exdate as $ex => $theExdate ) { + if( empty( $theExdate['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'EXDATE' ); + continue; + } + $content = $attributes = null; + foreach( $theExdate['value'] as $eix => $exdatePart ) { + $parno = count( $exdatePart ); + $formatted = iCalUtilityFunctions::_format_date_time( $exdatePart, $parno ); + if( isset( $theExdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted); + if( 0 < $eix ) { + if( isset( $theExdate['value'][0]['tz'] )) { + if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) || + ( 'Z' == $theExdate['value'][0]['tz'] )) { + if( 'Z' != substr( $formatted, -1 )) + $formatted .= 'Z'; + } + else + $formatted = str_replace( 'Z', '', $formatted ); + } + else + $formatted = str_replace( 'Z', '', $formatted ); + } + $content .= ( 0 < $eix ) ? ','.$formatted : $formatted; + } + $attributes .= $this->_createParams( $theExdate['params'] ); + $output .= $this->_createElement( 'EXDATE', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-01-19 + * @param array exdates + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setExdate( $exdates, $params=FALSE, $index=FALSE ) { + if( empty( $exdates )) { + if( $this->getConfig( 'allowEmpty' )) { + iCalUtilityFunctions::_setMval( $this->exdate, null, $params, FALSE, $index ); + return TRUE; + } + else + return FALSE; + } + $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); + $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE; + /* ev. check 1:st date and save ev. timezone **/ + iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] ); + iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter + foreach( $exdates as $eix => $theExdate ) { + iCalUtilityFunctions::_strDate2arr( $theExdate ); + if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) + $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno ); + elseif( is_array( $theExdate )) + $exdatea = iCalUtilityFunctions::_date_time_array( $theExdate, $parno ); + elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18 + $exdatea = iCalUtilityFunctions::_date_time_string( $theExdate, $parno ); + unset( $exdatea['unparsedtext'] ); + } + if( 3 == $parno ) + unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] ); + elseif( isset( $exdatea['tz'] )) + $exdatea['tz'] = (string) $exdatea['tz']; + if( isset( $input['params']['TZID'] ) || + ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) || + ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) || + ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] ))) + unset( $exdatea['tz'] ); + if( $toZ ) // time zone Z + $exdatea['tz'] = 'Z'; + $input['value'][] = $exdatea; + } + if( 0 >= count( $input['value'] )) + return FALSE; + if( 3 == $parno ) { + $input['params']['VALUE'] = 'DATE'; + unset( $input['params']['TZID'] ); + } + if( $toZ ) // time zone Z + unset( $input['params']['TZID'] ); + iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: EXRULE + */ +/** + * creates formatted output for calendar component property exrule + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createExrule() { + if( empty( $this->exrule )) return FALSE; + return $this->_format_recur( 'EXRULE', $this->exrule ); + } +/** + * set calendar component property exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param array $exruleset + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setExrule( $exruleset, $params=FALSE, $index=FALSE ) { + if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: FREEBUSY + */ +/** + * creates formatted output for calendar component property freebusy + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.1.23 - 2012-02-16 + * @return string + */ + function createFreebusy() { + if( empty( $this->freebusy )) return FALSE; + $output = null; + foreach( $this->freebusy as $freebusyPart ) { + if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' ); + continue; + } + $attributes = $content = null; + if( isset( $freebusyPart['value']['fbtype'] )) { + $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype']; + unset( $freebusyPart['value']['fbtype'] ); + $freebusyPart['value'] = array_values( $freebusyPart['value'] ); + } + else + $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY'; + $attributes .= $this->_createParams( $freebusyPart['params'] ); + $fno = 1; + $cnt = count( $freebusyPart['value']); + foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) { + $formatted = iCalUtilityFunctions::_format_date_time( $freebusyPeriod[0] ); + $content .= $formatted; + $content .= '/'; + $cnt2 = count( $freebusyPeriod[1]); + if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time + $cnt2 = 7; + elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration + $cnt2 = 5; + if(( 7 == $cnt2 ) && // period= -> date-time + isset( $freebusyPeriod[1]['year'] ) && + isset( $freebusyPeriod[1]['month'] ) && + isset( $freebusyPeriod[1]['day'] )) { + $content .= iCalUtilityFunctions::_format_date_time( $freebusyPeriod[1] ); + } + else { // period= -> dur-time + $content .= iCalUtilityFunctions::_format_duration( $freebusyPeriod[1] ); + } + if( $fno < $cnt ) + $content .= ','; + $fno++; + } + $output .= $this->_createElement( 'FREEBUSY', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property freebusy + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.30 - 2012-01-16 + * @param string $fbType + * @param array $fbValues + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) { + if( empty( $fbValues )) { + if( $this->getConfig( 'allowEmpty' )) { + iCalUtilityFunctions::_setMval( $this->freebusy, null, $params, FALSE, $index ); + return TRUE; + } + else + return FALSE; + } + $fbType = strtoupper( $fbType ); + if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) && + ( 'X-' != substr( $fbType, 0, 2 ))) + $fbType = 'BUSY'; + $input = array( 'fbtype' => $fbType ); + foreach( $fbValues as $fbPeriod ) { // periods => period + if( empty( $fbPeriod )) + continue; + $freebusyPeriod = array(); + foreach( $fbPeriod as $fbMember ) { // pairs => singlepart + $freebusyPairMember = array(); + if( is_array( $fbMember )) { + if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value + $freebusyPairMember = iCalUtilityFunctions::_date_time_array( $fbMember, 7 ); + $freebusyPairMember['tz'] = 'Z'; + } + elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value + $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 ); + $freebusyPairMember['tz'] = 'Z'; + } + else { // array format duration + $freebusyPairMember = iCalUtilityFunctions::_duration_array( $fbMember ); + } + } + elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration + ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) { + if( 'P' != $fbMember{0} ) + $fbmember = substr( $fbMember, 1 ); + $freebusyPairMember = iCalUtilityFunctions::_duration_string( $fbMember ); + } + elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18 + $freebusyPairMember = iCalUtilityFunctions::_date_time_string( $fbMember, 7 ); + unset( $freebusyPairMember['unparsedtext'] ); + $freebusyPairMember['tz'] = 'Z'; + } + $freebusyPeriod[] = $freebusyPairMember; + } + $input[] = $freebusyPeriod; + } + iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: GEO + */ +/** + * creates formatted output for calendar component property geo + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createGeo() { + if( empty( $this->geo )) return FALSE; + if( empty( $this->geo['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE; + $attributes = $this->_createParams( $this->geo['params'] ); + $content = null; + $content .= number_format( (float) $this->geo['value']['latitude'], 6, '.', ''); + $content .= ';'; + $content .= number_format( (float) $this->geo['value']['longitude'], 6, '.', ''); + return $this->_createElement( 'GEO', $attributes, $content ); + } +/** + * set calendar component property geo + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param float $latitude + * @param float $longitude + * @param array $params optional + * @return bool + */ + function setGeo( $latitude, $longitude, $params=FALSE ) { + if( !empty( $latitude ) && !empty( $longitude )) { + if( !is_array( $this->geo )) $this->geo = array(); + $this->geo['value']['latitude'] = $latitude; + $this->geo['value']['longitude'] = $longitude; + $this->geo['params'] = iCalUtilityFunctions::_setParams( $params ); + } + elseif( $this->getConfig( 'allowEmpty' )) + $this->geo = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) ); + else + return FALSE; + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: LAST-MODIFIED + */ +/** + * creates formatted output for calendar component property last-modified + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createLastModified() { + if( empty( $this->lastmodified )) return FALSE; + $attributes = $this->_createParams( $this->lastmodified['params'] ); + $formatted = iCalUtilityFunctions::_format_date_time( $this->lastmodified['value'], 7 ); + return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted ); + } +/** + * set calendar component property completed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @param mixed $year optional + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @return boll + */ + function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $year )) + $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' ))); + $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: LOCATION + */ +/** + * creates formatted output for calendar component property location + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + */ + function createLocation() { + if( empty( $this->location )) return FALSE; + if( empty( $this->location['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE; + $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = $this->_strrep( $this->location['value'] ); + return $this->_createElement( 'LOCATION', $attributes, $content ); + } +/** + * set calendar component property location + ' + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param array params optional + * @return bool + */ + function setLocation( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: ORGANIZER + */ +/** + * creates formatted output for calendar component property organizer + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.33 - 2010-12-17 + * @return string + */ + function createOrganizer() { + if( empty( $this->organizer )) return FALSE; + if( empty( $this->organizer['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE; + $attributes = $this->_createParams( $this->organizer['params'] + , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' )); + return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] ); + } +/** + * set calendar component property organizer + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.27 - 2010-11-29 + * @param string $value + * @param array params optional + * @return bool + */ + function setOrganizer( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) + $value = 'MAILTO:'.$value; + else + $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); + $value = str_replace( 'mailto:', 'MAILTO:', $value ); + $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + if( isset( $this->organizer['params']['SENT-BY'] )){ + if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 ))) + $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY']; + else + $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 ); + } + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: PERCENT-COMPLETE + */ +/** + * creates formatted output for calendar component property percent-complete + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @return string + */ + function createPercentComplete() { + if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE; + if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] ))) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE; + $attributes = $this->_createParams( $this->percentcomplete['params'] ); + return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] ); + } +/** + * set calendar component property percent-complete + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @param int $value + * @param array $params optional + * @return bool + */ + function setPercentComplete( $value, $params=FALSE ) { + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: PRIORITY + */ +/** + * creates formatted output for calendar component property priority + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @return string + */ + function createPriority() { + if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE; + if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] ))) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE; + $attributes = $this->_createParams( $this->priority['params'] ); + return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] ); + } +/** + * set calendar component property priority + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @param int $value + * @param array $params optional + * @return bool + */ + function setPriority( $value, $params=FALSE ) { + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RDATE + */ +/** + * creates formatted output for calendar component property rdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-10-26 + * @return string + */ + function createRdate() { + if( empty( $this->rdate )) return FALSE; + $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE; + $output = null; + if( $utctime ) + unset( $this->rdate['params']['TZID'] ); + foreach( $this->rdate as $theRdate ) { + if( empty( $theRdate['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' ); + continue; + } + if( $utctime ) + unset( $theRdate['params']['TZID'] ); + $attributes = $this->_createParams( $theRdate['params'] ); + $cnt = count( $theRdate['value'] ); + $content = null; + $rno = 1; + foreach( $theRdate['value'] as $rpix => $rdatePart ) { + $contentPart = null; + if( is_array( $rdatePart ) && + isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD + if( $utctime ) + unset( $rdatePart[0]['tz'] ); + $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[0]); // PERIOD part 1 + if( $utctime || !empty( $theRdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted); + if( 0 < $rpix ) { + if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) { + if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z'; + } + else + $formatted = str_replace( 'Z', '', $formatted ); + } + $contentPart .= $formatted; + $contentPart .= '/'; + $cnt2 = count( $rdatePart[1]); + if( array_key_exists( 'year', $rdatePart[1] )) { + if( array_key_exists( 'hour', $rdatePart[1] )) + $cnt2 = 7; // date-time + else + $cnt2 = 3; // date + } + elseif( array_key_exists( 'week', $rdatePart[1] )) // duration + $cnt2 = 5; + if(( 7 == $cnt2 ) && // period= -> date-time + isset( $rdatePart[1]['year'] ) && + isset( $rdatePart[1]['month'] ) && + isset( $rdatePart[1]['day'] )) { + if( $utctime ) + unset( $rdatePart[1]['tz'] ); + $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[1] ); // PERIOD part 2 + if( $utctime || !empty( $theRdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted); + if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) { + if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z'; + } + else + $formatted = str_replace( 'Z', '', $formatted ); + $contentPart .= $formatted; + } + else { // period= -> dur-time + $contentPart .= iCalUtilityFunctions::_format_duration( $rdatePart[1] ); + } + } // PERIOD end + else { // SINGLE date start + if( $utctime ) + unset( $rdatePart['tz'] ); + $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart); + if( $utctime || !empty( $theRdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted); + if( !$utctime && ( 0 < $rpix )) { + if( !empty( $theRdate['value'][0]['tz'] ) && iCalUtilityFunctions::_isOffset( $theRdate['value'][0]['tz'] )) { + if( 'Z' != substr( $formatted, -1 )) + $formatted .= 'Z'; + } + else + $formatted = str_replace( 'Z', '', $formatted ); + } + $contentPart .= $formatted; + } + $content .= $contentPart; + if( $rno < $cnt ) + $content .= ','; + $rno++; + } + $output .= $this->_createElement( 'RDATE', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property rdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-01-31 + * @param array $rdates + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setRdate( $rdates, $params=FALSE, $index=FALSE ) { + if( empty( $rdates )) { + if( $this->getConfig( 'allowEmpty' )) { + iCalUtilityFunctions::_setMval( $this->rdate, null, $params, FALSE, $index ); + return TRUE; + } + else + return FALSE; + } + $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); + if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) { + unset( $input['params']['TZID'] ); + $input['params']['VALUE'] = 'DATE-TIME'; + } + $zArr = array( 'GMT', 'UTC', 'Z' ); + $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE; + /* check if PERIOD, if not set */ + if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) && + isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) && + isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) && + (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) || + iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) || + ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) && + ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] )))))) + $input['params']['VALUE'] = 'PERIOD'; + /* check 1:st date, upd. $parno (opt) and save ev. timezone **/ + $date = reset( $rdates ); + if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD + $date = reset( $date ); + iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] ); + iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default + foreach( $rdates as $rpix => $theRdate ) { + $inputa = null; + iCalUtilityFunctions::_strDate2arr( $theRdate ); + if( is_array( $theRdate )) { + if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD + foreach( $theRdate as $rix => $rPeriod ) { + iCalUtilityFunctions::_strDate2arr( $theRdate ); + if( is_array( $rPeriod )) { + if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) // timestamp + $inputab = ( isset( $rPeriod['tz'] )) ? iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ) : iCalUtilityFunctions::_timestamp2date( $rPeriod, 6 ); + elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) + $inputab = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_date_time_array( $rPeriod, $parno ) : iCalUtilityFunctions::_date_time_array( $rPeriod, 6 ); + elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date + $inputab = iCalUtilityFunctions::_date_time_string( reset( $rPeriod ), $parno ); + unset( $inputab['unparsedtext'] ); + } + else // array format duration + $inputab = iCalUtilityFunctions::_duration_array( $rPeriod ); + } + elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration + ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) { + if( 'P' != $rPeriod[0] ) + $rPeriod = substr( $rPeriod, 1 ); + $inputab = iCalUtilityFunctions::_duration_string( $rPeriod ); + } + elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18 + $inputab = iCalUtilityFunctions::_date_time_string( $rPeriod, $parno ); + unset( $inputab['unparsedtext'] ); + } + if( isset( $input['params']['TZID'] ) || + ( isset( $inputab['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputab['tz'] )) || + ( isset( $inputa[0] ) && ( !isset( $inputa[0]['tz'] ))) || + ( isset( $inputa[0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa[0]['tz'] ))) + unset( $inputab['tz'] ); + if( $toZ ) + $inputab['tz'] = 'Z'; + $inputa[] = $inputab; + } + } // PERIOD end + elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp + $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno ); + if( $toZ ) + $inputa['tz'] = 'Z'; + } + else { // date[-time] + $inputa = iCalUtilityFunctions::_date_time_array( $theRdate, $parno ); + $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE; + if( $toZ ) + $inputa['tz'] = 'Z'; + } + } + elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18 + $inputa = iCalUtilityFunctions::_date_time_string( $theRdate, $parno ); + unset( $inputa['unparsedtext'] ); + if( $toZ ) + $inputa['tz'] = 'Z'; + } + if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD + if( 3 == $parno ) + unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] ); + elseif( isset( $inputa['tz'] )) + $inputa['tz'] = (string) $inputa['tz']; + if( isset( $input['params']['TZID'] ) || + ( isset( $inputa['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa['tz'] )) || + ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) || + ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] ))) + if( !$toZ ) + unset( $inputa['tz'] ); + } + $input['value'][] = $inputa; + } + if( 3 == $parno ) { + $input['params']['VALUE'] = 'DATE'; + unset( $input['params']['TZID'] ); + } + if( $toZ ) + unset( $input['params']['TZID'] ); + iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RECURRENCE-ID + */ +/** + * creates formatted output for calendar component property recurrence-id + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-15 + * @return string + */ + function createRecurrenceid() { + if( empty( $this->recurrenceid )) return FALSE; + if( empty( $this->recurrenceid['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE; + $formatted = iCalUtilityFunctions::_format_date_time( $this->recurrenceid['value'] ); + if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) && + ( !isset( $this->recurrenceid['params']['VALUE'] ) || ( $this->recurrenceid['params']['VALUE'] != 'DATE' )) && + !isset( $this->recurrenceid['params']['TZID'] )) + $this->recurrenceid['params']['TZID'] = $tzid; + $attributes = $this->_createParams( $this->recurrenceid['params'] ); + return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted ); + } +/** + * set calendar component property recurrence-id + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-15 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @return bool + */ + function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->recurrenceid = array( 'value' => null, 'params' => null ); + return TRUE; + } + else + return FALSE; + } + $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RELATED-TO + */ +/** + * creates formatted output for calendar component property related-to + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.24 - 2012-02-23 + * @return string + */ + function createRelatedTo() { + if( empty( $this->relatedto )) return FALSE; + $output = null; + foreach( $this->relatedto as $relation ) { + if( !empty( $relation['value'] )) + $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), $this->_strrep( $relation['value'] ) ); + elseif( $this->getConfig( 'allowEmpty' )) + $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] )); + } + return $output; + } +/** + * set calendar component property related-to + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.24 - 2012-02-23 + * @param float $relid + * @param array $params, optional + * @param index $index, optional + * @return bool + */ + function setRelatedTo( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default + iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: REPEAT + */ +/** + * creates formatted output for calendar component property repeat + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @return string + */ + function createRepeat() { + if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE; + if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] ))) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE; + $attributes = $this->_createParams( $this->repeat['params'] ); + return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] ); + } +/** + * set calendar component property repeat + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @param string $value + * @param array $params optional + * @return void + */ + function setRepeat( $value, $params=FALSE ) { + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: REQUEST-STATUS + */ +/** + * creates formatted output for calendar component property request-status + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @return string + */ + function createRequestStatus() { + if( empty( $this->requeststatus )) return FALSE; + $output = null; + foreach( $this->requeststatus as $rstat ) { + if( empty( $rstat['value']['statcode'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' ); + continue; + } + $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' )); + $content = number_format( (float) $rstat['value']['statcode'], 2, '.', ''); + $content .= ';'.$this->_strrep( $rstat['value']['text'] ); + if( isset( $rstat['value']['extdata'] )) + $content .= ';'.$this->_strrep( $rstat['value']['extdata'] ); + $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property request-status + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param float $statcode + * @param string $text + * @param string $extdata, optional + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) { + if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = null; else return FALSE; + $input = array( 'statcode' => $statcode, 'text' => $text ); + if( $extdata ) + $input['extdata'] = $extdata; + iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RESOURCES + */ +/** + * creates formatted output for calendar component property resources + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @return string + */ + function createResources() { + if( empty( $this->resources )) return FALSE; + $output = null; + foreach( $this->resources as $resource ) { + if( empty( $resource['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' ); + continue; + } + $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' )); + if( is_array( $resource['value'] )) { + foreach( $resource['value'] as $rix => $resourcePart ) + $resource['value'][$rix] = $this->_strrep( $resourcePart ); + $content = implode( ',', $resource['value'] ); + } + else + $content = $this->_strrep( $resource['value'] ); + $output .= $this->_createElement( 'RESOURCES', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property recources + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param mixed $value + * @param array $params, optional + * @param integer $index, optional + * @return bool + */ + function setResources( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RRULE + */ +/** + * creates formatted output for calendar component property rrule + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createRrule() { + if( empty( $this->rrule )) return FALSE; + return $this->_format_recur( 'RRULE', $this->rrule ); + } +/** + * set calendar component property rrule + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param array $rruleset + * @param array $params, optional + * @param integer $index, optional + * @return void + */ + function setRrule( $rruleset, $params=FALSE, $index=FALSE ) { + if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: SEQUENCE + */ +/** + * creates formatted output for calendar component property sequence + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @return string + */ + function createSequence() { + if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE; + if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) && + ( '0' != $this->sequence['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE; + $attributes = $this->_createParams( $this->sequence['params'] ); + return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] ); + } +/** + * set calendar component property sequence + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.8 - 2011-09-19 + * @param int $value optional + * @param array $params optional + * @return bool + */ + function setSequence( $value=FALSE, $params=FALSE ) { + if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value )) + $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0'; + $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: STATUS + */ +/** + * creates formatted output for calendar component property status + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createStatus() { + if( empty( $this->status )) return FALSE; + if( empty( $this->status['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE; + $attributes = $this->_createParams( $this->status['params'] ); + return $this->_createElement( 'STATUS', $attributes, $this->status['value'] ); + } +/** + * set calendar component property status + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param array $params optional + * @return bool + */ + function setStatus( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: SUMMARY + */ +/** + * creates formatted output for calendar component property summary + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createSummary() { + if( empty( $this->summary )) return FALSE; + if( empty( $this->summary['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE; + $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = $this->_strrep( $this->summary['value'] ); + return $this->_createElement( 'SUMMARY', $attributes, $content ); + } +/** + * set calendar component property summary + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param string $params optional + * @return bool + */ + function setSummary( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TRANSP + */ +/** + * creates formatted output for calendar component property transp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createTransp() { + if( empty( $this->transp )) return FALSE; + if( empty( $this->transp['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE; + $attributes = $this->_createParams( $this->transp['params'] ); + return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] ); + } +/** + * set calendar component property transp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param string $params optional + * @return bool + */ + function setTransp( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TRIGGER + */ +/** + * creates formatted output for calendar component property trigger + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-10-21 + * @return string + */ + function createTrigger() { + if( empty( $this->trigger )) return FALSE; + if( empty( $this->trigger['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE; + $content = $attributes = null; + if( isset( $this->trigger['value']['year'] ) && + isset( $this->trigger['value']['month'] ) && + isset( $this->trigger['value']['day'] )) + $content .= iCalUtilityFunctions::_format_date_time( $this->trigger['value'] ); + else { + if( TRUE !== $this->trigger['value']['relatedStart'] ) + $attributes .= $this->intAttrDelimiter.'RELATED=END'; + if( $this->trigger['value']['before'] ) + $content .= '-'; + $content .= iCalUtilityFunctions::_format_duration( $this->trigger['value'] ); + } + $attributes .= $this->_createParams( $this->trigger['params'] ); + return $this->_createElement( 'TRIGGER', $attributes, $content ); + } +/** + * set calendar component property trigger + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.30 - 2012-01-16 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $week optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param bool $relatedStart optional + * @param bool $before optional + * @param array $params optional + * @return bool + */ + function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) { + if( empty( $year ) && empty( $month ) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec )) + if( $this->getConfig( 'allowEmpty' )) { + $this->trigger = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) ); + return TRUE; + } + else + return FALSE; + if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp + $params = iCalUtilityFunctions::_setParams( $month ); + $date = iCalUtilityFunctions::_timestamp2date( $year, 7 ); + foreach( $date as $k => $v ) + $$k = $v; + } + elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) { + $params = iCalUtilityFunctions::_setParams( $month ); + if(!(array_key_exists( 'year', $year ) && // exclude date-time + array_key_exists( 'month', $year ) && + array_key_exists( 'day', $year ))) { // when this must be a duration + if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) + $relatedStart = FALSE; + else + $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE; + $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE; + } + $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null; + $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null; + $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null; + $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null; + $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null; + $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null; + $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null; + $year = $SSYY; + } + elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string + $params = iCalUtilityFunctions::_setParams( $month ); + if( in_array( $year[0], array( 'P', '+', '-' ))) { // duration + $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE; + $before = ( '-' == $year[0] ) ? TRUE : FALSE; + if( 'P' != $year[0] ) + $year = substr( $year, 1 ); + $date = iCalUtilityFunctions::_duration_string( $year); + } + else // date + $date = iCalUtilityFunctions::_date_time_string( $year, 7 ); + unset( $year, $month, $day, $date['unparsedtext'] ); + if( empty( $date )) + $sec = 0; + else + foreach( $date as $k => $v ) + $$k = $v; + } + else // single values in function input parameters + $params = iCalUtilityFunctions::_setParams( $params ); + if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date + $params['VALUE'] = 'DATE-TIME'; + $hour = ( $hour ) ? $hour : 0; + $min = ( $min ) ? $min : 0; + $sec = ( $sec ) ? $sec : 0; + $this->trigger = array( 'params' => $params ); + $this->trigger['value'] = array( 'year' => $year + , 'month' => $month + , 'day' => $day + , 'hour' => $hour + , 'min' => $min + , 'sec' => $sec + , 'tz' => 'Z' ); + return TRUE; + } + elseif(( empty( $year ) && empty( $month )) && // duration + (( !empty( $week ) || ( 0 == $week )) || + ( !empty( $day ) || ( 0 == $day )) || + ( !empty( $hour ) || ( 0 == $hour )) || + ( !empty( $min ) || ( 0 == $min )) || + ( !empty( $sec ) || ( 0 == $sec )))) { + unset( $params['RELATED'] ); // set at output creation (END only) + unset( $params['VALUE'] ); // 'DURATION' default + $this->trigger = array( 'params' => $params ); + $this->trigger['value'] = array(); + if( !empty( $week )) $this->trigger['value']['week'] = $week; + if( !empty( $day )) $this->trigger['value']['day'] = $day; + if( !empty( $hour )) $this->trigger['value']['hour'] = $hour; + if( !empty( $min )) $this->trigger['value']['min'] = $min; + if( !empty( $sec )) $this->trigger['value']['sec'] = $sec; + if( empty( $this->trigger['value'] )) { + $this->trigger['value']['sec'] = 0; + $before = FALSE; + } + $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE; + $before = ( FALSE !== $before ) ? TRUE : FALSE; + $this->trigger['value']['relatedStart'] = $relatedStart; + $this->trigger['value']['before'] = $before; + return TRUE; + } + return FALSE; + } +/*********************************************************************************/ +/** + * Property Name: TZID + */ +/** + * creates formatted output for calendar component property tzid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createTzid() { + if( empty( $this->tzid )) return FALSE; + if( empty( $this->tzid['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE; + $attributes = $this->_createParams( $this->tzid['params'] ); + return $this->_createElement( 'TZID', $attributes, $this->_strrep( $this->tzid['value'] )); + } +/** + * set calendar component property tzid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param array $params optional + * @return bool + */ + function setTzid( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * .. . + * Property Name: TZNAME + */ +/** + * creates formatted output for calendar component property tzname + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createTzname() { + if( empty( $this->tzname )) return FALSE; + $output = null; + foreach( $this->tzname as $theName ) { + if( !empty( $theName['value'] )) { + $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' )); + $output .= $this->_createElement( 'TZNAME', $attributes, $this->_strrep( $theName['value'] )); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' ); + } + return $output; + } +/** + * set calendar component property tzname + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param string $value + * @param string $params, optional + * @param integer $index, optional + * @return bool + */ + function setTzname( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TZOFFSETFROM + */ +/** + * creates formatted output for calendar component property tzoffsetfrom + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createTzoffsetfrom() { + if( empty( $this->tzoffsetfrom )) return FALSE; + if( empty( $this->tzoffsetfrom['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE; + $attributes = $this->_createParams( $this->tzoffsetfrom['params'] ); + return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] ); + } +/** + * set calendar component property tzoffsetfrom + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param string $params optional + * @return bool + */ + function setTzoffsetfrom( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TZOFFSETTO + */ +/** + * creates formatted output for calendar component property tzoffsetto + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createTzoffsetto() { + if( empty( $this->tzoffsetto )) return FALSE; + if( empty( $this->tzoffsetto['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE; + $attributes = $this->_createParams( $this->tzoffsetto['params'] ); + return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] ); + } +/** + * set calendar component property tzoffsetto + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param string $params optional + * @return bool + */ + function setTzoffsetto( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TZURL + */ +/** + * creates formatted output for calendar component property tzurl + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createTzurl() { + if( empty( $this->tzurl )) return FALSE; + if( empty( $this->tzurl['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE; + $attributes = $this->_createParams( $this->tzurl['params'] ); + return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] ); + } +/** + * set calendar component property tzurl + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param string $params optional + * @return boll + */ + function setTzurl( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: UID + */ +/** + * creates formatted output for calendar component property uid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 0.9.7 - 2006-11-20 + * @return string + */ + function createUid() { + if( 0 >= count( $this->uid )) + $this->_makeuid(); + $attributes = $this->_createParams( $this->uid['params'] ); + return $this->_createElement( 'UID', $attributes, $this->uid['value'] ); + } +/** + * create an unique id for this calendar component object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.2.7 - 2007-09-04 + * @return void + */ + function _makeUid() { + $date = date('Ymd\THisT'); + $unique = substr(microtime(), 2, 4); + $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890'; + $start = 0; + $end = strlen( $base ) - 1; + $length = 6; + $str = null; + for( $p = 0; $p < $length; $p++ ) + $unique .= $base{mt_rand( $start, $end )}; + $this->uid = array( 'params' => null ); + $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' ); + } +/** + * set calendar component property uid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param string $params optional + * @return bool + */ + function setUid( $value, $params=FALSE ) { + if( empty( $value )) return FALSE; // no allowEmpty check here !!!! + $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: URL + */ +/** + * creates formatted output for calendar component property url + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @return string + */ + function createUrl() { + if( empty( $this->url )) return FALSE; + if( empty( $this->url['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE; + $attributes = $this->_createParams( $this->url['params'] ); + return $this->_createElement( 'URL', $attributes, $this->url['value'] ); + } +/** + * set calendar component property url + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-11-04 + * @param string $value + * @param string $params optional + * @return bool + */ + function setUrl( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: x-prop + */ +/** + * creates formatted output for calendar component property x-prop + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @return string + */ + function createXprop() { + if( empty( $this->xprop )) return FALSE; + $output = null; + foreach( $this->xprop as $label => $xpropPart ) { + if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $label ); + continue; + } + $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' )); + if( is_array( $xpropPart['value'] )) { + foreach( $xpropPart['value'] as $pix => $theXpart ) + $xpropPart['value'][$pix] = $this->_strrep( $theXpart ); + $xpropPart['value'] = implode( ',', $xpropPart['value'] ); + } + else + $xpropPart['value'] = $this->_strrep( $xpropPart['value'] ); + $output .= $this->_createElement( $label, $attributes, $xpropPart['value'] ); + } + return $output; + } +/** + * set calendar component property x-prop + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.9 - 2012-01-16 + * @param string $label + * @param mixed $value + * @param array $params optional + * @return bool + */ + function setXprop( $label, $value, $params=FALSE ) { + if( empty( $label )) + return FALSE; + if( 'X-' != strtoupper( substr( $label, 0, 2 ))) + return FALSE; + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; + $xprop = array( 'value' => $value ); + $xprop['params'] = iCalUtilityFunctions::_setParams( $params ); + if( !is_array( $this->xprop )) $this->xprop = array(); + $this->xprop[strtoupper( $label )] = $xprop; + return TRUE; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * create element format parts + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.0.6 - 2006-06-20 + * @return string + */ + function _createFormat() { + $objectname = null; + switch( $this->format ) { + case 'xcal': + $objectname = ( isset( $this->timezonetype )) ? + strtolower( $this->timezonetype ) : strtolower( $this->objName ); + $this->componentStart1 = $this->elementStart1 = '<'; + $this->componentStart2 = $this->elementStart2 = '>'; + $this->componentEnd1 = $this->elementEnd1 = 'componentEnd2 = $this->elementEnd2 = '>'.$this->nl; + $this->intAttrDelimiter = ''; + $this->attributeDelimiter = $this->nl; + $this->valueInit = null; + break; + default: + $objectname = ( isset( $this->timezonetype )) ? + strtoupper( $this->timezonetype ) : strtoupper( $this->objName ); + $this->componentStart1 = 'BEGIN:'; + $this->componentStart2 = null; + $this->componentEnd1 = 'END:'; + $this->componentEnd2 = $this->nl; + $this->elementStart1 = null; + $this->elementStart2 = null; + $this->elementEnd1 = null; + $this->elementEnd2 = $this->nl; + $this->intAttrDelimiter = ''; + $this->attributeDelimiter = ';'; + $this->valueInit = ':'; + break; + } + return $objectname; + } +/** + * creates formatted output for calendar component property + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @param string $label property name + * @param string $attributes property attributes + * @param string $content property content (optional) + * @return string + */ + function _createElement( $label, $attributes=null, $content=FALSE ) { + switch( $this->format ) { + case 'xcal': + $label = strtolower( $label ); + break; + default: + $label = strtoupper( $label ); + break; + } + $output = $this->elementStart1.$label; + $categoriesAttrLang = null; + $attachInlineBinary = FALSE; + $attachfmttype = null; + if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) { + $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT' + , 'ref' => $label + , 'type2' => '(#PCDATA)' ); + } + if( !empty( $attributes )) { + $attributes = trim( $attributes ); + if ( 'xcal' == $this->format ) { + $attributes2 = explode( $this->intAttrDelimiter, $attributes ); + $attributes = null; + foreach( $attributes2 as $aix => $attribute ) { + $attrKVarr = explode( '=', $attribute ); + if( empty( $attrKVarr[0] )) + continue; + if( !isset( $attrKVarr[1] )) { + $attrValue = $attrKVarr[0]; + $attrKey = $aix; + } + elseif( 2 == count( $attrKVarr)) { + $attrKey = strtolower( $attrKVarr[0] ); + $attrValue = $attrKVarr[1]; + } + else { + $attrKey = strtolower( $attrKVarr[0] ); + unset( $attrKVarr[0] ); + $attrValue = implode( '=', $attrKVarr ); + } + if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) { + $attachInlineBinary = TRUE; + if( 'fmttype' == $attrKey ) + $attachfmttype = $attrKey.'='.$attrValue; + continue; + } + elseif(( 'categories' == $label ) && ( 'language' == $attrKey )) + $categoriesAttrLang = $attrKey.'='.$attrValue; + else { + $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; + $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null; + if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) { + $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 )); + $attrValue = str_replace( '"', '', $attrValue ); + } + $attributes .= '"'.htmlspecialchars( $attrValue ).'"'; + } + } + } + else { + $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); + } + } + if(( 'xcal' == $this->format) && + ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) { + $pos = strrpos($content, "/"); + $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content; + $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY' + , 'uri' => $docname + , 'ref' => 'SYSTEM' + , 'external' => $content + , 'type' => 'NDATA' + , 'type2' => 'BINERY' ); + $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; + $attributes .= 'uri="'.$docname.'"'; + $content = null; + if( 'attach' == $label ) { + $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes ); + $content = $this->nl.$this->_createElement( 'extref', $attributes, null ); + $attributes = null; + } + } + elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) { + $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute + } + $output .= $attributes; + if( !$content && ( '0' != $content )) { + switch( $this->format ) { + case 'xcal': + $output .= ' /'; + $output .= $this->elementStart2.$this->nl; + return $output; + break; + default: + $output .= $this->elementStart2.$this->valueInit; + return $this->_size75( $output ); + break; + } + } + $output .= $this->elementStart2; + $output .= $this->valueInit.$content; + switch( $this->format ) { + case 'xcal': + return $output.$this->elementEnd1.$label.$this->elementEnd2; + break; + default: + return $this->_size75( $output ); + break; + } + } +/** + * creates formatted output for calendar component property parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.27 - 2012-01-16 + * @param array $params optional + * @param array $ctrKeys optional + * @return string + */ + function _createParams( $params=array(), $ctrKeys=array() ) { + if( !is_array( $params ) || empty( $params )) + $params = array(); + $attrLANG = $attr1 = $attr2 = $lang = null; + $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ; + $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ; + $CNattrExist = $LANGattrExist = FALSE; + $xparams = array(); + foreach( $params as $paramKey => $paramValue ) { + if(( FALSE !== strpos( $paramValue, ':' )) || + ( FALSE !== strpos( $paramValue, ';' )) || + ( FALSE !== strpos( $paramValue, ',' ))) + $paramValue = '"'.$paramValue.'"'; + if( ctype_digit( (string) $paramKey )) { + $xparams[] = $paramValue; + continue; + } + $paramKey = strtoupper( $paramKey ); + if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' ))) + $xparams[$paramKey] = $paramValue; + else + $params[$paramKey] = $paramValue; + } + ksort( $xparams, SORT_STRING ); + foreach( $xparams as $paramKey => $paramValue ) { + if( ctype_digit( (string) $paramKey )) + $attr2 .= $this->intAttrDelimiter.$paramValue; + else + $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue"; + } + if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) { + $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2; + $attr2 = null; + } + if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) { + if( !empty( $attr2 )) { + $attr1 .= $attr2; + $attr2 = null; + } + $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING']; + } + if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE']; + if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) { + $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID']; + } + if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE']; + if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE']; + if( isset( $params['CN'] ) && $CNattrKey ) { + $attr1 = $this->intAttrDelimiter.'CN='.$params['CN']; + $CNattrExist = TRUE; + } + if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) { + $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"'; + $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim; + } + if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY']; + if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) { + $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"'; + $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim; + } + if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) { + $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE']; + $LANGattrExist = TRUE; + } + if( !$LANGattrExist ) { + $lang = $this->getConfig( 'language' ); + if(( $CNattrExist || $LANGattrKey ) && $lang ) + $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang; + } + return $attr1.$attrLANG.$attr2; + } +/** + * creates formatted output for calendar component property data value type recur + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @param array $recurlabel + * @param array $recurdata + * @return string + */ + function _format_recur( $recurlabel, $recurdata ) { + $output = null; + foreach( $recurdata as $therule ) { + if( empty( $therule['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel ); + continue; + } + $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null; + $content1 = $content2 = null; + foreach( $therule['value'] as $rulelabel => $rulevalue ) { + switch( $rulelabel ) { + case 'FREQ': { + $content1 .= "FREQ=$rulevalue"; + break; + } + case 'UNTIL': { + $content2 .= ";UNTIL="; + $content2 .= iCalUtilityFunctions::_format_date_time( $rulevalue ); + break; + } + case 'COUNT': + case 'INTERVAL': + case 'WKST': { + $content2 .= ";$rulelabel=$rulevalue"; + break; + } + case 'BYSECOND': + case 'BYMINUTE': + case 'BYHOUR': + case 'BYMONTHDAY': + case 'BYYEARDAY': + case 'BYWEEKNO': + case 'BYMONTH': + case 'BYSETPOS': { + $content2 .= ";$rulelabel="; + if( is_array( $rulevalue )) { + foreach( $rulevalue as $vix => $valuePart ) { + $content2 .= ( $vix ) ? ',' : null; + $content2 .= $valuePart; + } + } + else + $content2 .= $rulevalue; + break; + } + case 'BYDAY': { + $content2 .= ";$rulelabel="; + $bydaycnt = 0; + foreach( $rulevalue as $vix => $valuePart ) { + $content21 = $content22 = null; + if( is_array( $valuePart )) { + $content2 .= ( $bydaycnt ) ? ',' : null; + foreach( $valuePart as $vix2 => $valuePart2 ) { + if( 'DAY' != strtoupper( $vix2 )) + $content21 .= $valuePart2; + else + $content22 .= $valuePart2; + } + $content2 .= $content21.$content22; + $bydaycnt++; + } + else { + $content2 .= ( $bydaycnt ) ? ',' : null; + if( 'DAY' != strtoupper( $vix )) + $content21 .= $valuePart; + else { + $content22 .= $valuePart; + $bydaycnt++; + } + $content2 .= $content21.$content22; + } + } + break; + } + default: { + $content2 .= ";$rulelabel=$rulevalue"; + break; + } + } + } + $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 ); + } + return $output; + } +/** + * check if property not exists within component + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-15 + * @param string $propName + * @return bool + */ + function _notExistProp( $propName ) { + if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed + $propName = strtolower( $propName ); + if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; } + elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; } + elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; } + elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; } + elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; } + elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE; + return FALSE; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * get general component config variables or info about subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-14 + * @param mixed $config + * @return value + */ + function getConfig( $config = FALSE) { + if( !$config ) { + $return = array(); + $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); + $return['FORMAT'] = $this->getConfig( 'FORMAT' ); + if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) + $return['LANGUAGE'] = $lang; + $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); + $return['TZTD'] = $this->getConfig( 'TZID' ); + $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); + return $return; + } + switch( strtoupper( $config )) { + case 'ALLOWEMPTY': + return $this->allowEmpty; + break; + case 'COMPSINFO': + unset( $this->compix ); + $info = array(); + if( isset( $this->components )) { + foreach( $this->components as $cix => $component ) { + if( empty( $component )) continue; + $info[$cix]['ordno'] = $cix + 1; + $info[$cix]['type'] = $component->objName; + $info[$cix]['uid'] = $component->getProperty( 'uid' ); + $info[$cix]['props'] = $component->getConfig( 'propinfo' ); + $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); + } + } + return $info; + break; + case 'FORMAT': + return $this->format; + break; + case 'LANGUAGE': + // get language for calendar component as defined in [RFC 1766] + return $this->language; + break; + case 'NL': + case 'NEWLINECHAR': + return $this->nl; + break; + case 'PROPINFO': + $output = array(); + if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) { + if( empty( $this->uid['value'] )) $this->_makeuid(); + $output['UID'] = 1; + } + if( !empty( $this->dtstamp )) $output['DTSTAMP'] = 1; + if( !empty( $this->summary )) $output['SUMMARY'] = 1; + if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description ); + if( !empty( $this->dtstart )) $output['DTSTART'] = 1; + if( !empty( $this->dtend )) $output['DTEND'] = 1; + if( !empty( $this->due )) $output['DUE'] = 1; + if( !empty( $this->duration )) $output['DURATION'] = 1; + if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule ); + if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate ); + if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate ); + if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule ); + if( !empty( $this->action )) $output['ACTION'] = 1; + if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach ); + if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee ); + if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories ); + if( !empty( $this->class )) $output['CLASS'] = 1; + if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment ); + if( !empty( $this->completed )) $output['COMPLETED'] = 1; + if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact ); + if( !empty( $this->created )) $output['CREATED'] = 1; + if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy ); + if( !empty( $this->geo )) $output['GEO'] = 1; + if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1; + if( !empty( $this->location )) $output['LOCATION'] = 1; + if( !empty( $this->organizer )) $output['ORGANIZER'] = 1; + if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1; + if( !empty( $this->priority )) $output['PRIORITY'] = 1; + if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1; + if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto ); + if( !empty( $this->repeat )) $output['REPEAT'] = 1; + if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus ); + if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources ); + if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; + if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; + if( !empty( $this->status )) $output['STATUS'] = 1; + if( !empty( $this->transp )) $output['TRANSP'] = 1; + if( !empty( $this->trigger )) $output['TRIGGER'] = 1; + if( !empty( $this->tzid )) $output['TZID'] = 1; + if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname ); + if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1; + if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1; + if( !empty( $this->tzurl )) $output['TZURL'] = 1; + if( !empty( $this->url )) $output['URL'] = 1; + if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop ); + return $output; + break; + case 'TZID': + return $this->dtzid; + break; + case 'UNIQUE_ID': + if( empty( $this->unique_id )) + $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; + return $this->unique_id; + break; + } + } +/** + * general component config setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.18 - 2011-10-28 + * @param mixed $config + * @param string $value + * @param bool $softUpdate + * @return void + */ + function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) { + if( is_array( $config )) { + $ak = array_keys( $config ); + foreach( $ak as $k ) { + if( 'NEWLINECHAR' == strtoupper( $k )) { + if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] )) + return FALSE; + unset( $config[$k] ); + break; + } + } + foreach( $config as $cKey => $cValue ) { + if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate )) + return FALSE; + } + return TRUE; + } + $res = FALSE; + switch( strtoupper( $config )) { + case 'ALLOWEMPTY': + $this->allowEmpty = $value; + $subcfg = array( 'ALLOWEMPTY' => $value ); + $res = TRUE; + break; + case 'FORMAT': + $value = trim( strtolower( $value )); + $this->format = $value; + $this->_createFormat(); + $subcfg = array( 'FORMAT' => $value ); + $res = TRUE; + break; + case 'LANGUAGE': + // set language for calendar component as defined in [RFC 1766] + $value = trim( $value ); + if( empty( $this->language ) || !$softUpdate ) + $this->language = $value; + $subcfg = array( 'LANGUAGE' => $value ); + $res = TRUE; + break; + case 'NL': + case 'NEWLINECHAR': + $this->nl = $value; + $this->_createFormat(); + $subcfg = array( 'NL' => $value ); + $res = TRUE; + break; + case 'TZID': + $this->dtzid = $value; + $subcfg = array( 'TZID' => $value ); + $res = TRUE; + break; + case 'UNIQUE_ID': + $value = trim( $value ); + $this->unique_id = $value; + $subcfg = array( 'UNIQUE_ID' => $value ); + $res = TRUE; + break; + default: // any unvalid config key.. . + return TRUE; + } + if( !$res ) return FALSE; + if( isset( $subcfg ) && !empty( $this->components )) { + foreach( $subcfg as $cfgkey => $cfgvalue ) { + foreach( $this->components as $cix => $component ) { + $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate ); + if( !$res ) + break 2; + $this->components[$cix] = $component->copy(); // PHP4 compliant + } + } + } + return $res; + } +/*********************************************************************************/ +/** + * delete component property value + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $propName, bool FALSE => X-property + * @param int $propix, optional, if specific property is wanted in case of multiply occurences + * @return bool, if successfull delete TRUE + */ + function deleteProperty( $propName=FALSE, $propix=FALSE ) { + if( $this->_notExistProp( $propName )) return FALSE; + $propName = strtoupper( $propName ); + if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE', + 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) { + if( !$propix ) + $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; + $this->propdelix[$propName] = --$propix; + } + $return = FALSE; + switch( $propName ) { + case 'ACTION': + if( !empty( $this->action )) { + $this->action = ''; + $return = TRUE; + } + break; + case 'ATTACH': + return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] ); + break; + case 'ATTENDEE': + return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] ); + break; + case 'CATEGORIES': + return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] ); + break; + case 'CLASS': + if( !empty( $this->class )) { + $this->class = ''; + $return = TRUE; + } + break; + case 'COMMENT': + return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] ); + break; + case 'COMPLETED': + if( !empty( $this->completed )) { + $this->completed = ''; + $return = TRUE; + } + break; + case 'CONTACT': + return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] ); + break; + case 'CREATED': + if( !empty( $this->created )) { + $this->created = ''; + $return = TRUE; + } + break; + case 'DESCRIPTION': + return $this->deletePropertyM( $this->description, $this->propdelix[$propName] ); + break; + case 'DTEND': + if( !empty( $this->dtend )) { + $this->dtend = ''; + $return = TRUE; + } + break; + case 'DTSTAMP': + if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) + return FALSE; + if( !empty( $this->dtstamp )) { + $this->dtstamp = ''; + $return = TRUE; + } + break; + case 'DTSTART': + if( !empty( $this->dtstart )) { + $this->dtstart = ''; + $return = TRUE; + } + break; + case 'DUE': + if( !empty( $this->due )) { + $this->due = ''; + $return = TRUE; + } + break; + case 'DURATION': + if( !empty( $this->duration )) { + $this->duration = ''; + $return = TRUE; + } + break; + case 'EXDATE': + return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] ); + break; + case 'EXRULE': + return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] ); + break; + case 'FREEBUSY': + return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] ); + break; + case 'GEO': + if( !empty( $this->geo )) { + $this->geo = ''; + $return = TRUE; + } + break; + case 'LAST-MODIFIED': + if( !empty( $this->lastmodified )) { + $this->lastmodified = ''; + $return = TRUE; + } + break; + case 'LOCATION': + if( !empty( $this->location )) { + $this->location = ''; + $return = TRUE; + } + break; + case 'ORGANIZER': + if( !empty( $this->organizer )) { + $this->organizer = ''; + $return = TRUE; + } + break; + case 'PERCENT-COMPLETE': + if( !empty( $this->percentcomplete )) { + $this->percentcomplete = ''; + $return = TRUE; + } + break; + case 'PRIORITY': + if( !empty( $this->priority )) { + $this->priority = ''; + $return = TRUE; + } + break; + case 'RDATE': + return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] ); + break; + case 'RECURRENCE-ID': + if( !empty( $this->recurrenceid )) { + $this->recurrenceid = ''; + $return = TRUE; + } + break; + case 'RELATED-TO': + return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] ); + break; + case 'REPEAT': + if( !empty( $this->repeat )) { + $this->repeat = ''; + $return = TRUE; + } + break; + case 'REQUEST-STATUS': + return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] ); + break; + case 'RESOURCES': + return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] ); + break; + case 'RRULE': + return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] ); + break; + case 'SEQUENCE': + if( !empty( $this->sequence )) { + $this->sequence = ''; + $return = TRUE; + } + break; + case 'STATUS': + if( !empty( $this->status )) { + $this->status = ''; + $return = TRUE; + } + break; + case 'SUMMARY': + if( !empty( $this->summary )) { + $this->summary = ''; + $return = TRUE; + } + break; + case 'TRANSP': + if( !empty( $this->transp )) { + $this->transp = ''; + $return = TRUE; + } + break; + case 'TRIGGER': + if( !empty( $this->trigger )) { + $this->trigger = ''; + $return = TRUE; + } + break; + case 'TZID': + if( !empty( $this->tzid )) { + $this->tzid = ''; + $return = TRUE; + } + break; + case 'TZNAME': + return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] ); + break; + case 'TZOFFSETFROM': + if( !empty( $this->tzoffsetfrom )) { + $this->tzoffsetfrom = ''; + $return = TRUE; + } + break; + case 'TZOFFSETTO': + if( !empty( $this->tzoffsetto )) { + $this->tzoffsetto = ''; + $return = TRUE; + } + break; + case 'TZURL': + if( !empty( $this->tzurl )) { + $this->tzurl = ''; + $return = TRUE; + } + break; + case 'UID': + if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) + return FALSE; + if( !empty( $this->uid )) { + $this->uid = ''; + $return = TRUE; + } + break; + case 'URL': + if( !empty( $this->url )) { + $this->url = ''; + $return = TRUE; + } + break; + default: + $reduced = ''; + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) return FALSE; + foreach( $this->xprop as $k => $a ) { + if(( $k != $propName ) && !empty( $a )) + $reduced[$k] = $a; + } + } + else { + if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; } + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix != $xpropno ) + $reduced[$xpropkey] = $xpropvalue; + $xpropno++; + } + } + $this->xprop = $reduced; + if( empty( $this->xprop )) { + unset( $this->propdelix[$propName] ); + return FALSE; + } + return TRUE; + } + return $return; + } +/*********************************************************************************/ +/** + * delete component property value, fixing components with multiple occurencies + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param array $multiprop, reference to a component property + * @param int $propix, reference to removal counter + * @return bool TRUE + */ + function deletePropertyM( & $multiprop, & $propix ) { + if( isset( $multiprop[$propix] )) + unset( $multiprop[$propix] ); + if( empty( $multiprop )) { + $multiprop = ''; + unset( $propix ); + return FALSE; + } + else + return TRUE; + } +/** + * get component property value/params + * + * if property has multiply values, consequtive function calls are needed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.3 - 2012-01-10 + * @param string $propName, optional + * @param int @propix, optional, if specific property is wanted in case of multiply occurences + * @param bool $inclParam=FALSE + * @param bool $specform=FALSE + * @return mixed + */ + function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) { + if( $this->_notExistProp( $propName )) return FALSE; + $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; + if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE', + 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) { + if( !$propix ) + $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; + $this->propix[$propName] = --$propix; + } + switch( $propName ) { + case 'ACTION': + if( !empty( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value']; + break; + case 'ATTACH': + $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array(); + while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value']; + break; + case 'ATTENDEE': + $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array(); + while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value']; + break; + case 'CATEGORIES': + $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array(); + while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value']; + break; + case 'CLASS': + if( !empty( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value']; + break; + case 'COMMENT': + $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array(); + while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value']; + break; + case 'COMPLETED': + if( !empty( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value']; + break; + case 'CONTACT': + $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array(); + while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value']; + break; + case 'CREATED': + if( !empty( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value']; + break; + case 'DESCRIPTION': + $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array(); + while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value']; + break; + case 'DTEND': + if( !empty( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value']; + break; + case 'DTSTAMP': + if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) + return; + if( !isset( $this->dtstamp['value'] )) + $this->_makeDtstamp(); + return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value']; + break; + case 'DTSTART': + if( !empty( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value']; + break; + case 'DUE': + if( !empty( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value']; + break; + case 'DURATION': + if( !isset( $this->duration['value'] )) return FALSE; + $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value']; + return ( $inclParam ) ? array( 'value' => $value, 'params' => $this->duration['params'] ) : $value; + break; + case 'EXDATE': + $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array(); + while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value']; + break; + case 'EXRULE': + $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array(); + while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value']; + break; + case 'FREEBUSY': + $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array(); + while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value']; + break; + case 'GEO': + if( !empty( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value']; + break; + case 'LAST-MODIFIED': + if( !empty( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value']; + break; + case 'LOCATION': + if( !empty( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value']; + break; + case 'ORGANIZER': + if( !empty( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value']; + break; + case 'PERCENT-COMPLETE': + if( !empty( $this->percentcomplete['value'] ) || ( isset( $this->percentcomplete['value'] ) && ( '0' == $this->percentcomplete['value'] ))) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value']; + break; + case 'PRIORITY': + if( !empty( $this->priority['value'] ) || ( isset( $this->priority['value'] ) && ('0' == $this->priority['value'] ))) return ( $inclParam ) ? $this->priority : $this->priority['value']; + break; + case 'RDATE': + $ak = ( is_array( $this->rdate )) ? array_keys( $this->rdate ) : array(); + while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value']; + break; + case 'RECURRENCE-ID': + if( !empty( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value']; + break; + case 'RELATED-TO': + $ak = ( is_array( $this->relatedto )) ? array_keys( $this->relatedto ) : array(); + while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value']; + break; + case 'REPEAT': + if( !empty( $this->repeat['value'] ) || ( isset( $this->repeat['value'] ) && ( '0' == $this->repeat['value'] ))) return ( $inclParam ) ? $this->repeat : $this->repeat['value']; + break; + case 'REQUEST-STATUS': + $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array(); + while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value']; + break; + case 'RESOURCES': + $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array(); + while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value']; + break; + case 'RRULE': + $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array(); + while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value']; + break; + case 'SEQUENCE': + if( isset( $this->sequence['value'] ) && ( isset( $this->sequence['value'] ) && ( '0' <= $this->sequence['value'] ))) return ( $inclParam ) ? $this->sequence : $this->sequence['value']; + break; + case 'STATUS': + if( !empty( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value']; + break; + case 'SUMMARY': + if( !empty( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value']; + break; + case 'TRANSP': + if( !empty( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value']; + break; + case 'TRIGGER': + if( !empty( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value']; + break; + case 'TZID': + if( !empty( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value']; + break; + case 'TZNAME': + $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array(); + while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value']; + break; + case 'TZOFFSETFROM': + if( !empty( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value']; + break; + case 'TZOFFSETTO': + if( !empty( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value']; + break; + case 'TZURL': + if( !empty( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value']; + break; + case 'UID': + if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) + return FALSE; + if( empty( $this->uid['value'] )) + $this->_makeuid(); + return ( $inclParam ) ? $this->uid : $this->uid['value']; + break; + case 'URL': + if( !empty( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value']; + break; + default: + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) return FALSE; + return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) + : array( $propName, $this->xprop[$propName]['value'] ); + } + else { + if( empty( $this->xprop )) return FALSE; + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix == $xpropno ) + return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) + : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); + else + $xpropno++; + } + return FALSE; // not found ?? + } + } + return FALSE; + } +/** + * returns calendar property unique values for 'CATEGORIES', 'RESOURCES' or 'ATTENDEE' and each number of ocurrence + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-04-13 + * @param string $propName + * @param array $output, incremented result array + */ + function _getProperties( $propName, & $output ) { + if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'RESOURCES' ))) + return output; + while( FALSE !== ( $content = $this->getProperty( $propName ))) { + if( is_array( $content )) { + foreach( $content as $part ) { + if( FALSE !== strpos( $part, ',' )) { + $part = explode( ',', $part ); + foreach( $part as $thePart ) { + $thePart = trim( $thePart ); + if( !empty( $thePart )) { + if( !isset( $output[$thePart] )) + $output[$thePart] = 1; + else + $output[$thePart] += 1; + } + } + } + else { + $part = trim( $part ); + if( !isset( $output[$part] )) + $output[$part] = 1; + else + $output[$part] += 1; + } + } + } + elseif( FALSE !== strpos( $content, ',' )) { + $content = explode( ',', $content ); + foreach( $content as $thePart ) { + $thePart = trim( $thePart ); + if( !empty( $thePart )) { + if( !isset( $output[$thePart] )) + $output[$thePart] = 1; + else + $output[$thePart] += 1; + } + } + } + else { + $content = trim( $content ); + if( !empty( $content )) { + if( !isset( $output[$content] )) + $output[$content] = 1; + else + $output[$content] += 1; + } + } + } + ksort( $output ); + return $output; + } +/** + * general component property setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param mixed $args variable number of function arguments, + * first argument is ALWAYS component name, + * second ALWAYS component value! + * @return void + */ + function setProperty() { + $numargs = func_num_args(); + if( 1 > $numargs ) return FALSE; + $arglist = func_get_args(); + if( $this->_notExistProp( $arglist[0] )) return FALSE; + if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] ))) + return FALSE; + $arglist[0] = strtoupper( $arglist[0] ); + for( $argix=$numargs; $argix < 12; $argix++ ) { + if( !isset( $arglist[$argix] )) + $arglist[$argix] = null; + } + switch( $arglist[0] ) { + case 'ACTION': + return $this->setAction( $arglist[1], $arglist[2] ); + case 'ATTACH': + return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] ); + case 'ATTENDEE': + return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] ); + case 'CATEGORIES': + return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] ); + case 'CLASS': + return $this->setClass( $arglist[1], $arglist[2] ); + case 'COMMENT': + return $this->setComment( $arglist[1], $arglist[2], $arglist[3] ); + case 'COMPLETED': + return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'CONTACT': + return $this->setContact( $arglist[1], $arglist[2], $arglist[3] ); + case 'CREATED': + return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'DESCRIPTION': + return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] ); + case 'DTEND': + return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'DTSTAMP': + return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'DTSTART': + return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'DUE': + return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'DURATION': + return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] ); + case 'EXDATE': + return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] ); + case 'EXRULE': + return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] ); + case 'FREEBUSY': + return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] ); + case 'GEO': + return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] ); + case 'LAST-MODIFIED': + return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'LOCATION': + return $this->setLocation( $arglist[1], $arglist[2] ); + case 'ORGANIZER': + return $this->setOrganizer( $arglist[1], $arglist[2] ); + case 'PERCENT-COMPLETE': + return $this->setPercentComplete( $arglist[1], $arglist[2] ); + case 'PRIORITY': + return $this->setPriority( $arglist[1], $arglist[2] ); + case 'RDATE': + return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] ); + case 'RECURRENCE-ID': + return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'RELATED-TO': + return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] ); + case 'REPEAT': + return $this->setRepeat( $arglist[1], $arglist[2] ); + case 'REQUEST-STATUS': + return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] ); + case 'RESOURCES': + return $this->setResources( $arglist[1], $arglist[2], $arglist[3] ); + case 'RRULE': + return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] ); + case 'SEQUENCE': + return $this->setSequence( $arglist[1], $arglist[2] ); + case 'STATUS': + return $this->setStatus( $arglist[1], $arglist[2] ); + case 'SUMMARY': + return $this->setSummary( $arglist[1], $arglist[2] ); + case 'TRANSP': + return $this->setTransp( $arglist[1], $arglist[2] ); + case 'TRIGGER': + return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] ); + case 'TZID': + return $this->setTzid( $arglist[1], $arglist[2] ); + case 'TZNAME': + return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] ); + case 'TZOFFSETFROM': + return $this->setTzoffsetfrom( $arglist[1], $arglist[2] ); + case 'TZOFFSETTO': + return $this->setTzoffsetto( $arglist[1], $arglist[2] ); + case 'TZURL': + return $this->setTzurl( $arglist[1], $arglist[2] ); + case 'UID': + return $this->setUid( $arglist[1], $arglist[2] ); + case 'URL': + return $this->setUrl( $arglist[1], $arglist[2] ); + default: + return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); + } + return FALSE; + } +/*********************************************************************************/ +/** + * parse component unparsed data into properties + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.17 - 2012-02-03 + * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of strings + * @return bool FALSE if error occurs during parsing + * + */ + function parse( $unparsedtext=null ) { + if( !empty( $unparsedtext )) { + $nl = $this->getConfig( 'nl' ); + if( is_array( $unparsedtext )) + $unparsedtext = implode( '\n'.$nl, $unparsedtext ); + /* fix line folding */ + $eolchars = array( "\r\n", "\n\r", "\n", "\r" ); // check all line endings + $EOLmark = FALSE; + foreach( $eolchars as $eolchar ) { + if( !$EOLmark && ( FALSE !== strpos( $unparsedtext, $eolchar ))) { + $unparsedtext = str_replace( $eolchar." ", '', $unparsedtext ); + $unparsedtext = str_replace( $eolchar."\t", '', $unparsedtext ); + if( $eolchar != $nl ) + $unparsedtext = str_replace( $eolchar, $nl, $unparsedtext ); + $EOLmark = TRUE; + } + } + $tmp = explode( $nl, $unparsedtext ); + $unparsedtext = array(); + foreach( $tmp as $tmpr ) + if( !empty( $tmpr )) + $unparsedtext[] = $tmpr; + } + elseif( !isset( $this->unparsed )) + $unparsedtext = array(); + else + $unparsedtext = $this->unparsed; + $this->unparsed = array(); + $comp = & $this; + $config = $this->getConfig(); + foreach ( $unparsedtext as $line ) { + if( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VA', 'END:DA' ))) + $this->components[] = $comp->copy(); + elseif( 'END:ST' == strtoupper( substr( $line, 0, 6 ))) + array_unshift( $this->components, $comp->copy()); + elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) + break; + elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) + $comp = new valarm( $config); + elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) + $comp = new vtimezone( 'standard', $config ); + elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) + $comp = new vtimezone( 'daylight', $config ); + elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) + continue; + else + $comp->unparsed[] = $line; + } + unset( $config ); + /* concatenate property values spread over several lines */ + $lastix = -1; + $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed' + , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart' + , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo' + , 'last-modified', 'location', 'organizer', 'percent-complete' + , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat' + , 'request-status', 'resources', 'rrule', 'sequence', 'status' + , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom' + , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' ); + $proprows = array(); + foreach( $this->unparsed as $line ) { + $newProp = FALSE; + foreach ( $propnames as $propname ) { + if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) { + $newProp = TRUE; + break; + } + } + if( $newProp ) { + $newProp = FALSE; + $lastix++; + $proprows[$lastix] = $line; + } + else + $proprows[$lastix] .= '!"#¤%&/()=?'.$line; + } + /* parse each property 'line' */ + $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' ); + $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ); + $paramProto4 = array( 'crid:', 'news:', 'pres:' ); + foreach( $proprows as $line ) { + $line = str_replace( '!"#¤%&/()=? ', '', $line ); + $line = str_replace( '!"#¤%&/()=?', '', $line ); + if( '\n' == substr( $line, -2 )) + $line = substr( $line, 0, strlen( $line ) - 2 ); + /* get propname, (problem with x-properties, otherwise in previous loop) */ + $cix = $propname = null; + for( $cix=0, $clen = strlen( $line ); $cix < $clen; $cix++ ) { + if( in_array( $line[$cix], array( ':', ';' ))) + break; + else + $propname .= $line[$cix]; + } + if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) { + $propname2 = $propname; + $propname = 'X-'; + } + /* rest of the line is opt.params and value */ + $line = substr( $line, $cix ); + /* separate attributes from value */ + $attr = array(); + $attrix = -1; + $clen = strlen( $line ); + $WithinQuotes = FALSE; + for( $cix=0; $cix < $clen; $cix++ ) { + if( ( ':' == $line[$cix] ) && + ( substr( $line,$cix, 3 ) != '://' ) && + ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) && + ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) && + ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) && + ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) && + !$WithinQuotes ) { + $attrEnd = TRUE; + if(( $cix < ( $clen - 4 )) && + ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr?? + for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) { + if( '://' == substr( $line, $c2ix - 2, 3 )) { + $attrEnd = FALSE; + break; // an URI with a portnr!! + } + } + } + if( $attrEnd) { + $line = substr( $line, ( $cix + 1 )); + break; + } + } + if( '"' == $line[$cix] ) + $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE; + if( ';' == $line[$cix] ) + $attr[++$attrix] = null; + else + $attr[$attrix] .= $line[$cix]; + } + /* make attributes in array format */ + $propattr = array(); + foreach( $attr as $attribute ) { + $attrsplit = explode( '=', $attribute, 2 ); + if( 1 < count( $attrsplit )) + $propattr[$attrsplit[0]] = $attrsplit[1]; + else + $propattr[] = $attribute; + } + /* call setProperty( $propname.. . */ + switch( strtoupper( $propname )) { + case 'ATTENDEE': + foreach( $propattr as $pix => $attr ) { + if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ))) + continue; + $attr2 = explode( ',', $attr ); + if( 1 < count( $attr2 )) + $propattr[$pix] = $attr2; + } + $this->setProperty( $propname, $line, $propattr ); + break; + case 'X-': + $propname = ( isset( $propname2 )) ? $propname2 : $propname; + unset( $propname2 ); + case 'CATEGORIES': + case 'RESOURCES': + if( FALSE !== strpos( $line, ',' )) { + $llen = strlen( $line ); + $content = array( 0 => '' ); + $cix = 0; + for( $lix = 0; $lix < $llen; $lix++ ) { + if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { + $cix++; + $content[$cix] = ''; + } + else + $content[$cix] .= $line[$lix]; + } + if( 1 < count( $content )) { + $content = array_values( $content ); + foreach( $content as $cix => $contentPart ) + $content[$cix] = calendarComponent::_strunrep( $contentPart ); + $this->setProperty( $propname, $content, $propattr ); + break; + } + else + $line = reset( $content ); + } + case 'COMMENT': + case 'CONTACT': + case 'DESCRIPTION': + case 'LOCATION': + case 'SUMMARY': + if( empty( $line )) + $propattr = null; + $this->setProperty( $propname, calendarComponent::_strunrep( $line ), $propattr ); + break; + case 'REQUEST-STATUS': + $values = explode( ';', $line, 3 ); + $values[1] = ( !isset( $values[1] )) ? null : calendarComponent::_strunrep( $values[1] ); + $values[2] = ( !isset( $values[2] )) ? null : calendarComponent::_strunrep( $values[2] ); + $this->setProperty( $propname + , $values[0] // statcode + , $values[1] // statdesc + , $values[2] // extdata + , $propattr ); + break; + case 'FREEBUSY': + $fbtype = ( isset( $propattr['FBTYPE'] )) ? $propattr['FBTYPE'] : ''; // force setting default, if missing + unset( $propattr['FBTYPE'] ); + $values = explode( ',', $line ); + foreach( $values as $vix => $value ) { + $value2 = explode( '/', $value ); + if( 1 < count( $value2 )) + $values[$vix] = $value2; + } + $this->setProperty( $propname, $fbtype, $values, $propattr ); + break; + case 'GEO': + $value = explode( ';', $line, 2 ); + if( 2 > count( $value )) + $value[1] = null; + $this->setProperty( $propname, $value[0], $value[1], $propattr ); + break; + case 'EXDATE': + $values = ( !empty( $line )) ? explode( ',', $line ) : null; + $this->setProperty( $propname, $values, $propattr ); + break; + case 'RDATE': + if( empty( $line )) { + $this->setProperty( $propname, $line, $propattr ); + break; + } + $values = explode( ',', $line ); + foreach( $values as $vix => $value ) { + $value2 = explode( '/', $value ); + if( 1 < count( $value2 )) + $values[$vix] = $value2; + } + $this->setProperty( $propname, $values, $propattr ); + break; + case 'EXRULE': + case 'RRULE': + $values = explode( ';', $line ); + $recur = array(); + foreach( $values as $value2 ) { + if( empty( $value2 )) + continue; // ;-char in ending position ??? + $value3 = explode( '=', $value2, 2 ); + $rulelabel = strtoupper( $value3[0] ); + switch( $rulelabel ) { + case 'BYDAY': { + $value4 = explode( ',', $value3[1] ); + if( 1 < count( $value4 )) { + foreach( $value4 as $v5ix => $value5 ) { + $value6 = array(); + $dayno = $dayname = null; + $value5 = trim( (string) $value5 ); + if(( ctype_alpha( substr( $value5, -1 ))) && + ( ctype_alpha( substr( $value5, -2, 1 )))) { + $dayname = substr( $value5, -2, 2 ); + if( 2 < strlen( $value5 )) + $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); + } + if( $dayno ) + $value6[] = $dayno; + if( $dayname ) + $value6['DAY'] = $dayname; + $value4[$v5ix] = $value6; + } + } + else { + $value4 = array(); + $dayno = $dayname = null; + $value5 = trim( (string) $value3[1] ); + if(( ctype_alpha( substr( $value5, -1 ))) && + ( ctype_alpha( substr( $value5, -2, 1 )))) { + $dayname = substr( $value5, -2, 2 ); + if( 2 < strlen( $value5 )) + $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); + } + if( $dayno ) + $value4[] = $dayno; + if( $dayname ) + $value4['DAY'] = $dayname; + } + $recur[$rulelabel] = $value4; + break; + } + default: { + $value4 = explode( ',', $value3[1] ); + if( 1 < count( $value4 )) + $value3[1] = $value4; + $recur[$rulelabel] = $value3[1]; + break; + } + } // end - switch $rulelabel + } // end - foreach( $values.. . + $this->setProperty( $propname, $recur, $propattr ); + break; + case 'ACTION': + case 'CLASSIFICATION': + case 'STATUS': + case 'TRANSP': + case 'UID': + case 'TZID': + case 'RELATED-TO': + case 'TZNAME': + $line = calendarComponent::_strunrep( $line ); + default: + $this->setProperty( $propname, $line, $propattr ); + break; + } // end switch( $propname.. . + } // end - foreach( $proprows.. . + unset( $unparsedtext, $this->unparsed, $proprows ); + if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) { + $ckeys = array_keys( $this->components ); + foreach( $ckeys as $ckey ) { + if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { + $this->components[$ckey]->parse(); + } + } + } + return TRUE; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * return a copy of this component + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @return object + */ + function copy() { + $serialized_contents = serialize( $this ); + $copy = unserialize( $serialized_contents ); + return $copy; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * delete calendar subcomponent from component container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $arg1 ordno / component type / component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @return void + */ + function deleteComponent( $arg1, $arg2=FALSE ) { + if( !isset( $this->components )) return FALSE; + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { + $argType = strtolower( $arg1 ); + $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; + } + $cix2dC = 0; + foreach ( $this->components as $cix => $component) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { + unset( $this->components[$cix] ); + return TRUE; + } + elseif( $argType == $component->objName ) { + if( $index == $cix2dC ) { + unset( $this->components[$cix] ); + return TRUE; + } + $cix2dC++; + } + elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { + unset( $this->components[$cix] ); + return TRUE; + } + } + return FALSE; + } +/** + * get calendar component subcomponent from component container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $arg1 optional, ordno/component type/ component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @return object + */ + function getComponent ( $arg1=FALSE, $arg2=FALSE ) { + if( !isset( $this->components )) return FALSE; + $index = $argType = null; + if ( !$arg1 ) { + $argType = 'INDEX'; + $index = $this->compix['INDEX'] = + ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; + } + elseif ( ctype_digit( (string) $arg1 )) { + $argType = 'INDEX'; + $index = (int) $arg1; + unset( $this->compix ); + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { + unset( $this->compix['INDEX'] ); + $argType = strtolower( $arg1 ); + if( !$arg2 ) + $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; + else + $index = (int) $arg2; + } + $index -= 1; + $ckeys = array_keys( $this->components ); + if( !empty( $index) && ( $index > end( $ckeys ))) + return FALSE; + $cix2gC = 0; + foreach( $this->components as $cix => $component ) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) + return $component->copy(); + elseif( $argType == $component->objName ) { + if( $index == $cix2gC ) + return $component->copy(); + $cix2gC++; + } + elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' ))) + return $component->copy(); + } + /* not found.. . */ + unset( $this->compix ); + return false; + } +/** + * add calendar component as subcomponent to container for subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 1.x.x - 2007-04-24 + * @param object $component calendar component + * @return void + */ + function addSubComponent ( $component ) { + $this->setComponent( $component ); + } +/** + * create new calendar component subcomponent, already included within component + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.33 - 2011-01-03 + * @param string $compType subcomponent type + * @return object (reference) + */ + function & newComponent( $compType ) { + $config = $this->getConfig(); + $keys = array_keys( $this->components ); + $ix = end( $keys) + 1; + switch( strtoupper( $compType )) { + case 'ALARM': + case 'VALARM': + $this->components[$ix] = new valarm( $config ); + break; + case 'STANDARD': + array_unshift( $this->components, new vtimezone( 'STANDARD', $config )); + $ix = 0; + break; + case 'DAYLIGHT': + $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config ); + break; + default: + return FALSE; + } + return $this->components[$ix]; + } +/** + * add calendar component as subcomponent to container for subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param object $component calendar component + * @param mixed $arg1 optional, ordno/component type/ component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @return bool + */ + function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { + if( !isset( $this->components )) return FALSE; + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + if( !in_array( $component->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) { + /* make sure dtstamp and uid is set */ + $dummy = $component->getProperty( 'dtstamp' ); + $dummy = $component->getProperty( 'uid' ); + } + if( !$arg1 ) { // plain insert, last in chain + $this->components[] = $component->copy(); + return TRUE; + } + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { // index insert/replace + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) { + $argType = strtolower( $arg1 ); + $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; + } + // else if arg1 is set, arg1 must be an UID + $cix2sC = 0; + foreach ( $this->components as $cix => $component2 ) { + if( empty( $component2 )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + elseif( $argType == $component2->objName ) { // component Type index insert/replace + if( $index == $cix2sC ) { + $this->components[$cix] = $component->copy(); + return TRUE; + } + $cix2sC++; + } + elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + } + /* arg1=index and not found.. . insert at index .. .*/ + if( 'INDEX' == $argType ) { + $this->components[$index] = $component->copy(); + ksort( $this->components, SORT_NUMERIC ); + } + else /* not found.. . insert last in chain anyway .. .*/ + $this->components[] = $component->copy(); + return TRUE; + } +/** + * creates formatted output for subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.20 - 2012-02-06 + * @param array $xcaldecl + * @return string + */ + function createSubComponent() { + $output = null; + if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order + $stdarr = $dlarr = array(); + foreach( $this->components as $component ) { + if( empty( $component )) + continue; + $dt = $component->getProperty( 'dtstart' ); + $key = sprintf( '%04d%02d%02d%02d%02d%02d000', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] ); + if( 'standard' == $component->objName ) { + while( isset( $stdarr[$key] )) + $key += 1; + $stdarr[$key] = $component->copy(); + } + elseif( 'daylight' == $component->objName ) { + while( isset( $dlarr[$key] )) + $key += 1; + $dlarr[$key] = $component->copy(); + } + } // end foreach( $this->components as $component ) + $this->components = array(); + ksort( $stdarr, SORT_NUMERIC ); + foreach( $stdarr as $std ) + $this->components[] = $std->copy(); + unset( $stdarr ); + ksort( $dlarr, SORT_NUMERIC ); + foreach( $dlarr as $dl ) + $this->components[] = $dl->copy(); + unset( $dlarr ); + } // end if( 'vtimezone' == $this->objName ) + foreach( $this->components as $component ) { + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + $output .= $component->createComponent( $this->xcaldecl ); + } + return $output; + } +/********************************************************************************/ +/** + * break lines at pos 75 + * + * Lines of text SHOULD NOT be longer than 75 octets, excluding the line + * break. Long content lines SHOULD be split into a multiple line + * representations using a line "folding" technique. That is, a long + * line can be split between any two characters by inserting a CRLF + * immediately followed by a single linear white space character (i.e., + * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence + * of CRLF followed immediately by a single linear white space character + * is ignored (i.e., removed) when processing the content type. + * + * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where + * the reserved expression "\n" in the arg $string could be broken up by the + * folding of lines, causing ambiguity in the return string. + * Fix uses var $breakAtChar=75 and breaks the line at $breakAtChar-1 if need be. + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.13 - 2012-02-14 + * @param string $value + * @return string + */ + function _size75( $string ) { + $tmp = $string; + $string = ''; + $eolcharlen = strlen( '\n' ); + /* if PHP is config with mb_string and conf overload.. . */ + if( defined( 'MB_OVERLOAD_STRING' ) && ( 1 < ini_get( 'mbstring.func_overload' ))) { + $strlen = mb_strlen( $tmp ); + while( $strlen > 75 ) { + if( '\n' == mb_substr( $tmp, 75, $eolcharlen )) + $breakAtChar = 74; + else + $breakAtChar = 75; + $string .= mb_substr( $tmp, 0, $breakAtChar ); + if( $this->nl != mb_substr( $string, ( 0 - mb_strlen( $this->nl )))) + $string .= $this->nl; + $tmp = mb_substr( $tmp, $breakAtChar ); + if( !empty( $tmp )) + $tmp = ' '.$tmp; + $strlen = mb_strlen( $tmp ); + } // end while + if( 0 < $strlen ) { + $string .= $tmp; // the rest + if( $this->nl != mb_substr( $string, ( 0 - mb_strlen( $this->nl )))) + $string .= $this->nl; + } + return $string; + } + /* if PHP is not config with mb_string.. . */ + while( TRUE ) { + $bytecnt = strlen( $tmp ); + $charCnt = $ix = 0; + for( $ix = 0; $ix < $bytecnt; $ix++ ) { + if(( 73 < $charCnt ) && ( '\n' == substr( $tmp, $ix, $eolcharlen ))) + break; // break before '\n' + elseif( 74 < $charCnt ) { + if( '\n' == substr( $tmp, $ix, $eolcharlen )) + $ix -= 1; // don't break inside '\n' + break; // always break while-loop here + } + else { + $byte = ord( $tmp[$ix] ); + if ($byte <= 127) { // add a one byte character + $string .= substr( $tmp, $ix, 1 ); + $charCnt += 1; + } + else if ($byte >= 194 && $byte <= 223) { // start byte in two byte character + $string .= substr( $tmp, $ix, 2 ); // add a two bytes character + $charCnt += 1; + } + else if ($byte >= 224 && $byte <= 239) { // start byte in three bytes character + $string .= substr( $tmp, $ix, 3 ); // add a three bytes character + $charCnt += 1; + } + else if ($byte >= 240 && $byte <= 244) { // start byte in four bytes character + $string .= substr( $tmp, $ix, 4 ); // add a four bytes character + $charCnt += 1; + } + } + } // end for + if( $this->nl != substr( $string, ( 0 - strlen( $this->nl )))) + $string .= $this->nl; + if( FALSE === ( $tmp = substr( $tmp, $ix ))) + break; // while-loop breakes here + else + $tmp = ' '.$tmp; + } // end while + if( '\n'.$this->nl == substr( $string, ( 0 - strlen( '\n'.$this->nl )))) + $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n'.$this->nl ))).$this->nl; + return $string; + } +/** + * special characters management output + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.15 - 2010-09-24 + * @param string $string + * @return string + */ + function _strrep( $string ) { + switch( $this->format ) { + case 'xcal': + $string = str_replace( '\n', $this->nl, $string); + $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string )))); + break; + default: + $pos = 0; + $specChars = array( 'n', 'N', 'r', ',', ';' ); + while( $pos <= strlen( $string )) { + $pos = strpos( $string, "\\", $pos ); + if( FALSE === $pos ) + break; + if( !in_array( substr( $string, $pos, 1 ), $specChars )) { + $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 )); + $pos += 1; + } + $pos += 1; + } + if( FALSE !== strpos( $string, '"' )) + $string = str_replace('"', "'", $string); + if( FALSE !== strpos( $string, ',' )) + $string = str_replace(',', '\,', $string); + if( FALSE !== strpos( $string, ';' )) + $string = str_replace(';', '\;', $string); + + if( FALSE !== strpos( $string, "\r\n" )) + $string = str_replace( "\r\n", '\n', $string); + elseif( FALSE !== strpos( $string, "\r" )) + $string = str_replace( "\r", '\n', $string); + + elseif( FALSE !== strpos( $string, "\n" )) + $string = str_replace( "\n", '\n', $string); + + if( FALSE !== strpos( $string, '\N' )) + $string = str_replace( '\N', '\n', $string); +// if( FALSE !== strpos( $string, $this->nl )) + $string = str_replace( $this->nl, '\n', $string); + break; + } + return $string; + } +/** + * special characters management input (from iCal file) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.22 - 2010-10-17 + * @param string $string + * @return string + */ + static function _strunrep( $string ) { + $string = str_replace( '\\\\', '\\', $string); + $string = str_replace( '\,', ',', $string); + $string = str_replace( '\;', ';', $string); +// $string = str_replace( '\n', $this->nl, $string); // ?? + return $string; + } +} +/*********************************************************************************/ +/*********************************************************************************/ +/** + * class for calendar component VEVENT + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + */ +class vevent extends calendarComponent { + var $attach; + var $attendee; + var $categories; + var $comment; + var $contact; + var $class; + var $created; + var $description; + var $dtend; + var $dtstart; + var $duration; + var $exdate; + var $exrule; + var $geo; + var $lastmodified; + var $location; + var $organizer; + var $priority; + var $rdate; + var $recurrenceid; + var $relatedto; + var $requeststatus; + var $resources; + var $rrule; + var $sequence; + var $status; + var $summary; + var $transp; + var $url; + var $xprop; + // component subcomponents container + var $components; +/** + * constructor for calendar component VEVENT object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @return void + */ + function vevent( $config = array()) { + $this->calendarComponent(); + + $this->attach = ''; + $this->attendee = ''; + $this->categories = ''; + $this->class = ''; + $this->comment = ''; + $this->contact = ''; + $this->created = ''; + $this->description = ''; + $this->dtstart = ''; + $this->dtend = ''; + $this->duration = ''; + $this->exdate = ''; + $this->exrule = ''; + $this->geo = ''; + $this->lastmodified = ''; + $this->location = ''; + $this->organizer = ''; + $this->priority = ''; + $this->rdate = ''; + $this->recurrenceid = ''; + $this->relatedto = ''; + $this->requeststatus = ''; + $this->resources = ''; + $this->rrule = ''; + $this->sequence = ''; + $this->status = ''; + $this->summary = ''; + $this->transp = ''; + $this->url = ''; + $this->xprop = ''; + + $this->components = array(); + + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + + } +/** + * create formatted output for calendar component VEVENT object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @param array $xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createCategories(); + $component .= $this->createComment(); + $component .= $this->createContact(); + $component .= $this->createClass(); + $component .= $this->createCreated(); + $component .= $this->createDescription(); + $component .= $this->createDtstart(); + $component .= $this->createDtend(); + $component .= $this->createDuration(); + $component .= $this->createExdate(); + $component .= $this->createExrule(); + $component .= $this->createGeo(); + $component .= $this->createLastModified(); + $component .= $this->createLocation(); + $component .= $this->createOrganizer(); + $component .= $this->createPriority(); + $component .= $this->createRdate(); + $component .= $this->createRrule(); + $component .= $this->createRelatedTo(); + $component .= $this->createRequestStatus(); + $component .= $this->createRecurrenceid(); + $component .= $this->createResources(); + $component .= $this->createSequence(); + $component .= $this->createStatus(); + $component .= $this->createSummary(); + $component .= $this->createTransp(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->createSubComponent(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} +/*********************************************************************************/ +/*********************************************************************************/ +/** + * class for calendar component VTODO + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + */ +class vtodo extends calendarComponent { + var $attach; + var $attendee; + var $categories; + var $comment; + var $completed; + var $contact; + var $class; + var $created; + var $description; + var $dtstart; + var $due; + var $duration; + var $exdate; + var $exrule; + var $geo; + var $lastmodified; + var $location; + var $organizer; + var $percentcomplete; + var $priority; + var $rdate; + var $recurrenceid; + var $relatedto; + var $requeststatus; + var $resources; + var $rrule; + var $sequence; + var $status; + var $summary; + var $url; + var $xprop; + // component subcomponents container + var $components; +/** + * constructor for calendar component VTODO object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @return void + */ + function vtodo( $config = array()) { + $this->calendarComponent(); + + $this->attach = ''; + $this->attendee = ''; + $this->categories = ''; + $this->class = ''; + $this->comment = ''; + $this->completed = ''; + $this->contact = ''; + $this->created = ''; + $this->description = ''; + $this->dtstart = ''; + $this->due = ''; + $this->duration = ''; + $this->exdate = ''; + $this->exrule = ''; + $this->geo = ''; + $this->lastmodified = ''; + $this->location = ''; + $this->organizer = ''; + $this->percentcomplete = ''; + $this->priority = ''; + $this->rdate = ''; + $this->recurrenceid = ''; + $this->relatedto = ''; + $this->requeststatus = ''; + $this->resources = ''; + $this->rrule = ''; + $this->sequence = ''; + $this->status = ''; + $this->summary = ''; + $this->url = ''; + $this->xprop = ''; + + $this->components = array(); + + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + + } +/** + * create formatted output for calendar component VTODO object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-07 + * @param array $xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createCategories(); + $component .= $this->createClass(); + $component .= $this->createComment(); + $component .= $this->createCompleted(); + $component .= $this->createContact(); + $component .= $this->createCreated(); + $component .= $this->createDescription(); + $component .= $this->createDtstart(); + $component .= $this->createDue(); + $component .= $this->createDuration(); + $component .= $this->createExdate(); + $component .= $this->createExrule(); + $component .= $this->createGeo(); + $component .= $this->createLastModified(); + $component .= $this->createLocation(); + $component .= $this->createOrganizer(); + $component .= $this->createPercentComplete(); + $component .= $this->createPriority(); + $component .= $this->createRdate(); + $component .= $this->createRelatedTo(); + $component .= $this->createRequestStatus(); + $component .= $this->createRecurrenceid(); + $component .= $this->createResources(); + $component .= $this->createRrule(); + $component .= $this->createSequence(); + $component .= $this->createStatus(); + $component .= $this->createSummary(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->createSubComponent(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} +/*********************************************************************************/ +/*********************************************************************************/ +/** + * class for calendar component VJOURNAL + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + */ +class vjournal extends calendarComponent { + var $attach; + var $attendee; + var $categories; + var $comment; + var $contact; + var $class; + var $created; + var $description; + var $dtstart; + var $exdate; + var $exrule; + var $lastmodified; + var $organizer; + var $rdate; + var $recurrenceid; + var $relatedto; + var $requeststatus; + var $rrule; + var $sequence; + var $status; + var $summary; + var $url; + var $xprop; +/** + * constructor for calendar component VJOURNAL object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @return void + */ + function vjournal( $config = array()) { + $this->calendarComponent(); + + $this->attach = ''; + $this->attendee = ''; + $this->categories = ''; + $this->class = ''; + $this->comment = ''; + $this->contact = ''; + $this->created = ''; + $this->description = ''; + $this->dtstart = ''; + $this->exdate = ''; + $this->exrule = ''; + $this->lastmodified = ''; + $this->organizer = ''; + $this->rdate = ''; + $this->recurrenceid = ''; + $this->relatedto = ''; + $this->requeststatus = ''; + $this->rrule = ''; + $this->sequence = ''; + $this->status = ''; + $this->summary = ''; + $this->url = ''; + $this->xprop = ''; + + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + + } +/** + * create formatted output for calendar component VJOURNAL object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + * @param array $xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createCategories(); + $component .= $this->createClass(); + $component .= $this->createComment(); + $component .= $this->createContact(); + $component .= $this->createCreated(); + $component .= $this->createDescription(); + $component .= $this->createDtstart(); + $component .= $this->createExdate(); + $component .= $this->createExrule(); + $component .= $this->createLastModified(); + $component .= $this->createOrganizer(); + $component .= $this->createRdate(); + $component .= $this->createRequestStatus(); + $component .= $this->createRecurrenceid(); + $component .= $this->createRelatedTo(); + $component .= $this->createRrule(); + $component .= $this->createSequence(); + $component .= $this->createStatus(); + $component .= $this->createSummary(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} +/*********************************************************************************/ +/*********************************************************************************/ +/** + * class for calendar component VFREEBUSY + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + */ +class vfreebusy extends calendarComponent { + var $attendee; + var $comment; + var $contact; + var $dtend; + var $dtstart; + var $duration; + var $freebusy; + var $organizer; + var $requeststatus; + var $url; + var $xprop; + // component subcomponents container + var $components; +/** + * constructor for calendar component VFREEBUSY object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @return void + */ + function vfreebusy( $config = array()) { + $this->calendarComponent(); + + $this->attendee = ''; + $this->comment = ''; + $this->contact = ''; + $this->dtend = ''; + $this->dtstart = ''; + $this->duration = ''; + $this->freebusy = ''; + $this->organizer = ''; + $this->requeststatus = ''; + $this->url = ''; + $this->xprop = ''; + + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + + } +/** + * create formatted output for calendar component VFREEBUSY object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.3.1 - 2007-11-19 + * @param array $xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttendee(); + $component .= $this->createComment(); + $component .= $this->createContact(); + $component .= $this->createDtstart(); + $component .= $this->createDtend(); + $component .= $this->createDuration(); + $component .= $this->createFreebusy(); + $component .= $this->createOrganizer(); + $component .= $this->createRequestStatus(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} +/*********************************************************************************/ +/*********************************************************************************/ +/** + * class for calendar component VALARM + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + */ +class valarm extends calendarComponent { + var $action; + var $attach; + var $attendee; + var $description; + var $duration; + var $repeat; + var $summary; + var $trigger; + var $xprop; +/** + * constructor for calendar component VALARM object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @return void + */ + function valarm( $config = array()) { + $this->calendarComponent(); + + $this->action = ''; + $this->attach = ''; + $this->attendee = ''; + $this->description = ''; + $this->duration = ''; + $this->repeat = ''; + $this->summary = ''; + $this->trigger = ''; + $this->xprop = ''; + + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + + } +/** + * create formatted output for calendar component VALARM object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-22 + * @param array $xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createAction(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createDescription(); + $component .= $this->createDuration(); + $component .= $this->createRepeat(); + $component .= $this->createSummary(); + $component .= $this->createTrigger(); + $component .= $this->createXprop(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} +/********************************************************************************** +/*********************************************************************************/ +/** + * class for calendar component VTIMEZONE + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + */ +class vtimezone extends calendarComponent { + var $timezonetype; + + var $comment; + var $dtstart; + var $lastmodified; + var $rdate; + var $rrule; + var $tzid; + var $tzname; + var $tzoffsetfrom; + var $tzoffsetto; + var $tzurl; + var $xprop; + // component subcomponents container + var $components; +/** + * constructor for calendar component VTIMEZONE object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param mixed $timezonetype optional, default FALSE ( STANDARD / DAYLIGHT ) + * @param array $config + * @return void + */ + function vtimezone( $timezonetype=FALSE, $config = array()) { + if( is_array( $timezonetype )) { + $config = $timezonetype; + $timezonetype = FALSE; + } + if( !$timezonetype ) + $this->timezonetype = 'VTIMEZONE'; + else + $this->timezonetype = strtoupper( $timezonetype ); + $this->calendarComponent(); + + $this->comment = ''; + $this->dtstart = ''; + $this->lastmodified = ''; + $this->rdate = ''; + $this->rrule = ''; + $this->tzid = ''; + $this->tzname = ''; + $this->tzoffsetfrom = ''; + $this->tzoffsetto = ''; + $this->tzurl = ''; + $this->xprop = ''; + + $this->components = array(); + + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + + } +/** + * create formatted output for calendar component VTIMEZONE object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-25 + * @param array $xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createTzid(); + $component .= $this->createLastModified(); + $component .= $this->createTzurl(); + $component .= $this->createDtstart(); + $component .= $this->createTzoffsetfrom(); + $component .= $this->createTzoffsetto(); + $component .= $this->createComment(); + $component .= $this->createRdate(); + $component .= $this->createRrule(); + $component .= $this->createTzname(); + $component .= $this->createXprop(); + $component .= $this->createSubComponent(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} +/*********************************************************************************/ +/*********************************************************************************/ +/** + * moving all utility (static) functions to a utility class + * 20111223 - move iCalUtilityFunctions class to the end of the iCalcreator class file + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.1 - 2011-07-16 + * + */ +class iCalUtilityFunctions { + // Store the single instance of iCalUtilityFunctions + private static $m_pInstance; + + // Private constructor to limit object instantiation to within the class + private function __construct() { + $m_pInstance = FALSE; + } + + // Getter method for creating/returning the single instance of this class + public static function getInstance() { + if (!self::$m_pInstance) + self::$m_pInstance = new iCalUtilityFunctions(); + + return self::$m_pInstance; + } +/** + * check a date(-time) for an opt. timezone and if it is a DATE-TIME or DATE + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.30 - 2012-01-16 + * @param array $date, date to check + * @param int $parno, no of date parts (i.e. year, month.. .) + * @return array $params, property parameters + */ + public static function _chkdatecfg( $theDate, & $parno, & $params ) { + if( isset( $params['TZID'] )) + $parno = 6; + elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) + $parno = 3; + else { + if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] )) + $parno = 7; + if( is_array( $theDate )) { + if( isset( $theDate['timestamp'] )) + $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null; + else + $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null; + if( !empty( $tzid )) { + $parno = 7; + if( !iCalUtilityFunctions::_isOffset( $tzid )) + $params['TZID'] = $tzid; // save only timezone + } + elseif( !$parno && ( 3 == count( $theDate )) && + ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))) + $parno = 3; + else + $parno = 6; + } + else { // string + $date = trim( $theDate ); + if( 'Z' == substr( $date, -1 )) + $parno = 7; // UTC DATE-TIME + elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) && + ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' )))) + $parno = 3; // DATE + $date = iCalUtilityFunctions::_date_time_string( $date, $parno ); + unset( $date['unparsedtext'] ); + if( !empty( $date['tz'] )) { + $parno = 7; + if( !iCalUtilityFunctions::_isOffset( $date['tz'] )) + $params['TZID'] = $date['tz']; // save only timezone + } + elseif( empty( $parno )) + $parno = 6; + } + if( isset( $params['TZID'] )) + $parno = 6; + } + } +/** + * create timezone and standard/daylight components + * + * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone: + * + * BEGIN:VTIMEZONE + * TZID:Europe/Stockholm + * BEGIN:STANDARD + * DTSTART:20101031T020000 + * TZOFFSETFROM:+0200 + * TZOFFSETTO:+0100 + * TZNAME:CET + * END:STANDARD + * BEGIN:DAYLIGHT + * DTSTART:20100328T030000 + * TZOFFSETFROM:+0100 + * TZOFFSETTO:+0200 + * TZNAME:CEST + * END:DAYLIGHT + * END:VTIMEZONE + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-02-06 + * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi + * @param object $calendar, reference to an iCalcreator calendar instance + * @param string $timezone, a PHP5 (DateTimeZone) valid timezone + * @param array $xProp, *[x-propName => x-propValue], optional + * @param int $from an unix timestamp + * @param int $to an unix timestamp + * @return bool + */ + public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) { + if( !class_exists( 'DateTimeZone' )) + return FALSE; + if( empty( $timezone )) + return FALSE; + try { + $dtz = new DateTimeZone( $timezone ); + $transitions = $dtz->getTransitions(); + unset( $dtz ); + $utcTz = new DateTimeZone( 'UTC' ); + } + catch( Exception $e ) { + return FALSE; + } + if( empty( $to )) + $dates = array_keys( $calendar->getProperty( 'dtstart' )); + $transCnt = 2; // number of transitions in output if empty input $from/$to and an empty dates-array + $dateFrom = new DateTime( 'now' ); + $dateTo = new DateTime( 'now' ); + if( !empty( $from )) + $dateFrom->setTimestamp( $from ); + else { + if( !empty( $dates )) + $dateFrom = new DateTime( reset( $dates )); // set lowest date to the lowest dtstart date + $dateFrom->modify( '-1 month' ); // set $dateFrom to one month before the lowest date + } + $dateFrom->setTimezone( $utcTz ); // convert local date to UTC + if( !empty( $to )) + $dateTo->setTimestamp( $to ); + else { + if( !empty( $dates )) { + $dateTo = new DateTime( end( $dates )); // set highest date to the highest dtstart date + $to = $dateTo->getTimestamp(); // set mark that a highest date is found + } + $dateTo->modify( '+1 year' ); // set $dateTo to one year after the highest date + } + $dateTo->setTimezone( $utcTz ); // convert local date to UTC + $transTemp = array(); + $prevOffsetfrom = $stdCnt = $dlghtCnt = 0; + $stdIx = $dlghtIx = null; + $date = new DateTime( 'now', $utcTz ); + foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!! + $date->setTimestamp( $trans['ts'] ); // set transition date (UTC) + if ( $date < $dateFrom ) { + $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom + continue; + } + if( $date > $dateTo ) + break; // loop always (?) breaks here + if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) { + $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom + $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date + $trans['time'] = array( 'year' => $date->format( 'Y' ) // set dtstart to array to ease up dtstart and (opt) rdate setting + , 'month' => $date->format( 'n' ) + , 'day' => $date->format( 'j' ) + , 'hour' => $date->format( 'G' ) + , 'min' => $date->format( 'i' ) + , 'sec' => $date->format( 's' )); + } + $prevOffsetfrom = $trans['offset']; + $trans['prevYear'] = $trans['time']['year']; + if( TRUE !== $trans['isdst'] ) { // standard timezone + if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any rdate's (in strict year order) + ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) && + ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) && + ( $transTemp[$stdIx]['offset'] == $trans['offset'] ) && + (($transTemp[$stdIx]['prevYear'] + 1) == $trans['time']['year'] )) { + $transTemp[$stdIx]['prevYear'] = $trans['time']['year']; + $transTemp[$stdIx]['rdate'][] = $trans['time']; + continue; + } + $stdIx = $tix; + $stdCnt += 1; + } // end standard timezone + else { // daylight timezone + if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any rdate's (in strict year order) + ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) && + ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) && + ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] ) && + (($transTemp[$dlghtIx]['prevYear'] + 1) == $trans['time']['year'] )) { + $transTemp[$dlghtIx]['prevYear'] = $trans['time']['year']; + $transTemp[$dlghtIx]['rdate'][] = $trans['time']; + continue; + } + $dlghtIx = $tix; + $dlghtCnt += 1; + } // end daylight timezone + if( empty( $to ) && ( $transCnt == count( $transTemp ))) { // store only $transCnt transitions + if( TRUE !== $transTemp[0]['isdst'] ) + $stdCnt -= 1; + else + $dlghtCnt -= 1; + array_shift( $transTemp ); + } // end if( empty( $to ) && ( $transCnt == count( $transTemp ))) + $transTemp[$tix] = $trans; + } // end foreach( $transitions as $tix => $trans ) + unset( $transitions ); + if( empty( $transTemp )) + return FALSE; + $tz = & $calendar->newComponent( 'vtimezone' ); + $tz->setproperty( 'tzid', $timezone ); + if( !empty( $xProp )) { + foreach( $xProp as $xPropName => $xPropValue ) + if( 'x-' == strtolower( substr( $xPropName, 0, 2 ))) + $tz->setproperty( $xPropName, $xPropValue ); + } + foreach( $transTemp as $trans ) { + $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight'; + $scomp = & $tz->newComponent( $type ); + $scomp->setProperty( 'dtstart', $trans['time'] ); +// $scomp->setProperty( 'x-utc-timestamp', $trans['ts'] ); // test ### + if( !empty( $trans['abbr'] )) + $scomp->setProperty( 'tzname', $trans['abbr'] ); + $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] )); + $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] )); + if( isset( $trans['rdate'] )) + $scomp->setProperty( 'RDATE', $trans['rdate'] ); + } + return TRUE; + } +/** + * convert a date/datetime (array) to timestamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-30 + * @param array $datetime datetime/(date) + * @param string $tz timezone + * @return timestamp + */ + public static function _date2timestamp( $datetime, $tz=null ) { + $output = null; + if( !isset( $datetime['hour'] )) $datetime['hour'] = '0'; + if( !isset( $datetime['min'] )) $datetime['min'] = '0'; + if( !isset( $datetime['sec'] )) $datetime['sec'] = '0'; + foreach( $datetime as $dkey => $dvalue ) { + if( 'tz' != $dkey ) + $datetime[$dkey] = (integer) $dvalue; + } + if( $tz ) + $datetime['tz'] = $tz; + $offset = ( isset( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) ? iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) : 0; + $output = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] + $offset), $datetime['month'], $datetime['day'], $datetime['year'] ); + return $output; + } +/** + * ensures internal date-time/date format for input date-time/date in array format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.4 - 2012-03-18 + * @param array $datetime + * @param int $parno optional, default FALSE + * @return array + */ + public static function _date_time_array( $datetime, $parno=FALSE ) { + $output = array(); + foreach( $datetime as $dateKey => $datePart ) { + switch ( $dateKey ) { + case '0': case 'year': $output['year'] = $datePart; break; + case '1': case 'month': $output['month'] = $datePart; break; + case '2': case 'day': $output['day'] = $datePart; break; + } + if( 3 != $parno ) { + switch ( $dateKey ) { + case '0': + case '1': + case '2': break; + case '3': case 'hour': $output['hour'] = $datePart; break; + case '4': case 'min' : $output['min'] = $datePart; break; + case '5': case 'sec' : $output['sec'] = $datePart; break; + case '6': case 'tz' : $output['tz'] = $datePart; break; + } + } + } + if( 3 != $parno ) { + if( !isset( $output['hour'] )) + $output['hour'] = 0; + if( !isset( $output['min'] )) + $output['min'] = 0; + if( !isset( $output['sec'] )) + $output['sec'] = 0; + if( isset( $output['tz'] ) && ( 'Z' != $output['tz'] ) && + (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] ))) + $output['tz'] = 'Z'; + } + return $output; + } +/** + * ensures internal date-time/date format for input date-time/date in string fromat + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.30 - 2012-01-06 + * Modified to also return original string value by Yitzchok Lavi + * @param array $datetime + * @param int $parno optional, default FALSE + * @return array + */ + public static function _date_time_string( $datetime, $parno=FALSE ) { + // save original input string to return it later + $unparseddatetime = $datetime; + $datetime = (string) trim( $datetime ); + $tz = null; + $len = strlen( $datetime ) - 1; + if( 'Z' == substr( $datetime, -1 )) { + $tz = 'Z'; + $datetime = trim( substr( $datetime, 0, $len )); + } + elseif( ( ctype_digit( substr( $datetime, -2, 2 ))) && // time or date + ( '-' == substr( $datetime, -3, 1 )) || + ( ':' == substr( $datetime, -3, 1 )) || + ( '.' == substr( $datetime, -3, 1 ))) { + $continue = TRUE; + } + elseif( ( ctype_digit( substr( $datetime, -4, 4 ))) && // 4 pos offset + ( ' +' == substr( $datetime, -6, 2 )) || + ( ' -' == substr( $datetime, -6, 2 ))) { + $tz = substr( $datetime, -5, 5 ); + $datetime = substr( $datetime, 0, ($len - 5)); + } + elseif( ( ctype_digit( substr( $datetime, -6, 6 ))) && // 6 pos offset + ( ' +' == substr( $datetime, -8, 2 )) || + ( ' -' == substr( $datetime, -8, 2 ))) { + $tz = substr( $datetime, -7, 7 ); + $datetime = substr( $datetime, 0, ($len - 7)); + } + elseif( ( 6 < $len ) && ( ctype_digit( substr( $datetime, -6, 6 )))) { + $continue = TRUE; + } + elseif( 'T' == substr( $datetime, -7, 1 )) { + $continue = TRUE; + } + else { + $cx = $tx = 0; // 19970415T133000 US-Eastern + for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) { + $char = substr( $datetime, $cx, 1 ); + if(( ' ' == $char) || ctype_digit( $char)) + break; // if exists, tz ends here.. . ? + else + $tx--; // tz length counter + } + if( 0 > $tx ) { + $tz = substr( $datetime, $tx ); + $datetime = trim( substr( $datetime, 0, $len + $tx + 1 )); + } + } + if( 0 < substr_count( $datetime, '-' )) { + $datetime = str_replace( '-', '/', $datetime ); + } + elseif( ctype_digit( substr( $datetime, 0, 8 )) && + ( 'T' == substr( $datetime, 8, 1 )) && + ctype_digit( substr( $datetime, 9, 6 ))) { + } + $datestring = date( 'Y-m-d H:i:s', strtotime( $datetime )); + $tz = trim( $tz ); + $output = array(); + $output['year'] = substr( $datestring, 0, 4 ); + $output['month'] = substr( $datestring, 5, 2 ); + $output['day'] = substr( $datestring, 8, 2 ); + if(( 6 == $parno ) || ( 7 == $parno ) || ( !$parno && ( 'Z' == $tz ))) { + $output['hour'] = substr( $datestring, 11, 2 ); + $output['min'] = substr( $datestring, 14, 2 ); + $output['sec'] = substr( $datestring, 17, 2 ); + if( !empty( $tz )) + $output['tz'] = $tz; + } + elseif( 3 != $parno ) { + if(( '00' < substr( $datestring, 11, 2 )) || + ( '00' < substr( $datestring, 14, 2 )) || + ( '00' < substr( $datestring, 17, 2 ))) { + $output['hour'] = substr( $datestring, 11, 2 ); + $output['min'] = substr( $datestring, 14, 2 ); + $output['sec'] = substr( $datestring, 17, 2 ); + } + if( !empty( $tz )) + $output['tz'] = $tz; + } + // return original string in the array in case strtotime failed to make sense of it + $output['unparsedtext'] = $unparseddatetime; + return $output; + } +/** + * convert local startdate/enddate (Ymd[His]) to duration array + * + * uses this component dates if missing input dates + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.11 - 2010-10-21 + * @param array $startdate + * @param array $duration + * @return array duration + */ + public static function _date2duration( $startdate, $enddate ) { + $startWdate = mktime( 0, 0, 0, $startdate['month'], $startdate['day'], $startdate['year'] ); + $endWdate = mktime( 0, 0, 0, $enddate['month'], $enddate['day'], $enddate['year'] ); + $wduration = $endWdate - $startWdate; + $dur = array(); + $dur['week'] = (int) floor( $wduration / ( 7 * 24 * 60 * 60 )); + $wduration = $wduration % ( 7 * 24 * 60 * 60 ); + $dur['day'] = (int) floor( $wduration / ( 24 * 60 * 60 )); + $wduration = $wduration % ( 24 * 60 * 60 ); + $dur['hour'] = (int) floor( $wduration / ( 60 * 60 )); + $wduration = $wduration % ( 60 * 60 ); + $dur['min'] = (int) floor( $wduration / ( 60 )); + $dur['sec'] = (int) $wduration % ( 60 ); + return $dur; + } +/** + * ensures internal duration format for input in array format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.1.1 - 2007-06-24 + * @param array $duration + * @return array + */ + public static function _duration_array( $duration ) { + $output = array(); + if( is_array( $duration ) && + ( 1 == count( $duration )) && + isset( $duration['sec'] ) && + ( 60 < $duration['sec'] )) { + $durseconds = $duration['sec']; + $output['week'] = floor( $durseconds / ( 60 * 60 * 24 * 7 )); + $durseconds = $durseconds % ( 60 * 60 * 24 * 7 ); + $output['day'] = floor( $durseconds / ( 60 * 60 * 24 )); + $durseconds = $durseconds % ( 60 * 60 * 24 ); + $output['hour'] = floor( $durseconds / ( 60 * 60 )); + $durseconds = $durseconds % ( 60 * 60 ); + $output['min'] = floor( $durseconds / ( 60 )); + $output['sec'] = ( $durseconds % ( 60 )); + } + else { + foreach( $duration as $durKey => $durValue ) { + if( empty( $durValue )) continue; + switch ( $durKey ) { + case '0': case 'week': $output['week'] = $durValue; break; + case '1': case 'day': $output['day'] = $durValue; break; + case '2': case 'hour': $output['hour'] = $durValue; break; + case '3': case 'min': $output['min'] = $durValue; break; + case '4': case 'sec': $output['sec'] = $durValue; break; + } + } + } + if( isset( $output['week'] ) && ( 0 < $output['week'] )) { + unset( $output['day'], $output['hour'], $output['min'], $output['sec'] ); + return $output; + } + unset( $output['week'] ); + if( empty( $output['day'] )) + unset( $output['day'] ); + if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) { + if( !isset( $output['hour'] )) $output['hour'] = 0; + if( !isset( $output['min'] )) $output['min'] = 0; + if( !isset( $output['sec'] )) $output['sec'] = 0; + if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] )) + unset( $output['hour'], $output['min'], $output['sec'] ); + } + return $output; + } +/** + * ensures internal duration format for input in string format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.0.5 - 2007-03-14 + * @param string $duration + * @return array + */ + public static function _duration_string( $duration ) { + $duration = (string) trim( $duration ); + while( 'P' != strtoupper( substr( $duration, 0, 1 ))) { + if( 0 < strlen( $duration )) + $duration = substr( $duration, 1 ); + else + return false; // no leading P !?!? + } + $duration = substr( $duration, 1 ); // skip P + $duration = str_replace ( 't', 'T', $duration ); + $duration = str_replace ( 'T', '', $duration ); + $output = array(); + $val = null; + for( $ix=0; $ix < strlen( $duration ); $ix++ ) { + switch( strtoupper( substr( $duration, $ix, 1 ))) { + case 'W': + $output['week'] = $val; + $val = null; + break; + case 'D': + $output['day'] = $val; + $val = null; + break; + case 'H': + $output['hour'] = $val; + $val = null; + break; + case 'M': + $output['min'] = $val; + $val = null; + break; + case 'S': + $output['sec'] = $val; + $val = null; + break; + default: + if( !ctype_digit( substr( $duration, $ix, 1 ))) + return false; // unknown duration control character !?!? + else + $val .= substr( $duration, $ix, 1 ); + } + } + return iCalUtilityFunctions::_duration_array( $output ); + } +/** + * convert duration to date in array format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.7 - 2011-03-03 + * @param array $startdate + * @param array $duration + * @return array, date format + */ + public static function _duration2date( $startdate=null, $duration=null ) { + if( empty( $startdate )) return FALSE; + if( empty( $duration )) return FALSE; + $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE; + $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0; + $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0; + $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0; + $dtend = 0; + if( isset( $duration['week'] )) + $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 ); + if( isset( $duration['day'] )) + $dtend += ( $duration['day'] * 24 * 60 * 60 ); + if( isset( $duration['hour'] )) + $dtend += ( $duration['hour'] * 60 *60 ); + if( isset( $duration['min'] )) + $dtend += ( $duration['min'] * 60 ); + if( isset( $duration['sec'] )) + $dtend += $duration['sec']; + $dtend = mktime( $startdate['hour'], $startdate['min'], ( $startdate['sec'] + $dtend ), $startdate['month'], $startdate['day'], $startdate['year'] ); + $dtend2 = array(); + $dtend2['year'] = date('Y', $dtend ); + $dtend2['month'] = date('m', $dtend ); + $dtend2['day'] = date('d', $dtend ); + $dtend2['hour'] = date('H', $dtend ); + $dtend2['min'] = date('i', $dtend ); + $dtend2['sec'] = date('s', $dtend ); + if( isset( $startdate['tz'] )) + $dtend2['tz'] = $startdate['tz']; + if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] ))) + unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] ); + return $dtend2; + } +/** + * if not preSet, if exist, remove key with expected value from array and return hit value else return elseValue + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-11-08 + * @param array $array + * @param string $expkey, expected key + * @param string $expval, expected value + * @param int $hitVal optional, return value if found + * @param int $elseVal optional, return value if not found + * @param int $preSet optional, return value if already preset + * @return int + */ + public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) { + if( $preSet ) + return $preSet; + if( !is_array( $array ) || ( 0 == count( $array ))) + return $elseVal; + foreach( $array as $key => $value ) { + if( strtoupper( $expkey ) == strtoupper( $key )) { + if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) { + unset( $array[$key] ); + return $hitVal; + } + } + } + return $elseVal; + } +/** + * creates formatted output for calendar component property data value type date/date-time + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-03-17 + * @param array $datetime + * @param int $parno, optional, default 6 + * @return string + */ + public static function _format_date_time( $datetime, $parno=6 ) { + if( !isset( $datetime['year'] ) && + !isset( $datetime['month'] ) && + !isset( $datetime['day'] ) && + !isset( $datetime['hour'] ) && + !isset( $datetime['min'] ) && + !isset( $datetime['sec'] )) + return ; + $output = null; + foreach( $datetime as $dkey => & $dvalue ) + if( 'tz' != $dkey ) $dvalue = (integer) $dvalue; + $output = sprintf( '%04d%02d%02d', $datetime['year'], $datetime['month'], $datetime['day'] ); + if( isset( $datetime['hour'] ) || + isset( $datetime['min'] ) || + isset( $datetime['sec'] ) || + isset( $datetime['tz'] )) { + if( isset( $datetime['tz'] ) && + !isset( $datetime['hour'] )) + $datetime['hour'] = 0; + if( isset( $datetime['hour'] ) && + !isset( $datetime['min'] )) + $datetime['min'] = 0; + if( isset( $datetime['hour'] ) && + isset( $datetime['min'] ) && + !isset( $datetime['sec'] )) + $datetime['sec'] = 0; + $output .= sprintf( 'T%02d%02d%02d', $datetime['hour'], $datetime['min'], $datetime['sec'] ); + if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) { + $datetime['tz'] = trim( $datetime['tz'] ); + if( 'Z' == $datetime['tz'] ) + $output .= 'Z'; + $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ); + if( 0 != $offset ) { + $date = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year']); + $output = date( 'Ymd\THis\Z', $date ); + } + } + elseif( 7 == $parno ) + $output .= 'Z'; + } + return $output; + } +/** + * creates formatted output for calendar component property data value type duration + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.9 - 2011-06-17 + * @param array $duration ( week, day, hour, min, sec ) + * @return string + */ + public static function _format_duration( $duration ) { + if( isset( $duration['week'] ) || + isset( $duration['day'] ) || + isset( $duration['hour'] ) || + isset( $duration['min'] ) || + isset( $duration['sec'] )) + $ok = TRUE; + else + return; + if( isset( $duration['week'] ) && ( 0 < $duration['week'] )) + return 'P'.$duration['week'].'W'; + $output = 'P'; + if( isset($duration['day'] ) && ( 0 < $duration['day'] )) + $output .= $duration['day'].'D'; + if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) || + ( isset( $duration['min']) && ( 0 < $duration['min'] )) || + ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) + $output .= 'T'; + $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : ''; + $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : ''; + $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : ''; + if( 'P' == $output ) + $output = 'PT0S'; + return $output; + } +/** + * checks if input array contains a date + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-01-20 + * @param array $input + * @return bool + */ + public static function _isArrayDate( $input ) { + if( !is_array( $input )) + return FALSE; + if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 )))) + return FALSE; + if( 7 == count( $input )) + return TRUE; + if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) + return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); + if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] )) + return FALSE; + if( in_array( 0, $input )) + return FALSE; + if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] )) + return FALSE; + if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) && + checkdate( (int) $input[1], (int) $input[2], (int) $input[0] )) + return TRUE; + $input = iCalUtilityFunctions::_date_time_string( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y + if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) + return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); + return FALSE; + } +/** + * checks if input array contains a timestamp date + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-10-18 + * @param array $input + * @return bool + */ + public static function _isArrayTimestampDate( $input ) { + return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ; + } +/** + * controll if input string contains trailing UTC offset + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-10-19 + * @param string $input + * @return bool + */ + public static function _isOffset( $input ) { + $input = trim( (string) $input ); + if( 'Z' == substr( $input, -1 )) + return TRUE; + elseif(( 5 <= strlen( $input )) && + ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) && + ( '0000' < substr( $input, -4 )) && ( '9999' >= substr( $input, -4 ))) + return TRUE; + elseif(( 7 <= strlen( $input )) && + ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) && + ( '000000' < substr( $input, -6 )) && ( '999999' >= substr( $input, -6 ))) + return TRUE; + return FALSE; + } +/** + * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone + * matching (MS) UCT offset and time zone descriptors + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.29 - 2012-01-11 + * @param string $timezone, input/output variable reference + * @return bool + */ + public static function ms2phpTZ( & $timezone ) { + if( !class_exists( 'DateTimeZone' )) + return FALSE; + if( empty( $timezone )) + return FALSE; + $search = str_replace( '"', '', $timezone ); + $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search ); + if( '(UTC' != substr( $search, 0, 4 )) + return FALSE; + if( FALSE === ( $pos = strpos( $search, ')' ))) + return FALSE; + $pos = strpos( $search, ')' ); + $searchOffset = substr( $search, 4, ( $pos - 4 )); + $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset )); + while( ' ' ==substr( $search, ( $pos + 1 ))) + $pos += 1; + $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) )); + $searchWords = explode( ' ', $searchText ); + $timezone_abbreviations = DateTimeZone::listAbbreviations(); + $hits = array(); + foreach( $timezone_abbreviations as $name => $transitions ) { + foreach( $transitions as $cnt => $transition ) { + if( empty( $transition['offset'] ) || + empty( $transition['timezone_id'] ) || + ( $transition['offset'] != $searchOffset )) + continue; + $cWords = explode( '/', $transition['timezone_id'] ); + $cPrio = $hitCnt = $rank = 0; + foreach( $cWords as $cWord ) { + if( empty( $cWord )) + continue; + $cPrio += 1; + $sPrio = 0; + foreach( $searchWords as $sWord ) { + if( empty( $sWord ) || ( 'time' == strtolower( $sWord ))) + continue; + $sPrio += 1; + if( strtolower( $cWord ) == strtolower( $sWord )) { + $hitCnt += 1; + $rank += ( $cPrio + $sPrio ); + } + else + $rank += 10; + } + } + if( 0 < $hitCnt ) { + $hits[$rank][] = $transition['timezone_id']; + } + } + } + unset( $timezone_abbreviations ); + if( empty( $hits )) + return FALSE; + ksort( $hits ); + foreach( $hits as $rank => $tzs ) { + if( !empty( $tzs )) { + $timezone = reset( $tzs ); + return TRUE; + } + } + return FALSE; + } +/** + * transform offset in seconds to [-/+]hhmm[ss] + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2011-05-02 + * @param string $seconds + * @return string + */ + public static function offsetSec2His( $seconds ) { + if( '-' == substr( $seconds, 0, 1 )) { + $prefix = '-'; + $seconds = substr( $seconds, 1 ); + } + elseif( '+' == substr( $seconds, 0, 1 )) { + $prefix = '+'; + $seconds = substr( $seconds, 1 ); + } + else + $prefix = '+'; + $output = ''; + $hour = (int) floor( $seconds / 3600 ); + if( 10 > $hour ) + $hour = '0'.$hour; + $seconds = $seconds % 3600; + $min = (int) floor( $seconds / 60 ); + if( 10 > $min ) + $min = '0'.$min; + $output = $hour.$min; + $seconds = $seconds % 60; + if( 0 < $seconds) { + if( 9 < $seconds) + $output .= $seconds; + else + $output .= '0'.$seconds; + } + return $prefix.$output; + } +/** + * remakes a recur pattern to an array of dates + * + * if missing, UNTIL is set 1 year from startdate (emergency break) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.19 - 2011-10-31 + * @param array $result, array to update, array([timestamp] => timestamp) + * @param array $recur, pattern for recurrency (only value part, params ignored) + * @param array $wdate, component start date + * @param array $startdate, start date + * @param array $enddate, optional + * @return array of recurrence (start-)dates as index + * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start + */ + public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) { + foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v; + $wdateStart = $wdate; + $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); + $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate ); + if( !$enddate ) { + $enddate = $startdate; + $enddate['year'] += 1; + } +// echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."
\n";print_r($recur);echo "
\n";//test### + $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break + if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] )) + $recur['UNTIL'] = $enddate; // create break + if( isset( $recur['UNTIL'] )) { + $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] ); + if( $endDatets > $tdatets ) { + $endDatets = $tdatets; // emergency break + $enddate = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 ); + } + else + $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 ); + } + if( $wdatets > $endDatets ) { +// echo "recur out of date ".date('Y-m-d H:i:s',$wdatets)."
\n";//test + return array(); // nothing to do.. . + } + if( !isset( $recur['FREQ'] )) // "MUST be specified.. ." + $recur['FREQ'] = 'DAILY'; // ?? + $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ?? + $weekStart = (int) date( 'W', ( $wdatets + $wkst )); + if( !isset( $recur['INTERVAL'] )) + $recur['INTERVAL'] = 1; + $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence + /* find out how to step up dates and set index for interval count */ + $step = array(); + if( 'YEARLY' == $recur['FREQ'] ) + $step['year'] = 1; + elseif( 'MONTHLY' == $recur['FREQ'] ) + $step['month'] = 1; + elseif( 'WEEKLY' == $recur['FREQ'] ) + $step['day'] = 7; + else + $step['day'] = 1; + if( isset( $step['year'] ) && isset( $recur['BYMONTH'] )) + $step = array( 'month' => 1 ); + if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ?? + $step = array( 'day' => 7 ); + if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] )) + $step = array( 'day' => 1 ); + $intervalarr = array(); + if( 1 < $recur['INTERVAL'] ) { + $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); + $intervalarr = array( $intervalix => 0 ); + } + if( isset( $recur['BYSETPOS'] )) { // save start date + weekno + $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array(); +// echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold
\n"; // test ### + if( is_array( $recur['BYSETPOS'] )) { + foreach( $recur['BYSETPOS'] as $bix => $bval ) + $recur['BYSETPOS'][$bix] = (int) $bval; + } + else + $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] ); + if( 'YEARLY' == $recur['FREQ'] ) { + $wdate['month'] = $wdate['day'] = 1; // start from beginning of year + $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); + iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'year' => 1 )); // make sure to count whole last year + } + elseif( 'MONTHLY' == $recur['FREQ'] ) { + $wdate['day'] = 1; // start from beginning of month + $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); + iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'month' => 1 )); // make sure to count whole last month + } + else + iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period +// echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."
\n";//test### + $bysetposWold = (int) date( 'W', ( $wdatets + $wkst )); + $bysetposYold = $wdate['year']; + $bysetposMold = $wdate['month']; + $bysetposDold = $wdate['day']; + } + else + iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); + $year_old = null; + $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' ); + /* MAIN LOOP */ +// echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."
\n";//test + while( TRUE ) { + if( isset( $endDatets ) && ( $wdatets > $endDatets )) + break; + if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) + break; + if( $year_old != $wdate['year'] ) { + $year_old = $wdate['year']; + $daycnts = array(); + $yeardays = $weekno = 0; + $yeardaycnt = array(); + foreach( $daynames as $dn ) + $yeardaycnt[$dn] = 0; + for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters + $daycnts[$m] = array(); + $weekdaycnt = array(); + foreach( $daynames as $dn ) + $weekdaycnt[$dn] = 0; + $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); + for( $d = 1; $d <= $mcnt; $d++ ) { + $daycnts[$m][$d] = array(); + if( isset( $recur['BYYEARDAY'] )) { + $yeardays++; + $daycnts[$m][$d]['yearcnt_up'] = $yeardays; + } + if( isset( $recur['BYDAY'] )) { + $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] )); + $day = $daynames[$day]; + $daycnts[$m][$d]['DAY'] = $day; + $weekdaycnt[$day]++; + $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day]; + $yeardaycnt[$day]++; + $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day]; + } + if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) + $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year'])); + } + } + $daycnt = 0; + $yeardaycnt = array(); + if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) { + $weekno = null; + for( $d=31; $d > 25; $d-- ) { // get last weekno for year + if( !$weekno ) + $weekno = $daycnts[12][$d]['weekno_up']; + elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) { + $weekno = $daycnts[12][$d]['weekno_up']; + break; + } + } + } + for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters + $weekdaycnt = array(); + foreach( $daynames as $dn ) + $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0; + $monthcnt = 0; + $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); + for( $d = $mcnt; $d > 0; $d-- ) { + if( isset( $recur['BYYEARDAY'] )) { + $daycnt -= 1; + $daycnts[$m][$d]['yearcnt_down'] = $daycnt; + } + if( isset( $recur['BYMONTHDAY'] )) { + $monthcnt -= 1; + $daycnts[$m][$d]['monthcnt_down'] = $monthcnt; + } + if( isset( $recur['BYDAY'] )) { + $day = $daycnts[$m][$d]['DAY']; + $weekdaycnt[$day] -= 1; + $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day]; + $yeardaycnt[$day] -= 1; + $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day]; + } + if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) + $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1); + } + } + } + /* check interval */ + if( 1 < $recur['INTERVAL'] ) { + /* create interval index */ + $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); + /* check interval */ + $currentKey = array_keys( $intervalarr ); + $currentKey = end( $currentKey ); // get last index + if( $currentKey != $intervalix ) + $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 )); + if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) && + ( 0 != $intervalarr[$intervalix] )) { + /* step up date */ +// echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test + iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); + continue; + } + else // continue within the selected interval + $intervalarr[$intervalix] = 0; +// echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test + } + $updateOK = TRUE; + if( $updateOK && isset( $recur['BYMONTH'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH'] + , $wdate['month'] + ,($wdate['month'] - 13)); + if( $updateOK && isset( $recur['BYWEEKNO'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO'] + , $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] + , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] ); + if( $updateOK && isset( $recur['BYYEARDAY'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY'] + , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up'] + , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] ); + if( $updateOK && isset( $recur['BYMONTHDAY'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY'] + , $wdate['day'] + , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] ); +// echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n";//test### + if( $updateOK && isset( $recur['BYDAY'] )) { + $updateOK = FALSE; + $m = $wdate['month']; + $d = $wdate['day']; + if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no + $daynoexists = $daynosw = $daynamesw = FALSE; + if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] ) + $daynamesw = TRUE; + if( isset( $recur['BYDAY'][0] )) { + $daynoexists = TRUE; + if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] + , $daycnts[$m][$d]['monthdayno_up'] + , $daycnts[$m][$d]['monthdayno_down'] ); + elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] + , $daycnts[$m][$d]['yeardayno_up'] + , $daycnts[$m][$d]['yeardayno_down'] ); + } + if(( $daynoexists && $daynosw && $daynamesw ) || + ( !$daynoexists && !$daynosw && $daynamesw )) { + $updateOK = TRUE; +// echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### + } +//echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### + } + else { + foreach( $recur['BYDAY'] as $bydayvalue ) { + $daynoexists = $daynosw = $daynamesw = FALSE; + if( isset( $bydayvalue['DAY'] ) && + ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] )) + $daynamesw = TRUE; + if( isset( $bydayvalue[0] )) { + $daynoexists = TRUE; + if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || + isset( $recur['BYMONTH'] )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] + , $daycnts[$m][$d]['monthdayno_up'] + , $daycnts[$m][$d]['monthdayno_down'] ); + elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] + , $daycnts[$m][$d]['yeardayno_up'] + , $daycnts[$m][$d]['yeardayno_down'] ); + } +// echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw
\n"; // test ### + if(( $daynoexists && $daynosw && $daynamesw ) || + ( !$daynoexists && !$daynosw && $daynamesw )) { + $updateOK = TRUE; + break; + } + } + } + } +// echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n"; // test ### + /* check BYSETPOS */ + if( $updateOK ) { + if( isset( $recur['BYSETPOS'] ) && + ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) { + if( isset( $recur['WEEKLY'] )) { + if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] ) + $bysetposw1[] = $wdatets; + else + $bysetposw2[] = $wdatets; + } + else { + if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && + ( $bysetposYold == $wdate['year'] )) || + ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) && + (( $bysetposYold == $wdate['year'] ) && + ( $bysetposMold == $wdate['month'] ))) || + ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && + (( $bysetposYold == $wdate['year'] ) && + ( $bysetposMold == $wdate['month']) && + ( $bysetposDold == $wdate['day'] )))) { +// echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test + $bysetposymd1[] = $wdatets; + } + else { +// echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test + $bysetposymd2[] = $wdatets; + } + } + } + else { + /* update result array if BYSETPOS is set */ + $countcnt++; + if( $startdatets <= $wdatets ) { // only output within period + $result[$wdatets] = TRUE; +// echo "recur ".date('Y-m-d H:i:s',$wdatets)."
\n";//test + } +// echo "recur undate ".date('Y-m-d H:i:s',$wdatets)." okdatstart ".date('Y-m-d H:i:s',$startdatets)."
\n";//test + $updateOK = FALSE; + } + } + /* step up date */ + iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); + /* check if BYSETPOS is set for updating result array */ + if( $updateOK && isset( $recur['BYSETPOS'] )) { + $bysetpos = FALSE; + if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && + ( $bysetposYold != $wdate['year'] )) { + $bysetpos = TRUE; + $bysetposYold = $wdate['year']; + } + elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] && + (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) { + $bysetpos = TRUE; + $bysetposYold = $wdate['year']; + $bysetposMold = $wdate['month']; + } + elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) { + $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'])); + if( $bysetposWold != $weekno ) { + $bysetposWold = $weekno; + $bysetpos = TRUE; + } + } + elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && + (( $bysetposYold != $wdate['year'] ) || + ( $bysetposMold != $wdate['month'] ) || + ( $bysetposDold != $wdate['day'] ))) { + $bysetpos = TRUE; + $bysetposYold = $wdate['year']; + $bysetposMold = $wdate['month']; + $bysetposDold = $wdate['day']; + } + if( $bysetpos ) { + if( isset( $recur['BYWEEKNO'] )) { + $bysetposarr1 = & $bysetposw1; + $bysetposarr2 = & $bysetposw2; + } + else { + $bysetposarr1 = & $bysetposymd1; + $bysetposarr2 = & $bysetposymd2; + } +// echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ### + foreach( $recur['BYSETPOS'] as $ix ) { + if( 0 > $ix ) // both positive and negative BYSETPOS allowed + $ix = ( count( $bysetposarr1 ) + $ix + 1); + $ix--; + if( isset( $bysetposarr1[$ix] )) { + if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period +// $testdate = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 ); // test ### +// $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ### +// echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)"; // test ### + $result[$bysetposarr1[$ix]] = TRUE; +// echo " recur ".date('Y-m-d H:i:s',$bysetposarr1[$ix]); // test ### + } + $countcnt++; + } + if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) + break; + } +// echo "
\n"; // test ### + $bysetposarr1 = $bysetposarr2; + $bysetposarr2 = array(); + } + } + } + } + public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) { + if( is_array( $BYvalue ) && + ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue ))) + return TRUE; + elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue )) + return TRUE; + else + return FALSE; + } + public static function _recurIntervalIx( $freq, $date, $wkst ) { + /* create interval index */ + switch( $freq ) { + case 'YEARLY': + $intervalix = $date['year']; + break; + case 'MONTHLY': + $intervalix = $date['year'].'-'.$date['month']; + break; + case 'WEEKLY': + $wdatets = iCalUtilityFunctions::_date2timestamp( $date ); + $intervalix = (int) date( 'W', ( $wdatets + $wkst )); + break; + case 'DAILY': + default: + $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day']; + break; + } + return $intervalix; + } +/** + * convert input format for exrule and rrule to internal format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.15 - 2012-01-31 + * @param array $rexrule + * @return array + */ + public static function _setRexrule( $rexrule ) { + $input = array(); + if( empty( $rexrule )) + return $input; + foreach( $rexrule as $rexrulelabel => $rexrulevalue ) { + $rexrulelabel = strtoupper( $rexrulelabel ); + if( 'UNTIL' != $rexrulelabel ) + $input[$rexrulelabel] = $rexrulevalue; + else { + iCalUtilityFunctions::_strDate2arr( $rexrulevalue ); + if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time + $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 6 ); + elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or date-time + $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 6 : 3; + $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_array( $rexrulevalue, $parno ); + } + elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual datetime/date 2006-08-03 10:12:18 + $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_string( $rexrulevalue ); + unset( $input['$rexrulelabel']['unparsedtext'] ); + } + if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] )) + $input[$rexrulelabel]['tz'] = 'Z'; + } + } + /* set recurrence rule specification in rfc2445 order */ + $input2 = array(); + if( isset( $input['FREQ'] )) + $input2['FREQ'] = $input['FREQ']; + if( isset( $input['UNTIL'] )) + $input2['UNTIL'] = $input['UNTIL']; + elseif( isset( $input['COUNT'] )) + $input2['COUNT'] = $input['COUNT']; + if( isset( $input['INTERVAL'] )) + $input2['INTERVAL'] = $input['INTERVAL']; + if( isset( $input['BYSECOND'] )) + $input2['BYSECOND'] = $input['BYSECOND']; + if( isset( $input['BYMINUTE'] )) + $input2['BYMINUTE'] = $input['BYMINUTE']; + if( isset( $input['BYHOUR'] )) + $input2['BYHOUR'] = $input['BYHOUR']; + if( isset( $input['BYDAY'] )) { + if( !is_array( $input['BYDAY'] )) // ensure upper case.. . + $input2['BYDAY'] = strtoupper( $input['BYDAY'] ); + else { + foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) { + if( 'DAY' == strtoupper( $BYDAYx )) + $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv ); + elseif( !is_array( $BYDAYv )) { + $input2['BYDAY'][$BYDAYx] = $BYDAYv; + } + else { + foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) { + if( 'DAY' == strtoupper( $BYDAYx2 )) + $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 ); + else + $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2; + } + } + } + } + } + if( isset( $input['BYMONTHDAY'] )) + $input2['BYMONTHDAY'] = $input['BYMONTHDAY']; + if( isset( $input['BYYEARDAY'] )) + $input2['BYYEARDAY'] = $input['BYYEARDAY']; + if( isset( $input['BYWEEKNO'] )) + $input2['BYWEEKNO'] = $input['BYWEEKNO']; + if( isset( $input['BYMONTH'] )) + $input2['BYMONTH'] = $input['BYMONTH']; + if( isset( $input['BYSETPOS'] )) + $input2['BYSETPOS'] = $input['BYSETPOS']; + if( isset( $input['WKST'] )) + $input2['WKST'] = $input['WKST']; + return $input2; + } +/** + * convert format for input date to internal date with parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-03-18 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param string $tz optional + * @param array $params optional + * @param string $caller optional + * @param string $objName optional + * @param string $tzid optional + * @return array + */ + public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null, $tzid=FALSE ) { + $input = $parno = null; + $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE; + iCalUtilityFunctions::_strDate2arr( $year ); + if( iCalUtilityFunctions::_isArrayDate( $year )) { + if( $localtime ) unset ( $month['VALUE'], $month['TZID'] ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + if( isset( $input['params']['TZID'] )) { + $input['params']['VALUE'] = 'DATE-TIME'; + unset( $year['tz'] ); + } + $hitval = ( isset( $year['tz'] ) || isset( $year[6] )) ? 7 : 6; + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval ); + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $year ), $parno ); + $input['value'] = iCalUtilityFunctions::_date_time_array( $year, $parno ); + } + elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { + if( $localtime ) unset ( $month['VALUE'], $month['TZID'] ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + if( isset( $input['params']['TZID'] )) { + $input['params']['VALUE'] = 'DATE-TIME'; + unset( $year['tz'] ); + } + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); + $hitval = ( isset( $year['tz'] )) ? 7 : 6; + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno ); + $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno ); + } + elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 + if( $localtime ) unset ( $month['VALUE'], $month['TZID'] ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + if( isset( $input['params']['TZID'] )) { + $input['params']['VALUE'] = 'DATE-TIME'; + $parno = 6; + } + elseif( $tzid && iCalUtilityFunctions::_isOffset( substr( $year, -7 ))) { + if(( in_array( substr( $year, -5, 1 ), array( '+', '-' ))) && + ( '0000' < substr( $year, -4 )) && ( '9999' >= substr( $year, -4 ))) + $year = substr( $year, 0, ( strlen( $year ) - 5 )); + elseif(( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) && + ( '000000' < substr( $input, -6 )) && ( '999999' >= substr( $input, -6 ))) + $year = substr( $year, 0, ( strlen( $year ) - 7 )); + $parno = 6; + } + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno ); + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno ); + $input['value'] = iCalUtilityFunctions::_date_time_string( $year, $parno ); + unset( $input['value']['unparsedtext'] ); + } + else { + if( is_array( $params )) { + if( $localtime ) unset ( $params['VALUE'], $params['TZID'] ); + $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); + } + elseif( is_array( $tz )) { + $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' )); + $tz = FALSE; + } + elseif( is_array( $hour )) { + $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' )); + $hour = $min = $sec = $tz = FALSE; + } + if( isset( $input['params']['TZID'] )) { + $tz = null; + $input['params']['VALUE'] = 'DATE-TIME'; + } + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); + $hitval = ( !empty( $tz )) ? 7 : 6; + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno ); + $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day ); + if( 3 != $parno ) { + $input['value']['hour'] = ( $hour ) ? $hour : '0'; + $input['value']['min'] = ( $min ) ? $min : '0'; + $input['value']['sec'] = ( $sec ) ? $sec : '0'; + if( !empty( $tz )) + $input['value']['tz'] = $tz; + } + } + if( 3 == $parno ) { + $input['params']['VALUE'] = 'DATE'; + unset( $input['value']['tz'] ); + unset( $input['params']['TZID'] ); + } + elseif( isset( $input['params']['TZID'] )) + unset( $input['value']['tz'] ); + if( $localtime ) + unset( $input['value']['tz'], $input['params']['TZID'] ); + elseif(( !isset( $input['params']['VALUE'] ) || ( $input['params']['VALUE'] != 'DATE' )) && !isset( $input['params']['TZID'] ) && $tzid ) + $input['params']['TZID'] = $tzid; + if( isset( $input['value']['tz'] )) + $input['value']['tz'] = (string) $input['value']['tz']; + if( !empty( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && // real time zone in tz to TZID + ( !iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))) { + $input['params']['TZID'] = $input['value']['tz']; + unset( $input['value']['tz'] ); + } + if( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) { + if(( 'Z' != $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { // utc offset in TZID to tz + $input['value']['tz'] = $input['params']['TZID']; + unset( $input['params']['TZID'] ); + } + elseif( in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) { // time zone Z + $input['value']['tz'] = 'Z'; + unset( $input['params']['TZID'] ); + } + } + return $input; + } +/** + * convert format for input date (UTC) to internal date with parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-01-19 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @return array + */ + public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + $input = null; + iCalUtilityFunctions::_strDate2arr( $year ); + if( iCalUtilityFunctions::_isArrayDate( $year )) { + $input['value'] = iCalUtilityFunctions::_date_time_array( $year, 7 ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) ); + } + elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { + $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) ); + } + elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 + $input['value'] = iCalUtilityFunctions::_date_time_string( $year, 7 ); + unset( $input['value']['unparsedtext'] ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) ); + } + else { + $input['value'] = array( 'year' => $year + , 'month' => $month + , 'day' => $day + , 'hour' => $hour + , 'min' => $min + , 'sec' => $sec ); + $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); + } + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default + if( !isset( $input['value']['hour'] )) + $input['value']['hour'] = 0; + if( !isset( $input['value']['min'] )) + $input['value']['min'] = 0; + if( !isset( $input['value']['sec'] )) + $input['value']['sec'] = 0; + if( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) { + if(( 'Z' != $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { // utc offset in TZID to tz + $input['value']['tz'] = $input['params']['TZID']; + unset( $input['params']['TZID'] ); + } + elseif( in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) { // time zone Z + $input['value']['tz'] = 'Z'; + unset( $input['params']['TZID'] ); + } + } + if( !isset( $input['value']['tz'] ) || !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) + $input['value']['tz'] = 'Z'; + return $input; + } +/** + * check index and set (an indexed) content in multiple value array + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.12 - 2011-01-03 + * @param array $valArr + * @param mixed $value + * @param array $params + * @param array $defaults + * @param int $index + * @return void + */ + public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) { + if( !is_array( $valArr )) $valArr = array(); + if( $index ) + $index = $index - 1; + elseif( 0 < count( $valArr )) { + $keys = array_keys( $valArr ); + $index = end( $keys ) + 1; + } + else + $index = 0; + $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults )); + ksort( $valArr ); + } +/** + * set input (formatted) parameters- component property attributes + * + * default parameters can be set, if missing + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 1.x.x - 2007-05-01 + * @param array $params + * @param array $defaults + * @return array + */ + public static function _setParams( $params, $defaults=FALSE ) { + if( !is_array( $params)) + $params = array(); + $input = array(); + foreach( $params as $paramKey => $paramValue ) { + if( is_array( $paramValue )) { + foreach( $paramValue as $pkey => $pValue ) { + if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 ))) + $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 )); + } + } + elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 ))) + $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 )); + if( 'VALUE' == strtoupper( $paramKey )) + $input['VALUE'] = strtoupper( $paramValue ); + else + $input[strtoupper( $paramKey )] = $paramValue; + } + if( is_array( $defaults )) { + foreach( $defaults as $paramKey => $paramValue ) { + if( !isset( $input[$paramKey] )) + $input[$paramKey] = $paramValue; + } + } + return (0 < count( $input )) ? $input : null; + } +/** + * step date, return updated date, array and timpstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-10-18 + * @param array $date, date to step + * @param int $timestamp + * @param array $step, default array( 'day' => 1 ) + * @return void + */ + public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) { + foreach( $step as $stepix => $stepvalue ) + $date[$stepix] += $stepvalue; + $timestamp = iCalUtilityFunctions::_date2timestamp( $date ); + $date = iCalUtilityFunctions::_timestamp2date( $timestamp, 6 ); + foreach( $date as $k => $v ) { + if( ctype_digit( $v )) + $date[$k] = (int) $v; + } + } +/** + * convert a date from specific string to array format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-01-27 + * @param mixed $input + * @return bool, TRUE on success + */ + public static function _strDate2arr( & $input ) { + if( is_array( $input )) + return FALSE; + if( 5 > strlen( (string) $input )) + return FALSE; + $work = $input; + if( 2 == substr_count( $work, '-' )) + $work = str_replace( '-', '', $work ); + if( 2 == substr_count( $work, '/' )) + $work = str_replace( '/', '', $work ); + if( !ctype_digit( substr( $work, 0, 8 ))) + return FALSE; + if( !checkdate( (int) substr( $work, 4, 2 ), (int) substr( $work, 6, 2 ), (int) substr( $work, 0, 4 ))) + return FALSE; + $temp = array( 'year' => substr( $work, 0, 4 ) + , 'month' => substr( $work, 4, 2 ) + , 'day' => substr( $work, 6, 2 )); + if( 8 == strlen( $work )) { + $input = $temp; + return TRUE; + } + if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 ))) + $work = substr( $work, 9 ); + elseif( ctype_digit( substr( $work, 8, 1 ))) + $work = substr( $work, 8 ); + else + return FALSE; + if( 2 == substr_count( $work, ':' )) + $work = str_replace( ':', '', $work ); + if( !ctype_digit( substr( $work, 0, 4 ))) + return FALSE; + $temp['hour'] = substr( $work, 0, 2 ); + $temp['min'] = substr( $work, 2, 2 ); + if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) || + (( 0 > $temp['min'] ) || ( $temp['min'] > 59 ))) + return FALSE; + if( ctype_digit( substr( $work, 4, 2 ))) { + $temp['sec'] = substr( $work, 4, 2 ); + if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 )) + return FALSE; + $len = 6; + } + else { + $temp['sec'] = 0; + $len = 4; + } + if( $len < strlen( $work)) + $temp['tz'] = trim( substr( $work, 6 )); + $input = $temp; + return TRUE; + } +/** + * convert timestamp to date array + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-11-01 + * @param mixed $timestamp + * @param int $parno + * @return array + */ + public static function _timestamp2date( $timestamp, $parno=6 ) { + if( is_array( $timestamp )) { + if(( 7 == $parno ) && !empty( $timestamp['tz'] )) + $tz = $timestamp['tz']; + $timestamp = $timestamp['timestamp']; + } + $output = array( 'year' => date( 'Y', $timestamp ) + , 'month' => date( 'm', $timestamp ) + , 'day' => date( 'd', $timestamp )); + if( 3 != $parno ) { + $output['hour'] = date( 'H', $timestamp ); + $output['min'] = date( 'i', $timestamp ); + $output['sec'] = date( 's', $timestamp ); + if( isset( $tz )) + $output['tz'] = $tz; + } + return $output; + } +/** + * convert timestamp to duration in array format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.23 - 2010-10-23 + * @param int $timestamp + * @return array, duration format + */ + public static function _timestamp2duration( $timestamp ) { + $dur = array(); + $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 )); + $timestamp = $timestamp % ( 7 * 24 * 60 * 60 ); + $dur['day'] = (int) floor( $timestamp / ( 24 * 60 * 60 )); + $timestamp = $timestamp % ( 24 * 60 * 60 ); + $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 )); + $timestamp = $timestamp % ( 60 * 60 ); + $dur['min'] = (int) floor( $timestamp / ( 60 )); + $dur['sec'] = (int) $timestamp % ( 60 ); + return $dur; + } +/** + * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.14 - 2012-01-24 + * @param mixed $date, date to alter + * @param string $tzFrom, PHP valid old timezone + * @param string $tzTo, PHP valid new timezone, default 'UTC' + * @param string $format, date output format, default 'Ymd\THis' + * @return bool + */ + public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) { + if( !class_exists( 'DateTime' ) || !class_exists( 'DateTimeZone' )) + return FALSE; + if( is_array( $date ) && isset( $date['timestamp'] )) + $timestamp = $date['timestamp']; + elseif( iCalUtilityFunctions::_isArrayDate( $date )) { + if(isset( $date['tz'] )) + unset( $date['tz'] ); + $date = iCalUtilityFunctions::_format_date_time( iCalUtilityFunctions::_date_time_array( $date )); + if( 'Z' == substr( $date, -1 )) + $date = substr( $date, 0, ( strlen( $date ) - 2 )); + if( FALSE === ( $timestamp = strtotime( $date ))) + return FALSE; + } + elseif( FALSE === ( $timestamp = @strtotime( $date ))) + return FALSE; + try { + $d = new DateTime( date( 'Y-m-d H:i:s', $timestamp ), new DateTimeZone( $tzFrom )); + $d->setTimezone( new DateTimeZone( $tzTo )); + } + catch (Exception $e) { + return FALSE; + } + $date = $d->format( $format ); + return TRUE; + } +/** + * convert (numeric) local time offset, ("+" / "-")HHmm[ss], to seconds correcting localtime to GMT + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.4 - 2012-01-11 + * @param string $offset + * @return integer + */ + public static function _tz2offset( $tz ) { + $tz = trim( (string) $tz ); + $offset = 0; + if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) || + (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) || + (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) || + (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 )))) + return $offset; + $hours2sec = (int) substr( $tz, 1, 2 ) * 3600; + $min2sec = (int) substr( $tz, 3, 2 ) * 60; + $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00'; + $offset = $hours2sec + $min2sec + $sec; + $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset; + return $offset; + } +} +/*********************************************************************************/ +/* iCalcreator XML (rfc6321) helper functions */ +/*********************************************************************************/ +/** + * format iCal XML output, rfc6321, using PHP SimpleXMLElement + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.1 - 2012-02-22 + * @param object $calendar, iCalcreator vcalendar instance reference + * @return string + */ +function iCal2XML( & $calendar ) { + /** fix an SimpleXMLElement instance and create root element */ + $xmlstr = ''; + $xmlstr .= ''; + $xmlstr .= ''; + $xml = new SimpleXMLElement( $xmlstr ); + $vcalendar = $xml->addChild( 'vcalendar' ); + /** fix calendar properties */ + $properties = $vcalendar->addChild( 'properties' ); + $calProps = array( 'prodid', 'version', 'calscale', 'method' ); + foreach( $calProps as $calProp ) { + if( FALSE !== ( $content = $calendar->getProperty( $calProp ))) + _addXMLchild( $properties, $calProp, 'text', $content ); + } + while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE ))) + _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); + $langCal = $calendar->getConfig( 'language' ); + /** prepare to fix components with properties */ + $components = $vcalendar->addChild( 'components' ); + $comps = array( 'vtimezone', 'vevent', 'vtodo', 'vjournal', 'vfreebusy' ); + $eventProps = array( 'dtstamp', 'dtstart', 'uid', + 'class', 'created', 'description', 'geo', 'last-modified', 'location', 'organizer', 'priority', + 'sequence', 'status', 'summary', 'transp', 'url', 'recurrence-id', 'rrule', 'dtend', 'duration', + 'attach', 'attendee', 'categories', 'comment', 'contact', 'exdate', 'request-status', 'related-to', 'resources', 'rdate', + 'x-prop' ); + $todoProps = array( 'dtstamp', 'uid', + 'class', 'completed', 'created', 'description', 'geo', 'last-modified', 'location', 'organizer', 'percent-complete', 'priority', + 'recurrence-id', 'sequence', 'status', 'summary', 'url', 'rrule', 'dtstart', 'due', 'duration', + 'attach', 'attendee', 'categories', 'comment', 'contact', 'exdate', 'request-status', 'related-to', 'resources', 'rdate', + 'x-prop' ); + $journalProps = array( 'dtstamp', 'uid', + 'class', 'created', 'dtstart', 'last-modified', 'organizer', 'recurrence-id', 'sequence', 'status', 'summary', 'url', 'rrule', + 'attach', 'attendee', 'categories', 'comment', 'contact', + 'description', + 'exdate', 'related-to', 'rdate', 'request-status', + 'x-prop' ); + $freebusyProps = array( 'dtstamp', 'uid', + 'contact', 'dtstart', 'dtend', 'duration', 'organizer', 'url', + 'attendee', 'comment', 'freebusy', 'request-status', + 'x-prop' ); + $timezoneProps = array( 'tzid', + 'last-modified', 'tzurl', + 'x-prop' ); + $alarmProps = array( 'action', 'description', 'trigger', 'summary', + 'attendee', + 'duration', 'repeat', 'attach', + 'x-prop' ); + $stddghtProps = array( 'dtstart', 'tzoffsetto', 'tzoffsetfrom', + 'rrule', + 'comment', 'rdate', 'tzname', + 'x-prop' ); + foreach( $comps as $compName ) { + switch( $compName ) { + case 'vevent': + $props = & $eventProps; + $subComps = array( 'valarm' ); + $subCompProps = & $alarmProps; + break; + case 'vtodo': + $props = & $todoProps; + $subComps = array( 'valarm' ); + $subCompProps = & $alarmProps; + break; + case 'vjournal': + $props = & $journalProps; + $subComps = array(); + $subCompProps = array(); + break; + case 'vfreebusy': + $props = & $freebusyProps; + $subComps = array(); + $subCompProps = array(); + break; + case 'vtimezone': + $props = & $timezoneProps; + $subComps = array( 'standard', 'daylight' ); + $subCompProps = & $stddghtProps; + break; + } // end switch( $compName ) + /** fix component properties */ + while( FALSE !== ( $component = $calendar->getComponent( $compName ))) { + $child = $components->addChild( $compName ); + $properties = $child->addChild( 'properties' ); + $langComp = $component->getConfig( 'language' ); + foreach( $props as $prop ) { + switch( $prop ) { + case 'attach': // may occur multiple times, below + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'attendee': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); + } + break; + case 'exdate': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; + unset( $content['params']['VALUE'] ); + foreach( $content['value'] as & $exDate ) { + if( ( isset( $exDate['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $exDate['tz'] ) && + ( 'Z' != $exDate['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $exDate['tz'] ) ? $exDate['tz'] : $content['params']['TZID']; + $date = mktime( (int) $exDate['hour'], + (int) $exDate['min'], + (int) ($exDate['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $exDate['month'], + (int) $exDate['day'], + (int) $exDate['year'] ); + unset( $exDate['tz'] ); + $exDate = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $exDate['unparsedtext'] ); + } + } + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'freebusy': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] ); + break; + case 'request-status': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] ); + } + break; + case 'rdate': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + $type = 'date-time'; + if( isset( $content['params']['VALUE'] )) { + if( 'DATE' == $content['params']['VALUE'] ) + $type = 'date'; + elseif( 'PERIOD' == $content['params']['VALUE'] ) + $type = 'period'; + } + if( 'period' == $type ) { + foreach( $content['value'] as & $rDates ) { + if( ( isset( $rDates[0]['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $rDates[0]['tz'] ) && + ( 'Z' != $rDates[0]['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $rDates[0]['tz'] ) ? $rDates[0]['tz'] : $content['params']['TZID']; + $date = mktime( (int) $rDates[0]['hour'], + (int) $rDates[0]['min'], + (int) ($rDates[0]['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $rDates[0]['month'], + (int) $rDates[0]['day'], + (int) $rDates[0]['year'] ); + unset( $rDates[0]['tz'] ); + $rDates[0] = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $rDates[0]['unparsedtext'] ); + } + if( isset( $rDates[1]['year'] )) { + if( ( isset( $rDates[1]['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $rDates[1]['tz'] ) && + ( 'Z' != $rDates[1]['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $rDates[1]['tz'] ) ? $rDates[1]['tz'] : $content['params']['TZID']; + $date = mktime( (int) $rDates[1]['hour'], + (int) $rDates[1]['min'], + (int) ($rDates[1]['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $rDates[1]['month'], + (int) $rDates[1]['day'], + (int) $rDates[1]['year'] ); + unset( $rDates[1]['tz'] ); + $rDates[1] = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $rDates[1]['unparsedtext'] ); + } + } + } + } + elseif( 'date-time' == $type ) { + foreach( $content['value'] as & $rDate ) { + if( ( isset( $rDate['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $rDate['tz'] ) && + ( 'Z' != $rDate['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $rDate['tz'] ) ? $rDate['tz'] : $content['params']['TZID']; + $date = mktime( (int) $rDate['hour'], + (int) $rDate['min'], + (int) ($rDate['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $rDate['month'], + (int) $rDate['day'], + (int) $rDate['year'] ); + unset( $rDate['tz'] ); + $rDate = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $rDate['unparsedtext'] ); + } + } + } + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'categories': + case 'comment': + case 'contact': + case 'description': + case 'related-to': + case 'resources': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'x-prop': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); + break; + case 'created': // single occurence below, if set + case 'completed': + case 'dtstamp': + case 'last-modified': + $utcDate = TRUE; + case 'dtstart': + case 'dtend': + case 'due': + case 'recurrence-id': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) { + $type = 'date'; + unset( $content['value']['hour'], $content['value']['min'], $content['value']['sec'] ); + } + else { + $type = 'date-time'; + if( isset( $utcDate ) && !isset( $content['value']['tz'] )) + $content['value']['tz'] = 'Z'; + if( ( isset( $content['value']['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $content['value']['tz'] ) && + ( 'Z' != $content['value']['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $content['value']['tz'] ) ? $content['value']['tz'] : $content['params']['TZID']; + $date = mktime( (int) $content['value']['hour'], + (int) $content['value']['min'], + (int) ($content['value']['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $content['value']['month'], + (int) $content['value']['day'], + (int) $content['value']['year'] ); + unset( $content['value']['tz'], $content['params']['TZID'] ); + $content['value'] = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $content['value']['unparsedtext'] ); + } + elseif( isset( $content['value']['tz'] ) && !empty( $content['value']['tz'] ) && + ( 'Z' != $content['value']['tz'] ) && !isset( $content['params']['TZID'] )) { + $content['params']['TZID'] = $content['value']['tz']; + unset( $content['value']['tz'] ); + } + } + unset( $content['params']['VALUE'] ); + if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] )) + unset( $content['params']['TZID'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + unset( $utcDate ); + break; + case 'duration': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); + break; + case 'rrule': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); + break; + case 'class': + case 'location': + case 'status': + case 'summary': + case 'transp': + case 'tzid': + case 'uid': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'geo': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] ); + break; + case 'organizer': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); + } + break; + case 'percent-complete': + case 'priority': + case 'sequence': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); + break; + case 'tzurl': + case 'url': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] ); + break; + } // end switch( $prop ) + } // end foreach( $props as $prop ) + /** fix subComponent properties, if any */ + foreach( $subComps as $subCompName ) { + while( FALSE !== ( $subcomp = $component->getComponent( $subCompName ))) { + $child2 = $child->addChild( $subCompName ); + $properties = $child2->addChild( 'properties' ); + $langComp = $subcomp->getConfig( 'language' ); + foreach( $subCompProps as $prop ) { + switch( $prop ) { + case 'attach': // may occur multiple times, below + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'attendee': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); + } + break; + case 'comment': + case 'tzname': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if( !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'rdate': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + $type = 'date-time'; + if( isset( $content['params']['VALUE'] )) { + if( 'DATE' == $content['params']['VALUE'] ) + $type = 'date'; + elseif( 'PERIOD' == $content['params']['VALUE'] ) + $type = 'period'; + } + if( 'period' == $type ) { + foreach( $content['value'] as & $rDates ) { + if( ( isset( $rDates[0]['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $rDates[0]['tz'] ) && + ( 'Z' != $rDates[0]['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $rDates[0]['tz'] ) ? $rDates[0]['tz'] : $content['params']['TZID']; + $date = mktime( (int) $rDates[0]['hour'], + (int) $rDates[0]['min'], + (int) ($rDates[0]['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $rDates[0]['month'], + (int) $rDates[0]['day'], + (int) $rDates[0]['year'] ); + unset( $rDates[0]['tz'] ); + $rDates[0] = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $rDates[0]['unparsedtext'] ); + } + if( isset( $rDates[1]['year'] )) { + if( ( isset( $rDates[1]['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $rDates[1]['tz'] ) && + ( 'Z' != $rDates[1]['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $rDates[1]['tz'] ) ? $rDates[1]['tz'] : $content['params']['TZID']; + $date = mktime( (int) $rDates[1]['hour'], + (int) $rDates[1]['min'], + (int) ($rDates[1]['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $rDates[1]['month'], + (int) $rDates[1]['day'], + (int) $rDates[1]['year'] ); + unset( $rDates[1]['tz'] ); + $rDates[1] = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $rDates[1]['unparsedtext'] ); + } + } + } + } + elseif( 'date-time' == $type ) { + foreach( $content['value'] as & $rDate ) { + if( ( isset( $rDate['tz'] ) && // fix UTC-date if offset set + iCalUtilityFunctions::_isOffset( $rDate['tz'] ) && + ( 'Z' != $rDate['tz'] )) + || ( isset( $content['params']['TZID'] ) && + iCalUtilityFunctions::_isOffset( $content['params']['TZID'] ) && + ( 'Z' != $content['params']['TZID'] ))) { + $offset = isset( $rDate['tz'] ) ? $rDate['tz'] : $content['params']['TZID']; + $date = mktime( (int) $rDate['hour'], + (int) $rDate['min'], + (int) ($rDate['sec'] + iCalUtilityFunctions::_tz2offset( $offset )), + (int) $rDate['month'], + (int) $rDate['day'], + (int) $rDate['year'] ); + unset( $rDate['tz'] ); + $rDate = iCalUtilityFunctions::_date_time_string( date( 'YmdTHis\Z', $date ), 6 ); + unset( $rDate['unparsedtext'] ); + } + } + } + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'x-prop': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); + break; + case 'action': // single occurence below, if set + case 'description': + case 'summary': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'dtstart': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time + _addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] ); + } + break; + case 'duration': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); + break; + case 'repeat': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); + break; + case 'trigger': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['value']['year'] ) && + isset( $content['value']['month'] ) && + isset( $content['value']['day'] )) + $type = 'date-time'; + else + $type = 'duration'; + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'tzoffsetto': + case 'tzoffsetfrom': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] ); + break; + case 'rrule': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); + break; + } // switch( $prop ) + } // end foreach( $subCompProps as $prop ) + } // end while( FALSE !== ( $subcomp = $component->getComponent( subCompName ))) + } // end foreach( $subCombs as $subCompName ) + } // end while( FALSE !== ( $component = $calendar->getComponent( $compName ))) + } // end foreach( $comps as $compName) + return $xml->asXML(); +} +/** + * Add children to a SimpleXMLelement + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.1 - 2012-01-16 + * @param object $parent, reference to a SimpleXMLelement node + * @param string $name, new element node name + * @param string $type, content type, subelement(-s) name + * @param string $content, new subelement content + * @param array $params, new element 'attributes' + * @return void + */ +function _addXMLchild( & $parent, $name, $type, $content, $params=array()) { + /** create new child node */ + $child = $parent->addChild( strtolower( $name )); + /** fix attributes */ + if( is_array( $content ) && isset( $content['fbtype'] )) { + $params['FBTYPE'] = $content['fbtype']; + unset( $content['fbtype'] ); + } + if( isset( $params['VALUE'] )) + unset( $params['VALUE'] ); + if(( 'trigger' == $name ) && ( 'duration' == $type ) && ( TRUE !== $content['relatedStart'] )) + $params['RELATED'] = 'END'; + if( !empty( $params )) { + $parameters = $child->addChild( 'parameters' ); + foreach( $params as $param => $parVal ) { + $param = strtolower( $param ); + if( 'x-' == substr( $param, 0, 2 )) { + $p1 = $parameters->addChild( $param ); + $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal )); + } + else { + $p1 = $parameters->addChild( $param ); + switch( $param ) { + case 'altrep': + case 'dir': $ptype = 'uri'; break; + case 'delegated-from': + case 'delegated-to': + case 'member': + case 'sent-by': $ptype = 'cal-address'; break; + case 'rsvp': $ptype = 'boolean'; break ; + default: $ptype = 'text'; break; + } + if( is_array( $parVal )) { + foreach( $parVal as $pV ) + $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV )); + } + else + $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal )); + } + } + } + if( empty( $content ) && ( '0' != $content )) + return; + /** store content */ + switch( $type ) { + case 'binary': + $v = $child->addChild( $type, $content ); + break; + case 'boolean': + break; + case 'cal-address': + $v = $child->addChild( $type, $content ); + break; + case 'date': + if( array_key_exists( 'year', $content )) + $content = array( $content ); + foreach( $content as $date ) { + $str = sprintf( '%04d-%02d-%02d', $date['year'], $date['month'], $date['day'] ); + $v = $child->addChild( $type, $str ); + } + break; + case 'date-time': + if( array_key_exists( 'year', $content )) + $content = array( $content ); + foreach( $content as $dt ) { + if( !isset( $dt['hour'] )) $dt['hour'] = 0; + if( !isset( $dt['min'] )) $dt['min'] = 0; + if( !isset( $dt['sec'] )) $dt['sec'] = 0; + $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] ); + if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] )) + $str .= 'Z'; + $v = $child->addChild( $type, $str ); + } + break; + case 'duration': + $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : ''; + $v = $child->addChild( $type, $output.iCalUtilityFunctions::_format_duration( $content ) ); + break; + case 'geo': + $v1 = $child->addChild( 'latitude', number_format( (float) $content['latitude'], 6, '.', '' )); + $v1 = $child->addChild( 'longitude', number_format( (float) $content['longitude'], 6, '.', '' )); + break; + case 'integer': + $v = $child->addChild( $type, $content ); + break; + case 'period': + if( !is_array( $content )) + break; + foreach( $content as $period ) { + $v1 = $child->addChild( $type ); + $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $period[0]['year'], $period[0]['month'], $period[0]['day'], $period[0]['hour'], $period[0]['min'], $period[0]['sec'] ); + if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] )) + $str .= 'Z'; + $v2 = $v1->addChild( 'start', $str ); + if( array_key_exists( 'year', $period[1] )) { + $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $period[1]['year'], $period[1]['month'], $period[1]['day'], $period[1]['hour'], $period[1]['min'], $period[1]['sec'] ); + if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] )) + $str .= 'Z'; + $v2 = $v1->addChild( 'end', $str ); + } + else + $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_format_duration( $period[1] )); + } + break; + case 'recur': + foreach( $content as $rulelabel => $rulevalue ) { + $rulelabel = strtolower( $rulelabel ); + switch( $rulelabel ) { + case 'until': + if( isset( $rulevalue['hour'] )) + $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02dZ', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'], $rulevalue['hour'], $rulevalue['min'], $rulevalue['sec'] ); + else + $str = sprintf( '%04d-%02d-%02d', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'] ); + $v = $child->addChild( $rulelabel, $str ); + break; + case 'bysecond': + case 'byminute': + case 'byhour': + case 'bymonthday': + case 'byyearday': + case 'byweekno': + case 'bymonth': + case 'bysetpos': { + if( is_array( $rulevalue )) { + foreach( $rulevalue as $vix => $valuePart ) + $v = $child->addChild( $rulelabel, $valuePart ); + } + else + $v = $child->addChild( $rulelabel, $rulevalue ); + break; + } + case 'byday': { + if( isset( $rulevalue['DAY'] )) { + $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : ''; + $str .= $rulevalue['DAY']; + $p = $child->addChild( $rulelabel, $str ); + } + else { + foreach( $rulevalue as $valuePart ) { + if( isset( $valuePart['DAY'] )) { + $str = ( isset( $valuePart[0] )) ? $valuePart[0] : ''; + $str .= $valuePart['DAY']; + $p = $child->addChild( $rulelabel, $str ); + } + else + $p = $child->addChild( $rulelabel, $valuePart ); + } + } + break; + } + case 'freq': + case 'count': + case 'interval': + case 'wkst': + default: + $p = $child->addChild( $rulelabel, $rulevalue ); + break; + } // end switch( $rulelabel ) + } // end foreach( $content as $rulelabel => $rulevalue ) + break; + case 'rstatus': + $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', '')); + $v = $child->addChild( 'description', htmlspecialchars( $content['text'] )); + if( isset( $content['extdata'] )) + $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] )); + break; + case 'text': + if( !is_array( $content )) + $content = array( $content ); + foreach( $content as $part ) + $v = $child->addChild( $type, htmlspecialchars( $part )); + break; + case 'time': + break; + case 'uri': + $v = $child->addChild( $type, $content ); + break; + case 'utc-offset': + if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) { + $str = substr( $content, 0, 1 ); + $content = substr( $content, 1 ); + } + else + $str = '+'; + $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 ); + if( 4 < strlen( $content )) + $str .= ':'.substr( $content, 4 ); + $v = $child->addChild( $type, $str ); + break; + case 'unknown': + default: + if( is_array( $content )) + $content = implode( '', $content ); + $v = $child->addChild( 'unknown', htmlspecialchars( $content )); + break; + } +} +/** + * parse xml string into iCalcreator instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.2 - 2012-01-31 + * @param string $xmlstr + * @param array $iCalcfg iCalcreator config array (opt) + * @return mixed iCalcreator instance or FALSE on error + */ +function & XMLstr2iCal( $xmlstr, $iCalcfg=array()) { + libxml_use_internal_errors( TRUE ); + $xml = simplexml_load_string( $xmlstr ); + if( !$xml ) { + $str = ''; + $return = FALSE; + foreach( libxml_get_errors() as $error ) { + switch ( $error->level ) { + case LIBXML_ERR_FATAL: $str .= ' FATAL '; break; + case LIBXML_ERR_ERROR: $str .= ' ERROR '; break; + case LIBXML_ERR_WARNING: + default: $str .= ' WARNING '; break; + } + $str .= PHP_EOL.'Error when loading XML'; + if( !empty( $error->file )) + $str .= ', file:'.$error->file.', '; + $str .= ', line:'.$error->line; + $str .= ', ('.$error->code.') '.$error->message; + } + error_log( $str ); + if( LIBXML_ERR_WARNING != $error->level ) + return $return; + libxml_clear_errors(); + } + return xml2iCal( $xml, $iCalcfg ); +} +/** + * parse xml file into iCalcreator instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.2 - 2012-01-20 + * @param string $xmlfile + * @param array$iCalcfg iCalcreator config array (opt) + * @return mixediCalcreator instance or FALSE on error + */ +function & XMLfile2iCal( $xmlfile, $iCalcfg=array()) { + libxml_use_internal_errors( TRUE ); + $xml = simplexml_load_file( $xmlfile ); + if( !$xml ) { + $str = ''; + foreach( libxml_get_errors() as $error ) { + switch ( $error->level ) { + case LIBXML_ERR_FATAL: $str .= 'FATAL '; break; + case LIBXML_ERR_ERROR: $str .= 'ERROR '; break; + case LIBXML_ERR_WARNING: + default: $str .= 'WARNING '; break; + } + $str .= 'Failed loading XML'.PHP_EOL; + if( !empty( $error->file )) + $str .= ' file:'.$error->file.', '; + $str .= 'line:'.$error->line.PHP_EOL; + $str .= '('.$error->code.') '.$error->message.PHP_EOL; + } + error_log( $str ); + if( LIBXML_ERR_WARNING != $error->level ) + return FALSE; + libxml_clear_errors(); + } + return xml2iCal( $xml, $iCalcfg ); +} +/** + * parse SimpleXMLElement xCal into iCalcreator instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.2 - 2012-01-27 + * @param object $xmlobj SimpleXMLElement + * @param array $iCalcfg iCalcreator config array (opt) + * @return mixed iCalcreator instance or FALSE on error + */ +function & XML2iCal( $xmlobj, $iCalcfg=array()) { + $iCal = new vcalendar( $iCalcfg ); + foreach( $xmlobj->children() as $icalendar ) { // vcalendar + foreach( $icalendar->children() as $calPart ) { // calendar properties and components + if( 'components' == $calPart->getName()) { + foreach( $calPart->children() as $component ) { // single components + if( 0 < $component->count()) + _getXMLComponents( $iCal, $component ); + } + } + elseif(( 'properties' == $calPart->getName()) && ( 0 < $calPart->count())) { + foreach( $calPart->children() as $calProp ) { // calendar properties + $propName = $calProp->getName(); + if(( 'calscale' != $propName ) && ( 'method' != $propName ) && ( 'x-' != substr( $propName,0,2 ))) + continue; + $params = array(); + foreach( $calProp->children() as $calPropElem ) { // single calendar property + if( 'parameters' == $calPropElem->getName()) + $params = _getXMLParams( $calPropElem ); + else + $iCal->setProperty( $propName, reset( $calPropElem ), $params ); + } // end foreach( $calProp->children() as $calPropElem ) + } // end foreach( $calPart->properties->children() as $calProp ) + } // end if( 0 < $calPart->properties->count()) + } // end foreach( $icalendar->children() as $calPart ) + } // end foreach( $xmlobj->children() as $icalendar ) + return $iCal; +} +/** + * parse SimpleXMLElement xCal property parameters and return iCalcreator property parameter array + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.2 - 2012-01-15 + * @param object $parameters SimpleXMLElement + * @return array iCalcreator property parameter array + */ +function _getXMLParams( & $parameters ) { + if( 1 > $parameters->count()) + return array(); + $params = array(); + foreach( $parameters->children() as $parameter ) { // single parameter key + $key = strtoupper( $parameter->getName()); + $value = array(); + foreach( $parameter->children() as $paramValue ) // skip parameter value type + $value[] = reset( $paramValue ); + if( 2 > count( $value )) + $params[$key] = html_entity_decode( reset( $value )); + else + $params[$key] = $value; + } + return $params; +} +/** + * parse SimpleXMLElement xCal components, create iCalcreator component and update + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.2 - 2012-01-15 + * @param array $iCal iCalcreator calendar instance + * @param object $component SimpleXMLElement + * @return void + */ +function _getXMLComponents( & $iCal, & $component ) { + $compName = $component->getName(); + $comp = & $iCal->newComponent( $compName ); + $subComponents = array( 'valarm', 'standard', 'daylight' ); + foreach( $component->children() as $compPart ) { // properties and (opt) subComponents + if( 1 > $compPart->count()) + continue; + if( in_array( $compPart->getName(), $subComponents )) + _getXMLComponents( $comp, $compPart ); + elseif( 'properties' == $compPart->getName()) { + foreach( $compPart->children() as $property ) // properties as single property + _getXMLProperties( $comp, $property ); + } + } // end foreach( $component->children() as $compPart ) +} +/** + * parse SimpleXMLElement xCal property, create iCalcreator component property + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.2 - 2012-01-27 + * @param array $iCal iCalcreator calendar instance + * @param object $component SimpleXMLElement + * @return void + */ +function _getXMLProperties( & $iCal, & $property ) { + $propName = $property->getName(); + $value = $params = array(); + $valueType = ''; + foreach( $property->children() as $propPart ) { // calendar property parameters (opt) and value(-s) + $valueType = $propPart->getName(); + if( 'parameters' == $valueType) { + $params = _getXMLParams( $propPart ); + continue; + } + switch( $valueType ) { + case 'binary': + $value = reset( $propPart ); + break; + case 'boolean': + break; + case 'cal-address': + $value = reset( $propPart ); + break; + case 'date': + $params['VALUE'] = 'DATE'; + case 'date-time': + if(( 'exdate' == $propName ) || ( 'rdate' == $propName )) + $value[] = reset( $propPart ); + else + $value = reset( $propPart ); + break; + case 'duration': + $value = reset( $propPart ); + break; +// case 'geo': + case 'latitude': + case 'longitude': + $value[$valueType] = reset( $propPart ); + break; + case 'integer': + $value = reset( $propPart ); + break; + case 'period': + if( 'rdate' == $propName ) + $params['VALUE'] = 'PERIOD'; + $pData = array(); + foreach( $propPart->children() as $periodPart ) + $pData[] = reset( $periodPart ); + if( !empty( $pData )) + $value[] = $pData; + break; +// case 'rrule': + case 'freq': + case 'count': + case 'until': + case 'interval': + case 'wkst': + $value[$valueType] = reset( $propPart ); + break; + case 'bysecond': + case 'byminute': + case 'byhour': + case 'bymonthday': + case 'byyearday': + case 'byweekno': + case 'bymonth': + case 'bysetpos': + $value[$valueType][] = reset( $propPart ); + break; + case 'byday': + $byday = reset( $propPart ); + if( 2 == strlen( $byday )) + $value[$valueType][] = array( 'DAY' => $byday ); + else { + $day = substr( $byday, -2 ); + $key = substr( $byday, 0, ( strlen( $byday ) - 2 )); + $value[$valueType][] = array( $key, 'DAY' => $day ); + } + break; +// case 'rstatus': + case 'code': + $value[0] = reset( $propPart ); + break; + case 'description': + $value[1] = reset( $propPart ); + break; + case 'data': + $value[2] = reset( $propPart ); + break; + case 'text': + $text = str_replace( array( "\r\n", "\n\r", "\r", "\n"), '\n', reset( $propPart )); + $value['text'][] = html_entity_decode( $text ); + break; + case 'time': + break; + case 'uri': + $value = reset( $propPart ); + break; + case 'utc-offset': + $value = str_replace( ':', '', reset( $propPart )); + break; + case 'unknown': + default: + $value = html_entity_decode( reset( $propPart )); + break; + } // end switch( $valueType ) + } // end foreach( $property->children() as $propPart ) + if( 'freebusy' == $propName ) { + $fbtype = $params['FBTYPE']; + unset( $params['FBTYPE'] ); + $iCal->setProperty( $propName, $fbtype, $value, $params ); + } + elseif( 'geo' == $propName ) + $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params ); + elseif( 'request-status' == $propName ) { + if( !isset( $value[2] )) + $value[2] = FALSE; + $iCal->setProperty( $propName, $value[0], $value[1], $value[2], $params ); + } + else { + if( isset( $value['text'] ) && is_array( $value['text'] )) { + if(( 'categories' == $propName ) || ( 'resources' == $propName )) + $value = $value['text']; + else + $value = reset( $value['text'] ); + } + $iCal->setProperty( $propName, $value, $params ); + } +} +/** + * Additional functions to use with vtimezone components + * For use with + * iCalcreator (kigkonsult.se/iCalcreator/index.php) + * copyright (c) 2011 Yitzchok Lavi + * icalcreator@onebigsystem.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/** + * Additional functions to use with vtimezone components + * + * Before calling the functions, set time zone 'GMT' ('date_default_timezone_set')! + * + * @author Yitzchok Lavi + * adjusted for iCalcreator Kjell-Inge Gustafsson, kigkonsult + * @version 1.0.2 - 2011-02-24 + * + */ +/** + * Returns array with the offset information from UTC for a (UTC) datetime/timestamp in the + * timezone, according to the VTIMEZONE information in the input array. + * + * $param array $timezonesarray, output from function getTimezonesAsDateArrays (below) + * $param string $tzid, time zone identifier + * $param mixed $timestamp, timestamp or a UTC datetime (in array format) + * @return array, time zone data with keys for 'offsetHis', 'offsetSec' and 'tzname' + * + */ +function getTzOffsetForDate($timezonesarray, $tzid, $timestamp) { + if( is_array( $timestamp )) { +//$disp = sprintf( '%04d%02d%02d %02d%02d%02d', $timestamp['year'], $timestamp['month'], $timestamp['day'], $timestamp['hour'], $timestamp['min'], $timestamp['sec'] ); + $timestamp = gmmktime( + $timestamp['hour'], + $timestamp['min'], + $timestamp['sec'], + $timestamp['month'], + $timestamp['day'], + $timestamp['year'] + ) ; +//echo ' '."\n".' '.$timestamp.''.$disp.' '."\n".' '; // test ### + } + $tzoffset = array(); + // something to return if all goes wrong (such as if $tzid doesn't find us an array of dates) + $tzoffset['offsetHis'] = '+0000'; + $tzoffset['offsetSec'] = 0; + $tzoffset['tzname'] = '?'; + if( !isset( $timezonesarray[$tzid] )) + return $tzoffset; + $tzdatearray = $timezonesarray[$tzid]; + if ( is_array($tzdatearray) ) { + sort($tzdatearray); // just in case + if ( $timestamp < $tzdatearray[0]['timestamp'] ) { + // our date is before the first change + $tzoffset['offsetHis'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; + $tzoffset['offsetSec'] = $tzdatearray[0]['tzbefore']['offsetSec'] ; + $tzoffset['tzname'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; // we don't know the tzname in this case + } elseif ( $timestamp >= $tzdatearray[count($tzdatearray)-1]['timestamp'] ) { + // our date is after the last change (we do this so our scan can stop at the last record but one) + $tzoffset['offsetHis'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetHis'] ; + $tzoffset['offsetSec'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetSec'] ; + $tzoffset['tzname'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['tzname'] ; + } else { + // our date somewhere in between + // loop through the list of dates and stop at the one where the timestamp is before our date and the next one is after it + // we don't include the last date in our loop as there isn't one after it to check + for ( $i = 0 ; $i <= count($tzdatearray)-2 ; $i++ ) { + if(( $timestamp >= $tzdatearray[$i]['timestamp'] ) && ( $timestamp < $tzdatearray[$i+1]['timestamp'] )) { + $tzoffset['offsetHis'] = $tzdatearray[$i]['tzafter']['offsetHis'] ; + $tzoffset['offsetSec'] = $tzdatearray[$i]['tzafter']['offsetSec'] ; + $tzoffset['tzname'] = $tzdatearray[$i]['tzafter']['tzname'] ; + break; + } + } + } + } + return $tzoffset; +} +/** + * Returns an array containing all the timezone data in the vcalendar object + * + * @param object $vcalendar, iCalcreator calendar instance + * @return array, time zone transition timestamp, array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname) + * based on the timezone data in the vcalendar object + * + */ +function getTimezonesAsDateArrays($vcalendar) { + $timezonedata = array(); + while( $vtz = $vcalendar->getComponent( 'vtimezone' )) { + $tzid = $vtz->getProperty('tzid'); + $alltzdates = array(); + while ( $vtzc = $vtz->getComponent( 'standard' )) { + $newtzdates = expandTimezoneDates($vtzc); + $alltzdates = array_merge($alltzdates, $newtzdates); + } + while ( $vtzc = $vtz->getComponent( 'daylight' )) { + $newtzdates = expandTimezoneDates($vtzc); + $alltzdates = array_merge($alltzdates, $newtzdates); + } + sort($alltzdates); + $timezonedata[$tzid] = $alltzdates; + } + return $timezonedata; +} +/** + * Returns an array containing time zone data from vtimezone standard/daylight instances + * + * @param object $vtzc, an iCalcreator calendar standard/daylight instance + * @return array, time zone data; array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname) + * + */ +function expandTimezoneDates($vtzc) { + $tzdates = array(); + // prepare time zone "description" to attach to each change + $tzbefore = array(); + $tzbefore['offsetHis'] = $vtzc->getProperty('tzoffsetfrom') ; + $tzbefore['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzbefore['offsetHis']); + if(( '-' != substr( (string) $tzbefore['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzbefore['offsetSec'], 0, 1 ))) + $tzbefore['offsetSec'] = '+'.$tzbefore['offsetSec']; + $tzafter = array(); + $tzafter['offsetHis'] = $vtzc->getProperty('tzoffsetto') ; + $tzafter['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzafter['offsetHis']); + if(( '-' != substr( (string) $tzafter['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzafter['offsetSec'], 0, 1 ))) + $tzafter['offsetSec'] = '+'.$tzafter['offsetSec']; + if( FALSE === ( $tzafter['tzname'] = $vtzc->getProperty('tzname'))) + $tzafter['tzname'] = $tzafter['offsetHis']; + // find out where to start from + $dtstart = $vtzc->getProperty('dtstart'); + $dtstarttimestamp = mktime( + $dtstart['hour'], + $dtstart['min'], + $dtstart['sec'], + $dtstart['month'], + $dtstart['day'], + $dtstart['year'] + ) ; + if( !isset( $dtstart['unparsedtext'] )) // ?? + $dtstart['unparsedtext'] = sprintf( '%04d%02d%02dT%02d%02d%02d', $dtstart['year'], $dtstart['month'], $dtstart['day'], $dtstart['hour'], $dtstart['min'], $dtstart['sec'] ); + if ( $dtstarttimestamp == 0 ) { + // it seems that the dtstart string may not have parsed correctly + // let's set a timestamp starting from 1902, using the time part of the original string + // so that the time will change at the right time of day + // at worst we'll get midnight again + $origdtstartsplit = explode('T',$dtstart['unparsedtext']) ; + $dtstarttimestamp = strtotime("19020101",0); + $dtstarttimestamp = strtotime($origdtstartsplit[1],$dtstarttimestamp); + } + // the date (in dtstart and opt RDATE/RRULE) is ALWAYS LOCAL (not utc!!), adjust from 'utc' to 'local' timestamp + $diff = -1 * $tzbefore['offsetSec']; + $dtstarttimestamp += $diff; + // add this (start) change to the array of changes + $tzdates[] = array( + 'timestamp' => $dtstarttimestamp, + 'tzbefore' => $tzbefore, + 'tzafter' => $tzafter + ); + $datearray = getdate($dtstarttimestamp); + // save original array to use time parts, because strtotime (used below) apparently loses the time + $changetime = $datearray ; + // generate dates according to an RRULE line + $rrule = $vtzc->getProperty('rrule') ; + if ( is_array($rrule) ) { + if ( $rrule['FREQ'] == 'YEARLY' ) { + // calculate transition dates starting from DTSTART + $offsetchangetimestamp = $dtstarttimestamp; + // calculate transition dates until 10 years in the future + $stoptimestamp = strtotime("+10 year",time()); + // if UNTIL is set, calculate until then (however far ahead) + if ( isset( $rrule['UNTIL'] ) && ( $rrule['UNTIL'] != '' )) { + $stoptimestamp = mktime( + $rrule['UNTIL']['hour'], + $rrule['UNTIL']['min'], + $rrule['UNTIL']['sec'], + $rrule['UNTIL']['month'], + $rrule['UNTIL']['day'], + $rrule['UNTIL']['year'] + ) ; + } + $count = 0 ; + $stopcount = isset( $rrule['COUNT'] ) ? $rrule['COUNT'] : 0 ; + $daynames = array( + 'SU' => 'Sunday', + 'MO' => 'Monday', + 'TU' => 'Tuesday', + 'WE' => 'Wednesday', + 'TH' => 'Thursday', + 'FR' => 'Friday', + 'SA' => 'Saturday' + ); + // repeat so long as we're between DTSTART and UNTIL, or we haven't prepared COUNT dates + while ( $offsetchangetimestamp < $stoptimestamp && ( $stopcount == 0 || $count < $stopcount ) ) { + // break up the timestamp into its parts + $datearray = getdate($offsetchangetimestamp); + if ( isset( $rrule['BYMONTH'] ) && ( $rrule['BYMONTH'] != 0 )) { + // set the month + $datearray['mon'] = $rrule['BYMONTH'] ; + } + if ( isset( $rrule['BYMONTHDAY'] ) && ( $rrule['BYMONTHDAY'] != 0 )) { + // set specific day of month + $datearray['mday'] = $rrule['BYMONTHDAY']; + } elseif ( is_array($rrule['BYDAY']) ) { + // find the Xth WKDAY in the month + // the starting point for this process is the first of the month set above + $datearray['mday'] = 1 ; + // turn $datearray as it is now back into a timestamp + $offsetchangetimestamp = mktime( + $datearray['hours'], + $datearray['minutes'], + $datearray['seconds'], + $datearray['mon'], + $datearray['mday'], + $datearray['year'] + ); + if ($rrule['BYDAY'][0] > 0) { + // to find Xth WKDAY in month, we find last WKDAY in month before + // we do that by finding first WKDAY in this month and going back one week + // then we add X weeks (below) + $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp); + $offsetchangetimestamp = strtotime("-1 week",$offsetchangetimestamp); + } else { + // to find Xth WKDAY before the end of the month, we find the first WKDAY in the following month + // we do that by going forward one month and going to WKDAY there + // then we subtract X weeks (below) + $offsetchangetimestamp = strtotime("+1 month",$offsetchangetimestamp); + $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp); + } + // now move forward or back the appropriate number of weeks, into the month we want + $offsetchangetimestamp = strtotime($rrule['BYDAY'][0] . " week",$offsetchangetimestamp); + $datearray = getdate($offsetchangetimestamp); + } + // convert the date parts back into a timestamp, setting the time parts according to the + // original time data which we stored + $offsetchangetimestamp = mktime( + $changetime['hours'], + $changetime['minutes'], + $changetime['seconds'] + $diff, + $datearray['mon'], + $datearray['mday'], + $datearray['year'] + ); + // add this change to the array of changes + $tzdates[] = array( + 'timestamp' => $offsetchangetimestamp, + 'tzbefore' => $tzbefore, + 'tzafter' => $tzafter + ); + // update counters (timestamp and count) + $offsetchangetimestamp = strtotime("+" . (( isset( $rrule['INTERVAL'] ) && ( $rrule['INTERVAL'] != 0 )) ? $rrule['INTERVAL'] : 1 ) . " year",$offsetchangetimestamp); + $count += 1 ; + } + } + } + // generate dates according to RDATE lines + while ($rdates = $vtzc->getProperty('rdate')) { + if ( is_array($rdates) ) { + + foreach ( $rdates as $rdate ) { + // convert the explicit change date to a timestamp + $offsetchangetimestamp = mktime( + $rdate['hour'], + $rdate['min'], + $rdate['sec'] + $diff, + $rdate['month'], + $rdate['day'], + $rdate['year'] + ) ; + // add this change to the array of changes + $tzdates[] = array( + 'timestamp' => $offsetchangetimestamp, + 'tzbefore' => $tzbefore, + 'tzafter' => $tzafter + ); + } + } + } + return $tzdates; +} +?> \ No newline at end of file diff --git a/dav/iCalcreator/lgpl.txt b/dav/iCalcreator/lgpl.txt new file mode 100755 index 000000000..5ab7695ab --- /dev/null +++ b/dav/iCalcreator/lgpl.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/dav/iCalcreator/releaseNotes-2.12.txt b/dav/iCalcreator/releaseNotes-2.12.txt new file mode 100755 index 000000000..2a145dd34 --- /dev/null +++ b/dav/iCalcreator/releaseNotes-2.12.txt @@ -0,0 +1,88 @@ +2.10.24 ###################### +upd returnCalendar, only create header 'content-length' when gziping output + +2.10.26 ###################### +Concatenate iCalcreator, utilityFunction and helper functions in iCalcreator.class.php file + +2.10.27 ###################### +parsing dates with extended (MS outlook) time zones + +2.10.28 ###################### +bug in function selectComponents, (continue if) missing dtstart + +2.10.29 ###################### +new function ms2phpTZ, (very) simple mapping of MS outlook time zones to PHP + +2.10.30 ###################### thanks Yitzchok +external (iCalUtilityFunctions) time zone helper contributions:getTimezonesAsDateArrays (expandTimezoneDates) and getTzOffsetForDate functions + +2.11.1 ###################### +creation of rfc6321 XML output, input iCalcreator instance +new (helper) function iCal2XML ('inner' _addXMLchild), + +2.11.2 ###################### +parse of rfc6321 XML input (string/file), output iCalcreator instance +new (helper) functions: XMLstr2iCal, XMLfile2iCal, XML2iCal +('inner' functions:_getXMLParams, _getXMLComponents, _getXMLProperties) + +2.11.3 ###################### +bug in getProperty, management of properties with multiple ocurrences + +2.11.4 ###################### +bug in _tz2offset, plus/minus error + +2.11.5 ###################### +updated _setDate _setDate2, setExdate, setRdate, utc offset management, Z-suffix + +2.11.7 ###################### +bug in function getConfig, 'directory' or 'filename' couldn't accept '0' (zero) + +2.11.8 ###################### +upd createTimezone (_setTZrrule), fixing from/to UTC offset and rdate(-s) +upd functions: _format_date_time, _setDate, _setDate2, setRdate, setExdate, + _setRexrule, _isArrayDate +new function: _strDate2arr + +2.11.9 ###################### +check x-props names, start with 'x-'/'X-' + +2.11.10 ###################### +function parse, management of list content in TEXT properties + +2.11.11 ###################### +always update PRODID when (re-)setting 'unique_id' + +2.11.12 ###################### +update management of (Attendee) parameter value lists +update rfc5545 parameters with 'DQUOTE' settings +function createAttendee and _createParams + +2.11.13 ###################### +upd _size75, correct line break when property content ends with '0' at pos 76 + +2.11.14 ###################### +upd function transformDateTime, now accepting input date in array format + +2.11.15 ###################### +bug in function _setRexrule, UNTIL in DATE format + +2.11.16 ###################### +property ATTACH and large file attachments + +2.11.17 ###################### +bug in ATTENDEE, parsing error + +2.11.19 ###################### +upd using.html + +2.11.20 ###################### +upd createComponent, sorting standard/daylight subComponents + +2.11.21 ###################### +bug in selectComponents: long 'event' starting before period + +2.11.23 ###################### +bug: create FREEBUSY, empty property + +2.11.24 ###################### +bug: set/create RELATED-TO mgnt diff --git a/dav/iCalcreator/releaseSummary.txt b/dav/iCalcreator/releaseSummary.txt new file mode 100755 index 000000000..c78b19394 --- /dev/null +++ b/dav/iCalcreator/releaseSummary.txt @@ -0,0 +1,31 @@ +A major subrelease: +- Concatenate iCalcreator, utilityFunction and helper functions in iCalcreator.class.php file +- new functionality: +-- external time zone helper contributions:getTimezonesAsDateArrays + (expandTimezoneDates) and getTzOffsetForDate functions +-- create and parse of rfc6321 XML +-- ms2phpTZ, mapping of MS outlook time zones to PHP +-- createComponent, sorting standard/daylight subComponents +- uppdates: +-- parsing dates with extended (MS outlook) time zones +-- returnCalendar, only create header 'content-length' when gziping output +-- _setDate _setDate2, setExdate, setRdate, utc offset management, Z-suffix +-- createTimezone (_setTZrrule), fixing from/to UTC offset and rdate(-s) +-- checking x-props names, start with 'x-'/'X-' +-- parse, management of list content in TEXT properties +-- all rfc5545 parameters with 'DQUOTE' settings +-- transformDateTime, accepting input date in array format +-- property ATTACH and large file attachments +- fixed bugs: +-- selectComponents, (continue if) missing dtstart +-- selectComponents: long 'event' starting before period +-- getProperty, management of properties with multiple ocurrences +-- _tz2offset, plus/minus error +-- getConfig, 'directory' or 'filename' couldn't accept '0' (zero) +-- _size75, correct line break when property content ends with '0' at pos 76-- +-- _setRexrule, UNTIL in DATE format +-- ATTENDEE, parsing error +-- setFREEBUSY, empty property +-- set/create RELATED-TO mgnt +-- update of PRODID when (re-)setting 'unique_id' +- updated using manual diff --git a/dav/iCalcreator/summary.html b/dav/iCalcreator/summary.html new file mode 100755 index 000000000..40e5f6c5c --- /dev/null +++ b/dav/iCalcreator/summary.html @@ -0,0 +1,387 @@ + + + +iCalcreator 2.12 summary + + + + + + + + +

iCalcreator v2.12

+iCalcreator v2.12
+copyright (c) 2007-2012 Kjell-Inge Gustafsson, kigkonsult
+kigkonsult.se iCalcreator
+kigkonsult.se contact
+
+iCalcreator is a PHP class package managing iCal files, supporting (non-)calendar +systems and applications to process and communicate calendar information like +events, agendas, tasks, reports, totos and journaling information. +

+This is a short summary how to use iCalcreator; create, parse, edit, select and output functionality. +

+The iCalcreator package, built of a calendar class with support of a function class and helper functions, are calendar +component property oriented. Development environment is PHP version 5.x but coding is done +to meet 4.x backward compatibility and may work. Some functions requires PHP >= 5.2.0. +

+The iCalcreator main class, utility class and helper functions are included in the "iCalcreator.class.php" file. +

+More iCalcreator supplementary and "howto" information will be found at +kigkonsult.se iCalcreator implement examples and test pages. +A strong recommendation is to have the document user's manual +open in parallell when exploiting the link. + +

iCal

+A short iCal description is found at Wikipedia. If You are not familiar with iCal, read this first!
+Knowledge of calendar protocol rfc5545/rfc5546 is to recommend;
+rfc5545 + - Internet Calendaring and Scheduling Core Object Specification (iCalendar)
+rfc5546 + - iCalendar Transport-Independent Interoperability Protocol (iTIP) Scheduling Events, BusyTime, To-dos and Journal Entries
+rfc5545 and +rfc5546 +obsoletes, respectively, +rfc2445 and +rfc2446. +
+ +

xCal

+iCalcreator also supports xCal (iCal xml)
+rfc6321 + - "xCal: The XML Format for iCalendar" +
+ +

SUPPORT

+The main support channel is using iCalcreator +Sourceforge forum. +
+
+kigkonsult offer services for software support, design and development of customizations and adaptations of PHP/MySQL solutions +with a special focus on software long term utility and reliability, +supported through our agile acquire/design/transition process model. +
+ +

DONATE

+You can show your appreciation for our free software, +and can support future development by making a donation to the kigkonsult GPL/LGPL projects. +
+
+Make a donation of any size by clicking here. +Thanks in advance! +
+ +

Contact

+Use the contact page +for queries, improvement/development issues or professional support and development. +Please note that paid support or consulting service has the highest priority. +
+ +

Downloads and usage examples

+On kigkonsult.se can you download the +complete manual +and review +coding and test examples. +
+ +

INSTALL

+Unpack to any folder
+- add this folder to your include-path
+- or unpack to your application-(include)-folder
+Add "require_once '[folder/]iCalcreator.class.php';" to your php-script. +
+
+If using PHP version 5.1 or higher, the default timezone need to be set/altered, now "Europe/Stockholm", +line 50 in the iCalcreator.class.php file. +
+When creating a new calendar/component instance, review config settings. +
+
+To really boost performance, visit kigkonsult.se contact page for information. +
+
+ + +

CREATE

+ +

require_once( "iCalcreator.class.php" ); +$config = array( "unique_id" => "kigkonsult.se" ); // set a (site) unique id +$v = new vcalendar( $config ); // create a new calendar instance +$tz = "Europe/Stockholm"; // define time zone + +$v->setProperty( "method", "PUBLISH" ); // required of some calendar software +$v->setProperty( "x-wr-calname", "Calendar Sample" ); // required of some calendar software +$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // required of some calendar software +$v->setProperty( "X-WR-TIMEZONE", $tz ); // required of some calendar software +.. . +$xprops = array( "X-LIC-LOCATION" => $tz ); // required of some calendar software +iCalUtilityFunctions::createTimezone( $v, $tz, $xprops ); // create timezone component(-s) opt. 1 +.. . // based on present date +.. . +$vevent = & $v->newComponent( "vevent" ); // create an event calendar component +$vevent->setProperty( "dtstart", array( "year"=>2007, "month"=>4, "day"=>1, "hour"=>19, "min"=>0, "sec"=>0 )); +$vevent->setProperty( "dtend", array( "year"=>2007, "month"=>4, "day"=>1, "hour"=>22, "min"=>30, "sec"=>0 )); +$vevent->setProperty( "LOCATION", "Central Placa" ); // property name - case independent +$vevent->setProperty( "summary", "PHP summit" ); +$vevent->setProperty( "description", "This is a description" ); +$vevent->setProperty( "comment", "This is a comment" ); +$vevent->setProperty( "attendee", "attendee1@icaldomain.net" ); +.. . +$valarm = & $vevent->newComponent( "valarm" ); // create an event alarm +$valarm->setProperty("action", "DISPLAY" ); +$valarm->setProperty("description", $vevent->getProperty( "description" ); +.. . // reuse the event description +.. . +$d = sprintf( '%04d%02d%02d %02d%02d%02d', 2007, 3, 31, 15, 0, 0 ); +iCalUtilityFunctions::transformDateTime( $d, $tz, "UTC", "Ymd\THis\Z"); +$valarm->setProperty( "trigger", $d ); // create alarm trigger (in UTC datetime) +.. . +$vevent = & $v->newComponent( "vevent" ); // create next event calendar component +$vevent->setProperty( "dtstart", "20070401", array("VALUE" => "DATE"));// alt. date format, now for an all-day event +$vevent->setProperty( "organizer" , "boss@icaldomain.com" ); +$vevent->setProperty( "summary", "ALL-DAY event" ); +$vevent->setProperty( "description", "This is a description for an all-day event" ); +$vevent->setProperty( "resources", "COMPUTER PROJECTOR" ); +$vevent->setProperty( "rrule", array( "FREQ" => "WEEKLY", "count" => 4));// weekly, four occasions +$vevent->parse( "LOCATION:1CP Conference Room 4350" ); // supporting parse of strict rfc5545 formatted text +.. . +.. .// all calendar components are described in rfc5545 +.. .// a complete iCalcreator function list (ex. setProperty) in iCalcreator manual +.. . +iCalUtilityFunctions::createTimezone( $v, $tz, $xprops); // create timezone component(-s) opt. 2 +.. . // based on all start dates in events (i.e. dtstart) +.. . +.. . +

+
+
+ +

PARSE

+

iCal, rfc5545 / rfc2445

+

require_once( "iCalcreator.class.php" ); +$config = array( "unique_id" => "kigkonsult.se" ); // set a (site) unique id, required if any component UID is missing +$v = new vcalendar( $config ); // create a new calendar instance + + /* start parse of local iCal file */ +$config = array( "directory" => "calendar", "filename" => "file.ics" ); +$v->setConfig( $config ); // set directory and file name +$v->parse(); + + /* start parse of remote iCal file */ +$v->setConfig( "url", "http://www.aDomain.net/file.ics" ); // iCalcreator also support parse of remote files +$v->parse(); + +$v->setProperty( "method", "PUBLISH" ); // required of some calendar software +$v->setProperty( "x-wr-calname", "Calendar Sample" ); // required of some calendar software +$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // required of some calendar software +$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" ); // required of some calendar software + +.. . +$v->sort(); // ensure start date order +.. . +.. . // continue process (edit, parse,select) the iCalcreator instance +.. . +

+ +

xCal, rfc6321 (XML)

+

require_once( "iCalcreator.class.php" ); +$config = array( "unique_id" => "kigkonsult.se" ); // set a (site) unique id, required if any component UID is missing +.. . +$filename = 'xmlfile.xml'; // use a local xCal file +// $filename = 'http://kigkonsult.se/xcal.php?a=1&b=2&c=3';// or a remote xCal resource +if( FALSE === ( $v = XMLfile2iCal( $filename, $config ))) // convert the XML resource to an iCalcreator instance + exit( "Error when parsing $filename" ); +.. . +.. . // continue process (edit, parse,select) the iCalcreator instance +.. . +

+
+ +

EDIT

+

require_once( "iCalcreator.class.php" ); +$config = array( "unique_id" => "kigkonsult.se", "directory" => "calendar", "filename" => "file.ics" ); + // set the (site) unique id, the import directory and file name +$v = new vcalendar( $config ); // create a new calendar instance + +$v->parse(); + +$v->setProperty( "method", "PUBLISH" ); // required of some calendar software +$v->setProperty( "x-wr-calname", "Calendar Sample" ); // required of some calendar software +$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // required of some calendar software +$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" ); // required of some calendar software + +while( $vevent = $v->getComponent( "vevent" )) { // read events, one by one + $uid = $vevent->getProperty( "uid" ); // uid required, one occurrence (unique id/key for component) + .. . + $dtstart = $vevent->getProperty( "dtstart" ); // dtstart required, one occurrence + .. . + if( $description = $vevent->getProperty( "description", 1 )) { // description optional, first occurrence + .. . // edit the description + $vevent->setProperty( "description", $description, FALSE, 1 ); // update/replace the description + } + while( $comment = $vevent->getProperty( "comment" )) { // comment optional, may occur more than once + .. . // manage comments + } + .. . + while( $vevent->deleteProperty( "attendee" )) + continue; // remove all ATTENDEE properties .. . + .. . + $v->setComponent ( $vevent, $uid ); // update/replace event in calendar with UID as key +} +.. . +.. .// a complete iCalcreator function list (ex. getProperty, deleteProperty) in iCalcreator manual +.. . +

+
+
+ +

SELECT

+

require_once( "iCalcreator.class.php" ); +$config = array( "unique_id" => "kigkonsult.se" ); // set a (site) unique id +$v = new vcalendar( $config ); // create a new calendar instance + +$v->setConfig( "url", "http://www.aDomain.net/file.ics" ); // iCalcreator also support remote files +$v->parse(); +$v->sort(); // ensure start date order + +$v->setProperty( "method", "PUBLISH" ); // required of some calendar software +$v->setProperty( "x-wr-calname", "Calendar Sample" ); // required of some calendar software +$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // required of some calendar software +$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" ); // required of some calendar software +

+

Select components based on specific date period

+

$eventArray = $v->selectComponents(); // select components occurring today + // (including components with recurrence pattern) +foreach( $eventArray as $year => $yearArray) { + foreach( $yearArray as $month => $monthArray ) { + foreach( $monthArray as $day => $dailyEventsArray ) { + foreach( $dailyEventsArray as $vevent ) { + $currddate = $event->getProperty( "x-current-dtstart" ); + // if member of a recurrence set (2nd occurrence etc) + // returns array( "x-current-dtstart" + // , <(string) date("Y-m-d [H:i:s][timezone/UTC offset]")>) + $dtstart = $vevent->getProperty( "dtstart" ); // dtstart required, one occurrence, (orig. start date) + $summary = $vevent->getProperty( "summary" ); + $description = $vevent->getProperty( "description" ); + .. . + .. . + } + } + } +} +

+

Select specific property values

+

$valueOccur = $v->getProperty( "RESOURCES" ); // fetch specific property (unique) values and occurrences + // ATTENDEE, CATEGORIES, DTSTART, LOCATION, + // ORGANIZER, PRIORITY, RESOURCES, STATUS, + // SUMMARY, UID +foreach( $valueOccur as $uniqueValue => $occurCnt ) { + echo "The RESOURCES value <b>$uniqueValue</b> occurs <b>$occurCnt</b> times<br />"; + .. . +} +

+

Select components based on specific property value

+

$selectSpec = array( "CATEGORIES" => "course1" ); +$specComps = $v->selectComponents( $selectSpec ); // selects components based on specific property value(-s) + // ATTENDEE, CATEGORIES, LOCATION, ORGANIZER, + // PRIORITY, RESOURCES, STATUS, SUMMARY, UID +foreach( $specComps as $comp ) { + .. . +} +

+
+
+ +

OUTPUT

+

require_once( "iCalcreator.class.php" ); +$config = array( "unique_id" => "kigkonsult.se" ); // set a (site) unique id +$v = new vcalendar( $config ); // create a new calendar instance + +$v->setProperty( "method", "PUBLISH" ); // required of some calendar software +$v->setProperty( "x-wr-calname", "Calendar Sample" ); // required of some calendar software +$v->setProperty( "X-WR-CALDESC", "Calendar Description" ); // required of some calendar software +$v->setProperty( "X-WR-TIMEZONE", "Europe/Stockholm" ); // required of some calendar software +.. . +.. . // continue process (edit, parse,select) the iCalcreator instance +.. . +

+

opt 1

+

.. . +$v->returnCalendar(); // redirect calendar file to browser +

+ +

opt 2

+

.. . +$config = array( "directory" => "depot", "filename" => "calendar.ics" ); +$v->setConfig( $config ); // set output directory and file name +$v->saveCalendar(); // save calendar to (local) file +.. . +

+ +

opt 3, xCal

+

.. . +$xmlstr = iCal2XML( $v ); // create well-formed XML, rfc6321 +... +

+
+
+ + +

COPYRIGHT AND LICENSE

+ +

Copyright

+iCalcreator v2.12
+copyright (c) 2007-2012 Kjell-Inge Gustafsson, kigkonsult
+kigkonsult.se iCalcreator
+kigkonsult.se contact
+
+ +

License

+ +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +

+This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. +

+You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +or download it here. + + \ No newline at end of file diff --git a/dav/jqueryui/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/dav/jqueryui/images/ui-bg_diagonals-thick_18_b81900_40x40.png new file mode 100644 index 000000000..954e22dbd Binary files /dev/null and b/dav/jqueryui/images/ui-bg_diagonals-thick_18_b81900_40x40.png differ diff --git a/dav/jqueryui/images/ui-bg_diagonals-thick_20_666666_40x40.png b/dav/jqueryui/images/ui-bg_diagonals-thick_20_666666_40x40.png new file mode 100644 index 000000000..64ece5707 Binary files /dev/null and b/dav/jqueryui/images/ui-bg_diagonals-thick_20_666666_40x40.png differ diff --git a/dav/jqueryui/images/ui-bg_flat_10_000000_40x100.png b/dav/jqueryui/images/ui-bg_flat_10_000000_40x100.png new file mode 100644 index 000000000..abdc01082 Binary files /dev/null and b/dav/jqueryui/images/ui-bg_flat_10_000000_40x100.png differ diff --git a/dav/jqueryui/images/ui-bg_glass_100_f6f6f6_1x400.png b/dav/jqueryui/images/ui-bg_glass_100_f6f6f6_1x400.png new file mode 100644 index 000000000..9b383f4d2 Binary files /dev/null and b/dav/jqueryui/images/ui-bg_glass_100_f6f6f6_1x400.png differ diff --git a/dav/jqueryui/images/ui-bg_glass_100_fdf5ce_1x400.png b/dav/jqueryui/images/ui-bg_glass_100_fdf5ce_1x400.png new file mode 100644 index 000000000..a23baad25 Binary files /dev/null and b/dav/jqueryui/images/ui-bg_glass_100_fdf5ce_1x400.png differ diff --git a/dav/jqueryui/images/ui-bg_glass_65_ffffff_1x400.png b/dav/jqueryui/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 000000000..42ccba269 Binary files /dev/null and b/dav/jqueryui/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/dav/jqueryui/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/dav/jqueryui/images/ui-bg_gloss-wave_35_f6a828_500x100.png new file mode 100644 index 000000000..39d5824d6 Binary files /dev/null and b/dav/jqueryui/images/ui-bg_gloss-wave_35_f6a828_500x100.png differ diff --git a/dav/jqueryui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/dav/jqueryui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png new file mode 100644 index 000000000..f1273672d Binary files /dev/null and b/dav/jqueryui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png differ diff --git a/dav/jqueryui/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/dav/jqueryui/images/ui-bg_highlight-soft_75_ffe45c_1x100.png new file mode 100644 index 000000000..359397acf Binary files /dev/null and b/dav/jqueryui/images/ui-bg_highlight-soft_75_ffe45c_1x100.png differ diff --git a/dav/jqueryui/images/ui-icons_222222_256x240.png b/dav/jqueryui/images/ui-icons_222222_256x240.png new file mode 100644 index 000000000..b273ff111 Binary files /dev/null and b/dav/jqueryui/images/ui-icons_222222_256x240.png differ diff --git a/dav/jqueryui/images/ui-icons_228ef1_256x240.png b/dav/jqueryui/images/ui-icons_228ef1_256x240.png new file mode 100644 index 000000000..a641a371a Binary files /dev/null and b/dav/jqueryui/images/ui-icons_228ef1_256x240.png differ diff --git a/dav/jqueryui/images/ui-icons_ef8c08_256x240.png b/dav/jqueryui/images/ui-icons_ef8c08_256x240.png new file mode 100644 index 000000000..85e63e9f6 Binary files /dev/null and b/dav/jqueryui/images/ui-icons_ef8c08_256x240.png differ diff --git a/dav/jqueryui/images/ui-icons_ffd27a_256x240.png b/dav/jqueryui/images/ui-icons_ffd27a_256x240.png new file mode 100644 index 000000000..e117effa3 Binary files /dev/null and b/dav/jqueryui/images/ui-icons_ffd27a_256x240.png differ diff --git a/dav/jqueryui/images/ui-icons_ffffff_256x240.png b/dav/jqueryui/images/ui-icons_ffffff_256x240.png new file mode 100644 index 000000000..42f8f992c Binary files /dev/null and b/dav/jqueryui/images/ui-icons_ffffff_256x240.png differ diff --git a/dav/jqueryui/jquery-ui-1.8.20.custom.css b/dav/jqueryui/jquery-ui-1.8.20.custom.css new file mode 100644 index 000000000..089d68e17 --- /dev/null +++ b/dav/jqueryui/jquery-ui-1.8.20.custom.css @@ -0,0 +1,354 @@ +/*! + * jQuery UI CSS Framework 1.8.20 + * + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; } +.ui-helper-clearfix:after { clear: both; } +.ui-helper-clearfix { zoom: 1; } +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/*! + * jQuery UI CSS Framework 1.8.20 + * + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; } +.ui-widget-content a { color: #333333; } +.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } +.ui-widget-header a { color: #ffffff; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; } +.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); } +.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*! + * jQuery UI Datepicker 1.8.20 + * + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +} \ No newline at end of file diff --git a/dav/jqueryui/jquery-ui-1.8.20.custom.min.js b/dav/jqueryui/jquery-ui-1.8.20.custom.min.js new file mode 100644 index 000000000..bb6ed07d3 --- /dev/null +++ b/dav/jqueryui/jquery-ui-1.8.20.custom.min.js @@ -0,0 +1,9 @@ +/*! jQuery UI - v1.8.20 - 2012-04-30 +* https://github.com/jquery/jquery-ui +* Includes: jquery.ui.core.js +* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ +(function(a,b){function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;return!b.href||!g||f.nodeName.toLowerCase()!=="map"?!1:(h=a("img[usemap=#"+g+"]")[0],!!h&&d(h))}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.ui=a.ui||{};if(a.ui.version)return;a.extend(a.ui,{version:"1.8.20",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;return a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0),/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){return a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)}),c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){return c===b?g["inner"+d].call(this):this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){return typeof b!="number"?g["outer"+d].call(this,b):this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!d||!a.element[0].parentNode)return;for(var e=0;e0?!0:(b[d]=1,e=b[d]>0,b[d]=0,e)},isOverAxis:function(a,b,c){return a>b&&a
'))}function bindHover(a){var b="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout",function(a){var c=$(a.target).closest(b);if(!c.length)return;c.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(c){var d=$(c.target).closest(b);if($.datepicker._isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])||!d.length)return;d.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-prev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-next")&&d.addClass("ui-datepicker-next-hover")})}function extendRemove(a,b){$.extend(a,b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}function isArray(a){return a&&($.browser.safari&&typeof a=="object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(\)/))}$.extend($.ui,{datepicker:{version:"1.8.20"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){return extendRemove(this._defaults,a||{}),this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(a,b){var c=a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:b?bindHover($('
')):this.dpDiv}},_connectDatepicker:function(a,b){var c=$(a);b.append=$([]),b.trigger=$([]);if(c.hasClass(this.markerClassName))return;this._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.settings.disabled&&this._disableDatepicker(a)},_attachments:function(a,b){var c=this._get(b,"appendText"),d=this._get(b,"isRTL");b.append&&b.append.remove(),c&&(b.append=$(''+c+""),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),b.trigger&&b.trigger.remove();var e=this._get(b,"showOn");(e=="focus"||e=="both")&&a.focus(this._showDatepicker);if(e=="button"||e=="both"){var f=this._get(b,"buttonText"),g=this._get(b,"buttonImage");b.trigger=$(this._get(b,"buttonImageOnly")?$("").addClass(this._triggerClass).attr({src:g,alt:f,title:f}):$('').addClass(this._triggerClass).html(g==""?f:$("").attr({src:g,alt:f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){return $.datepicker._datepickerShowing&&$.datepicker._lastInput==a[0]?$.datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker._lastInput!=a[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepicker(a[0])):$.datepicker._showDatepicker(a[0]),!1})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var d=function(a){var b=0,c=0;for(var d=0;db&&(b=a[d].length,c=d);return c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=$(a);if(c.hasClass(this.markerClassName))return;c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._getDefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.settings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block")},_dialogDatepicker:function(a,b,c,d,e){var f=this._dialogInst;if(!f){this.uuid+=1;var g="dp"+this.uuid;this._dialogInput=$(''),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),f=this._dialogInst=this._newInst(this._dialogInput,!1),f.settings={},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),b=b&&b.constructor==Date?this._formatDate(f,b):b,this._dialogInput.val(b),this._pos=e?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h=document.documentElement.clientWidth,i=document.documentElement.clientHeight,j=document.documentElement.scrollLeft||document.body.scrollLeft,k=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[h/2-100+j,i/2-150+k]}return this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=c,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,f),this},_destroyDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();$.removeData(a,PROP_NAME),d=="input"?(c.append.remove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=="div"||d=="span")&&b.removeClass(this.markerClassName).empty()},_enableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!1,c.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().removeClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b})},_disableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!0,c.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().addClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b}),this._disabledInputs[this._disabledInputs.length]=a},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b-1}},_doKeyUp:function(a){var b=$.datepicker._getInst(a.target);if(b.input.val()!=b.lastVal)try{var c=$.datepicker.parseDate($.datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlternate(b),$.datepicker._updateDatepicker(b))}catch(d){$.datepicker.log(d)}return!0},_showDatepicker:function(a){a=a.target||a,a.nodeName.toLowerCase()!="input"&&(a=$("input",a.parentNode)[0]);if($.datepicker._isDisabledDatepicker(a)||$.datepicker._lastInput==a)return;var b=$.datepicker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=b&&($.datepicker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var c=$.datepicker._get(b,"beforeShow"),d=c?c.apply(a,[a,b]):{};if(d===!1)return;extendRemove(b.settings,d),b.lastVal=null,$.datepicker._lastInput=a,$.datepicker._setDateFromField(b),$.datepicker._inDialog&&(a.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(a),$.datepicker._pos[1]+=a.offsetHeight);var e=!1;$(a).parents().each(function(){return e|=$(this).css("position")=="fixed",!e}),e&&$.browser.opera&&($.datepicker._pos[0]-=document.documentElement.scrollLeft,$.datepicker._pos[1]-=document.documentElement.scrollTop);var f={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,b.dpDiv.empty(),b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b),f=$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=$.datepicker._get(b,"showAnim"),h=$.datepicker._get(b,"duration"),i=function(){var a=b.dpDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=$.datepicker._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._curInst=b}},_updateDatepicker:function(a){var b=this;b.maxRows=4;var c=$.datepicker._getBorders(a.dpDiv);instActive=a,a.dpDiv.empty().append(this._generateHTML(a));var d=a.dpDiv.find("iframe.ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mouseover();var e=this._getNumberOfMonths(a),f=e[1],g=17;a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"em"),a.dpDiv[(e[0]!=1||e[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),a==$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var h=a.yearshtml;setTimeout(function(){h===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=a.dpDiv.outerWidth(),e=a.dpDiv.outerHeight(),f=a.input?a.input.outerWidth():0,g=a.input?a.input.outerHeight():0,h=document.documentElement.clientWidth+$(document).scrollLeft(),i=document.documentElement.clientHeight+$(document).scrollTop();return b.left-=this._get(a,"isRTL")?d-f:0,b.left-=c&&b.left==a.input.offset().left?$(document).scrollLeft():0,b.top-=c&&b.top==a.input.offset().top+g?$(document).scrollTop():0,b.left-=Math.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=Math.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0),b},_findPos:function(a){var b=this._getInst(a),c=this._get(b,"isRTL");while(a&&(a.type=="hidden"||a.nodeType!=1||$.expr.filters.hidden(a)))a=a[c?"previousSibling":"nextSibling"];var d=$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=this._curInst;if(!b||a&&b!=$.data(a,PROP_NAME))return;if(this._datepickerShowing){var c=this._get(b,"showAnim"),d=this._get(b,"duration"),e=function(){$.datepicker._tidyDialog(b)};$.effects&&$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,e):b.dpDiv[c=="slideDown"?"slideUp":c=="fadeIn"?"fadeOut":"hide"](c?d:null,e),c||e(),this._datepickerShowing=!1;var f=this._get(b,"onClose");f&&f.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(!$.datepicker._curInst)return;var b=$(a.target),c=$.datepicker._getInst(b[0]);(b[0].id!=$.datepicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length==0&&!b.hasClass($.datepicker.markerClassName)&&!b.closest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=c)&&$.datepicker._hideDatepicker()},_adjustDate:function(a,b,c){var d=$(a),e=this._getInst(d[0]);if(this._isDisabledDatepicker(d[0]))return;this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c),this._updateDatepicker(e)},_gotoToday:function(a){var b=$(a),c=this._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=c.currentDay,c.drawMonth=c.selectedMonth=c.currentMonth,c.drawYear=c.selectedYear=c.currentYear;else{var d=new Date;c.selectedDay=d.getDate(),c.drawMonth=c.selectedMonth=d.getMonth(),c.drawYear=c.selectedYear=d.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthYear:function(a,b,c){var d=$(a),e=this._getInst(d[0]);e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=$(a);if($(d).hasClass(this._unselectableClass)||this._isDisabledDatepicker(e[0]))return;var f=this._getInst(e[0]);f.selectedDay=f.currentDay=$("a",d).html(),f.selectedMonth=f.currentMonth=b,f.selectedYear=f.currentYear=c,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))},_clearDate:function(a){var b=$(a),c=this._getInst(b[0]);this._selectDate(b,"")},_selectDate:function(a,b){var c=$(a),d=this._getInst(c[0]);b=b!=null?b:this._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDatepicker(),this._lastInput=d.input[0],typeof d.input[0]!="object"&&d.input.focus(),this._lastInput=null)},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),d=this._getDate(a),e=this.formatDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=new Date(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var d=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=typeof d!="string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=-1,j=-1,k=-1,l=-1,m=!1,n=function(b){var c=s+1-1){j=1,k=l;do{var u=this._getDaysInMonth(i,j-1);if(k<=u)break;j++,k-=u}while(!0)}var t=this._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=i||t.getMonth()+1!=j||t.getDate()!=k)throw"Invalid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,e=(c?c.dayNames:null)||this._defaults.dayNames,f=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,h=function(b){var c=m+112?a.getHours()+2:0),a):null},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),(e!=a.selectedMonth||f!=a.selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),d=this._get(a,"showButtonPanel"),e=this._get(a,"hideIfNoPrevNext"),f=this._get(a,"navigationAsDateFormat"),g=this._getNumberOfMonths(a),h=this._get(a,"showCurrentAtPos"),i=this._get(a,"stepMonths"),j=g[0]!=1||g[1]!=1,k=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=this._getMinMaxDate(a,"min"),m=this._getMinMaxDate(a,"max"),n=a.drawMonth-h,o=a.drawYear;n<0&&(n+=12,o--);if(m){var p=this._daylightSavingAdjust(new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=l&&pp)n--,n<0&&(n=11,o--)}a.drawMonth=n,a.drawYear=o;var q=this._get(a,"prevText");q=f?this.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=this._canAdjustMonth(a,-1,o,n)?''+q+"":e?"":''+q+"",s=this._get(a,"nextText");s=f?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatConfig(a)):s;var t=this._canAdjustMonth(a,1,o,n)?''+s+"":e?"":''+s+"",u=this._get(a,"currentText"),v=this._get(a,"gotoCurrent")&&a.currentDay?k:b;u=f?this.formatDate(u,v,this._getFormatConfig(a)):u;var w=a.inline?"":'",x=d?'
'+(c?w:"")+(this._isInRange(a,v)?'":"")+(c?"":w)+"
":"",y=parseInt(this._get(a,"firstDay"),10);y=isNaN(y)?0:y;var z=this._get(a,"showWeek"),A=this._get(a,"dayNames"),B=this._get(a,"dayNamesShort"),C=this._get(a,"dayNamesMin"),D=this._get(a,"monthNames"),E=this._get(a,"monthNamesShort"),F=this._get(a,"beforeShowDay"),G=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths"),I=this._get(a,"calculateWeek")||this.iso8601Week,J=this._getDefaultDate(a),K="";for(var L=0;L1)switch(N){case 0:Q+=" ui-datepicker-group-first",P=" ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=" ui-datepicker-group-last",P=" ui-corner-"+(c?"left":"right");break;default:Q+=" ui-datepicker-group-middle",P=""}Q+='">'}Q+='
'+(/all|left/.test(P)&&L==0?c?t:r:"")+(/all|right/.test(P)&&L==0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+'
'+"";var R=z?'":"";for(var S=0;S<7;S++){var T=(S+y)%7;R+="=5?' class="ui-datepicker-week-end"':"")+">"+''+C[T]+""}Q+=R+"";var U=this._getDaysInMonth(o,n);o==a.selectedYear&&n==a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,U));var V=(this._getFirstDayOfMonth(o,n)-y+7)%7,W=Math.ceil((V+U)/7),X=j?this.maxRows>W?this.maxRows:W:W;this.maxRows=X;var Y=this._daylightSavingAdjust(new Date(o,n,1-V));for(var Z=0;Z";var _=z?'":"";for(var S=0;S<7;S++){var ba=F?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=Y.getMonth()!=n,bc=bb&&!H||!ba[0]||l&&Ym;_+='",Y.setDate(Y.getDate()+1),Y=this._daylightSavingAdjust(Y)}Q+=_+""}n++,n>11&&(n=0,o++),Q+="
'+this._get(a,"weekHeader")+"
'+this._get(a,"calculateWeek")(Y)+""+(bb&&!G?" ":bc?''+Y.getDate()+"":''+Y.getDate()+"")+"
"+(j?"
"+(g[0]>0&&N==g[1]-1?'
':""):""),M+=Q}K+=M}return K+=x+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.inline?'':""),a._keyEvent=!1,K},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=this._get(a,"changeMonth"),j=this._get(a,"changeYear"),k=this._get(a,"showMonthAfterYear"),l='
',m="";if(f||!i)m+=''+g[b]+"";else{var n=d&&d.getFullYear()==c,o=e&&e.getFullYear()==c;m+='"}k||(l+=m+(f||!i||!j?" ":""));if(!a.yearshtml){a.yearshtml="";if(f||!j)l+=''+c+"";else{var q=this._get(a,"yearRange").split(":"),r=(new Date).getFullYear(),s=function(a){var b=a.match(/c[+-].*/)?c+parseInt(a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t=s(q[0]),u=Math.max(t,s(q[1]||""));t=d?Math.max(t,d.getFullYear()):t,u=e?Math.min(u,e.getFullYear()):u,a.yearshtml+='",l+=a.yearshtml,a.yearshtml=null}}return l+=this._get(a,"yearSuffix"),k&&(l+=(f||!i||!j?" ":"")+m),l+="
",l},_adjustInstDate:function(a,b,c){var d=a.drawYear+(c=="Y"?b:0),e=a.drawMonth+(c=="M"?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+(c=="D"?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),(c=="M"||c=="Y")&&this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&bd?d:e,e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return b==null?[1,1]:typeof b=="number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));return b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth())),this._isInRange(a,f)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!d||b.getTime()<=d.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");return b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10),{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),$.fn.datepicker=function(a){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.initialized=!0);var b=Array.prototype.slice.call(arguments,1);return typeof a!="string"||a!="isDisabled"&&a!="getDate"&&a!="widget"?a=="option"&&arguments.length==2&&typeof arguments[1]=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b)):this.each(function(){typeof a=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)}):$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b))},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.8.20",window["DP_jQuery_"+dpuuid]=$})(jQuery);; \ No newline at end of file diff --git a/dav/layout.fnk.php b/dav/layout.fnk.php new file mode 100644 index 000000000..a712a09bc --- /dev/null +++ b/dav/layout.fnk.php @@ -0,0 +1,460 @@ +page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; + + $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; + + $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; + + switch (get_config("system", "language")) { + case "de": $a->page['htmlhead'] .= '' . "\r\n"; break; + default: $a->page['htmlhead'] .= '' . "\r\n"; + } + + $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; +} + +/** + * + */ +function wdcal_addRequiredHeadersEdit() +{ + $a = get_app(); + + $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; + + $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; + + $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; + + $a->page['htmlhead'] .= '' . "\r\n"; + $a->page['htmlhead'] .= '' . "\r\n"; + +} + + +/** + * @param array|DBClass_friendica_calendars[] $calendars + * @param array $calendar_preselected + * @param string $data_feed_url + * @param string $view + * @param int $theme + * @param int $height_diff + * @param bool $readonly + * @param string $curr_day + * @param array $add_params + * @param bool $show_nav + * @return string + */ +function wdcal_printCalendar($calendars, $calendar_preselected, $data_feed_url, $view = "week", $theme = 0, $height_diff = 175, $readonly = false, $curr_day = "", $add_params = array(), $show_nav = true) +{ + + $a = get_app(); + $localization = wdcal_local::getInstanceByUser($a->user["uid"]); + + $cals_avail = array(); + foreach ($calendars as $c) $cals_avail[] = array("ns" => $c->namespace, "id" => $c->namespace_id, "displayname" => $c->displayname); + $opts = array( + "view" => $view, + "theme" => $theme, + "readonly" => $readonly, + "height_diff" => $height_diff, + "weekstartday" => $localization->getFirstDayOfWeek(), + "data_feed_url" => $data_feed_url, + "date_format_dm1" => $localization->dateformat_js_dm1(), + "date_format_dm2" => $localization->dateformat_js_dm2(), + "date_format_dm3" => $localization->dateformat_js_dm3(), + "date_format_full" => $localization->dateformat_datepicker_js(), + ); + + $x = ' + + +
+
Available Calendars:'; + + foreach ($cals_avail as $cal) { + $x .= '
+
+ +
'; + + if ($show_nav) { + + $x .= '
+ +
+
+
' . t("Today") . '
+
+
+ +
+
+
+
+ +
+
+
+
' . t("Reload") . '
+
+
+
+ +
+
+ +
+
+ + ' . t("Date") . ' +
+
+
+
+
'; + } + $x .= ' +
+
+
+
+
+
'; + + return $x; +} + + +/** + * @param string $uri + * @param string $recurr_uri + * @return string + */ +function wdcal_getDetailPage($uri, $recurr_uri) +{ + $a = get_app(); + + $details = null; + $cals = dav_getMyCals($a->user["uid"]); + foreach ($cals as $c) { + $cs = wdcal_calendar_factory($a->user["uid"], $c->namespace, $c->namespace_id); + $p = $cs->getPermissionsItem($a->user["uid"], $uri, $recurr_uri); + if ($p["read"]) try { + $redirect = $cs->getItemDetailRedirect($uri); + if ($redirect !== null) goaway($redirect); + $details = $cs->getItemByUri($uri); + } catch (Exception $e) { + notification(t("Error") . ": " . $e); + goaway($a->get_baseurl() . "/dav/wdcal/"); + } + } + + + return $uri . " / " . $recurr_uri . "
" . print_r($details, true); +} + + +/** + * @param string $uri + * @param string $recurr_uri + * @return string + */ +function wdcal_postEditPage($uri, $recurr_uri) +{ + + $a = get_app(); + $localization = wdcal_local::getInstanceByUser($a->user["uid"]); + + check_form_security_token_redirectOnErr($a->get_baseurl() . "/dav/wdcal/", "caledit"); + + if (isset($_REQUEST["allday"])) { + $start = $localization->date_parseLocal($_REQUEST["start_date"] . " 00:00"); + $end = $localization->date_parseLocal($_REQUEST["end_date"] . " 20:00"); + $isallday = true; + } else { + $start = $localization->date_parseLocal($_REQUEST["start_date"] . " " . $_REQUEST["start_time"]); + $end = $localization->date_parseLocal($_REQUEST["end_date"] . " " . $_REQUEST["end_time"]); + $isallday = false; + } + + + $cals = dav_getMyCals($a->user["uid"]); + foreach ($cals as $c) { + $cs = wdcal_calendar_factory($a->user["uid"], $c->namespace, $c->namespace_id); + $p = $cs->getPermissionsItem($a->user["uid"], $uri, $recurr_uri); + if ($p["write"]) try { + $cs->updateItem($uri, $start, $end, + stripslashes($_REQUEST["subject"]), $isallday, wdcal_parse_text_serverside($_REQUEST["wdcal_desc"]), + stripslashes($_REQUEST["location"]), $_REQUEST["color"], $a->timezone, + isset($_REQUEST["notification"]), $_REQUEST["notification_type"], $_REQUEST["notification_value"]); + } catch (Exception $e) { + notification(t("Error") . ": " . $e); + } + goaway($a->get_baseurl() . "/dav/wdcal/"); + } + +} + +/** + * @param string $uri + * @param string $recurr_uri + * @return string + */ +function wdcal_getEditPage($uri, $recurr_uri) +{ + + $a = get_app(); + $localization = wdcal_local::getInstanceByUser($a->user["uid"]); + + if ($uri != "" && $uri != "new") { + $o = q("SELECT * FROM %s%sjqcalendar WHERE `uid` = %d AND `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], dbesc($uri), dbesc($recurr_uri) + ); + if (count($o) != 1) return t('Not found'); + $event = $o[0]; + + $calendarSource = wdcal_calendar_factory($a->user["uid"], $event["namespace"], $event["namespace_id"]); + + $permissions = $calendarSource->getPermissionsItem($a->user["uid"], $uri, $recurr_uri, $event); + + if (!$permissions["write"]) return t('No access'); + + $n = q("SELECT * FROM %s%snotifications WHERE `uid` = %d AND `ical_uri` = '%s' AND `ical_recurr_uri` = '%s'", + CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $a->user["uid"], dbesc($uri), dbesc($recurr_uri) + ); + if (count($n) > 0) { + $notification_type = $n[0]["rel_type"]; + $notification_value = -1 * $n[0]["rel_value"]; + $notification = true; + } else { + if ($event["IsAllDayEvent"]) { + $notification_type = "hour"; + $notification_value = 24; + } else { + $notification_type = "minute"; + $notification_value = 60; + } + $notification = false; + } + + + } elseif (isset($_REQUEST["start"]) && $_REQUEST["start"] > 0) { + $event = array( + "id" => 0, + "Subject" => $_REQUEST["title"], + "Location" => "", + "Description" => "", + "StartTime" => $_REQUEST["start"], + "EndTime" => $_REQUEST["end"], + "IsAllDayEvent" => $_REQUEST["isallday"], + "Color" => null, + "RecurringRule" => null, + ); + if ($_REQUEST["isallday"]) { + $notification_type = "hour"; + $notification_value = 24; + } else { + $notification_type = "hour"; + $notification_value = 1; + } + + $notification = true; + } else { + $event = array( + "id" => 0, + "Subject" => "", + "Location" => "", + "Description" => "", + "StartTime" => "", + "EndTime" => "", + "IsAllDayEvent" => "", + "Color" => null, + "RecurringRule" => null, + ); + $notification_type = "hour"; + $notification_value = 1; + $notification = true; + } + + + $out = "" . t("Go back to the calendar") . "

"; + $out .= "
\n"; + + $out .= " + +
\n"; + $out .= "
\n"; + + $out .= ""; + $out .= ""; + $out .= ""; + $out .= "
\n"; + + $out .= ""; + $out .= ""; + $out .= ""; + $out .= "
\n"; + + $out .= "
\n"; + + $out .= " "; + $out .= "
"; + + $out .= ""; + $out .= ' + ' . t('before') . ' +

'; + + + $out .= ""; + + $out .= "
"; + + return $out; +} + + +/** + * @param App $a + * @return string + */ +function wdcal_getSettingsPage(&$a) +{ + + if (!local_user()) { + notice(t('Permission denied.') . EOL); + return ''; + } + + if (isset($_REQUEST["save"])) { + check_form_security_token_redirectOnErr($a->get_baseurl() . '/dav/settings/', 'calprop'); + set_pconfig($a->user["uid"], "dav", "dateformat", $_REQUEST["wdcal_date_format"]); + info(t('The new values have been saved.')); + } + + $o = ""; + + $o .= "" . t("Go back to the calendar") . "

"; + + $o .= '

' . t('Calendar Settings') . '

'; + + $current_format = wdcal_local::getInstanceByUser($a->user["uid"]); + $o .= '
'; + $o .= "\n"; + + $o .= '
'; + + $o .= '
'; + + $o .= ''; + $o .= '
'; + + $o .= "

" . t("Limitations") . "

"; + + $o .= "- The native friendica events are embedded as read-only, half-transparent in the calendar.
"; + + $o .= "

" . t("Warning") . "

"; + + $o .= "This plugin still is in a very early stage of development. Expect major bugs!
"; + + $o .= "

" . t("Synchronization (iPhone, Thunderbird Lightning, Android, ...)") . "

"; + + $o .= 'This plugin enables synchronization of your dates and contacts with CalDAV- and CardDAV-enabled programs or devices.
+ As an example, the instructions how to set up two-way synchronization with an iPhone/iPodTouch are provided below.
+ Unfortunately, Android does not have native support for CalDAV or CardDAV, so an app has to be installed.
+ On desktops, the Lightning-extension to Mozilla Thunderbird should be able to use this plugin as a backend.

'; + + $o .= '

' . t('Synchronizing this calendar with the iPhone') . '

'; + + $o .= "
    +
  • Go to the settings
  • +
  • Mail, contacts, settings
  • +
  • Add a new account
  • +
  • Other...
  • +
  • Calendar -> CalDAV-Account
  • +
  • Server: " . $a->get_baseurl() . "/dav/ / Username/Password: the same as your friendica-login
  • +
"; + + $o .= '

' . t('Synchronizing your Friendica-Contacts with the iPhone') . '

'; + + $o .= "
    +
  • Go to the settings
  • +
  • Mail, contacts, settings
  • +
  • Add a new account
  • +
  • Other...
  • +
  • Contacts -> CardDAV-Account
  • +
  • Server: " . $a->get_baseurl() . "/dav/ / Username/Password: the same as your friendica-login
  • +
"; + + return $o; +} + diff --git a/dav/main.php b/dav/main.php new file mode 100644 index 000000000..b79a47797 --- /dev/null +++ b/dav/main.php @@ -0,0 +1,288 @@ +argc >= 2 && $a->argv[1] == "wdcal") { + + if ($a->argc >= 3 && $a->argv[2] == "feed") { + wdcal_print_feed($a->get_baseurl() . "/dav/wdcal/"); + killme(); + } elseif ($a->argc >= 3 && strlen($a->argv[2]) > 0) { + wdcal_addRequiredHeadersEdit(); + } else { + wdcal_addRequiredHeaders(); + } + return; + } + + if ($a->argc >= 2 && $a->argv[1] == "settings") { + return; + } + + $authBackend = new Sabre_DAV_Auth_Backend_Friendica(); + $principalBackend = new Sabre_DAVACL_PrincipalBackend_Friendica($authBackend); + $caldavBackend_std = new Sabre_CalDAV_Backend_Std(); + $caldavBackend_community = new Sabre_CalDAV_Backend_Friendica(); + $carddavBackend_std = new Sabre_CardDAV_Backend_Std(); + $carddavBackend_community = new Sabre_CardDAV_Backend_FriendicaCommunity(); + + if (isset($_SERVER["PHP_AUTH_USER"])) { + $tree = new Sabre_DAV_SimpleCollection('root', array( + new Sabre_DAV_SimpleCollection('principals', array( + new Sabre_CalDAV_Principal_Collection($principalBackend, "principals/users"), + )), + new Sabre_CalDAV_AnimexxCalendarRootNode($principalBackend, array( + $caldavBackend_std, + $caldavBackend_community, + )), + new Sabre_CardDAV_AddressBookRootFriendica($principalBackend, array( + $carddavBackend_std, + $carddavBackend_community, + )), + )); + } else { + $tree = new Sabre_DAV_SimpleCollection('root', array()); + } + +// The object tree needs in turn to be passed to the server class + $server = new Sabre_DAV_Server($tree); + + $url = parse_url($a->get_baseurl()); + $server->setBaseUri(CALDAV_URL_PREFIX); + + $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend, 'SabreDAV'); + $server->addPlugin($authPlugin); + + $aclPlugin = new Sabre_DAVACL_Plugin_Friendica(); + $aclPlugin->defaultUsernamePath = "principals/users"; + $server->addPlugin($aclPlugin); + + $caldavPlugin = new Sabre_CalDAV_Plugin(); + $server->addPlugin($caldavPlugin); + + $carddavPlugin = new Sabre_CardDAV_Plugin(); + $server->addPlugin($carddavPlugin); + + $browser = new Sabre_DAV_Browser_Plugin(); + $server->addPlugin($browser); + + $server->exec(); + + killme(); +} + +/** + * @return string + */ +function dav_content() +{ + $a = get_app(); + if (!isset($a->user["uid"]) || $a->user["uid"] == 0) { + return login(); + } + + $x = ""; + + if ($a->argv[1] == "settings") { + return wdcal_getSettingsPage($a); + } elseif ($a->argv[1] == "wdcal") { + if ($a->argc >= 3 && strlen($a->argv[2]) > 0) { + $uri = $a->argv[2]; + $recurr_uri = ""; // @TODO + if (isset($a->argv[3]) && $a->argv[3] == "edit") { + $o = ""; + if (isset($_REQUEST["save"])) $o .= wdcal_postEditPage($uri, $recurr_uri); + $o .= wdcal_getEditPage($uri, $recurr_uri); + return $o; + } else { + return wdcal_getDetailPage($uri, $recurr_uri); + } + } else { + $cals = dav_getMyCals($a->user["uid"]); + $cals_show = array(); + foreach ($cals as $e) $cals_show[] = array("ns" => $e->namespace, "id" => $e->namespace_id, "displayname" => $e->displayname); + $x = wdcal_printCalendar($cals, $cals_show, $a->get_baseurl() . "/dav/wdcal/feed/", "week", 0, 200); + } + } + return $x; +} + + +/** + * @param App $a + * @param object $b + */ +function dav_event_created_hook(&$a, &$b) +{ + dav_include_files(); + // @TODO Updating the cache instead of completely invalidating and rebuilding it + FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_CONTACTS); + FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_MINE); +} + +/** + * @param App $a + * @param object $b + */ +function dav_event_updated_hook(&$a, &$b) +{ + dav_include_files(); + // @TODO Updating the cache instead of completely invalidating and rebuilding it + FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_CONTACTS); + FriendicaVirtualCalSourceBackend::invalidateCache($a->user["uid"], CALDAV_FRIENDICA_MINE); +} + +/** + * @param App $a + * @param object $b + */ +function dav_profile_tabs_hook(&$a, &$b) +{ + $b["tabs"][] = array( + "label" => t('Calendar'), + "url" => $a->get_baseurl() . "/dav/wdcal/", + "sel" => "", + "title" => t('Extended calendar with CalDAV-support'), + ); +} + +/** + * @param App $a + * @param null|object $o + */ +function dav_plugin_admin_post(&$a = null, &$o = null) +{ + check_form_security_token_redirectOnErr('/admin/plugins/dav', 'dav_admin_save'); + + require_once(__DIR__ . "/database-init.inc.php"); + + if (isset($_REQUEST["install"])) { + $errs = dav_create_tables(); + if (count($errs) == 0) info(t('The database tables have been installed.') . EOL); + else notice(t("An error occurred during the installation.") . EOL); + } +} + +/** + * @param App $a + * @param null|object $o + */ +function dav_plugin_admin(&$a, &$o) +{ + + require_once(__DIR__ . "/database-init.inc.php"); + + $dbstatus = dav_check_tables(); + + $o = ''; + $o .= '' . t("No system-wide settings yet.") . '

'; + + + $o .= '

' . t('Database status') . '

'; + switch ($dbstatus) { + case 0: + $o .= t('Installed'); + break; + case 1: + $o .= t('Upgrade needed') . "

"; + break; + case -1: + $o .= t('Not installed') . "

"; + break; + } + $o .= "

"; + + $o .= "

" . t("Troubleshooting") . "

"; + $o .= "

" . t("Manual creation of the database tables:") . "

"; + $o .= "" . t("Show SQL-statements") . ""; +} diff --git a/dav/timepicker/README.md b/dav/timepicker/README.md new file mode 100644 index 000000000..93ce750e4 --- /dev/null +++ b/dav/timepicker/README.md @@ -0,0 +1,56 @@ +timePicker +========== +A time picker control for textfields built using jQuery. Inspired by Google Calendar. + +Examples +-------- + +Default: + + $("#time1").timePicker(); + +02.00 AM - 03.30 PM, 15 minutes steps: + + $("#time2").timePicker({ + startTime: "02.00", // Using string. Can take string or Date object. + endTime: new Date(0, 0, 0, 15, 30, 0), // Using Date object here. + show24Hours: false, + separator: '.', + step: 15}); + +An example how the two helper functions can be used to achieve +advanced functionality. + + - Linking: When changing the first input the second input is updated and the + duration is kept. + - Validation: If the second input has a time earlier than the firs input, + an error class is added. + +The example: + + // Use default settings + $("#time3, #time4").timePicker(); + + // Store time used by duration. + var oldTime = $.timePicker("#time3").getTime(); + + // Keep the duration between the two inputs. + $("#time3").change(function() { + if ($("#time4").val()) { // Only update when second input has a value. + // Calculate duration. + var duration = ($.timePicker("#time4").getTime() - oldTime); + var time = $.timePicker("#time3").getTime(); + // Calculate and update the time in the second input. + $.timePicker("#time4").setTime(new Date(new Date(time.getTime() + duration))); + oldTime = time; + } + }); + // Validate. + $("#time4").change(function() { + if($.timePicker("#time3").getTime() > $.timePicker(this).getTime()) { + $(this).addClass("error"); + } + else { + $(this).removeClass("error"); + } + }); \ No newline at end of file diff --git a/dav/timepicker/index.htm b/dav/timepicker/index.htm new file mode 100644 index 000000000..4f7d6f4da --- /dev/null +++ b/dav/timepicker/index.htm @@ -0,0 +1,137 @@ + + + + jQuery timePicker + + + + + + + + + + +

jQuery timePicker

+

A time picker for jQuery inspired by Google Calendar

+

Get the latest code on Github (the files used on this page might not be the latest): http://github.com/perifer/timePicker + +

+
// Default.
+$("#time1").timePicker();
+ +
+
// 02.00 AM - 03.30 PM, 15 minutes steps.
+$("#time2").timePicker({
+  startTime: "02.00", // Using string. Can take string or Date object.
+  endTime: new Date(0, 0, 0, 15, 30, 0), // Using Date object here.
+  show24Hours: false,
+  separator: '.',
+  step: 15});
+ +
+
// An example how the two helper functions can be used to achieve 
+// advanced functionality.
+// - Linking: When changing the first input the second input is updated and the
+//   duration is kept.
+// - Validation: If the second input has a time earlier than the firs input,
+//   an error class is added.
+
+// Use default settings
+$("#time3, #time4").timePicker();
+    
+// Store time used by duration.
+var oldTime = $.timePicker("#time3").getTime();
+
+// Keep the duration between the two inputs.
+$("#time3").change(function() {
+  if ($("#time4").val()) { // Only update when second input has a value.
+    // Calculate duration.
+    var duration = ($.timePicker("#time4").getTime() - oldTime);
+    var time = $.timePicker("#time3").getTime();
+    // Calculate and update the time in the second input.
+    $.timePicker("#time4").setTime(new Date(new Date(time.getTime() + duration)));
+    oldTime = time;
+  }
+});
+// Validate.
+$("#time4").change(function() {
+  if($.timePicker("#time3").getTime() > $.timePicker(this).getTime()) {
+    $(this).addClass("error");
+  }
+  else {
+    $(this).removeClass("error");
+  }
+});
+ + + diff --git a/dav/timepicker/jquery.timePicker.js b/dav/timepicker/jquery.timePicker.js new file mode 100644 index 000000000..41b35c13b --- /dev/null +++ b/dav/timepicker/jquery.timePicker.js @@ -0,0 +1,277 @@ +/* + * A time picker for jQuery + * + * Dual licensed under the MIT and GPL licenses. + * Copyright (c) 2009 Anders Fajerson + * @name timePicker + * @author Anders Fajerson (http://perifer.se) + * @example $("#mytime").timePicker(); + * @example $("#mytime").timePicker({step:30, startTime:"15:00", endTime:"18:00"}); + * + * Based on timePicker by Sam Collet (http://www.texotela.co.uk/code/jquery/timepicker/) + * + * Options: + * step: # of minutes to step the time by + * startTime: beginning of the range of acceptable times + * endTime: end of the range of acceptable times + * separator: separator string to use between hours and minutes (e.g. ':') + * show24Hours: use a 24-hour scheme + */ + +(function($){ + $.fn.timePicker = function(options) { + // Build main options before element iteration + var settings = $.extend({}, $.fn.timePicker.defaults, options); + + return this.each(function() { + $.timePicker(this, settings); + }); + }; + + $.timePicker = function (elm, settings) { + var e = $(elm)[0]; + return e.timePicker || (e.timePicker = new jQuery._timePicker(e, settings)); + }; + + $.timePicker.version = '0.3'; + + $._timePicker = function(elm, settings) { + + var tpOver = false; + var keyDown = false; + var startTime = timeToDate(settings.startTime, settings); + var endTime = timeToDate(settings.endTime, settings); + var selectedClass = "selected"; + var selectedSelector = "li." + selectedClass; + + $(elm).attr('autocomplete', 'OFF'); // Disable browser autocomplete + + var times = []; + var time = new Date(startTime); // Create a new date object. + while(time <= endTime) { + times[times.length] = formatTime(time, settings); + time = new Date(time.setMinutes(time.getMinutes() + settings.step)); + } + + var $tpDiv = $('
'); + var $tpList = $('
    '); + + // Build the list. + for(var i = 0; i < times.length; i++) { + $tpList.append("
  • " + times[i] + "
  • "); + } + $tpDiv.append($tpList); + // Append the timPicker to the body and position it. + $tpDiv.appendTo('body').hide(); + + // Store the mouse state, used by the blur event. Use mouseover instead of + // mousedown since Opera fires blur before mousedown. + $tpDiv.mouseover(function() { + tpOver = true; + }).mouseout(function() { + tpOver = false; + }); + + $("li", $tpList).mouseover(function() { + if (!keyDown) { + $(selectedSelector, $tpDiv).removeClass(selectedClass); + $(this).addClass(selectedClass); + } + }).mousedown(function() { + tpOver = true; + }).click(function() { + setTimeVal(elm, this, $tpDiv, settings); + tpOver = false; + }); + + var showPicker = function() { + if ($tpDiv.is(":visible")) { + return false; + } + $("li", $tpDiv).removeClass(selectedClass); + + // Position + var elmOffset = $(elm).offset(); + $tpDiv.css({'top':elmOffset.top + elm.offsetHeight, 'left':elmOffset.left}); + + // Show picker. This has to be done before scrollTop is set since that + // can't be done on hidden elements. + $tpDiv.show(); + + // Try to find a time in the list that matches the entered time. + var time = elm.value ? timeStringToDate(elm.value, settings) : startTime; + var startMin = startTime.getHours() * 60 + startTime.getMinutes(); + var min = (time.getHours() * 60 + time.getMinutes()) - startMin; + var steps = Math.round(min / settings.step); + var roundTime = normaliseTime(new Date(0, 0, 0, 0, (steps * settings.step + startMin), 0)); + roundTime = (startTime < roundTime && roundTime <= endTime) ? roundTime : startTime; + var $matchedTime = $("li:contains(" + formatTime(roundTime, settings) + ")", $tpDiv); + + if ($matchedTime.length) { + $matchedTime.addClass(selectedClass); + // Scroll to matched time. + $tpDiv[0].scrollTop = $matchedTime[0].offsetTop; + } + return true; + }; + // Attach to click as well as focus so timePicker can be shown again when + // clicking on the input when it already has focus. + $(elm).focus(showPicker).click(showPicker); + // Hide timepicker on blur + $(elm).blur(function() { + if (!tpOver) { + $tpDiv.hide(); + } + }); + // Keypress doesn't repeat on Safari for non-text keys. + // Keydown doesn't repeat on Firefox and Opera on Mac. + // Using kepress for Opera and Firefox and keydown for the rest seems to + // work with up/down/enter/esc. + var event = ($.browser.opera || $.browser.mozilla) ? 'keypress' : 'keydown'; + $(elm)[event](function(e) { + var $selected; + keyDown = true; + var top = $tpDiv[0].scrollTop; + switch (e.keyCode) { + case 38: // Up arrow. + // Just show picker if it's hidden. + if (showPicker()) { + return false; + }; + $selected = $(selectedSelector, $tpList); + var prev = $selected.prev().addClass(selectedClass)[0]; + if (prev) { + $selected.removeClass(selectedClass); + // Scroll item into view. + if (prev.offsetTop < top) { + $tpDiv[0].scrollTop = top - prev.offsetHeight; + } + } + else { + // Loop to next item. + $selected.removeClass(selectedClass); + prev = $("li:last", $tpList).addClass(selectedClass)[0]; + $tpDiv[0].scrollTop = prev.offsetTop - prev.offsetHeight; + } + return false; + break; + case 40: // Down arrow, similar in behaviour to up arrow. + if (showPicker()) { + return false; + }; + $selected = $(selectedSelector, $tpList); + var next = $selected.next().addClass(selectedClass)[0]; + if (next) { + $selected.removeClass(selectedClass); + if (next.offsetTop + next.offsetHeight > top + $tpDiv[0].offsetHeight) { + $tpDiv[0].scrollTop = top + next.offsetHeight; + } + } + else { + $selected.removeClass(selectedClass); + next = $("li:first", $tpList).addClass(selectedClass)[0]; + $tpDiv[0].scrollTop = 0; + } + return false; + break; + case 13: // Enter + if ($tpDiv.is(":visible")) { + var sel = $(selectedSelector, $tpList)[0]; + setTimeVal(elm, sel, $tpDiv, settings); + } + return false; + break; + case 27: // Esc + $tpDiv.hide(); + return false; + break; + } + return true; + }); + $(elm).keyup(function(e) { + keyDown = false; + }); + // Helper function to get an inputs current time as Date object. + // Returns a Date object. + this.getTime = function() { + return timeStringToDate(elm.value, settings); + }; + // Helper function to set a time input. + // Takes a Date object or string. + this.setTime = function(time) { + elm.value = formatTime(timeToDate(time, settings), settings); + // Trigger element's change events. + $(elm).change(); + }; + + }; // End fn; + + // Plugin defaults. + $.fn.timePicker.defaults = { + step:30, + startTime: new Date(0, 0, 0, 0, 0, 0), + endTime: new Date(0, 0, 0, 23, 30, 0), + separator: ':', + show24Hours: true + }; + + // Private functions. + + function setTimeVal(elm, sel, $tpDiv, settings) { + // Update input field + elm.value = $(sel).text(); + // Trigger element's change events. + $(elm).change(); + // Keep focus for all but IE (which doesn't like it) + if (!$.browser.msie) { + elm.focus(); + } + // Hide picker + $tpDiv.hide(); + } + + function formatTime(time, settings) { + var h = time.getHours(); + var hours = settings.show24Hours ? h : (((h + 11) % 12) + 1); + var minutes = time.getMinutes(); + return formatNumber(hours) + settings.separator + formatNumber(minutes) + (settings.show24Hours ? '' : ((h < 12) ? ' AM' : ' PM')); + } + + function formatNumber(value) { + return (value < 10 ? '0' : '') + value; + } + + function timeToDate(input, settings) { + return (typeof input == 'object') ? normaliseTime(input) : timeStringToDate(input, settings); + } + + function timeStringToDate(input, settings) { + if (input) { + var array = input.split(settings.separator); + var hours = parseFloat(array[0]); + var minutes = parseFloat(array[1]); + + // Convert AM/PM hour to 24-hour format. + if (!settings.show24Hours) { + if (hours === 12 && input.indexOf('AM') !== -1) { + hours = 0; + } + else if (hours !== 12 && input.indexOf('PM') !== -1) { + hours += 12; + } + } + var time = new Date(0, 0, 0, hours, minutes, 0); + return normaliseTime(time); + } + return null; + } + + /* Normalise time object to a common date. */ + function normaliseTime(time) { + time.setFullYear(2001); + time.setMonth(0); + time.setDate(0); + return time; + } + +})(jQuery); diff --git a/dav/timepicker/jquery.timePicker.min.js b/dav/timepicker/jquery.timePicker.min.js new file mode 100644 index 000000000..2e092fda8 --- /dev/null +++ b/dav/timepicker/jquery.timePicker.min.js @@ -0,0 +1 @@ +(function(a){function g(a){a.setFullYear(2001),a.setMonth(0),a.setDate(0);return a}function f(a,b){if(a){var c=a.split(b.separator),d=parseFloat(c[0]),e=parseFloat(c[1]);b.show24Hours||(d===12&&a.indexOf("AM")!==-1?d=0:d!==12&&a.indexOf("PM")!==-1&&(d+=12));var f=new Date(0,0,0,d,e,0);return g(f)}return null}function e(a,b){return typeof a=="object"?g(a):f(a,b)}function d(a){return(a<10?"0":"")+a}function c(a,b){var c=a.getHours(),e=b.show24Hours?c:(c+11)%12+1,f=a.getMinutes();return d(e)+b.separator+d(f)+(b.show24Hours?"":c<12?" AM":" PM")}function b(b,c,d,e){b.value=a(c).text(),a(b).change(),a.browser.msie||b.focus(),d.hide()}a.fn.timePicker=function(b){var c=a.extend({},a.fn.timePicker.defaults,b);return this.each(function(){a.timePicker(this,c)})},a.timePicker=function(b,c){var d=a(b)[0];return d.timePicker||(d.timePicker=new jQuery._timePicker(d,c))},a.timePicker.version="0.3",a._timePicker=function(d,h){var i=!1,j=!1,k=e(h.startTime,h),l=e(h.endTime,h),m="selected",n="li."+m;a(d).attr("autocomplete","OFF");var o=[],p=new Date(k);while(p<=l)o[o.length]=c(p,h),p=new Date(p.setMinutes(p.getMinutes()+h.step));var q=a('
    '),r=a("
      ");for(var s=0;s"+o[s]+"");q.append(r),q.appendTo("body").hide(),q.mouseover(function(){i=!0}).mouseout(function(){i=!1}),a("li",r).mouseover(function(){j||(a(n,q).removeClass(m),a(this).addClass(m))}).mousedown(function(){i=!0}).click(function(){b(d,this,q,h),i=!1});var t=function(){if(q.is(":visible"))return!1;a("li",q).removeClass(m);var b=a(d).offset();q.css({top:b.top+d.offsetHeight,left:b.left}),q.show();var e=d.value?f(d.value,h):k,i=k.getHours()*60+k.getMinutes(),j=e.getHours()*60+e.getMinutes()-i,n=Math.round(j/h.step),o=g(new Date(0,0,0,0,n*h.step+i,0));o=kf+q[0].offsetHeight&&(q[0].scrollTop=f+i.offsetHeight)):(e.removeClass(m),i=a("li:first",r).addClass(m)[0],q[0].scrollTop=0);return!1;case 13:if(q.is(":visible")){var k=a(n,r)[0];b(d,k,q,h)}return!1;case 27:q.hide();return!1}return!0}),a(d).keyup(function(a){j=!1}),this.getTime=function(){return f(d.value,h)},this.setTime=function(b){d.value=c(e(b,h),h),a(d).change()}},a.fn.timePicker.defaults={step:30,startTime:new Date(0,0,0,0,0,0),endTime:new Date(0,0,0,23,30,0),separator:":",show24Hours:!0}})(jQuery) \ No newline at end of file diff --git a/dav/timepicker/timePicker.css b/dav/timepicker/timePicker.css new file mode 100644 index 000000000..07ea4139c --- /dev/null +++ b/dav/timepicker/timePicker.css @@ -0,0 +1,29 @@ +div.time-picker { + position: absolute; + height: 191px; + width:4em; /* needed for IE */ + overflow: auto; + background: #fff; + border: 1px solid #aaa; + z-index: 99; + margin: 0; +} +div.time-picker-12hours { + width:6em; /* needed for IE */ +} + +div.time-picker ul { + list-style-type: none; + margin: 0; + padding: 0; +} +div.time-picker li { + cursor: pointer; + height: 10px; + font: 12px/1 Helvetica, Arial, sans-serif; + padding: 4px 3px; +} +div.time-picker li.selected { + background: #0063CE; + color: #fff; +} \ No newline at end of file diff --git a/dav/virtual_cal_source_friendica.inc.php b/dav/virtual_cal_source_friendica.inc.php new file mode 100644 index 000000000..5123e4edd --- /dev/null +++ b/dav/virtual_cal_source_friendica.inc.php @@ -0,0 +1,166 @@ +setConfig('unique_id', $hostname); + + $v->setProperty('method', 'PUBLISH'); + $v->setProperty("x-wr-calname", "AnimexxCal"); + $v->setProperty("X-WR-CALDESC", "Animexx Calendar"); + $v->setProperty("X-WR-TIMEZONE", $timezone); + + if ($row["adjust"]) { + $start = datetime_convert('UTC', date_default_timezone_get(), $row["start"]); + $finish = datetime_convert('UTC', date_default_timezone_get(), $row["finish"]); + } else { + $start = $row["start"]; + $finish = $row["finish"]; + } + $allday = (strpos($start, "00:00:00") !== false && strpos($finish, "00:00:00") !== false); + + /* + + if ($allday) { + $dat = Datetime::createFromFormat("Y-m-d H:i:s", $finish_tmp); + $dat->sub(new DateInterval("P1D")); + $finish = datetime_convert("UTC", date_default_timezone_get(), $dat->format("Y-m-d H:i:s")); + var_dump($finish); + } + */ + + $subject = substr(preg_replace("/\[[^\]]*\]/", "", $row["desc"]), 0, 100); + $description = preg_replace("/\[[^\]]*\]/", "", $row["desc"]); + + $vevent = dav_create_vevent(wdcal_mySql2icalTime($row["start"]), wdcal_mySql2icalTime($row["finish"]), false); + $vevent->setLocation(icalendar_sanitize_string($row["location"])); + $vevent->setSummary(icalendar_sanitize_string($subject)); + $vevent->setDescription(icalendar_sanitize_string($description)); + + $v->setComponent($vevent); + $ical = $v->createCalendar(); + return array( + "uid" => $uid, + "namespace" => CALDAV_NAMESPACE_FRIENDICA_NATIVE, + "namespace_id" => $namespace_id, + "date" => $row["edited"], + "data_uri" => "friendica-" . $namespace_id . "-" . $row["id"] . "@" . $hostname, + "data_subject" => $subject, + "data_location" => $row["location"], + "data_description" => $description, + "data_start" => $start, + "data_end" => $finish, + "data_allday" => $allday, + "data_type" => $row["type"], + "ical" => $ical, + "ical_size" => strlen($ical), + "ical_etag" => md5($ical), + ); + + } + + /** + * @static + * @param int $uid + * @param int $namespace_id + * @param string|int $date_from + * @param string|int $date_to + * @throws Sabre_DAV_Exception_NotFound + * @return array + */ + static public function getItemsByTime($uid = 0, $namespace_id = 0, $date_from = "", $date_to = "") + { + $uid = IntVal($uid); + $namespace_id = IntVal($namespace_id); + + switch ($namespace_id) { + case CALDAV_FRIENDICA_MINE: + $sql_where = " AND cid = 0"; + break; + case CALDAV_FRIENDICA_CONTACTS: + $sql_where = " AND cid > 0"; + break; + default: + throw new Sabre_DAV_Exception_NotFound(); + } + + if ($date_from != "") { + if (is_numeric($date_from)) $sql_where .= " AND `finish` >= '" . date("Y-m-d H:i:s", $date_from) . "'"; + else $sql_where .= " AND `finish` >= '" . dbesc($date_from) . "'"; + } + if ($date_to != "") { + if (is_numeric($date_to)) $sql_where .= " AND `start` <= '" . date("Y-m-d H:i:s", $date_to) . "'"; + else $sql_where .= " AND `start` <= '" . dbesc($date_to) . "'"; + } + + $ret = array(); + $a = get_app(); + $host = $a->get_hostname(); + + + $r = q("SELECT * FROM `event` WHERE `uid` = %d " . $sql_where . " ORDER BY `start`", $uid); + foreach ($r as $row) $ret[] =self::row2array($row, $a->timezone, $host, $uid, $namespace_id); + + return $ret; + } + + + /** + * @static + * @param int $uid + * @param string $uri + * @throws Sabre_DAV_Exception_NotFound + * @return array + */ + static public function getItemsByUri($uid = 0, $uri) + { + $x = explode("-", $uri); + if ($x[0] != "friendica") throw new Sabre_DAV_Exception_NotFound(); + + $namespace_id = IntVal($x[1]); + switch ($namespace_id) { + case CALDAV_FRIENDICA_MINE: + $sql_where = " AND cid = 0"; + break; + case CALDAV_FRIENDICA_CONTACTS: + $sql_where = " AND cid > 0"; + break; + default: + throw new Sabre_DAV_Exception_NotFound(); + } + + $a = get_app(); + $host = $a->get_hostname(); + + $r = q("SELECT * FROM `event` WHERE `uid` = %d AND id = %d " . $sql_where, $uid, IntVal($x[2])); + if (count($r) != 1) throw new Sabre_DAV_Exception_NotFound(); + $ret =self::row2array($r[0], $a->timezone, $host, $uid, $namespace_id); + + return $ret; + } + + +} \ No newline at end of file diff --git a/dav/wdcal.css b/dav/wdcal.css new file mode 100644 index 000000000..c64f0cf45 --- /dev/null +++ b/dav/wdcal.css @@ -0,0 +1,13 @@ + +div.colorPicker-picker { display: inline-block; } +.colorPicker-palette { font-size: 12px; } +.animexxcalendar label, .colorPicker-palette label { background: none; border: none; padding: 0; margin: 0; box-shadow: none; display: inline; font-size: 14px; } +.animexxcalendar input, .colorPicker-palette input { font-size: 14px; } + + +.ui-datepicker { width: 200px; } +.ui-datepicker * { font-size: 12px; line-height: 12px; } +.ui-datepicker th { padding: 10px 2px; } +.ui-datepicker select.ui-datepicker-year { min-width: 0; width: 50px !important; } +#cal_start_time, #cal_end_time { width: 5em; margin-left: 1em; } +#cal_start_date, #cal_end_date { width: 6em;} \ No newline at end of file diff --git a/dav/wdcal/css/cal.gif b/dav/wdcal/css/cal.gif new file mode 100644 index 000000000..386af92fd Binary files /dev/null and b/dav/wdcal/css/cal.gif differ diff --git a/dav/wdcal/css/calendar.css b/dav/wdcal/css/calendar.css new file mode 100644 index 000000000..0f82e9ea8 --- /dev/null +++ b/dav/wdcal/css/calendar.css @@ -0,0 +1,690 @@ +.st-bg-table td { background-color: white; } +.st-grid tr {background-color: transparent; } +.st-c { font-size: 12px; line-height: 14px; } + +.calmain +{ +border-bottom:2px solid #c3c3ff; + background:#c3c3ff +} +.printborder { + border-left:9px solid #c3c3ff +} + +.scrolltimeevent +{ + position:relative; + overflow-y:scroll; + overflow-x:hidden; + border-top:1px solid #a2bbdd; + border-left:1px solid #a2bbdd; + border-bottom:1px solid #fff; + padding:0; + font-size:12px; + line-height:normal; +} +.wk-top +{ + table-layout:fixed; + width:100%; + background-color:#c3c3ff; + overflow:hidden; + font-size:12px; + line-height:14px + } +.gridcontainer { + height:300px; + background:#c3c3ff; + position:relative; + -webkit-user-select:none; + -khtml-user-select:none; +} +.wk-dayname { + font-weight:normal; + color:#112abb; + padding:2px 4px; + margin-left:4px; + white-space:nowrap +} +.wk-today { + color:#fff; + font-weight:bold; + background-color:#8ac; + padding:1px 3px; + border: 1px solid #6786a7; + border-right-color: #ace; + border-bottom-color: #ace; +} +.wk-dummyth { + vertical-align:top; + background-color:#c3c3ff; + padding:0 +} +.wk-daylink +{ + cursor:pointer; +} +.wk-daylink:hover +{ + text-decoration:underline; +} +.wk-allday { + background-color:#e8eef7; + font-size:12px; + line-height:14px; + border-color:#a2bbdd #fff #fff #a2bbdd; + border-style:solid; + border-width:1px +} +.wk-allday .st-c { + padding-left:0; + padding-right:0; + border-left:3px double #E0E0FF +} +.st-grid { + position:relative; + table-layout:fixed; + width:100%; +} + +.st-c { + padding:1px 1px 0 2px; + vertical-align:top; + font-family:Verdana, sans-serif +} +.st-s { + padding-bottom:2px; + cursor:default; +} +.t1 { + font-size:1px; + height:1px; + line-height:1px; + margin:0 2px; + overflow:hidden +} +.t2 { + font-size:1px; + height:1px; + line-height:1px; + margin:0 1px; + overflow:hidden +} +.chromecolor { + background:#c3c3ff; + border-color:#c3c3ff +} +.tg-timedevents { + background-color:#fff; + table-layout:fixed; + width:100%; + margin-top:-1px +} +.tg-timedevents tr { background: none; } + +.tg-times { + padding:0; + font-family:Arial; + font-size:11px; + background-color:#e8eef7; + color:#468; + text-align:right; + vertical-align:top; + padding-right:1px; + padding-top:1px; + overflow:hidden + +} +.tg-time { + border-bottom:1px solid #E0E0FF; + padding-right:2px +} +.tg-col { + border-left:3px double #E0E0FF; + overflow:hidden; + vertical-align:top +} +.tg-col-overlaywrapper { + position:relative; + height:0 +} +.tg-col-eventwrapper { + position:relative; + cursor:default; + margin-right:10px; +} +.tg-today { + background-color:#ffc; +} +.tg-spanningwrapper { + position:relative; + margin-left:3px; + height:1px; + top:1px; +} +.tg-hourmarkers { + position:absolute; + width:100% +} +.tg-dualmarker { + border-top:1px solid #E0E0FF; + height:1em; + line-height:1em; + border-bottom:1px dotted #E0E0FF; + margin-bottom:1em +} +.tg-nowmarker { + position:absolute; + left:0; + width:100%; + top:0; + height:0; + border-top:2px solid red; + overflow:hidden; + opacity:0.4; + filter:alpha(opacity=40); +} +.tg-nowptr { + position:absolute; + background-position:-60px -68px; + width:5px; + height:9px; + background-image:url(images/calendar/combined.gif) +} + +.gridcontainer .not_editable { + opacity: 0.5; +} +.chip { + position:absolute; + overflow:hidden; + cursor:default; + color:#000; + -moz-user-select:none; +} +.chip dl { + margin:0; + padding-bottom:1px; + border-left:1px solid; + border-right:1px solid; + overflow:hidden +} +.chip dt { + margin-top:-1px; + font:bold 80%/normal Verdana, Sans-serif; + white-space:nowrap; + overflow:hidden; + padding-left:1px; + text-align:left; + color: white; +} +.chip a:link, .chip a:visited, .chip a:active { + text-decoration:none; + color:#fff +} +.ie6 .chip dl { + zoom:1 +} +.ie6 .chip dt { + width:100%; + margin-right:-1px +} +.chip dd { + margin:0; + overflow:hidden; + padding:0 1px; + font-family: Verdana, Arial, Helvetica; + font-size: 11px; + text-align:left; + word-wrap:break-word +} +.cb1, .cb2 { + border-style:solid; + overflow:hidden; +} +.cb1 { + height:1px; + margin:0 1px; + border-width:0 1px +} +.cb2 { + height:0; + margin:0 2px; + border-width:0 0 1px +} +.ct { + height:0; + overflow:hidden; + border-style:solid; + border-width:0 2px 2px; + border-color:transparent +} +.ie6 .ct { + border-style:solid dotted +} + +.cic { + background-repeat:no-repeat; + display:inline-block; + margin-left:2px; + width:9px; + height:7px; + line-height:7px; + background-image:url(images/calendar/combined.gif) +} +.ff2 .cic { + display:-moz-inline-box; + vertical-align:bottom; + position:relative; + top:-2px +} +.cic-ques { + margin-left:0; + margin-right:2px; + width:9px; + height:9px; + line-height:9px; + background-position:-56px -50px +} +.cic-prsn { + background-position:-28px -50px +} +.cic-noprs { + background-position:-46px -50px +} +.cic-tmr { + background-position:-28px -57px +} +.cic-rcr { + background-position:-37px -50px +} +.cic-spcl { + background-position:-37px -57px +} +.cic-priv { + background-position:-11px -64px +} + +.resizer { + position:absolute; + height:7px; + line-height:7px; + width:100%; + bottom:0; + cursor:s-resize; + +} +.rszr_icon +{ + /*display:none;*/ + border-top:3px double white; + font-size:0; + line-height:0; + height:0; + width:10px; + margin-left:auto; + margin-right:auto +} +.rb-n { + padding:1px 1px 1px 3px; + overflow:hidden; + white-space:nowrap; + color:white; + -webkit-border-radius:3px; + -moz-border-radius:3px; + cursor:pointer +} +.rb-o { + margin:0 1px; + border-width:1px 0; + border-style:solid; + cursor:pointer +} +.rb-m { + padding-left:3px; + margin:0 -1px; + border-width:0 1px; + border-style:solid; + zoom:1 +} +.rb-i { + overflow:hidden; + color:white; + white-space:nowrap; + width:100% +} +.mv-container { + height:100%; + position:relative; + line-height:14px; + white-space:nowrap; +} +.mv-container th{ + font-size:12px; +} +.bg-exists .mv-container { + background-color:transparent +} +.st-contents, .mv-container { + font-size:11px; + line-height:14px +} +.mv-daynames-table { + position:absolute; + table-layout:fixed; + top:0; + left:0; + width:100%; + background:#c3c3ff; + color:#112abb +} +.mv-dayname { + font-weight:normal +} + +.mv-event-container { + overflow:hidden; + position:absolute; + left:0; + width:100%; + top:14px; + bottom:0; + background:white; +} +.month-row { + position:absolute; + left:0; + width:100%; + overflow:hidden +} +.bg-exists .month-row { + opacity:0.9; + filter:alpha(opacity=90) +} +.st-bg-table { + position:absolute; + top:0; + left:0; + height:100%; + width:100%; + table-layout:fixed +} +.st-bg { + border-left:1px solid #c3c3ff +} +.st-bg-nonmonth { + background-color: #f0f0FF; +} +.st-bg-today { + background-color:#ffc +} +.bg-exists .st-bg-today { + background-color:#eee; + border:1px solid #579; + opacity:0.8; +} +.st-dtitle { + border-left:1px solid #c3c3ff; + border-top:1px solid #c3c3ff; + background-color:#e8eef7; + color:#444; + padding-right:2px; + text-align:right; + line-height:16px; + overflow:hidden +} +.bg-exists .st-dtitle { + opacity:0.8; + filter:alpha(opacity=80) +} +.st-dtitle-nonmonth { + background-color:#f0f0FF; + color:#f0f0FF; +} +.st-dtitle-today { + background-color:#bcd +} + +.st-more { + color:#112abb; + text-align:center; + cursor:pointer; + /*font-family:Arial, sans-serif*/ +} +.st-moreul { + text-decoration:underline +} +.st-moreicon { + background-color:#d0d6de; + color:#627487; + margin:1px; + padding-right:5px; + text-align:right; + font-size:85% +} +.cc { + position:absolute; + border:1px solid gray; + background-color:white; + visibility:hidden +} +.cc-titlebar { + background-color:#e8eef7 +} +.cc-close { + margin:2px 2px 0 0; + position:absolute; + right:0; + cursor:pointer; + width:15px; + height:15px; + background:url(images/calendar/combined.gif) 0 -50px +} +.cc-title { + padding:2px; + font-weight:bold +} +.st-ad-ml, .st-ad-mr { + position:relative; + top:2px; + margin-bottom:-8px; + height:0; + width:0; + line-height:0; + font-size:0; + border-top:4px dotted; + border-top:4px solid transparent; + border-bottom:4px dotted; + border-bottom:4px solid transparent +} +.st-ad-mpad { + padding-left:10px +} +.st-ad-ml { + border-right:4px solid white; + margin-left:-7px; + margin-right:auto +} +.st-ad-mr { + border-left:4px solid white; + margin-left:auto; + margin-right:3px +} + +.bubble { + position: absolute; visibility: hidden; font-size: 9pt; top: 0px; left: 0px +} +.bubble-table { + width: 100%; TABLE-LAYOUT: fixed; + border-spacing: 0; +} +.bubble-table > tbody > tr > td { padding: 0; } +.bubble-cell-side { + width: 25px +} +.bubble-cell-main { + width: 100% +} +.bubble-sprite { + background-image: url(images/calendar/bubble_combined.png); position: absolute; width: 148px; height: 96px +} +.ie6 .bubble-sprite { + filter: progid:DXimageTransform.Microsoft.AlphaimageLoader(sizingMethod="crop",src="/Theme/Default/images/calendar/bubble_combined.png"); background: none transparent scroll repeat 0% 0% +} +.bubble-corner { + position: relative; width: 25px; height: 25px; overflow: hidden +} +.bubble-tl { + left: 0px +} +.bubble-tr { + left: -25px +} +.bubble-bl { + top: -25px; left: 0px +} +.bubble-br { + top: -25px; left: -25px +} +.bubble-closebutton { + position: absolute; width: 15px; background: url(images/calendar/combined.gif) 0px -50px; height: 15px; top: 10px; cursor: pointer; right: 10px +} +.bubble-top { + background: #fff; height: 24px +} +.bubble-bottom { + background: #fff; height: 24px +} +.bubble-top { + border-top: #ababab 1px solid +} +.bubble-bottom { + border-bottom: #ababab 1px solid +} +.bubble-mid { + border-left: #ababab 1px solid; padding-bottom: 0px; padding-left: 20px; padding-right: 20px; background: #fff; border-right: #ababab 1px solid; padding-top: 0px +} +.cb-root { + width: 100%; + font-size: 13px; + overflow: hidden +} +.cb-switcher { + margin-bottom: 0.8em +} +.cb-switcher-selected { + font-weight: bold +} +.cb-table { + width: 100%; margin-bottom: 0.8em +} +.cb-table tr { background: none; } +.cb-example { + font-size: 12px; padding-top: 2px; + font-style:italic +} +.cb-key { + text-align: left; padding-bottom: 0.4em; padding-left: 0px; padding-right: 1em; white-space: nowrap; vertical-align: top; font-weight: normal; padding-top: 0.4em +} +.cb-value { + padding-bottom: 0.4em; padding-left: 0px; width: 95%; padding-right: 0px; padding-top: 0.4em; +} +.cb-textarea { + height: 3.9em +} +.textbox-fill-input { + width:95%; + padding:2px; + border-width:1px; + border-color:#666 #ccc #ccc #666 +} +.lk { + white-space: nowrap; color: #112abb; cursor: pointer; text-decoration: underline +} +.cal-control +{ + +} +.control-main +{ + width: 100%; + vertical-align: bottom; + border-color:#c3c3ff; +} +.view-tab { + padding-bottom: 0px; padding-left: 2px; padding-right: 2px; vertical-align: bottom; cursor: pointer; padding-top: 0px +} +.view-tab-unselected { + background-color: #e8eef7 +} +.view-tab-selected { + background-color: #c3c3ff +} +.tab-name { + padding: 4px 6px 3px; + white-space: nowrap; +} +.tab-spacer { + width: 3px; cursor: default +} +.bbit-cs-split +{ + margin-top:2px; + padding:2px; + border-top:solid 1px #555; +} +.textbox-fill-div { + width:95%; + padding:2px; + font-size:14px; +} + +.drag-lasso-container { + position: absolute; + width: 0px; + height: 0px; + top: 0px; + left: 0px +} +.drag-lasso { + position: absolute; + filter: alpha(opacity=50); + LINE-height: 0; + background-color: #c3c3ff; + font-size: 0px; + opacity: 0.5; + -khtml-opacity: 0.5 +} +.drag-event { + Z-INDEX: 1000; + position: absolute; + font-FAMILY: Verdana,sans-serif; + cursor: pointer +} +.drag-chip-wrapper { + Z-INDEX: 1; + position: absolute; + width: 100%; + left: 0px +} +.drag-chip { + filter: alpha(opacity=80); + cursor: pointer; + opacity: .8; + -khtml-opacity: .8 +} +.prong { + position: relative; width: 98px; margin-bottom: -72px; height: 72px; overflow: hidden; top: -1px; + left:101px; +} +.prong .bubble-sprite { + top: -24px; left: -50px +} +.monthdayshow +{ + cursor:pointer; +} +.monthdayshow:hover +{ + text-decoration:underline; +} diff --git a/dav/wdcal/css/images/calendar/bubble_combined.png b/dav/wdcal/css/images/calendar/bubble_combined.png new file mode 100644 index 000000000..92737be6a Binary files /dev/null and b/dav/wdcal/css/images/calendar/bubble_combined.png differ diff --git a/dav/wdcal/css/images/calendar/combined.gif b/dav/wdcal/css/images/calendar/combined.gif new file mode 100644 index 000000000..93d0f4434 Binary files /dev/null and b/dav/wdcal/css/images/calendar/combined.gif differ diff --git a/dav/wdcal/css/images/calendar/grid.png b/dav/wdcal/css/images/calendar/grid.png new file mode 100644 index 000000000..c4da495db Binary files /dev/null and b/dav/wdcal/css/images/calendar/grid.png differ diff --git a/dav/wdcal/css/images/calendar/gridth.gif b/dav/wdcal/css/images/calendar/gridth.gif new file mode 100644 index 000000000..8d459a304 Binary files /dev/null and b/dav/wdcal/css/images/calendar/gridth.gif differ diff --git a/dav/wdcal/css/images/calendar/headbg.gif b/dav/wdcal/css/images/calendar/headbg.gif new file mode 100644 index 000000000..fe7dd1c1e Binary files /dev/null and b/dav/wdcal/css/images/calendar/headbg.gif differ diff --git a/dav/wdcal/css/images/calendar/indicator_web20_working.gif b/dav/wdcal/css/images/calendar/indicator_web20_working.gif new file mode 100644 index 000000000..6dc6215c5 Binary files /dev/null and b/dav/wdcal/css/images/calendar/indicator_web20_working.gif differ diff --git a/dav/wdcal/css/images/calendar/load-bg.png b/dav/wdcal/css/images/calendar/load-bg.png new file mode 100644 index 000000000..f00c932ee Binary files /dev/null and b/dav/wdcal/css/images/calendar/load-bg.png differ diff --git a/dav/wdcal/css/images/calendar/tbg.gif b/dav/wdcal/css/images/calendar/tbg.gif new file mode 100644 index 000000000..0b085bf24 Binary files /dev/null and b/dav/wdcal/css/images/calendar/tbg.gif differ diff --git a/dav/wdcal/css/images/icons/Btn.Close.gif b/dav/wdcal/css/images/icons/Btn.Close.gif new file mode 100644 index 000000000..79ab96df4 Binary files /dev/null and b/dav/wdcal/css/images/icons/Btn.Close.gif differ diff --git a/dav/wdcal/css/images/icons/Item.Add.gif b/dav/wdcal/css/images/icons/Item.Add.gif new file mode 100644 index 000000000..079ecfd95 Binary files /dev/null and b/dav/wdcal/css/images/icons/Item.Add.gif differ diff --git a/dav/wdcal/css/images/icons/Item.Delete.gif b/dav/wdcal/css/images/icons/Item.Delete.gif new file mode 100644 index 000000000..9939b7ea7 Binary files /dev/null and b/dav/wdcal/css/images/icons/Item.Delete.gif differ diff --git a/dav/wdcal/css/images/icons/Item.Edit.gif b/dav/wdcal/css/images/icons/Item.Edit.gif new file mode 100644 index 000000000..f8d9b5417 Binary files /dev/null and b/dav/wdcal/css/images/icons/Item.Edit.gif differ diff --git a/dav/wdcal/css/images/icons/Item.Input.gif b/dav/wdcal/css/images/icons/Item.Input.gif new file mode 100644 index 000000000..8c28a48c6 Binary files /dev/null and b/dav/wdcal/css/images/icons/Item.Input.gif differ diff --git a/dav/wdcal/css/images/icons/Item.Save.gif b/dav/wdcal/css/images/icons/Item.Save.gif new file mode 100644 index 000000000..d85915ba6 Binary files /dev/null and b/dav/wdcal/css/images/icons/Item.Save.gif differ diff --git a/dav/wdcal/css/images/icons/Item.Search.gif b/dav/wdcal/css/images/icons/Item.Search.gif new file mode 100644 index 000000000..1c149f9e8 Binary files /dev/null and b/dav/wdcal/css/images/icons/Item.Search.gif differ diff --git a/dav/wdcal/css/images/icons/add.png b/dav/wdcal/css/images/icons/add.png new file mode 100644 index 000000000..6332fefea Binary files /dev/null and b/dav/wdcal/css/images/icons/add.png differ diff --git a/dav/wdcal/css/images/icons/appt.gif b/dav/wdcal/css/images/icons/appt.gif new file mode 100644 index 000000000..b6f07d616 Binary files /dev/null and b/dav/wdcal/css/images/icons/appt.gif differ diff --git a/dav/wdcal/css/images/icons/au.gif b/dav/wdcal/css/images/icons/au.gif new file mode 100644 index 000000000..5269c6a0e Binary files /dev/null and b/dav/wdcal/css/images/icons/au.gif differ diff --git a/dav/wdcal/css/images/icons/cacheClear.png b/dav/wdcal/css/images/icons/cacheClear.png new file mode 100644 index 000000000..59a6d9c61 Binary files /dev/null and b/dav/wdcal/css/images/icons/cacheClear.png differ diff --git a/dav/wdcal/css/images/icons/cal-day.gif b/dav/wdcal/css/images/icons/cal-day.gif new file mode 100644 index 000000000..feecbc5ac Binary files /dev/null and b/dav/wdcal/css/images/icons/cal-day.gif differ diff --git a/dav/wdcal/css/images/icons/cal-month.gif b/dav/wdcal/css/images/icons/cal-month.gif new file mode 100644 index 000000000..8df4ef43e Binary files /dev/null and b/dav/wdcal/css/images/icons/cal-month.gif differ diff --git a/dav/wdcal/css/images/icons/cal-proc.gif b/dav/wdcal/css/images/icons/cal-proc.gif new file mode 100644 index 000000000..e7a3c16e5 Binary files /dev/null and b/dav/wdcal/css/images/icons/cal-proc.gif differ diff --git a/dav/wdcal/css/images/icons/cal-rsc-perm.gif b/dav/wdcal/css/images/icons/cal-rsc-perm.gif new file mode 100644 index 000000000..0fbefbbfb Binary files /dev/null and b/dav/wdcal/css/images/icons/cal-rsc-perm.gif differ diff --git a/dav/wdcal/css/images/icons/cal-week.gif b/dav/wdcal/css/images/icons/cal-week.gif new file mode 100644 index 000000000..7454d0f0d Binary files /dev/null and b/dav/wdcal/css/images/icons/cal-week.gif differ diff --git a/dav/wdcal/css/images/icons/cal-wk-week.gif b/dav/wdcal/css/images/icons/cal-wk-week.gif new file mode 100644 index 000000000..766f353c5 Binary files /dev/null and b/dav/wdcal/css/images/icons/cal-wk-week.gif differ diff --git a/dav/wdcal/css/images/icons/cal.gif b/dav/wdcal/css/images/icons/cal.gif new file mode 100644 index 000000000..386af92fd Binary files /dev/null and b/dav/wdcal/css/images/icons/cal.gif differ diff --git a/dav/wdcal/css/images/icons/calendar.gif b/dav/wdcal/css/images/icons/calendar.gif new file mode 100644 index 000000000..399d971ad Binary files /dev/null and b/dav/wdcal/css/images/icons/calendar.gif differ diff --git a/dav/wdcal/css/images/icons/calwrkwk.gif b/dav/wdcal/css/images/icons/calwrkwk.gif new file mode 100644 index 000000000..45209a912 Binary files /dev/null and b/dav/wdcal/css/images/icons/calwrkwk.gif differ diff --git a/dav/wdcal/css/images/icons/canmtg.gif b/dav/wdcal/css/images/icons/canmtg.gif new file mode 100644 index 000000000..d86c1dd4d Binary files /dev/null and b/dav/wdcal/css/images/icons/canmtg.gif differ diff --git a/dav/wdcal/css/images/icons/circle_animation.gif b/dav/wdcal/css/images/icons/circle_animation.gif new file mode 100644 index 000000000..92c5c30ca Binary files /dev/null and b/dav/wdcal/css/images/icons/circle_animation.gif differ diff --git a/dav/wdcal/css/images/icons/clndr.gif b/dav/wdcal/css/images/icons/clndr.gif new file mode 100644 index 000000000..5cbbf2cec Binary files /dev/null and b/dav/wdcal/css/images/icons/clndr.gif differ diff --git a/dav/wdcal/css/images/icons/clndrsmll.gif b/dav/wdcal/css/images/icons/clndrsmll.gif new file mode 100644 index 000000000..4aadf92c9 Binary files /dev/null and b/dav/wdcal/css/images/icons/clndrsmll.gif differ diff --git a/dav/wdcal/css/images/icons/cn.gif b/dav/wdcal/css/images/icons/cn.gif new file mode 100644 index 000000000..b05253097 Binary files /dev/null and b/dav/wdcal/css/images/icons/cn.gif differ diff --git a/dav/wdcal/css/images/icons/date.png b/dav/wdcal/css/images/icons/date.png new file mode 100644 index 000000000..783c83357 Binary files /dev/null and b/dav/wdcal/css/images/icons/date.png differ diff --git a/dav/wdcal/css/images/icons/date_add.png b/dav/wdcal/css/images/icons/date_add.png new file mode 100644 index 000000000..6a7ae025f Binary files /dev/null and b/dav/wdcal/css/images/icons/date_add.png differ diff --git a/dav/wdcal/css/images/icons/date_reflash.png b/dav/wdcal/css/images/icons/date_reflash.png new file mode 100644 index 000000000..5ab481fdf Binary files /dev/null and b/dav/wdcal/css/images/icons/date_reflash.png differ diff --git a/dav/wdcal/css/images/icons/delete.png b/dav/wdcal/css/images/icons/delete.png new file mode 100644 index 000000000..08f249365 Binary files /dev/null and b/dav/wdcal/css/images/icons/delete.png differ diff --git a/dav/wdcal/css/images/icons/edit.png b/dav/wdcal/css/images/icons/edit.png new file mode 100644 index 000000000..0bfecd50e Binary files /dev/null and b/dav/wdcal/css/images/icons/edit.png differ diff --git a/dav/wdcal/css/images/icons/folder_user.gif b/dav/wdcal/css/images/icons/folder_user.gif new file mode 100644 index 000000000..b91e6e086 Binary files /dev/null and b/dav/wdcal/css/images/icons/folder_user.gif differ diff --git a/dav/wdcal/css/images/icons/group.gif b/dav/wdcal/css/images/icons/group.gif new file mode 100644 index 000000000..981864b7f Binary files /dev/null and b/dav/wdcal/css/images/icons/group.gif differ diff --git a/dav/wdcal/css/images/icons/group_add.gif b/dav/wdcal/css/images/icons/group_add.gif new file mode 100644 index 000000000..378c9634b Binary files /dev/null and b/dav/wdcal/css/images/icons/group_add.gif differ diff --git a/dav/wdcal/css/images/icons/hk.png b/dav/wdcal/css/images/icons/hk.png new file mode 100644 index 000000000..1b6672c3d Binary files /dev/null and b/dav/wdcal/css/images/icons/hk.png differ diff --git a/dav/wdcal/css/images/icons/ico1.gif b/dav/wdcal/css/images/icons/ico1.gif new file mode 100644 index 000000000..c3c282097 Binary files /dev/null and b/dav/wdcal/css/images/icons/ico1.gif differ diff --git a/dav/wdcal/css/images/icons/indicator.gif b/dav/wdcal/css/images/icons/indicator.gif new file mode 100644 index 000000000..085ccaeca Binary files /dev/null and b/dav/wdcal/css/images/icons/indicator.gif differ diff --git a/dav/wdcal/css/images/icons/msg_center.gif b/dav/wdcal/css/images/icons/msg_center.gif new file mode 100644 index 000000000..1c4551fe6 Binary files /dev/null and b/dav/wdcal/css/images/icons/msg_center.gif differ diff --git a/dav/wdcal/css/images/icons/msg_email.gif b/dav/wdcal/css/images/icons/msg_email.gif new file mode 100644 index 000000000..df138fc13 Binary files /dev/null and b/dav/wdcal/css/images/icons/msg_email.gif differ diff --git a/dav/wdcal/css/images/icons/msg_im.gif b/dav/wdcal/css/images/icons/msg_im.gif new file mode 100644 index 000000000..85b86180f Binary files /dev/null and b/dav/wdcal/css/images/icons/msg_im.gif differ diff --git a/dav/wdcal/css/images/icons/msg_mobile.gif b/dav/wdcal/css/images/icons/msg_mobile.gif new file mode 100644 index 000000000..8ae50b789 Binary files /dev/null and b/dav/wdcal/css/images/icons/msg_mobile.gif differ diff --git a/dav/wdcal/css/images/icons/mtgreq-cancel.gif b/dav/wdcal/css/images/icons/mtgreq-cancel.gif new file mode 100644 index 000000000..d86c1dd4d Binary files /dev/null and b/dav/wdcal/css/images/icons/mtgreq-cancel.gif differ diff --git a/dav/wdcal/css/images/icons/mtgreq.gif b/dav/wdcal/css/images/icons/mtgreq.gif new file mode 100644 index 000000000..f40b5d9d6 Binary files /dev/null and b/dav/wdcal/css/images/icons/mtgreq.gif differ diff --git a/dav/wdcal/css/images/icons/mtgrsp-accept.gif b/dav/wdcal/css/images/icons/mtgrsp-accept.gif new file mode 100644 index 000000000..ed5820ca4 Binary files /dev/null and b/dav/wdcal/css/images/icons/mtgrsp-accept.gif differ diff --git a/dav/wdcal/css/images/icons/mtgrsp-decline.gif b/dav/wdcal/css/images/icons/mtgrsp-decline.gif new file mode 100644 index 000000000..d45b4bdbe Binary files /dev/null and b/dav/wdcal/css/images/icons/mtgrsp-decline.gif differ diff --git a/dav/wdcal/css/images/icons/mtgrsp-tent.gif b/dav/wdcal/css/images/icons/mtgrsp-tent.gif new file mode 100644 index 000000000..1da8e9cfb Binary files /dev/null and b/dav/wdcal/css/images/icons/mtgrsp-tent.gif differ diff --git a/dav/wdcal/css/images/icons/pgrs-sm.gif b/dav/wdcal/css/images/icons/pgrs-sm.gif new file mode 100644 index 000000000..2a96840f4 Binary files /dev/null and b/dav/wdcal/css/images/icons/pgrs-sm.gif differ diff --git a/dav/wdcal/css/images/icons/resultset_next.png b/dav/wdcal/css/images/icons/resultset_next.png new file mode 100644 index 000000000..e252606d3 Binary files /dev/null and b/dav/wdcal/css/images/icons/resultset_next.png differ diff --git a/dav/wdcal/css/images/icons/resultset_previous.png b/dav/wdcal/css/images/icons/resultset_previous.png new file mode 100644 index 000000000..18f9cc109 Binary files /dev/null and b/dav/wdcal/css/images/icons/resultset_previous.png differ diff --git a/dav/wdcal/css/images/icons/rowdelete.png b/dav/wdcal/css/images/icons/rowdelete.png new file mode 100644 index 000000000..54c69691e Binary files /dev/null and b/dav/wdcal/css/images/icons/rowdelete.png differ diff --git a/dav/wdcal/css/images/icons/table.gif b/dav/wdcal/css/images/icons/table.gif new file mode 100644 index 000000000..8c774f54b Binary files /dev/null and b/dav/wdcal/css/images/icons/table.gif differ diff --git a/dav/wdcal/css/images/icons/table_c.gif b/dav/wdcal/css/images/icons/table_c.gif new file mode 100644 index 000000000..aaffb7968 Binary files /dev/null and b/dav/wdcal/css/images/icons/table_c.gif differ diff --git a/dav/wdcal/css/images/icons/table_copy.png b/dav/wdcal/css/images/icons/table_copy.png new file mode 100644 index 000000000..0528dfa24 Binary files /dev/null and b/dav/wdcal/css/images/icons/table_copy.png differ diff --git a/dav/wdcal/css/images/icons/table_r.png b/dav/wdcal/css/images/icons/table_r.png new file mode 100644 index 000000000..28b8505c0 Binary files /dev/null and b/dav/wdcal/css/images/icons/table_r.png differ diff --git a/dav/wdcal/css/images/icons/table_refresh.png b/dav/wdcal/css/images/icons/table_refresh.png new file mode 100644 index 000000000..ab92010c2 Binary files /dev/null and b/dav/wdcal/css/images/icons/table_refresh.png differ diff --git a/dav/wdcal/css/images/icons/tick.png b/dav/wdcal/css/images/icons/tick.png new file mode 100644 index 000000000..a9925a06a Binary files /dev/null and b/dav/wdcal/css/images/icons/tick.png differ diff --git a/dav/wdcal/css/images/icons/turnback.png b/dav/wdcal/css/images/icons/turnback.png new file mode 100644 index 000000000..aa65210eb Binary files /dev/null and b/dav/wdcal/css/images/icons/turnback.png differ diff --git a/dav/wdcal/css/images/icons/us.gif b/dav/wdcal/css/images/icons/us.gif new file mode 100644 index 000000000..8f198f73a Binary files /dev/null and b/dav/wdcal/css/images/icons/us.gif differ diff --git a/dav/wdcal/css/images/icons/view.png b/dav/wdcal/css/images/icons/view.png new file mode 100644 index 000000000..6d7ab94b4 Binary files /dev/null and b/dav/wdcal/css/images/icons/view.png differ diff --git a/dav/wdcal/css/main.css b/dav/wdcal/css/main.css new file mode 100644 index 000000000..c9ca2d36f --- /dev/null +++ b/dav/wdcal/css/main.css @@ -0,0 +1,554 @@ +.toolBotton +{ + border: none; + overflow: hidden; + position: relative; + height: 25px; + padding:0; + margin:0; +} +.toolBotton a +{ + display:block; + width:auto; + cursor:pointer; +} +.toolBotton a span +{ + float: left; + display:block; + width:auto; + cursor:pointer; +} + +input +{ + border: solid 1px #3C7FB1; +} + +select +{ + border: #3C7FB1 1px solid; +} +input[type='text'] +{ + border: solid 1px #3C7FB1; + height:18px; +} +input[type='checkbox'] +{ + border: none; +} +input[type='radio'] +{ + border: none; +} +textarea +{ + border: solid 1px #3C7FB1; + overflow:auto; +} +.ellipsis +{ + white-space: nowrap; + text-overflow: ellipsis; /* for internet explorer */ + overflow: hidden; + display: block; +} +html > body .ellipsis +{ + clear: both; +} +.bbit-main +{ + position:absolute; + left:0; + top:0; + width:100%; + height:100%; + z-index:0; + background-color:#E8F1F8; +} +a.linkdelete span +{ + text-decoration:underline; + color: #000000; + padding-left:18px; + background: url(./images/icons/delete.png) no-repeat left 6px; + padding-top:5px; +} +a.imgbtn +{ + color: #000000; +} +a.imgbtn span +{ + border: #E8E8FF 1px solid; + padding: 4px 4px 3px 21px; + cursor: pointer; +} + +a.imgbtn:hover span +{ + border: #466094 1px solid; + cursor: pointer; + background-color: #C3C3FF; +} +a.imgbtn span.Save +{ + background: url(./images/icons/Item.Save.gif) no-repeat 3px 4px; +} +a.imgbtn span.Close +{ + background: url(./images/icons/Btn.Close.gif) no-repeat 3px 4px; +} +a.imgbtn span.Delete +{ + background: url(./images/icons/delete.png) no-repeat 3px 4px; +} + +span.addcal +{ + padding-left:20px; + background: url(./images/icons/date_add.png) no-repeat 1px 50%; +} +span.showdayview +{ + padding-left:20px; + background: url(./images/icons/cal-day.gif) no-repeat 1px 50%; +} +span.showweekview +{ + padding-left:20px; + background: url(./images/icons/cal-week.gif) no-repeat 1px 50%; +} +span.showwkweekview +{ + padding-left:20px; + background: url(./images/icons/cal-wk-week.gif) no-repeat 1px 50%; +} +span.chinese +{ + padding-left:20px; + background: url(./images/icons/cn.gif) no-repeat 1px 50%; +} +span.english +{ + padding-left:20px; + background: url(./images/icons/us.gif) no-repeat 1px 50%; +} +span.english_au +{ + padding-left:20px; + background: url(./images/icons/au.gif) no-repeat 1px 50%; +} +span.showmonthview +{ + padding-left:20px; + background: url(./images/icons/cal-month.gif) no-repeat 1px 50%; +} +span.showtoday +{ + padding-left:20px; + background: url(./images/icons/date.png) no-repeat 1px 50%; +} + +span.fprev +{ + width:18px; + background:url("./images/icons/resultset_previous.png") no-repeat 1px bottom; +} +span.fnext +{ + width:18px; + background:url("./images/icons/resultset_next.png") no-repeat 1px bottom; +} +span.showdayflash +{ + padding-left:20px; + background:url("./images/icons/date_reflash.png") no-repeat 1px 50%; +} +.size1 +{ + width:50px; +} +.size2 +{ + width:100px; +} +.size3 +{ + width:160px; +} +.size4 +{ + width:200px; +} +.size5 +{ + width:260px; +} +.size6 +{ + width:300px; +} +.size7 +{ + width:360px; +} + + +.X +{ + float:right; + padding-right:3px; + color:#555; + cursor:pointer; + height:16px; +} +.nodisplay +{ + display:none; +} +/* JQuery Validation */ +label.error +{ + padding: 2px 0 2px 20px; + display: block; + background: url(images/validation/exclamation.png) no-repeat 2px center; +} +input.error,select.error,textarea.error, input.cusErrorPanel,cusErrorPanel.cusErrorPanel,textarea.cusErrorPanel +{ + border:solid 1px red; +} +label.checked +{ + float:none; + position:relative; + padding: 0 0 0 18px; + background: url(images/validation/accept.png) no-repeat 2px center; +} +div.error +{ + +} + +div.cusErrorPanel +{ + z-index: auto; + position:absolute; + width: 276px; + height: 35px; + overflow: hidden; + text-indent: 5px; + line-height: 40px; + font-size: 12px; + font-family: Arial; + background: url(images/validation/tooltop.gif) no-repeat left top; + opacity:0.9; + filter: alpha(opacity=90); +} +.infocontainer +{ + margin:2px; + padding:4px; +} +.fform +{ + margin:0; +} +.fform label +{ + display:block; + margin-top:2px; + padding:2px; + vertical-align:top; +} +.fform label.checkp +{ + display:inline; +} +.fform label span +{ + font-weight:bold; + display:block; +} +input.noborder +{ + border:none; +} + + +.doing +{ + color: red; + vertical-align: middle; + display: none; + position: absolute; + left: 20%; + top: 20%; + border: solid 1px #555; + background-color: #FFFF00; + padding: 5px; +} +div.clear +{ + clear:both; +} + + + +div.cHead +{ + background: url(images/calendar/headbg.gif) repeat-x top; + border: 1px solid #99bbe8; + border-top: 0px; + font-weight: bold; + display: block; + overflow: hidden; + white-space: nowrap; + position: relative; +} + + div.cHead div +{ + padding:4px; + white-space: nowrap; +} + div.cHead div.ftitle +{ + background:url("images/calendar/grid.png") no-repeat left ; + color:#15428b; + margin-left:10px; + padding-left:20px; +} + div.cHead div.ptogtitle +{ + position: absolute; + top: 3px; + right: 15px; + height: 18px; + padding: 1px 2px 1px 2px; + font-weight: normal; +} + div.cHead div.loadicon +{ + background:#c44; + color:#fff; + +} + div.cHead div.loaderror +{ + padding: 1px 4px 1px 4px; + background:#fad163; +} + +div.cHead div.ptogtitle input +{ + height:16px; + border:solid 1px #ccc; + background-color:#fff; + padding-left:4px; +} + +div.ctoolbar +{ + OVERFLOW: hidden; + POSITION: relative; + HEIGHT: 25px; + background: #E0E0FF; + margin-bottom: 2px; +} +div.ctoolbar div.fbutton +{ + PADDING-RIGHT: 1px; + DISPLAY: block; + PADDING-LEFT: 1px; + FLOAT: left; + PADDING-BOTTOM: 1px; + CURSOR: pointer; + PADDING-TOP: 1px; + HEIGHT: 22px; + margin-left:1px; + +} + +div.ctoolbar div.fbutton div +{ + float: left; + padding: 2px 3px; +} +div.ctoolbar div.fbutton span +{ + float: left; + display: block; + height:18px; +} + +div.ctoolbar div.fbutton.fcurrent +{ + border: #555 1px solid; + padding: 0; + cursor: pointer; + background-color: #EDF1D5; +} + +div.ctoolbar div.fbutton.fcurrent div +{ + padding: 1px 2px; + border-left: 1px solid #fff; + border-top: 1px solid #fff; + border-right: 1px solid #eee; + border-bottom: 1px solid #eee; +} + +div.ctoolbar div.fbutton:hover, .div.ctoolbar div.fbutton.fbOver +{ + border: #466094 1px solid; + padding: 0; + cursor: pointer; + background-color: #EDF1D5; +} + +div.ctoolbar div.fbutton:hover div, .div.ctoolbar div.fbutton.fbOver div +{ + padding: 1px 2px; + border: 1px solid #fff; + border-right-color: #eee; + border-bottom-color: #eee; +} + +div.ctoolbar div.btnseparator +{ + float: left; + height: 18px; + border-left: 1px solid #ccc; + border-right: 1px solid #fff; + margin: 1px; + +} + +div.ctoolbar div.fshowdatep +{ + padding: 1px; + display: block; + float: left; + cursor:default; + height: 22px; +} +div.ctoolbar div.fshowdatep div +{ + float: left; + padding: 2px 3px; +} + +div.ctoolbar div.fshowdatep span +{ + float: left; + display: block; +} +div.cbody div.ctablehead +{ + border: 1px solid #ccc; + border-top-color: #fff; + border-bottom: 0 #9c9c9c; + background: #e3f7ff url('images/calendar/gridth.gif') repeat-x left bottom; + overflow: hidden; + position: relative ; + height:25px; +} +div.cbody div.ctablehead th div,div.cbody div.ctablebody td div +{ + padding: 4px; + border-left:none; + overflow:hidden; +} +div.cbody div.ctablehead th /* common cell properties*/ +{ + text-align: left; + border-right:solid 1px #ddd; + border-left:solid 1px #fff; + overflow: hidden; + vertical-align: top !important; + font-weight: normal; + cursor: default; + white-space: nowrap; +} +div.cbody div.ctablehead div.ctableheadp +{ + float:left; +} +div.cbody div.ctablehead th div +{ + height:17px; + + } + + div.cbody div.ctablebody + { + BORDER-RIGHT: #ccc 1px solid; + BORDER-TOP: #ccc 0px solid; + BACKGROUND: #dfe8f6; + OVERFLOW: auto; + BORDER-LEFT: #ccc 1px solid; + BORDER-BOTTOM: #ccc 1px solid; + POSITION: relative + } + + div.cbody div.ctablebody td /* common cell properties*/ +{ + text-align: left; + border-left:solid 1px #fff; + border-top: none; + border-bottom: 1px dotted #ddd; + border-right:solid 1px #ddd; + overflow: hidden; + vertical-align: top !important; + white-space: nowrap; + background-color:#fff; + cursor:default; +} + div.cbody div.ctablebody tr.erow td +{ + background: #F5FFEF; +} + div.cbody div.ctablebody tr:hover td, + div.cbody div.ctablebody tr.trOver td +{ + background: #FFFFBB; + border-left: 1px solid #eef8ff; + border-bottom: 1px dotted #a8d8eb; +} + + + div.cbody div.ctablebody td div +{ + border-top: 0 solid #fff; + padding-bottom: 2px; +} + + + +div.ViewgBlock +{ + background: #E3E3E3 url('images/calendar/load-bg.png'); +} +div.ViewgBlock>div.loading +{ + width: 100%; + height: 100%; + display: block; + opacity:0.5; + filter: alpha(opacity=50); + background:#f4f4f4 url('images/calendar/indicator_web20_working.gif') no-repeat center center; +} + + + +.transparent +{ + filter:alpha(opacity=0); + -moz-opacity:0; + opacity:0; + background-color:#000; +} \ No newline at end of file diff --git a/dav/wdcal_cal_source_friendicaevents.inc.php b/dav/wdcal_cal_source_friendicaevents.inc.php new file mode 100644 index 000000000..ed44bf965 --- /dev/null +++ b/dav/wdcal_cal_source_friendicaevents.inc.php @@ -0,0 +1,164 @@ +calendarDb->uid) return array("read"=> true, "write"=> false); + return array("read"=> false, "write"=> false); + } + + /** + * @param int $user + * @param string $item_uri + * @param string $recurrence_uri + * @param null|array $item_arr + * @return array + */ + public function getPermissionsItem($user, $item_uri, $recurrence_uri, $item_arr = null) + { + $cal_perm = $this->getPermissionsCalendar($user); + if (!$cal_perm["read"]) return array("read"=> false, "write"=> false); + return array("read"=> true, "write"=> false); + } + + + /** + * @param string $uri + * @param array $start + * @param array $end + * @param string $subject + * @param bool $allday + * @param string $description + * @param string $location + * @param null $color + * @param string $timezone + * @param bool $notification + * @param null $notification_type + * @param null $notification_value + * @throws Sabre_DAV_Exception_MethodNotAllowed + */ + public function updateItem($uri, $start, $end, $subject = "", $allday = false, $description = "", $location = "", $color = null, $timezone = "", $notification = true, $notification_type = null, $notification_value = null) + { + throw new Sabre_DAV_Exception_MethodNotAllowed(); + } + + /** + * @param array $start + * @param array $end + * @param string $subject + * @param bool $allday + * @param string $description + * @param string $location + * @param null $color + * @param string $timezone + * @param bool $notification + * @param null $notification_type + * @param null $notification_value + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return array|string + */ + public function addItem($start, $end, $subject, $allday = false, $description = "", $location = "", $color = null, + $timezone = "", $notification = true, $notification_type = null, $notification_value = null) + { + throw new Sabre_DAV_Exception_MethodNotAllowed(); + } + + /** + * @param array $row + * @return array + */ + private function virtualData2wdcal($row) { + $end = wdcal_mySql2PhpTime($row["data_end"]); + if ($row["data_allday"]) $end--; + $start = wdcal_mySql2PhpTime($row["data_start"]); + $a = get_app(); + $arr = array( + "uri" => $row["data_uri"], + "subject" => escape_tags($row["data_subject"]), + "start" => $start, + "end" => $end, + "is_allday" => ($row["data_allday"] == 1), + "is_moredays" => (date("Ymd", $start) != date("Ymd", $end)), + "is_recurring" => ($row["data_type"] == "birthday"), + "color" => "#ff0000", + "is_editable" => false, + "is_editable_quick" => false, + "location" => $row["data_location"], + "attendees" => '', + "has_notification" => false, + "url_detail" => $a->get_baseurl() . "/dav/wdcal/" . $row["data_uri"] . "/", + "url_edit" => "", + "special_type" => ($row["data_type"] == "birthday" ? "birthday" : ""), + ); + return $arr; + } + + /** + * @param string $sd + * @param string $ed + * @param string $base_path + * @return array + */ + public function listItemsByRange($sd, $ed, $base_path) + { + $usr_id = IntVal($this->calendarDb->uid); + + $evs = FriendicaVirtualCalSourceBackend::getItemsByTime($usr_id, $this->namespace_id, $sd, $ed); + $events = array(); + foreach ($evs as $row) $events[] = $this->virtualData2wdcal($row); + + return $events; + } + + /** + * @param string $uri + * @throws Sabre_DAV_Exception_MethodNotAllowed + * @return void + */ + public function removeItem($uri) { + throw new Sabre_DAV_Exception_MethodNotAllowed(); + } + + /** + * @param string $uri + * @return array + */ + public function getItemByUri($uri) + { + $usr_id = IntVal($this->calendarDb->uid); + $row = FriendicaVirtualCalSourceBackend::getItemsByUri($usr_id, $uri); + return $this->virtualData2wdcal($row); + } + + /** + * @param string $uri + * @return string + */ + public function getItemDetailRedirect($uri) { + $x = explode("@", $uri); + $y = explode("-", $x[0]); + $a = get_app(); + if (count($y) != 3) { + goaway($a->get_baseurl() . "/dav/wdcal/"); + killme(); + } + $a = get_app(); + $item = q("SELECT `id` FROM `item` WHERE `event-id` = %d AND `uid` = %d AND deleted = 0", IntVal($y[2]), $a->user["uid"]); + if (count($item) == 0) return "/events/"; + return "/display/" . $a->user["nickname"] . "/" . IntVal($item[0]["id"]); + } +} diff --git a/infiniteimprobabilitydrive.tgz b/infiniteimprobabilitydrive.tgz new file mode 100644 index 000000000..6d10fb691 Binary files /dev/null and b/infiniteimprobabilitydrive.tgz differ diff --git a/infiniteimprobabilitydrive/infiniteimprobabilitydrive.css b/infiniteimprobabilitydrive/infiniteimprobabilitydrive.css new file mode 100644 index 000000000..f4be60515 --- /dev/null +++ b/infiniteimprobabilitydrive/infiniteimprobabilitydrive.css @@ -0,0 +1,4 @@ +section { + padding-left: 0px; + +} \ No newline at end of file diff --git a/infiniteimprobabilitydrive/infiniteimprobabilitydrive.php b/infiniteimprobabilitydrive/infiniteimprobabilitydrive.php new file mode 100644 index 000000000..dce369245 --- /dev/null +++ b/infiniteimprobabilitydrive/infiniteimprobabilitydrive.php @@ -0,0 +1,49 @@ +' . t('Infinite Improbability Drive') . '
      '; +} + + +function infiniteimprobabilitydrive_module() { +return; +} + + +function infiniteimprobabilitydrive_content(&$a) { +$baseurl = $a->get_baseurl() . '/addon/infiniteimprobabilitydrive'; +$o = ''; + +$a->page['htmlhead'] .= ''; + + +$baseurl = $a->get_baseurl(); + + + $o .= <<< EOT + +

      +

      Try another destination with the Infinite Improbability Drive +

      GG]@Y)si"Ht/*iKgU#T=Zh_i5MnbdK%R=U_Ft3+?NO*3L6$\22:mWU_D/?mcfF[skE5bI02k<5Uk>,HK[+.U+VshV2)^?HJ-menkN5,rjQI?s0(^+h&F7T$@;h'=<+JB;Oko>@K-i=pkZ2qL*EuFHSLJM@o'7M?t,R&5s6KDBgHq7=!;f$S)ps(f^TC\C.`<(P9Wl1a0_%RQF)arpC*birpCFL3(UgIjFul&hhVA0?=E,:Ul,>+Ml*-PIUklEYiXmG@in?&oao(@`"!ucgUb\2HQgK?;"T)8h,0k[6~> +endstream +endobj +720 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 719 0 R +>> +endobj +721 0 obj +<< /Length 865 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0B_/e6`'YO#P5/L!4D)oVG[kpfkg6k\tNLLtU%`3\7.TiFN#YDeF&l@m3CdQ%8f8lGtF<:9W`\YVI"(t.ko0K"t''q#VGn.-c'=egR*N9+BI=HD>G#rS6XHEsJ^S/f1Sof*%\=G$MV1a6$'9)DIpZNkEpDAnL;9d1tfMrrhJh&8c$cn'POB$gS(O66%%dPIH["*8$b@!0=?/0j!4u&/=?4CU88>ncErU"f1*W$hN`[WBuS1a/%$0;k_Y+NGa,tLN%K]l)1&FNL!l%F8^c`%e[WZh4'*$.%BlVP&T7XCQW`r*2'^a4o,MD!5tra"aO5O73jKr3<8$WtG[54m=a=>_J_i@>s$-n62@(TM$>NeoGTL\+(hSc2X#_+A[GELt8YZVU"rRke,rC1.N'8m1MKa9!JMu(a-#P(@7sQhS8EarTU<)3)bbgfIs=c'(,WuKrEo43X>YdQB:NGm9-pe5u>&TiruhS&q7!V;B>Rcj76SrS07j&L,J,uDjLtFo`Z#TNLLg'#5Mfbd3;I*P?OajElK9WIlKjYh%8h?1QHYe^YW;OiW0>fo84Ys8DG>JSc8R'?/tk?4R7W0Ae?T\ZL9KtfAl!N&d6,^[=tSR$bLq6*bb^G4?)n&>?obf-qg?R1d]tShnP-BAYCj8.=H<;bRg:4dWBBXN(>dGa+h^R(HOphO1g+Nq<)aQj +endstream +endobj +722 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 721 0 R +>> +endobj +723 0 obj +<< /Length 1842 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=,9lo>Q%)(h*B_I\7`2^O_mk)d21[ZOEbf6,o-G'LdU2@@8W^rt+IsiJENW>KSSW5@h7$57enF3)XB4o!7`g.RrN\t%23S*cF[2etPUQAL)MJ)I:BDOt1$KbPu>2n.n&p!]B."t,+jlYKANSMFnTM8l7VlqWtNK^:X\'i2b7,_Q%s%'>q=NZP$%MIjWQrf/JugAZS"k#Ea1U&&!n#dluPY0cIuc0VmRH71S3XSZemr/0#U!I4HFO'Ei\=b3ZdA&Y`^9>3cm`Po)La.5Va!kPM<.pfp%l_hbJ)O[2'6ZYY&Hr*St4q7LH_*5p4VY'@&OQRINm8J&"3meUtsoJlsD<*>8[+fgR($^Pk('iKsDkO_lPHm&dUbu*,D,Phd$FN9_/@kkA$;?Bq[/'AjD3//[]DHl4L%n^a1cuaLW*s8Bs.CU;:jipI6)r"F6abf<^_8J4c`&ME-nk169<\A4C>#0gqAt"MI`,+pHjiYr1O>,7s#'fhrg8!PLheGgE2)&*L76RG6i/Jb.`XQWhWPH:Vn&iL+.N6q:i00b^rCMM+SUjMebD)*5L20Mt(+3=&5.)"?<)S,&[=1#PLI&I4*g:'GcT7H:_4FF#m]%ls@uFnuB`P(R&9"An2Ud?8c,pqf-U;:FE&"[nS(D?.T,UI[/3-p0/jJ@2"K:0"O=2!RS7J`-*pq%Mj3$r1B=bgmW[Us&j:U6?H0H\R7mN$lMtWGPA(J7G'72O#PK0a3p^8.a3A>Qf@kXXhhU/'8t"6)PD\N_T$B##jH82BEt*-jcc*EN=Oosp%H8M=_8]!qG(_nFk0/@?s(^.R*_ZXqeruF*6F11r_V4q@c,;QIZi:#?U99L~> +endstream +endobj +724 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 723 0 R +/Annots 725 0 R +>> +endobj +725 0 obj +[ +726 0 R +728 0 R +730 0 R +732 0 R +733 0 R +735 0 R +] +endobj +726 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 680.866 137.56 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 727 0 R +/H /I +>> +endobj +728 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 204.51 604.866 250.07 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 729 0 R +/H /I +>> +endobj +730 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 510.25 582.866 529.15 572.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 731 0 R +/H /I +>> +endobj +732 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 571.866 112.0 561.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 731 0 R +/H /I +>> +endobj +733 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 409.14 560.866 450.54 550.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 734 0 R +/H /I +>> +endobj +735 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 467.2 560.866 500.81 550.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 736 0 R +/H /I +>> +endobj +737 0 obj +<< /Length 2718 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>=c_T$&q6H[@E_X\d&km`,B(8Z%$3W87A`"h8Ff#=Zq[nQC.ZR>hI"#dq-]IUg0S175f5WVYIq.kc^.'7T3$K@CU]>Z\Bed-h1_G:=cTr_/b'ceJ,f)?]<6f>H`_STDMcqmroM=9GEEZ_o$L`pc?:FV,?_P&XO\"iF^9t020(1nJ.KV0OZQ81>\Vha[+bjX+<(FhdiCsnd$m;`(3B42-&jH%6@Qu@%]s&;MJIVLIjO`V`]+k_X1lO)Y2@9g,='kon_u7K*"ESq;-.il=c4RS'k`sK)"^f<%6>Vf?g/A[eicOX0ngZLgb;S=Fa5PLd=qMV=t(cti)]G#Mi&^n\5hPdlD:\;+a.5qU-P-_Qp#9@]l=HZ)Br3XZ"%F.N/!+RF7#@Vf>C*uVqaUjiPOTp6;X9EN:^fZSt`!0FtCY>a$MhIW1D0+WNA5Zk2Qr?a+JSd?UW/#:,qD1sWJ?aLlH^nb1W=6]@MKU4E_e@P%F(/J_c?TCb)Vt[W?;#-sL0RQA0KUTX/M]l#nRJ_d7$m]?n\lq"`H(&?BilrZ+a`$31n]jqHn3N>(]\)0F[7;!*klGl&uu=!k<5FU'9a'XIj5d.b.Ij)Y;.):R4(i#5?-mUVY=^3(-ZQAYR^7qiFJcMFYAE'u*nW'QG0/bOe]n5#2)bc#H)Gn(*>33833?A':j73F5*C7c=sR;,C5\HH9^6i)BhF3iP>EV&i1*dfC`pXjh.8J.mt^JNc`m*Uico"&MMe'@W$M#,1"N4aAeN^O:<[U#;"sUk8cILfRo8O&%kp7[mN:L^m6PmCnjn.(:;Ks\KNCYJ3LXjc9aWLnd:L1S.I?P1:a=T*\gL[A7(OLepW9\$5kc*37J!E;&<4Xig\1IIQjQIln89^q"\iXHjX8F`u0=>K#:bk5XtVS%k@+>0Y.?U#$X`6^7*=M.[dP7'5#l6_3GTXd9rpg[p:0Q3XO(K(Ys+a]_?4(:Y&gc^5E$oVO+Vc[(aRZHu#GAku=GV4]7sPm.j#tonY*([^3uQ'29Z(!]!M5d5a4k'a)BM<7Xcup"B_bg4/LG&$a/f:))*430GI$>?GNcR'0XAa^\F1/E8"5C`?tL6.,:+^;bJ/N%]>!ud6KZZ38=2A-ogU^+X":,%UoW#L#.nOk*,g+RLsW@j3F%[]BA@:P\[.j;DujE!65WH=gQg?ANJP5VFDWZM!Zf/FnN6e@>$0kZ''[GGMJN;()IFnEd%"6?Y0$_cT^3EKPA/Vk1T;<$?I!j^ZV_,Jb-%J^`mdWCbPpfBb;ZeBlu1dKg[2YI"bmO.Wk?f-GX&/Y.bB>([tgmW@b9Iqdth9"DdECrf?4&[R)qP1-A9OLPH:43fYhM=KA.^-V#5F,^iaV3h3!e?^Pip%&om]3HuWfcL:Md7r'?MFO6S?*%aZ%5OlR0#,g\().q@tdMITJ6]c!3'uK8miDHh2A0&h?r7d8)g4[B^k0TK8-0ll[^OVP=Fp'lpWi>JWO9GPiJE6EsEb'>MZ,AIP)4bOs^c4F[UR>8D^!NHWHSd1ZpifL\lG=Z."4psN^X2mgb@flq.X8$e:(,dh3Vq4=gMUr'=B$@bX.2HSD)psNI)MJcU4ch7)#?`Ku^l[`S'llB)gIi+/;(+e9^=@c];SQ;'cj.St])Q'8.k%^(*t(6?4YRBN@8C2r$fr%'*d$esA\Bj@H2[T^osIeCML_`))gS7E2nq]3aVP%7]6'si[F0SM;Z)TSgQYV_AtjRhbocS]Y1W<1*l8o5RpaVRd3J;dhEfR^"/M.;$0?tl7We&=FQ>"sJG-_^,E,T&`CH'76Df+7lbH@Q#p~> +endstream +endobj +738 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 737 0 R +>> +endobj +739 0 obj +<< /Length 450 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GasbV?VA9j'ZJu,.It!"`[#Dik-<2@'IVnW'ZM3"2Vj8h1Qi7?gRG>ba^g5"-0OFan@RlG?3GI.#&j\W&4M,b2S]=G%g,:;N?JdO(^!WfeHdrj#.G9iHSKh??W*p14fV*.Qnh`HG,7?]_&A+WlV%=KKCdc]pTAL&+Zd5m?"og+hI_OH24^cfN:QDKST:He[5*)sH)4a\AYX9IfR1=&gMGjPTf@%lBXJKqo4KFMV0X6d>q)m*-X)GhrHjr*FtK'(/6Lu=.!/\n$Wh(]Am(PQ;u:uCAo^gq*M%ESXu6gTdcmUV`kn"c(B3;4?9l^XkA9(g+:8DaXhk3 +endstream +endobj +740 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 739 0 R +>> +endobj +741 0 obj +<< /Length 1302 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%#9lJcG&A@7.ka2F2;_DUV>X-Ft^j"?(-^E)$5$ep8N'XCfH?eL)e8.eW=+oKCoW"PZA0-7@QSh9+AUDi-NBm;D[J>%g@^MeC$k]>TD82*/U=rEkJFd0@9:`GiQH>aNAtP#glsP$N/qOQDsV?+:_85MfTh=d#m?7P#e;#'LgO]sVd,!A^&#Foa^4fdCm)E.k`pWg7HkO]*/31()!*]:o`SrU@%>4QCV=oW2KR5p>WF"ko]"ed4S?J+Bg\!BAH$ADLQR![*<=_>@j,-*F6SiVhN,Q!A"M@J(*A3%:`1.aN-\u#l:?1cn.uX4Vtfq&.>i")bf0_P4;oH@&6)qF3lo.@4Y2+A']L&mfPlCU#L%Xg;FrsR[W`HbP7VQjo`giZWefFoMqe1pcmW5Tp/ChcC&qH4IBer?)aU>LsIX23-0mRMeI.hooN3uNdLFm20(UfQ_M@YtCh.]IG++Zmh:J'7RNAG+jK*;oGiI:=(@cDqAH[#L3Q_oT!Hdb7_N%O?YhRh3`5W;->6+WIaNNjZnO4+RoDrIS2Os;'Ce0G@mZN/!d(+Xk[0k6,:2T%pW4'6bn/Q8kO!-!IEeoVc%.i@$*Q8&eMm+fHPm,*hD.WPL!.UWb2<";SNLZLGXTNgELX`')O9EqCGW:]LjM`]Y:eK.`MVk+NkC8>Pdep`nIBO#^n2IFW9:3`r8*e)O`dd2Jo]E\s_/`]gYVm"!\'j"E"q1?bq,)<'Q98Y:GX"(^Z$CM +endstream +endobj +742 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 741 0 R +/Annots 743 0 R +>> +endobj +743 0 obj +[ +744 0 R +] +endobj +744 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 238.65 691.866 280.05 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 745 0 R +/H /I +>> +endobj +746 0 obj +<< /Length 931 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=)9lK#F&A@ZcEWiAd-@q7jD_3&(dF)&PC3N[((m531E8o#s@G,#\E#,tef'&,3\hE9,CYG0UWEF?=?gTdK[j]JRLCH;64Xh`D4JJ6aT[r4^Gp!e.P]47E/Nh[-=QgqWu>E6f4F"F>s@m+&D<298TlQ"=/OMX.Ys2)792l%j3g>UiY%o=oW<$E*jhP$iIL@ml;EEEPS(#,C4OJo[h2ahrn!*#3i8`6:5:7+X)^OeF5?GT"B:9\90Jf#`nhs9BhOR,[m;kTdb$Zm]G@U$"g)0(lj-=I,l5c_`@DAD#o7PO^#W'_K?=V10IDu4j@G/DU5J6;;$GjX+UkVg"1U>00G%Han*$N61EmR68.P.q*mc6,Kl#8:ldYbZ;H_3!M,9fKgr[D*Ls+c=_[5i[;0;PX65*Jo6Bg)7/C%q!YKqD&4-Z,&\j_6,.lP0X/GBV/+I7JLhSi)!P9nRAP3Q3E5>lkKn67'fX<:NQP!ZjHp'441pTksq@2Kt5c,YIjWU'@c]lH8+O+M6dl3]s_l3]8=7t3!>p:Kr_'Fhud\(gRls"Nd&9mD]Y2%fF"gNR#$)K8lu:j$=rNJ/TbGPCb&6)EqY%llPG^L8X`36CXM`BU/!Ei[q?LUe%MS~> +endstream +endobj +747 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 746 0 R +>> +endobj +748 0 obj +<< /Length 3107 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>?$"c/&q0MXi8i"JM,Al^lq8[Km)5mZptA>e4)#`Q)4!:JW0X1+\A/"q8-7Qm#mftSnV^!,So[GR#M:=t(Ip\pU`PSamk#T@HuF/G4Wj[5QNhC]olJ-j`*7&jcc:V:dQ:$1+3ktcM?d":C+4K[_3I]eE.?KWPtL;fSQUGl.A0.s&[0/56Z2m4NQTcVtk;t3+ZApX*q5c48&`TIf9-9E6nt>W87&=6M*BY0?sV`8D4=68+L:T5-9b?9%4.ANo=X"K:X`Vr]9=`^"[bf&c<\$Muc+UiAVh(;jQb0[HFl\8)E.[h9g_4:)>Cf??sFo.gVqi+Wol0+i3&\Ja]qdbVr:piK,+$(7Wd<7[8]RFsBg?#+ZH1&k236i2Vuo6[BZVaE!nVBV1'5WWBjj.AbCS.q\TuSgfZVGbLb6a>+%.=I"]lhUGc6#F!^NZD>WFDPH8k7a[H2`HrObm_.VSsM4QhRLGQfc'$H!\.=s(0F\9!$iTc_Lsq^mlCuY]WCPEA^Qm"-q0FqH#(-s8kD"<6NrSt%SHU\r4@m%p15g6Y_"fJV;RLgADG$["X*=S0B,"8**t"`M"G#1DdpDLR5d&P$8tYL4>P3P0]0R4Y>rGM5e\l9jWg0he+rer^pT`':P1LCoBDb;2mI.5=LKDBn(GQh;@WJRa)a()7JbYJ:/qj-k2lEu_D5+[KWqRT9SCl:*%m3gQK*f^J!d.f4=p&8].TGI\UT:e:G`i]fdVRS,^+]H#3b'N<9%_8"PV*#[c]A("-EBgrKN#0K?QLkb<1,&l=/EPe+>lAQ=TL)=idH?,TZr;eccS@`h3u;c-&i.[/8'OO!*+kXkETejbgdq$/rhmFC1ahQYW[*q6W*6/qM?&fTpYue2)\9/OSgHjPpl>3r5,QYl'<*6W89ZIV.$;u\.p8P#*SaqEH./T1;31A6^#;)cpBbs+S!m1@<,,f!;_CNM+DAi1jO[%8RNUJOA#[G49.Y.k/Rr]96SQ]O.'>S_Suhm;,iOS&E%FG\-lX]J@d9iMfF\5Fq`o1#kh!0*_dir,r-bQ:^+J%2?p,k$a[+,*r&jqUPBC:tDnP&=L/'^2(EQoT]b=-3[)8qBI._ftg98W;1OT"2J>XIbSrk4FB'"8kY@!,qq1t&LfE\0q09CD,p$PLL2\,=i.->:fk>CA4,TA3(Sc/%In6Y9UcU)qm;ffsI`Wk^P,mte\r3ET>ph_[*Q4sGQ`UuI:s%.'$#u;'j5lW>Xq!H?;^LG^$L5n?3D!&dPr_+Z.=d_J4[/k[+:9I:Xt($%ZWG=^kg0!g%Z;)huNgTb1Z\^&(3no30q9VN0XA\)61T+e%5r6NCMGkGa)3Q&(YW^isEqh$VW-#a3*Oet`V"T?e#..-KWAS0SSO":b;Jo=)P(ILV?Y[0-qj[KEU+\:#@`0p(B`YqkcX+Um0jQIJ5][uVg]!0PUEg^J`-Z*]?_bPMld[uViAd*1leaTqm2c`el_XA^DhSW.Xdk6P)9UiHe.Y*6kL:$p$`H)a9.;g!VV[QaVFTPJH.cZlglpo_.:2<.!E3Y=rl2Ra93fA+Y&/9B)'Mu^p'Dr`$>,pJc.cYj/Wc[-<#r"c,@[n;g5j]3h[H[24J4[KDe<[Z>'!^DEp$'5)\(KbFictJ>-h4R%s!PNu1Mjc]dCKh^JE#uk/3VLHS.mR5k-S4aibgi'G[Ff +endstream +endobj +749 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 748 0 R +/Annots 750 0 R +>> +endobj +750 0 obj +[ +751 0 R +752 0 R +753 0 R +754 0 R +755 0 R +756 0 R +757 0 R +758 0 R +759 0 R +760 0 R +761 0 R +] +endobj +751 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 175.77 669.866 361.21 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2026.txt) +/S /URI >> +/H /I +>> +endobj +752 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 175.77 653.866 419.82 643.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2119.txt) +/S /URI >> +/H /I +>> +endobj +753 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 197.71 626.866 389.26 616.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2277.txt) +/S /URI >> +/H /I +>> +endobj +754 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 177.43 610.866 368.15 600.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2279.txt) +/S /URI >> +/H /I +>> +endobj +755 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 323.25 594.866 510.89 584.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2396.txt) +/S /URI >> +/H /I +>> +endobj +756 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.0 583.866 155.78 573.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2396.txt) +/S /URI >> +/H /I +>> +endobj +757 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 389.35 567.866 528.98 557.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +758 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.0 556.866 224.65 546.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +759 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.0 529.866 296.77 519.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2616.txt) +/S /URI >> +/H /I +>> +endobj +760 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 294.38 513.866 378.17 503.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3023.txt) +/S /URI >> +/H /I +>> +endobj +761 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.21 497.866 368.14 487.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3066.txt) +/S /URI >> +/H /I +>> +endobj +762 0 obj +<< /Length 1647 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D>BALZ&:Vs/n7.glXsI;doSla0jYS>q^e*Z^dKdaIi5&g@Q_aQVY./`\ZCrZ1oS"dGlJ$6l]guMIMbi4mK/f`>KTceeGe"rO"ojD;;-%2R=r:^Dr+n/[J!`$>&OgDSl!-T#;3oicNR'IN%00@%8+Qg)/]b%]!ObB;W'm*)>P,1,Mb$E\8+r[1Y-M/01(>5[WX4l7rfTK%!Ei!`DiqG]XW.(bFBfiP8O05,;RDH9M2+4,/mli'tM1/Vn$L5iTYBG+tpng5$oYr8q4XHc]GQH52o4'nN*atG+r[hj4qJnN$F$Hlo8NEW?mSPMR[aWo.PT.RG(GR;LR7^%!+U,b@KDH+?sJA>'XGRbf+=G6/7SnGuBNgjA\>@g:@6RQcis/'p')M!APS'F&Ck_j[*dSQ*+:;ZUj!m7eU:2GYbHaqiQ[A,u&3>R.fB3<=+NBQqZ?5e>`h;MDg;u7=1H!99BsirrLiNe>2dUYT40h)p4+PfAk`IMX.%Q2-!/I.M`/sR\T(@TJEB_M@:,r7Z-9`ns[qoM8Ylj7C*@,)'e7+@lu`_3A0_UV9#1]/5QLuQpUi#dc:3>4WK/nPH23O9a!,l%(W>&I4,Ch+Q+(^n3b8+Es/@@a%UHR':`EWd&6b>nS`nQV5)P\Na@PXL#+(&bL??`FK.A6]mcR^iQDH`$c`RLa7[_AcZQ#-#CHj7t'hQ71gYEPJM4!>noPQDHb9_?3DKq.&"L%XE6*S?o'+E_aU%MOSZM$3fjii;WbhY<73\J/$CAM('Yi>s8g[0E`Pef7E2Z%C\_Gd"c:uAV^e]QqA>F2q>rMaLKRkm4>p<8#9F9gDF:N`&h'%P;(<(?f^;jl6VC8W$En,)'n8fXO7^W+6-*s'o`6Yc#EpP,IBi25HSD/uZrVm',Cig\[LCa4fDuG%E%k,t]:.c\sa21m*AAo5$s/JGkPF=N7f)2JJQqUnnZ#Hm(Lg!NKaa6'=Zi*#1t+b/"qn`gGC8YQS,^*^\@Y-.agLh?^..YXk^RD+QQ.>+JOcj#a'[3I87:88/cC*@`$>#'gcgVGSnm\8!P9HFYN+$q2,4VbiU6:`^.DZKbXL:^9:GXU,l\$he-L"_B\2aF:#`9&3Q*,+6%NA5lp#EJ=A@@E12M!O(Rr%QU2bgdZ_^UrdkFH]p>r&YtMT5Gju_D*bcM~> +endstream +endobj +763 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 762 0 R +/Annots 764 0 R +>> +endobj +764 0 obj +[ +765 0 R +766 0 R +767 0 R +768 0 R +769 0 R +770 0 R +771 0 R +] +endobj +765 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 486.66 637.866 539.16 627.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 61 0 R +/H /I +>> +endobj +766 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 322.23 626.866 374.73 616.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +767 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.39 496.394 173.95 486.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 501 0 R +/H /I +>> +endobj +768 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 145.61 480.394 191.17 470.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 499 0 R +/H /I +>> +endobj +769 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 247.8 284.422 293.36 274.422 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 499 0 R +/H /I +>> +endobj +770 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 241.7 231.422 287.26 221.422 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 499 0 R +/H /I +>> +endobj +771 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 241.44 215.422 287.0 205.422 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 501 0 R +/H /I +>> +endobj +772 0 obj +<< /Length 940 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauI59lldX&;KZQ'c`F*U.Ra=OB^^(rCZ+16et+mK8^P7.0`8_C%LdDYVFc7`GM-*0:EXQm'?NViesK(reaha")%k!4_W)daFa8D/6:D.hZ9nC=i%td4#mQ,e?UNVqVl$:A0.QRT>X&80eJA0Of(hiCDX@91X'N5U$4AVfE$e:!I799nRHl9Wu.,&)0KQ@b)9UDd.T2MG62/`e.LfXPM,DX3ee8sN3=HqH@XOhu.4],R`Z9B)j,"cJEib;IA`o`SO_0"o@PRTHnpU(AGq<$h[Hm7RUFfY5fq!t9ab05b$To,Ecot63allB'ZXcp6J[`EHJ_f"2:#ij3XYKnj+f8!B?\t,Aja$KaKn9_D:H.32-c,0&1nVaraK=CP,F+*!DC_^o2eZZY+9V!@gL(ja1pG\RdCJ4rIPt26t?JAF_T7;ZRQ<*esNe7A_T-Q&,H2R_t&@Ws?+e[AX\UH(FMg)R);k?)U"e@?aJBm[cnRTf([#n*+H!%-NjC76QYBZ8CDg^+aOQW0 +endstream +endobj +773 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 772 0 R +>> +endobj +774 0 obj +<< /Length 955 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHK_,B#A&A@6WHjhg=.`mufF/'mPBrAasJHOlh3[/ffCl^-Fo^=:VEKh#-,b;a3ilKf^Dg1+,.c97Bf/)\7%%n7uoq4,*+U?.#Ei;$o#_F0-STsE^oM5HMEa/[>I*j#uZh/s2h=Et)@5:'ZA%^+0_B"kpi+8LdZ$poAlE]@t4)uC5pkSK6*!@'a>E=p;.(oWq/NqrI5-]m@VcJ12/Xjk(+t7RkCMn&)ufbM-,DNV`8Ab=9PU\5f,rCin/5$],Jhp?M8q*j1,ReMn-F,SEQgOj[f9^e.j8dMm3RKE@$_hABe$//QF!S7C"tc]UV2lYQSUk$]?.hf$P;oZe<.D(Y4^'oWgQK,A"U+(bR`Gm0imfVe+BXOOV8f8Th/s6fg+RPC<$Z.q8bWj`djdNk9rNl9#'1CX+d_/g@"gYDopI6$o0)t&,FHO3Go.*js"!'mZi0u[&9e`(& +endstream +endobj +775 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 774 0 R +>> +endobj +776 0 obj +<< /Length 905 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau1-9lJ`>(ru)mMB"oc4MNuBXeW"q8X$"d[>30t)S?\?`0"Q^qX*GGM4mX;Uf)1TH*I=pg[?lr2B.(X@0(r[#.$N=(d*jU!::!p0t&t@A&(Me>6PPt3Q>kJKF"i/of1'7qhB;Jku+B3EY5=ZECMN1[FV=!QBa:"[A^kdGRS1KXD8Cj]Q;o=(%jX)!q7Vlq7Qn:=U>;A&pI5TR4=`hpT-,B^H08[]\0CnQb/mPPLc@!odEP[(dRWJhsJ!+Pr<[A,kGhnO@?-K.CI9.Fg2`H(8D0)7dNjsXtK/qRcl583k&V]h6*"uiMNS@a8WY]4o::dFsV3ngkD_2Q$JZ0hT!*j6'k@_EGtgLIIA^'ZQ28W]L+tp'EWi1$Q=2]h(/G%Q-#`bI/LH.Sn%h2)Ral78Nd]S!qg>5UX^tqX`+j3GoD2M$Z1k1QhQ&gO*1,;ce0uQt[e`gu./o0]LVce1!/V/s-K]CC7GUQ.g!:DVb*(F54]XshcKUo5h_.+%sqcijaS#MSuR+6sO_N81kijeZQ(!gdJofj[Zg0-M+;==6d5$;X/'dWtb(6!_i3aK<#(de.t/%Yh7J2N[kBeEJf05f+p5C&EE>+H!MJ;s*M,fLtTMh>t\3_<]D2&lu6U,nKn?chn*X("$p[L\Gr8?QU#s+>_Xa8~> +endstream +endobj +777 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 776 0 R +>> +endobj +778 0 obj +<< /Length 707 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau1-c#;;i(l.SX'g@4+>^q7qf!9=`1i6Ekp/$&@M$m0JJt,[)c^t;2$r`<4'T/?e+G@@\(`ue-]mI?h0XaOCC`;DDG%6dgi%]W'M9Bh\:[mC"@mgW;r,5lgH^gD@rKJ(B>a:Nf>VtP(+)a2Z^A*]HQ>jRj%;Sqr()Fi_qJ3jp33hP*@<*CMN+M-[5J,5^i1.Is$h3pZ!/Ki:%bPTa*ff%3H?Y";;Y.'[G[P2<#3]).hN_?>XILsHZeE=?C\)]!#+%S$7:VV!J);U9'ph!OAor7>pU[2A8^O@*!0$U^Jc$$F]CA$%*#CGK+`khSCh5B`md?MP;06p\]CkK$(HK0Edn)eXG>]0afTHtHnHA23?L&jI_<;Dmemo$~> +endstream +endobj +779 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 778 0 R +>> +endobj +780 0 obj +<< /Length 2183 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU4>BALX'Ro4HphecRXOgO4G@*[nPL'NTK%"P6l0KffJ2Y,)"qRR@5;uR@7cF4jWV?dr&r0U4%IA/W3';o3^?i^KRc`rq`cZST:1^1k2]We&%.b?,f@#Uo3#\]3@E71n2`LIBN88@[\Q(*H$Li[U8>;PCd?pM/+,qCf[VCV_;dq;7BJ^>"1)#+S1ar1>@e.7]&ceN`U#+U/*>2(Kl%cR_&01_f@;,WPrc,>&G\0,F2OU#mQ(%^V:`r_UeJ4k+joLcs!=*k[$ab3,=(5ZReo@\@[`&1N^rb[$9\XN$>p!2NCe=1H9b2s2p$Gqss'_Q%amE4');DBDTMieY'o?Fd.g8'L3(.g[GdOE'6dA4`O+@hnKKgk"[>dP9F<1Ck8@hRZ-'G.@_"W$l7mp7doH1A.r3X[jG+ZE)J?B't&n1in$J+Q_"AS?(":`G*bED$Ceq5=!*?TNPbL1&io7D!R_hh6%hKEIY*%KK\J>+upPXdk5\DTB=[7s$I)JgB*4R(:G-%FAQi2>\S0`cB$aH#,/Zb4AF$d*6i5*IWZrWPc[>:8^[L:*HdL:gSRFj'#%q7d!TGg:hc9>]%6h9?O^2Ge8q[i[S4P4]L@3-8Jk5=-r$Q((Hcud]'qs[uI@r@b[:h/f;Z(Z`)Z(]A4XpZsFH:Uj9ToHZD"2)2KUB4LbWOUMS'9[,I$Ja]"KYnF&dMQPH6$k9g_U;p*dgYjW0A>@m^5X=Y3`5Z8;OXpgL28Tujg]mXMph_SoaEk%/o/RuXq2's_Enm"!o3%)guaXgT7CD*e4IfmJZ:7#GJa#:3=*&a,EICt?A%&L1fZ[5Qe:l;)AW5Oq5K5.g8E)&f^d6h>M!K'rP4J>A46Th!ZrK6G$;N7L1r<&7Pif[r8CasEq$@@@"8+*]RR@X5sRq;h^BEM(bPX3/<1?W2Y:uWb#dLIXVY:D?4`MI*!\kOm1o+D<"F^4O8(cJ2me<.pj73A?tYrGPm"?1$'JWOOnTMfhdaeil$V*2+m*D?WEL(2'FcR2F@&?tpNbi(Aj9M"=UTmoG2Oh[F3.'K(!es!1$pD_9OX%K+LIZ946ri[gcT9GNs5n0$)+K3b)k;)%VGk[6n`YEnEq*]HIet=WE?a_eLJV"f0oIl5NDa-epUiXeulR^el@.&`o-o56X3?;3]q?t*RGua8>~> +endstream +endobj +781 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 780 0 R +/Annots 782 0 R +>> +endobj +782 0 obj +[ +783 0 R +784 0 R +785 0 R +786 0 R +787 0 R +] +endobj +783 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 631.866 227.85 621.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:geoffrey.clemm@rational.com) +/S /URI >> +/H /I +>> +endobj +784 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 555.866 193.99 545.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:jamsden@us.ibm.com) +/S /URI >> +/H /I +>> +endobj +785 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 479.866 206.78 469.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:tim_ellison@uk.ibm.com) +/S /URI >> +/H /I +>> +endobj +786 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 403.866 196.47 393.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ckaler@microsoft.com) +/S /URI >> +/H /I +>> +endobj +787 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 327.866 178.41 317.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ejw@cse.ucsc.edu) +/S /URI >> +/H /I +>> +endobj +788 0 obj +<< /Length 331 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GarW3_/@+D&4H!_MEM0CF_qB(p!KRlRYm7V0d)-/\DX6_,Y0)6IXZ-!FUjZij-eTNgR/D?1'Rk+%-fXaciaKUI09\\*lf64%]Y5"=C`(iUs)iVpU!P!j[.'l50qd!#=[`sQE*GXf-=dq8hWh?WB=>/jX;aJ)b`(rgd7J95"Ae[[#5e&TJj`p3R?/o/RcX8fs?G&4e]j@1h(V+R58/+q>m(L!fJ,'/bR/R=[0k)EBE@!!][L[i,$sr65EndL>b)p8:#MgDBIi..C?LPX7-`0!qcgHs5W>jk:_B+"<]/rTaJm\Q +endstream +endobj +789 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 788 0 R +>> +endobj +790 0 obj +<< /Length 1278 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gasap;/_pd'RfGRi9a16PlV2bBMVd_8t@fc)I5!0G?DoVP0$JGc'[o`pF%=Y!OVQn8birKXGV%CMAJ(@\*Q8@GLh.ieOpI6X.\jZ$F#Td9"NGdY(EpZTJ44W^V*=b*OW$_*!=%Rthb3;;+;RpeaQ_gUej,)\:0=jT3#Aq]M1I6<`AgA4;?7eVCO1A_#ca8p(mYpAVKe'j]0(LiX+.1'*4gPrt2etBp`,ZWZ_PQXDn-$a2)5PC;9o!20q)OJ#I##Hu]]?'Ip^i/F\uDsbXJm1EAS]36bT0ubZIoiH&hB:NNor/B1WP4,0!*(meMHH4<\Wer1ni769]I1RihqMDjW"Tk)ED8)D5*X!Y-\&O#/5PjISfrE4Dp`L5Ha/O@f623cGMcj7LlN@KIX8SM<47-he:X'Bnr?+d;@jA;<_"=4b:GMk;@BpY2`5FSCtU~> +endstream +endobj +791 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 790 0 R +>> +endobj +792 0 obj +<< /Length 4141 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#`gQL@#&Ui84bbJES1;k3SS%Xs-8o2U\Dk%'e&)8&jC.`R#E4D(%Y>9fF-:%\Q:+kY.Xjn>LF1MBo+'`El^45gRDq'O238*(O^.L:ZYK/&As2[Z,pmXlL9UOVj+#gsIIXBd[^ONNaD0,a/d`PNYa?XJQCJ?=3YM.!*fGL&C@r#Q=[bG9kBj^f.`?q<'JcGQBRk--tnn\_JHUqV.&0j-'r+XW'8iVZe@AmOI`[(Cu<^\3maWuujZci.jh_n/?i-!6Q6+mT]=q*1iNG4#jg1H]2S9kj(L_WFW`YG,^-q_Kg/m)]S[;SkiMkq\Aj7JKTp8;`_VuXD0\78)sL4uj69iD??(D@`>E-Hn.%SX`Kh4fAke!og_^3^OL]mk8+j88:+oOCpL3:@X$JL1Ms#K?X73ZeT:Z%QPjBe6too?2p]!24Z.3mR)6;:o3d*3Wa,Uk,Dr&*HTj4eHop@XgRIr#lh1Q+2Se+N7j=!p/)J*"jcQ4)j2D,:OlC1IQeaW>rS-pp>9(I_99V"(e'G]DHHYh.MNF=:3gDjk?3K?L/,$8O5><+[NDh]o[_?K_Dj6#c-9dHTX>\i]RMC.o83O*`qIk0OL2A.Aar*Q1,_b`*iEuk&XsR1gGa>+Hi%\iq4cQaNJs(@map(ToCHDJT.@mkq`X=qrKQC[Qp9!L0tm/3_-arZOtU4G;^c.3%ncup"kOBep0nOrTC\+T<`qp3_G5s-,_RQ$=1"6GC"6mP?'Q&N6cWhJ:'(J'Tt";R*Ri!1ku#VWrL(I>@V@'QUOS`rED(6M'["s^Q#c*q9'I)g.@"CDb-db>d=K9WAk-K2,1dP.9Q@6kE=NHr[gX)n)#R82Y/.U=Fk"h]4:oLW%_IQrL?]bNGoEC6_.jBT"l5TNOCOc11_GQBN;(cf8X"X]G5bPe9QI/is<&;A5Zl;I.]Kt0a6&ZK4-b:t::S;C2j\a$Kis+(SJHt\TH9Q^ZqV-'&E!>%iX(u-K_M*7TP\>=];BbQu0Q($Jo1TJ$\E]^7Z\oi@$94cU.==POGVg","?IjTNQ#Zhf`JY>Ye[V)?=k)b*nAr+NY`qRWZVksLs,E73<\mZ-;(F0sC+5&h9J`M7G;]&C,@qI@Bdl]_m`OZl9YX4BZGRC$TJXil>p,licJlAk99nqFjaTeY;Q+>Ij]s9ROmR7NTHIq%JpE&X7/#+6dg/Fm10?4S/)aAkPoEtPf-oKW4hdj)"$S+/1\g1!jScoL8.P^AU@FaljXdR`9me0_G.%pL<6MR7\E$t2+\#&ZfQ;shn!-`ek/h'#jP!i`2KrgU'6^A3Y[!Ahm+NLfI5Z#5r]jSgpb*11)]>G9S6qlp5s_'[lSMBZRF[h6>dZKqSI*A?;4hQmZL)c9a4fL",UN?rURToMeq4FGul(NB4d[bhVa6`R$'sUbKg\M)lI^l_dqh.)"1jpuPAP>okYR5S!4_^ZXQA3Asjio)tbg4>n!cm*GNYYOR2Vq9A&>&O4S(Jub6ZelpFqpX"g!iYLld28$n*er/qAa^pQO6-*5%$e3U.loQC@[<+dKZ&tp;t=^i!jofYb?N&/%]dMrK7SF@]]R4:/03N+p+Em?&Ym"p_M/_a33l2^rS$`\?bC8$Hh>?k+eA)G.t.Ni7H$Jdn?8nNnT;@TP:U*P#Wj/eE2I!2'T[E1%%#/BcLiDikfMCsKWo(eC!5B*![%H7L:WmpICF$<%PFLq"p#c0[t3a=m`s)<]F^=ZHYLJh!V3(e/')JNHQ`S^V)^^iGL)&^q=Dak#67j`(as@BE&$#22;HK?&aQ1K%.p5Fkp:%LlU2nNA#,,2+F.q7&t-9,%SU[VCYXla'rHf&i5WajSta$,ZkS&0#cg81^ASm@I7<"'+J>7YV60^VM>UF>Q_)nb`fEF@j')>8]F6`ZX50eFE"bTFLnai9RX`L[5O3*T30&M\UcU#iu3,q%RIeQ8pd*ZN&qX,';I'R"Aj8%SBM7P,NS^5CP_9BC_#)5m=8:g-L'&4\iZ\,a8[jJ_EoLc?!K9YeR3Jh;\F+1Iqi#?js5HiQ9`nT7+4tC+ul\&M/m`6(()XcQ'H1]26[0QMl9Lr1Wh@>i2/ZG0%Ikp:3M]4_Cp\^.lBTRL5Jop:1NO&8\64Xn^QW!K#2lclkE7$V^KU1WVDn-2u=e*bD2;6iaq+9f4;uE'E`I`g`2Jqs>Vc@@XS#VgWWCEqfR?0D=X=s0(cYLGn61k$T1dHdeEeU2f%5:g:\6m41FR0pjW=fb:ZRd3;"n(Z5SLo,=]f$8EG@!;<,g;gOdIZcf1kqRU\R>,;N=/UECKiAl/co(r%"uQNmKEpI\0C;n[sR(E^C!Y+N"MF;LrS!/A!T:8NgQ)CjVutr9%^P0I3OSa;>Cjls"]3;$^lnVWHKD7W;;"6:F(B_9P:NLaA?EA?MR:?%)hSKO;gnDue]#^(5a<8^"XKE!b3Rq?5gRMV);(f1=7+glgW)tr)e\9Ho+.oZY??b^E58f!%AA>()AB_1PejXrW@T6q0j#;0&#tF=&XWg(F6":,IXnaXeqVJ).^I*Rb<1f1lI9cdof`QJ8q]UpG=HoG2htB6q8X#r15srR=pdsahK=j9Va5`MW9jc>ELY2;1!s?D\oD5?dC3?!6H?ATHFDF)k73kkgf%-$lIRs@&UB]:Oq9*\6P;i6>rXHk2[]T~> +endstream +endobj +793 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 792 0 R +/Annots 794 0 R +>> +endobj +794 0 obj +[ +] +endobj +795 0 obj +<< /Length 4293 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm@?-h96'u&"tn6Jtco#,IPGAYo?(cJJZJdt0&$cQ0'f;NP3ZJVJRpH,0]MaucBP*1rs`%h'M4QQ>!Ttc*WjtArVQ!'HSpE/:WX/@me:$nc'?4,;Vu)r_lRD7&i"11]8pTsY]JiG$l0[rM/!STCKPU-e@iYYJ,PJeWtRbncKo5[t\&(<9)EEF*BLRo!eb2\Mi@%A#1;1dSXNE7ab>!uCtI&bpWfM63R05\&Q_oHVBDI2$kh9V#BjD6EOF3`,Y&=-c4:W#`G2DV^ATuHX3=DE)jiY[;<[]j6F4/22J.\:kI9@GLKMj0OCNaXKY;qM[?o&l=pF7lLbN_O3M&,"l`I(FuJ#7T1e9Jr)QKJ5OH=H:W0%Vp3?+JeuM,e6!#GueaU\_0/R9;bPkS>PYbcog6**8TcLjXOG,pXD+!\fEfNCnd*)",^Kc9HgE\^_p7KmMP;k&,!Z8Nn^9hSb?EJ9CiF;U-R1HK>DgYQKm\fKa-eUS@6sZE9JEW33erEfU'bq/?-%p#ZjIu9,4!-b&V)dFls7.`a[3Y@@`>1$!?]5-R=CnK(HfXhmNZ2!Mk4O+hPcs3Y$=8)@RL)nl1_8TWHr\!k*0/)ES&*$^3kZW6th")Ei@liEA+0YuX5XXgL5\N-ii-Adln.6k18h]LHE53f9/3DgncPLYUr@[O9h0$QD)5%'(S7F@Xh-WsM1L88]#6$Uuja;KO0,<@D4L)6t(S"U1!\1)PhDpdGZa*Jucu+S*EZcd.QY]a0>2>`6EQgOo$P:l7f5VGfU&bW0ffPe^TI3^m=:,".Ze;V&ZSh@lM;t3QZA!33)&I"caDjLaj>I>rA-[i)i\.;2)e&j^J4iE?u(XbrL;DaJTM(N1@+.?t7]?J%Clr>KnOAot*9]?sj>G%AXnu0Gr$lhIM0FX#la!mtdA5HUoQ+6AuRQr,I-C*5VhlPeE8p5U8mhZiN@LJnE,I,no$)Q*:H'^G?NK5D_.H(FT'"\U7#[#L"K@e/%X2Ji?CAT]3AX/\l%%L:8AYm8KH_9icl>T%aT?WE`LoVdj9@A:sb!7YqHVs[@1&3AA7CD\1ns!ApNQBFW0jmW,`n$N=^H''!X>'d5luuhbK6>:Pos6LaJ[&mi?=U0$a+L\IO&6]^A(2279VoTYDNIG&WL!e_[Zm^F85`VZKu-DO?gG@Y]/NNu:tLrTit5NoZtlEX)'\iJ\sAu.G$G(Yi+7ETB]lh\eE:o%/nSb@(jq(2P3`K0)Z-2!g]JL-])!0D8i$E7CRc+3eF$?n#7!7`:5RDO[@M@A+B/IA`q(Gj4,MJ>CA1B3A]Tq\E!G0X8W9"3iC%?q^dh:G<8HAAR]*bdGO2Y)mE^tCC8S6rNFdRXm=*@"Ku);i^Xq=WAp+TUBp'Z.@XBXmMn,-+SuZq[2U+SK61*4(`b6nL>I5n2n+#/'Mb@8-X/ae^!%BW:E5L_=5#bE7:`5cqGheUKK)EV(XF,sD.0_gll;i=glV[BeN@g/>DX\'d"V+7fjS)6[qq"XD*EIQd0@.mdAG6pnn)H=Gn$^uAV,hm7Ndk*sq753HR.(f"b\a4g%%d'6Di5HVfRDF[#l?"%-a;[fJ"3;''R#%(4=q#YF7:<9Daq.%F1N.I;7A&[1qt0^DoUrVEm![5@%d=lR)<:".4][VHi,M9i:aO#cL(u5R#X3__n3gahi+`K!i'cuM?W0uLh$##K\`>#f>4VJtuHq(82EW;ZnNb>W\<\Ybh+?O'ld6kBC7m8B><9*M/,R@Zj'^:[m"`LYM8(?*pcLNA"ZqG(MJC,\Tnu_L=0&/AhGOZ`fuKh(Yge+eGME1\&Ud.N"olE$QrB>;qB)6`4WtKB!B6`0sVR$a^,4cZQXtgYeb;i3C4Cd&&gnp:C[._OGsrp13dF[B0.'7[1olG'/*6(42Lto+PRbF80$6-X$@LhZW.q`EHi!IU&sS%_l4@_X:Nrp(.C?&TtRa'Vhrj5sk@LF,7E.cn'2qb(Nk:+iV"pHZB[MQlL.LYaBV;m1U+dO[Q"Fd?B3YXNVj>2A?o;El*=lR8L%";3h"hpY^uC[i_!\jBhOa"Kc-g[:>M,bEm5'l?O/DCPkKPZ-/H)-Q'$<=*#l+A.dlK-oqQjPu'OABt$?Rrq"n/X2J^Bm,cEic;\mS'ulCk1Xsh4Ej5@'^$`JSJ!PjPuR,/[c\jU/m,VFH22NYGIA#l&R)9BUF-=ED&FV/=DG2G;B-q"Uh2,8P[&]9'@\j9P\T\?8/,.s])3](/iSKHdZfh220]^'DS$qcOm:9Q#-tce8JtUS7W>UZZC[ajK*/Y>m&i>rjCXkf:dg;*'$8h)94N"/B%i5+T,MOJ&JuqIdbf,0C@E(#)`T3Z>7g@^O'-Z!5SH):9\q0M(Q$=O51)`/XAW'C@Rlg*-Ng'\ZHa8+FnukWSqs;Sr+#+U!thIOITX..!AqNB\O5\*")ca]M!,A&gH%am]5C^`:I9><(d_.$5ItfQ#g;DI5k3CR;1H9UVaap1b_@k$hNr+1^PR#WGVT`*d4!U=qQg'qnu/_UgHsS>@+IlcdG,.C)uZi43=C$9EFd=\D+T3Yrmmk"qSK6QJ_CAk%#7]@6h3`dX,OkG\p!iu=JXmDD0*1u"P4hc*Dh/G@eok@aDZ&O5%$fVc3f="fUIV1#UdIgQ_FVY*(agM@Lql@+#u;m_)6T1RSe#**\up%OIr"KM#MWfJYJ./QbG@C3D)!T=7;s.3U7p>B!T@ja-8SCX#^L7q$S>@t*0\?bLO$uu&RUf5gpa3/M+mb&Gn^p1"c$QK]"0-Cg1_9BOZ5st2N#T,9Fj0YM]38cV&[KGAW>KE51q3Fm9KCk6iGl;:%qi-G/fZH"^@/Oet*ITS_[^C%H4L*tI0:A,MFpg99o1>hQ%QX1/,C<6&k+DEU&LgM16eP),o+Y-hQWY +endstream +endobj +796 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 795 0 R +/Annots 797 0 R +>> +endobj +797 0 obj +[ +] +endobj +798 0 obj +<< /Length 4097 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0H998,C&\cSpn8T4G.h\IPDTQMb&0Sb4Bl"9;4fE4@[kP7%aR8K\r;%*R=^mM8PBnba(sEDcp_A_+HLXr6rSbq,Ic6b?8N./U?dd3jQY_l&YABD8n8C',8\sZRiOJ^^l`NG=X(t_-Ib=S'H2&`mb#&_!,s9EZZ.Ut[RphR5P6p1N0-oV_JepHm_A2oe$7\a`3>n(tY0\Fn`]SUm5=b+bSC/[r8c7Ue^\r:VSkes$H`Ztle&V/V2&@tD((T9Ve;&\1[sqATlOTI:i@1ktp"ZM>I'u0X>$ht#a_pE?4Ma?++N$h`*jI/V;imNTWH2"1-=iXlc:KojWm;o8g,CGR_G`V2fR9hZsS6lcAIPa(bfS>KOurR&XqcH[>):-YE>%UP8R[kMhtdbULV4u26(#eb>a)T$a#QuO;0K"V9B:GUIs:=]%J-)o;n6",Cur4"N>UJOD7r.H,7LV3bVWd[cQp\]i?Hhc;cKL.$Ea19#cNEShhmApWA'O^+DHF3(d%U";:40X<=[mUtc@ZT1qa#WGmQ/W1OniF`h97+D.^I(&)opIso1=Xh5`;i66[B7&r4OVCU[Sh24([ugIAG5h!m[p7(r,%Znub_;cCB*uU^cBPnY3>=dV]34I5]"W"TlEX?dIW29a\8[09ZHn!7!_^'HQ?em-,U!c]afZ%!b[2_GdO>81V,kC!4fn'B/'^C;/R\1qKOV9,X%G0Ff8&)f@?g@h-;>XnH:Tu`u$#Id1dLW&O&\BJ!J:?"]AoSH4It=5[kAL%u;hH'FR82s4-W<9@sh4fFhN+MpiO]H5#)kf&r+]Y#4:\?+)-RmMhhCD'M&c-7^c^B\#NREJ5`>GQNb:H\&-+%5!6u$X0kFrjU_JH/n=Mk?r@j_fYPu0kb7Ogo6\LX2jYXe;*3nQ.c"r]Y-$*:!aaUUp.-lVk6%;S?#hn#>Pm6"l#kI\3^cb2H9[aeGXQIK(#uA>IPsL:Xq7#&F6i)#LCAV!ohL_#qH1$$@ag*SaGmWiQO##%iecN,BUs]6HAIl;]iL/:ig_<\(I?M>7OMUp*c4n$,AQ-/8e,=N.nU7]$Y*Nq+uP(M>!Yp9JX]+Jmk=N/55#Ci<8])1f+kpj2Jt@bAB@rOCZQZaM/o`K#X7mtF9dgVc,*0;b9]Ej*kh+^UUH1Sj4kD+RGZuU>8/?>ZFQ\@C4@@TmHJV#:?Ta4gMBXKi%)QI?+<_@^PSmcnH:87ims#]SM].i96I_["g)[3b"`iJj<\eN-,_R;8F@pVE2:VAAH1F,C:N,(c3AH+.JQUOt<*>?E0mlIVL?&S-BCB"i'_n^B3(%tirhieFPCkXlG=H7C=Yl7%%f%1Ea\*LX`#(!/$R=`H%hL=TkCnJjeEeM/T?sKkh@n]<`GC+!q&"fkY_1C!Kc,7GGp/FaQ,co8=F3KLAm>P/$#P*Db1O=Lr;[f9.XhW+Uf-BcCJ-pK\<>I<>VF,GH0B=<>sn'l%l!C@-U?m9,?K-6CC;ShNhkHIXq(KR&%tu,BfOAVeNeo+/h!)c1TR"fs:,_K/5YiQP,CY&Q#FOddKP`d$-SLnY5DQ0P[*[g>p[L]k]"]+"m/-]'E5l!%'B*%9TBL$'X+(a7W,Ob3lm58_0EnHALtQ"mQM5F$*[`5HZ!PH:5%9G3#CX5b!!B;"+',g?d-\78W'I;X[8+P_aKPK'AjMZaIN"8=22\'ZglImbSuLB".NRBrcZ+iHTVhPt`8+@^"2Fj3=2tW%OK3gEHrT`At:m&*uMX=SZY9@N./F(1f/k;Q-!'NmOS8?[uE%e::h,;7Jqk:cT4KmU"pK_knp?!m>n((D)I/@&.1idj\pnmr$9n,4f($oV6)NO[YBI(pg:GX"igB,&bB789j[$`*6\,;dLjGu#56(PliL&DtfH'^NquGug%j#M'"c%7GqF:#[nm0Z7cVmm*]+^J/82m,'`?)4t0\kbqk'rS(aHKT/3u1>B`79m+406HrJjQV=qhmb=62=&R]n&t=Yk0+Hplge6:!#%H42!`[Lq0>Nef#,'"Nh0C?kO[YBHYkq6NknEsT&/P`=9B"kA3!sLoQ&Wc@+5"-h/n^X!A<+hICXgL'Gf_("K\ejUE_TAbB=hr7%`c`.O6bUOii>]Lf("J_q8a#"V(SD@&2ke(Ml*RR4[%8`!mG(4JT(+UtT+h:cbNu2h7C`#gI;jPFiZ.m+oHI"J"BSP7X.d74'0uLK>?%#9mH\8*I_S4[oStLB4AWWF#G]H;n'Co[gek@tu+]H?Ds%jcm.-2,.B4cnTn$OL;86#VGZ^"F:5gU*c+;B&.@c*fhE;XV:2m(QXiGfcYI)f/iTdS";R"1jh.RJp3=eA[7GG1$;?G=b[l9dsmoK>IcghK=4+V%[>Xp8s&Q?oULQUuKJ_\8`K_o3Pgi$PEXoQo&GfW7QEmjI'Tnm"kCi1Fs@Lk$-75F'cRL94g[:TeG2CkNW=IRc6(#;\g^Sp=?J$OQW42jlq6m*2ps_J'i^=KK52md129Y>@$4M^Rk/W=OtGp+gpR$Q4WoI"PJY0qj3qpX])GQr%0-B-ODA^c.Cj-dVeti5C\IPGNj?Y[su$@j.H@P*l5Xk,[:`(nNU(\`+?86e9P`MSc1oMR35BQ\_bHs^R"#C\1?M6["%kGfAu+M0E28I$lIn~> +endstream +endobj +799 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 798 0 R +/Annots 800 0 R +>> +endobj +800 0 obj +[ +] +endobj +801 0 obj +<< /Length 3593 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`W>AkIi&q801csmG_H^8mH]K)##puLGM;&gPiI?::b/]iuamUS^rBuQs.uI%4F3r87.V22/6%/dIc1fn+qRHNe9cY&U03`D.Bu15@N]EOcj5G[I4#\#))ain$o#X5jp1UEm2rd;nP1=e1o7FQ32]?6E'LcnZTjD03I;d2`0Mg._'W8.]!.aADeW[V_3X*Y``XLTfUb$I[`e4`fkML`-j:tiY^VkW+CZm!#grCs@\6@2bPh:X)K'/YIEh>!Zcj&hXVgoIY4lZ54?h_7^s(c1MetpO"&*eUB\6^F!0T+7;:K;0eO2qj2=eRRGmMB/nu*]!T'u(_]BjON3Qq4r^o%pU-?T_\L^`^fpERk*J>%e?I/E[E[qBFTZa@g3oUkPZ!r.sR`s-3CPIhQ!%aS%[a6[U:-,d_9M_^c1ShgNI*rm/Z>Q<+-@J1,Pk?T#.Ab[-*(U?Gd/e:aYh2\AmIhOA(M[Ze,rWkS\mEYcc2bm-f=U92ca#>`k-dk@fH:Wf0U.d0:p1Gj[V\-)^]MbM_*Fp"G4:f3@hn&IG3jH#r&JW#GfiKNBqTTG!aCJLdVrkMZk(XL_3+-\1tEamLPYFI80!h9,MrTb%4mD0LK\k2D=LT:[.Jg0miE7*:nlg%_LpLii!RZVPMs?:`4>52P)VR0=\S=HH5=t#0Sh4D"]Y2U[W*9mU/5'_#])"$*UJ^[d62B6A[:3B3s1>mg-f@j/>e/2;W6ZLJ'`EeXq]pm8Z%S\rk4K?tiX3-fO"F.1$nLblRkf;gTG/5++;4/&SeK-d,->3I7BPV05JIl<(E@c6'ijeR-m?QW579CMZ_>d%j*)qFcPfbm6@=7q1)@aL.dl+3ns4AN1#&n\Ikr1REip\Dp1WTrIpUf@aX5S7gejJC]Df%&)\u;W[K*+JeJn*3DG!AtX$#fY09_$^fElQOjD<]WEZIbHfgf%p8?i0ohB@,HM$LQ-5XpIPk)8^QM;%cd9;:*WR;Gt)/U2eBZNFQEET>7=cCUc]*n?)oZAOrkQH.o+9N'#1B^NGeR$r]X$UYmL<=skV0aoRuJSqH;@e2IZ-!jW9l>]X:IAF)V):Of]D%]lB]Na0&-g;ucL[DZ1a'(Ka_4aQ>CPbgCNl/\kY?XW!#Y[*J*"klC>2o.oK[LaYgF&mqXl%6(E"ifHMa`=N(gi).uk;b*1j$ke7_g49OIR\O&)6;g=DY5q9MUSK>d#Y-Ql2YVa'sjUbA!=[HjbbbN3LUUqg6kd*D`db89Gq;Q'a):b/I:?)ITTT!@W>]tlH,JldU?\Rh:k0<-uKEQ$k$gr999t".=T-c]E-m@cQUJ)_WM4(S(7Y[1I?h%=e_9QQV$[$SG,Z!b-)SWSm;HKKn-1b$b8Seg>$*6Smk+>>qp<$bFc(JIB/cAO:.?&,?FQN,@;$q!Lm.7pA$(ZgjAT97lP5#57Pm-B:`0#c4bg)G$;DHTMtJN3SM0GC*SVp<'Ug+krr[Dno;^%g(X1\o1+KLU;B@='GnKot#Wr,g@q*EIo@4e,7?+F<73ML;l@s;!h6o(daQjTc^&)T_98p<]6X+A$sS&IG(?s3.e#D=0+_%k\.YZ.RipfFD&F_?U!uKTN$e**8]Is/EtZ(m>t;)'_o.:j9_\]a/8<``8L)1QqSN=]Y+"rH[#-k2SHd&0Yb/If-?W$0M=FFqEck+.L:4/>)QY"R9dP?qTuQlV-IIeU$"MgSLpO!O,!#;X();W0Sce(bDB`%@_^Nek<4aP!ddiV%?,rWFP/7>?5nV'80'5=J`0D4@7D5t(!o'#k6=MN'FH]A8RQ+XOr*d[KZ4/u/(t-?sHqkDrA6XOTkeUV?ENtUkd"d7rYU#6FDeD#$Y-'ttiGN+o+c=g(*Wq^RL-j:&I'GlF.H/3oi6k_K>jTAu3GKI/0-GQ%*BTO0(j/e7W*V_-\h$_DfOmagJ/%bfQ0/EZDfF:hor72t&\aJjME"s!L=Ah&PuBVOBOu9AL8;Hm%Ee(k-h!dNHUF5JUp\CWVN-N4591#]i&ul'WhPNcrh0gT5W<[jX.C=;P4]n_E@pP/G2piE_tgEpJu7hClq05V`G\;=FT`c`4c\E]L+Z,`NT2sn^i-/68AZ;CG't)PlQ$6mf`\o2kBktEl_5f8i5'R\,`3Fo.AGO&885PNiC=,,DeGK@4P#CP4\X,C2_#FH+KWgJl$)geI?i$/RY+3;8flOV('O4i"202AD?M\M)i`+nI#o*Jh&ZsRWMMgX'*1X?k1qo]G1qNO[\h8sRW(X3I.#0sUNiV*]$D#WF4 +endstream +endobj +802 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 801 0 R +/Annots 803 0 R +>> +endobj +803 0 obj +[ +] +endobj +804 0 obj +<< /Length 3953 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#`>AkIi&q801csmGO>Mco;hH\)eEgk"&,Nh0'I?orRAMMc,bHY0Aq0cb]XqD:f:0hBrNEOfPNLbPfp*)ka8L=llTb6[hLFeip9C]B0P>jKH/)I?CZar'\UUX%?Y=l7(B8ekA,^,UqE-jd?s7*0$d=:'_mEpjKP0@+$/_(nn0@jLAWU*#8>I_[7QZ`pX.luBR%S],cA,GsEh9OK=k0#7DSN0"9MtuQQj=e`K$ldA-h7/mVY[lQr&h_HGFPLF':0U3!4;TqFgG1pl<[I83PAloG'.B8gSQ\Pm&j4k*hW>WoKQ8?nu^081_I/fo'qG?UpD!nsubsYTSo?KI&oB3F5I9GFaK=ZYHY>lp,!b698d0e-p47Zqo5;0=Z_=ns#>3,+m+?fnriipkK<^gRh\m^f(l3t>N\+P3eGnmW)0K+UsVF5%g1o?j9Xt0/_Qn3c&-egfQ=4Lne4_4+>mY+-iQ)B15tteqC=&MD%>WmfVWVkMcMtDe=j5d;T3o8n42:876HMoE@Ui?V;8Wcs@c2(:mdL6FBTWtQNGkt)cPE6'-F*h!F@,m#Zh`q9LtaP`8kXIR6R(kQHDMTE1Cs--tNMd%Z*5f3';'HQMWGZ#b$\[_24ORi-Co6LHqnt,R32/\]/kNO("]&W&cI6[r't^?QI1O3.#%U15'$MM]i.*PQO;`EGcC:E4'i.K-[C_e&#\QHb_>57(WUE$bW^*\?-BhmrAl>g3j$fFdgSEg3FmL31Jkep>_]knDHk@niA!aE&?k4YT/#YR_\c>rX!%SP33t'5[unlfE/&[G>aUuen3r@V8C&h-:Q]mBU>dh`H#.[9r[oH&=8Xu^X3uGQ,M(>M>PDP'9-690Xuhm^>"k"'ULb*PU*Y8JPYR8W.MTc]c2;p5nZ@+dUb;13)\[B4Z-Z#32K>oYjnHJ4=^aYW`P[FL[m/k\B.^(^ee"N#_tMUL$;S@RJs(PhlV+WqrNmZj[2q[!%+D5<'da.7!+!Tp$/NV?l.s(e(<0O+P`n`P(=F64so1!Z7(u1!LYd@(cX"3B4+[e8scUUol^T1.+L;H1_/L/.(+j4#NU5QP;G41OdsV[3[Q%=+_!G#!XIgYWO*8\3OU@o_s>lJ(:b.]%#MBMg.A$A(W5GNWI9Za]8@4*(k#BID#/"&UU8a4-9:'-&qnSD_F`ld&AW1:?W7-Agke7biQ,'YF!teX*GX+mF/B$]6)p?Cbiu:.k1j/6-GkF@pO(l'@p0jI/_:80ngRS1c*$#EjpFPNJB#3(7d`>6)&Io&ej,S,NQT`a9l;*oeJcMZ%dl:/A7'1sHgiH.56TmB"f-d(1($(V6QKXc`N9SgV!hSbhV&WNKU!&JMA1R_u5B)bTu]?_&dohaF#I-$Z;YhrJTQ5Oh3(r2K]\[b>=K[agHJjbKtP=i>/4jh8!K7^lsHNpSS6iuWQ9Nc!8Xl*:l6oD>LJ7SK3A`EA,3l$o`QW-HoBGs2'Ph\ZT5B"G^d]"B)iO-nkN:!Q!<7V.-ST\X\$>q$p0H:npCie*""3\=:O?fh>up(bp<_S2X_P[C?-FEb'tB]JC"PR2o`aSEgZ@JOiDLeP"\:Qg`X%_2P3Rc*Cl]"1YO0b^4a])09teq!n+ARkJke8.#ni%r.H0qlTCh%.MPi.di5>qoX.2NR"SG$C6hf&cWO%Sa3d!+hf+D`K5rHOmaHg/hjKM40>YNo=*ueuh#16E8n1\/et8+'YYkC"fe@8-MR1/E_Y>O_&@urrld?Le'+&:Dk`JKY6$f;k`=raMgjk.><8g_,kF4t%'Ho:%4VdTM\loo6%2&3M\,#G[Q1+M$H5O_9Ki;h)f/R0]ml2$SBSWahV$2hW,4T'V7%hP7,iQnpTMYj,:SM$_u994?*T72Lb=BZPK$($o!?[F=UN3@;hQ1WpgjrR/'j."&d.""TKQ57E*VX_cJ%mKlTe733$HrCA3pqH]nb[YAGo@s%2e!fa8_%^@m&!0/]N#\-%d3K)q8dZH1$]OYa15TYKS%*^G:K2[CKm0k,Uh(j_Qi%k5h[aTYEo)(Mn6GWK#_(O-,2Ndj2$,0;Et%4a`s;g(.J=A2pu4qO#Xr3-M1f8ES0URijdZ1ToG%]USZOY;A5tC!KrgMLo8I*kDfah6m(LLZkU/Ga_"kq='"SKnNHLj!Hq6+bVRARV(h-Bu8==!q6StoiO0"p3M+1H2^=`o/hZs,k'kYht&p]AR9$,L_ilKN/F7ur/OUoUiViaDVS$9IRd*0N#OW1.#AFK*rSM@Z-VV;GVTCP6;K9E'?IT7&FN+%4!#5Y]r)iBg@r"dB&dp_]?b+Op,8jNBjEBa4YXI4c^ps!b(12W2!\LPTp:JjqN-C'bMu8_U,f-182)kS\B^5d2VO/%ph*qtm[>0[521"W?c5gu^,l.)E$ttk+_t"`=l+u9mj##C'8*ScG)\THpZ9<1bC33#j]9S%bb,?"XVl*UY[9TN/ZUskcD"LF^7$d6BpIdl-%B2PmGH>jJ.^9qr7s-==M4DF4q#c@~> +endstream +endobj +805 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 804 0 R +/Annots 806 0 R +>> +endobj +806 0 obj +[ +] +endobj +807 0 obj +<< /Length 1195 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHK95iNL&AI`dHq^B@g%XNod%Ig>P:/`=+j+3q%RDhZZ5p@OpDma[Zc7n4OX^j.AS/YpG+QF8%*lj:0F.oIf)ZBD!to#:OMG6`;hQ>>=>3k^:FC@`Y:AYUopOG`u9P;HOuY!/ai(s*=B`GX]mS&VMg#1H=/&0BBT2R`3D+qVWu"j,[a)SH(.C53A=.P99p808XJVm)FZOi**^Xr-2\dY>@3C1@R):DLm9RV$/FQ;s[HEQuU38>0Q]W&>+ZI:u`CmUF%Ig[E]gj=B>Gf\uD32Wk/a0[IT;:(T91XsRoQTf^ldsHmdZ0l\hS]WX.:-UGOnZO2BS8VCP`aQM6%Zn;1'JGj?Rl+,$'t6H3+A@soViX])d,,tUUD&\67^E/:;Cu)RM.cnlmsQgMtokPZ_QY3A2!9X`5NDF9.TK#I;&;Fk?9+*WGC-Jcaf9_C[A"hj/4u8nFiTeMl($Gl-:GdAWpIkXnLL.E,eP*p)326."Q79h:nU1n\CZ&Nh)OOgF.GjO)&3RJ*OYn;`YZ**&<\I)S0E8)<$(E:S6dJt<==J(D="s/Z5!5)en>>o!#,J$T`D\qf[76jm(k#uAFG#ef)b4]f=*u+kq6sP(=<,r0GHZDYK"8hI4L[N0W)P"XE0)Qn-K7=;qcSE\7`4>f0Kr_Roe)a^JDp>9Njm2_0DMKnMS'ZKKSSell$Hl(PCKM7Fi+`"a3Jt]8o!HsSZhi?E.(@K1ef`mqT@qc=-F59bPJg5REuC[E(P9l[XdsCd1;Rp.l_inTOitl)+. +endstream +endobj +808 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 807 0 R +/Annots 809 0 R +>> +endobj +809 0 obj +[ +] +endobj +812 0 obj +<< + /Title (\376\377\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\157\0\146\0\40\0\164\0\150\0\151\0\163\0\40\0\115\0\145\0\155\0\157) + /Parent 810 0 R + /Next 814 0 R + /A 811 0 R +>> endobj +814 0 obj +<< + /Title (\376\377\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\116\0\157\0\164\0\151\0\143\0\145) + /Parent 810 0 R + /Prev 812 0 R + /Next 816 0 R + /A 813 0 R +>> endobj +816 0 obj +<< + /Title (\376\377\0\101\0\142\0\163\0\164\0\162\0\141\0\143\0\164) + /Parent 810 0 R + /Prev 814 0 R + /Next 818 0 R + /A 815 0 R +>> endobj +818 0 obj +<< + /Title (\376\377\0\124\0\141\0\142\0\154\0\145\0\40\0\157\0\146\0\40\0\103\0\157\0\156\0\164\0\145\0\156\0\164\0\163) + /Parent 810 0 R + /Prev 816 0 R + /Next 819 0 R + /A 817 0 R +>> endobj +819 0 obj +<< + /Title (\376\377\0\61\0\40\0\111\0\156\0\164\0\162\0\157\0\144\0\165\0\143\0\164\0\151\0\157\0\156) + /Parent 810 0 R + /First 820 0 R + /Last 837 0 R + /Prev 818 0 R + /Next 838 0 R + /Count -14 + /A 11 0 R +>> endobj +820 0 obj +<< + /Title (\376\377\0\61\0\56\0\61\0\40\0\122\0\145\0\154\0\141\0\164\0\151\0\157\0\156\0\163\0\150\0\151\0\160\0\40\0\164\0\157\0\40\0\127\0\145\0\142\0\104\0\101\0\126) + /Parent 819 0 R + /Next 821 0 R + /A 13 0 R +>> endobj +821 0 obj +<< + /Title (\376\377\0\61\0\56\0\62\0\40\0\116\0\157\0\164\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\157\0\156\0\166\0\145\0\156\0\164\0\151\0\157\0\156\0\163) + /Parent 819 0 R + /Prev 820 0 R + /Next 822 0 R + /A 15 0 R +>> endobj +822 0 obj +<< + /Title (\376\377\0\61\0\56\0\63\0\40\0\124\0\145\0\162\0\155\0\163) + /Parent 819 0 R + /Prev 821 0 R + /Next 823 0 R + /A 17 0 R +>> endobj +823 0 obj +<< + /Title (\376\377\0\61\0\56\0\64\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\126\0\141\0\154\0\165\0\145\0\163) + /Parent 819 0 R + /First 824 0 R + /Last 830 0 R + /Prev 822 0 R + /Next 831 0 R + /Count -5 + /A 19 0 R +>> endobj +824 0 obj +<< + /Title (\376\377\0\61\0\56\0\64\0\56\0\61\0\40\0\111\0\156\0\151\0\164\0\151\0\141\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\126\0\141\0\154\0\165\0\145) + /Parent 823 0 R + /Next 826 0 R + /A 21 0 R +>> endobj +826 0 obj +<< + /Title (\376\377\0\61\0\56\0\64\0\56\0\62\0\40\0\120\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\126\0\141\0\154\0\165\0\145) + /Parent 823 0 R + /Prev 824 0 R + /Next 828 0 R + /A 825 0 R +>> endobj +828 0 obj +<< + /Title (\376\377\0\61\0\56\0\64\0\56\0\63\0\40\0\103\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\126\0\141\0\154\0\165\0\145) + /Parent 823 0 R + /Prev 826 0 R + /Next 829 0 R + /A 827 0 R +>> endobj +829 0 obj +<< + /Title (\376\377\0\61\0\56\0\64\0\56\0\64\0\40\0\102\0\157\0\157\0\154\0\145\0\141\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\126\0\141\0\154\0\165\0\145) + /Parent 823 0 R + /Prev 828 0 R + /Next 830 0 R + /A 27 0 R +>> endobj +830 0 obj +<< + /Title (\376\377\0\61\0\56\0\64\0\56\0\65\0\40\0\104\0\101\0\126\0\72\0\150\0\162\0\145\0\146\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\126\0\141\0\154\0\165\0\145) + /Parent 823 0 R + /Prev 829 0 R + /A 29 0 R +>> endobj +831 0 obj +<< + /Title (\376\377\0\61\0\56\0\65\0\40\0\104\0\101\0\126\0\40\0\116\0\141\0\155\0\145\0\163\0\160\0\141\0\143\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164\0\163\0\40\0\151\0\156\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164\0\40\0\141\0\156\0\144\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\40\0\102\0\157\0\144\0\151\0\145\0\163) + /Parent 819 0 R + /Prev 823 0 R + /Next 833 0 R + /A 31 0 R +>> endobj +833 0 obj +<< + /Title (\376\377\0\61\0\56\0\66\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\40\0\120\0\162\0\145\0\143\0\157\0\156\0\144\0\151\0\164\0\151\0\157\0\156\0\163\0\40\0\141\0\156\0\144\0\40\0\120\0\157\0\163\0\164\0\143\0\157\0\156\0\144\0\151\0\164\0\151\0\157\0\156\0\163) + /Parent 819 0 R + /First 834 0 R + /Last 834 0 R + /Prev 831 0 R + /Next 836 0 R + /Count -1 + /A 832 0 R +>> endobj +834 0 obj +<< + /Title (\376\377\0\61\0\56\0\66\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\162\0\145\0\161\0\165\0\145\0\163\0\164\0\40\0\167\0\151\0\164\0\150\0\40\0\104\0\101\0\126\0\72\0\155\0\165\0\163\0\164\0\55\0\142\0\145\0\55\0\143\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\151\0\156\0\40\0\162\0\145\0\163\0\160\0\157\0\156\0\163\0\145) + /Parent 833 0 R + /A 35 0 R +>> endobj +836 0 obj +<< + /Title (\376\377\0\61\0\56\0\67\0\40\0\103\0\154\0\141\0\162\0\151\0\146\0\151\0\143\0\141\0\164\0\151\0\157\0\156\0\40\0\157\0\146\0\40\0\103\0\117\0\120\0\131\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163\0\40\0\167\0\151\0\164\0\150\0\40\0\117\0\166\0\145\0\162\0\167\0\162\0\151\0\164\0\145\0\72\0\124) + /Parent 819 0 R + /Prev 833 0 R + /Next 837 0 R + /A 835 0 R +>> endobj +837 0 obj +<< + /Title (\376\377\0\61\0\56\0\70\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\151\0\156\0\147\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\163\0\40\0\141\0\156\0\144\0\40\0\127\0\162\0\151\0\164\0\145\0\40\0\114\0\157\0\143\0\153\0\163) + /Parent 819 0 R + /Prev 836 0 R + /A 39 0 R +>> endobj +838 0 obj +<< + /Title (\376\377\0\62\0\40\0\102\0\141\0\163\0\151\0\143\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\151\0\156\0\147\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145\0\163) + /Parent 810 0 R + /First 839 0 R + /Last 841 0 R + /Prev 819 0 R + /Next 846 0 R + /Count -5 + /A 41 0 R +>> endobj +839 0 obj +<< + /Title (\376\377\0\62\0\56\0\61\0\40\0\102\0\141\0\163\0\151\0\143\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\151\0\156\0\147\0\40\0\120\0\141\0\143\0\153\0\141\0\147\0\145\0\163) + /Parent 838 0 R + /Next 841 0 R + /A 43 0 R +>> endobj +841 0 obj +<< + /Title (\376\377\0\62\0\56\0\62\0\40\0\102\0\141\0\163\0\151\0\143\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\151\0\156\0\147\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 838 0 R + /First 843 0 R + /Last 845 0 R + /Prev 839 0 R + /Count -3 + /A 840 0 R +>> endobj +843 0 obj +<< + /Title (\376\377\0\62\0\56\0\62\0\56\0\61\0\40\0\103\0\162\0\145\0\141\0\164\0\151\0\156\0\147\0\40\0\141\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 841 0 R + /Next 844 0 R + /A 842 0 R +>> endobj +844 0 obj +<< + /Title (\376\377\0\62\0\56\0\62\0\56\0\62\0\40\0\115\0\157\0\144\0\151\0\146\0\171\0\151\0\156\0\147\0\40\0\141\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 841 0 R + /Prev 843 0 R + /Next 845 0 R + /A 49 0 R +>> endobj +845 0 obj +<< + /Title (\376\377\0\62\0\56\0\62\0\56\0\63\0\40\0\122\0\145\0\160\0\157\0\162\0\164\0\151\0\156\0\147) + /Parent 841 0 R + /Prev 844 0 R + /A 51 0 R +>> endobj +846 0 obj +<< + /Title (\376\377\0\63\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 847 0 R + /Last 896 0 R + /Prev 838 0 R + /Next 898 0 R + /Count -32 + /A 53 0 R +>> endobj +847 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 846 0 R + /First 849 0 R + /Last 857 0 R + /Next 858 0 R + /Count -5 + /A 55 0 R +>> endobj +849 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\143\0\157\0\155\0\155\0\145\0\156\0\164) + /Parent 847 0 R + /Next 851 0 R + /A 848 0 R +>> endobj +851 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\143\0\162\0\145\0\141\0\164\0\157\0\162\0\55\0\144\0\151\0\163\0\160\0\154\0\141\0\171\0\156\0\141\0\155\0\145) + /Parent 847 0 R + /Prev 849 0 R + /Next 853 0 R + /A 850 0 R +>> endobj +853 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\56\0\63\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144\0\55\0\155\0\145\0\164\0\150\0\157\0\144\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 847 0 R + /Prev 851 0 R + /Next 855 0 R + /A 852 0 R +>> endobj +855 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\56\0\64\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144\0\55\0\154\0\151\0\166\0\145\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 847 0 R + /Prev 853 0 R + /Next 857 0 R + /A 854 0 R +>> endobj +857 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\56\0\65\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144\0\55\0\162\0\145\0\160\0\157\0\162\0\164\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 847 0 R + /Prev 855 0 R + /A 856 0 R +>> endobj +858 0 obj +<< + /Title (\376\377\0\63\0\56\0\62\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 846 0 R + /First 860 0 R + /Last 862 0 R + /Prev 847 0 R + /Next 863 0 R + /Count -2 + /A 67 0 R +>> endobj +860 0 obj +<< + /Title (\376\377\0\63\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\151\0\156\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 858 0 R + /Next 862 0 R + /A 859 0 R +>> endobj +862 0 obj +<< + /Title (\376\377\0\63\0\56\0\62\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\141\0\165\0\164\0\157\0\55\0\166\0\145\0\162\0\163\0\151\0\157\0\156) + /Parent 858 0 R + /Prev 860 0 R + /A 861 0 R +>> endobj +863 0 obj +<< + /Title (\376\377\0\63\0\56\0\63\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 846 0 R + /First 865 0 R + /Last 867 0 R + /Prev 858 0 R + /Next 868 0 R + /Count -2 + /A 73 0 R +>> endobj +865 0 obj +<< + /Title (\376\377\0\63\0\56\0\63\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\157\0\165\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 863 0 R + /Next 867 0 R + /A 864 0 R +>> endobj +867 0 obj +<< + /Title (\376\377\0\63\0\56\0\63\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\145\0\144\0\145\0\143\0\145\0\163\0\163\0\157\0\162\0\55\0\163\0\145\0\164) + /Parent 863 0 R + /Prev 865 0 R + /A 866 0 R +>> endobj +868 0 obj +<< + /Title (\376\377\0\63\0\56\0\64\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 846 0 R + /First 869 0 R + /Last 875 0 R + /Prev 863 0 R + /Next 877 0 R + /Count -4 + /A 79 0 R +>> endobj +869 0 obj +<< + /Title (\376\377\0\63\0\56\0\64\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\145\0\144\0\145\0\143\0\145\0\163\0\163\0\157\0\162\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 868 0 R + /Next 871 0 R + /A 81 0 R +>> endobj +871 0 obj +<< + /Title (\376\377\0\63\0\56\0\64\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\143\0\143\0\145\0\163\0\163\0\157\0\162\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 868 0 R + /Prev 869 0 R + /Next 873 0 R + /A 870 0 R +>> endobj +873 0 obj +<< + /Title (\376\377\0\63\0\56\0\64\0\56\0\63\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 868 0 R + /Prev 871 0 R + /Next 875 0 R + /A 872 0 R +>> endobj +875 0 obj +<< + /Title (\376\377\0\63\0\56\0\64\0\56\0\64\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\156\0\141\0\155\0\145\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 868 0 R + /Prev 873 0 R + /A 874 0 R +>> endobj +877 0 obj +<< + /Title (\376\377\0\63\0\56\0\65\0\40\0\126\0\105\0\122\0\123\0\111\0\117\0\116\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 846 0 R + /First 878 0 R + /Last 878 0 R + /Prev 868 0 R + /Next 880 0 R + /Count -1 + /A 876 0 R +>> endobj +878 0 obj +<< + /Title (\376\377\0\63\0\56\0\65\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\126\0\105\0\122\0\123\0\111\0\117\0\116\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114) + /Parent 877 0 R + /A 91 0 R +>> endobj +880 0 obj +<< + /Title (\376\377\0\63\0\56\0\66\0\40\0\122\0\105\0\120\0\117\0\122\0\124\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 846 0 R + /Prev 877 0 R + /Next 882 0 R + /A 879 0 R +>> endobj +882 0 obj +<< + /Title (\376\377\0\63\0\56\0\67\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\164\0\162\0\145\0\145\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 846 0 R + /First 883 0 R + /Last 883 0 R + /Prev 880 0 R + /Next 885 0 R + /Count -1 + /A 881 0 R +>> endobj +883 0 obj +<< + /Title (\376\377\0\63\0\56\0\67\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\164\0\162\0\145\0\145\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 882 0 R + /A 97 0 R +>> endobj +885 0 obj +<< + /Title (\376\377\0\63\0\56\0\70\0\40\0\104\0\101\0\126\0\72\0\145\0\170\0\160\0\141\0\156\0\144\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 846 0 R + /First 886 0 R + /Last 886 0 R + /Prev 882 0 R + /Next 887 0 R + /Count -1 + /A 884 0 R +>> endobj +886 0 obj +<< + /Title (\376\377\0\63\0\56\0\70\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\104\0\101\0\126\0\72\0\145\0\170\0\160\0\141\0\156\0\144\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 885 0 R + /A 104 0 R +>> endobj +887 0 obj +<< + /Title (\376\377\0\63\0\56\0\71\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 885 0 R + /Next 889 0 R + /A 106 0 R +>> endobj +889 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\60\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\120\0\125\0\124\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 887 0 R + /Next 890 0 R + /A 888 0 R +>> endobj +890 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\120\0\122\0\117\0\120\0\106\0\111\0\116\0\104\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 889 0 R + /Next 891 0 R + /A 110 0 R +>> endobj +891 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\62\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\120\0\122\0\117\0\120\0\120\0\101\0\124\0\103\0\110\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 890 0 R + /Next 893 0 R + /A 112 0 R +>> endobj +893 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\63\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 891 0 R + /Next 894 0 R + /A 892 0 R +>> endobj +894 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\117\0\120\0\131\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 893 0 R + /Next 895 0 R + /A 116 0 R +>> endobj +895 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\117\0\126\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 894 0 R + /Next 896 0 R + /A 118 0 R +>> endobj +896 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\125\0\116\0\114\0\117\0\103\0\113\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 846 0 R + /Prev 895 0 R + /A 120 0 R +>> endobj +898 0 obj +<< + /Title (\376\377\0\64\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\55\0\111\0\116\0\55\0\120\0\114\0\101\0\103\0\105\0\40\0\106\0\105\0\101\0\124\0\125\0\122\0\105) + /Parent 810 0 R + /First 899 0 R + /Last 916 0 R + /Prev 846 0 R + /Next 918 0 R + /Count -13 + /A 897 0 R +>> endobj +899 0 obj +<< + /Title (\376\377\0\64\0\56\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 898 0 R + /First 901 0 R + /Last 903 0 R + /Next 904 0 R + /Count -2 + /A 124 0 R +>> endobj +901 0 obj +<< + /Title (\376\377\0\64\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\146\0\157\0\162\0\153) + /Parent 899 0 R + /Next 903 0 R + /A 900 0 R +>> endobj +903 0 obj +<< + /Title (\376\377\0\64\0\56\0\61\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\151\0\156\0\55\0\146\0\157\0\162\0\153) + /Parent 899 0 R + /Prev 901 0 R + /A 902 0 R +>> endobj +904 0 obj +<< + /Title (\376\377\0\64\0\56\0\62\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 898 0 R + /First 905 0 R + /Last 906 0 R + /Prev 899 0 R + /Next 908 0 R + /Count -2 + /A 130 0 R +>> endobj +905 0 obj +<< + /Title (\376\377\0\64\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\146\0\157\0\162\0\153) + /Parent 904 0 R + /Next 906 0 R + /A 132 0 R +>> endobj +906 0 obj +<< + /Title (\376\377\0\64\0\56\0\62\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\151\0\156\0\55\0\146\0\157\0\162\0\153) + /Parent 904 0 R + /Prev 905 0 R + /A 134 0 R +>> endobj +908 0 obj +<< + /Title (\376\377\0\64\0\56\0\63\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\40\0\50\0\141\0\160\0\160\0\154\0\151\0\145\0\144\0\40\0\164\0\157\0\40\0\141\0\40\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\51) + /Parent 898 0 R + /First 909 0 R + /Last 909 0 R + /Prev 904 0 R + /Next 911 0 R + /Count -1 + /A 907 0 R +>> endobj +909 0 obj +<< + /Title (\376\377\0\64\0\56\0\63\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\157\0\146\0\40\0\141\0\40\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 908 0 R + /A 138 0 R +>> endobj +911 0 obj +<< + /Title (\376\377\0\64\0\56\0\64\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\40\0\50\0\141\0\160\0\160\0\154\0\151\0\145\0\144\0\40\0\164\0\157\0\40\0\141\0\40\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\51) + /Parent 898 0 R + /First 912 0 R + /Last 912 0 R + /Prev 908 0 R + /Next 914 0 R + /Count -1 + /A 910 0 R +>> endobj +912 0 obj +<< + /Title (\376\377\0\64\0\56\0\64\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116) + /Parent 911 0 R + /A 142 0 R +>> endobj +914 0 obj +<< + /Title (\376\377\0\64\0\56\0\65\0\40\0\125\0\116\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 898 0 R + /First 915 0 R + /Last 915 0 R + /Prev 911 0 R + /Next 916 0 R + /Count -1 + /A 913 0 R +>> endobj +915 0 obj +<< + /Title (\376\377\0\64\0\56\0\65\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\116\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124) + /Parent 914 0 R + /A 146 0 R +>> endobj +916 0 obj +<< + /Title (\376\377\0\64\0\56\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 898 0 R + /Prev 914 0 R + /A 148 0 R +>> endobj +918 0 obj +<< + /Title (\376\377\0\65\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\110\0\151\0\163\0\164\0\157\0\162\0\171\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 919 0 R + /Last 937 0 R + /Prev 898 0 R + /Next 939 0 R + /Count -15 + /A 917 0 R +>> endobj +919 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\110\0\151\0\163\0\164\0\157\0\162\0\171\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 918 0 R + /First 921 0 R + /Last 923 0 R + /Next 924 0 R + /Count -2 + /A 152 0 R +>> endobj +921 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 919 0 R + /Next 923 0 R + /A 920 0 R +>> endobj +923 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\162\0\157\0\157\0\164\0\55\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 919 0 R + /Prev 921 0 R + /A 922 0 R +>> endobj +924 0 obj +<< + /Title (\376\377\0\65\0\56\0\62\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 918 0 R + /First 926 0 R + /Last 926 0 R + /Prev 919 0 R + /Next 927 0 R + /Count -1 + /A 158 0 R +>> endobj +926 0 obj +<< + /Title (\376\377\0\65\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\150\0\151\0\163\0\164\0\157\0\162\0\171\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 924 0 R + /A 925 0 R +>> endobj +927 0 obj +<< + /Title (\376\377\0\65\0\56\0\63\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 918 0 R + /First 928 0 R + /Last 928 0 R + /Prev 924 0 R + /Next 930 0 R + /Count -1 + /A 162 0 R +>> endobj +928 0 obj +<< + /Title (\376\377\0\65\0\56\0\63\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\150\0\151\0\163\0\164\0\157\0\162\0\171\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 927 0 R + /A 164 0 R +>> endobj +930 0 obj +<< + /Title (\376\377\0\65\0\56\0\64\0\40\0\104\0\101\0\126\0\72\0\154\0\157\0\143\0\141\0\164\0\145\0\55\0\142\0\171\0\55\0\150\0\151\0\163\0\164\0\157\0\162\0\171\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 918 0 R + /First 931 0 R + /Last 931 0 R + /Prev 927 0 R + /Next 932 0 R + /Count -1 + /A 929 0 R +>> endobj +931 0 obj +<< + /Title (\376\377\0\65\0\56\0\64\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\104\0\101\0\126\0\72\0\154\0\157\0\143\0\141\0\164\0\145\0\55\0\142\0\171\0\55\0\150\0\151\0\163\0\164\0\157\0\162\0\171\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 930 0 R + /A 168 0 R +>> endobj +932 0 obj +<< + /Title (\376\377\0\65\0\56\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 918 0 R + /Prev 930 0 R + /Next 933 0 R + /A 170 0 R +>> endobj +933 0 obj +<< + /Title (\376\377\0\65\0\56\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 918 0 R + /Prev 932 0 R + /Next 934 0 R + /A 172 0 R +>> endobj +934 0 obj +<< + /Title (\376\377\0\65\0\56\0\67\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\117\0\120\0\131\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 918 0 R + /Prev 933 0 R + /Next 935 0 R + /A 174 0 R +>> endobj +935 0 obj +<< + /Title (\376\377\0\65\0\56\0\70\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\117\0\126\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 918 0 R + /Prev 934 0 R + /Next 936 0 R + /A 176 0 R +>> endobj +936 0 obj +<< + /Title (\376\377\0\65\0\56\0\71\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\105\0\122\0\123\0\111\0\117\0\116\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 918 0 R + /Prev 935 0 R + /Next 937 0 R + /A 178 0 R +>> endobj +937 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\60\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 918 0 R + /Prev 936 0 R + /A 180 0 R +>> endobj +939 0 obj +<< + /Title (\376\377\0\66\0\40\0\127\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 940 0 R + /Last 954 0 R + /Prev 918 0 R + /Next 956 0 R + /Count -12 + /A 938 0 R +>> endobj +940 0 obj +<< + /Title (\376\377\0\66\0\56\0\61\0\40\0\127\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 939 0 R + /First 942 0 R + /Last 942 0 R + /Next 943 0 R + /Count -1 + /A 184 0 R +>> endobj +942 0 obj +<< + /Title (\376\377\0\66\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\167\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\55\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 940 0 R + /A 941 0 R +>> endobj +943 0 obj +<< + /Title (\376\377\0\66\0\56\0\62\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 939 0 R + /First 945 0 R + /Last 945 0 R + /Prev 940 0 R + /Next 947 0 R + /Count -1 + /A 188 0 R +>> endobj +945 0 obj +<< + /Title (\376\377\0\66\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\167\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 943 0 R + /A 944 0 R +>> endobj +947 0 obj +<< + /Title (\376\377\0\66\0\56\0\63\0\40\0\115\0\113\0\127\0\117\0\122\0\113\0\123\0\120\0\101\0\103\0\105\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 939 0 R + /First 948 0 R + /Last 948 0 R + /Prev 943 0 R + /Next 950 0 R + /Count -1 + /A 946 0 R +>> endobj +948 0 obj +<< + /Title (\376\377\0\66\0\56\0\63\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\113\0\127\0\117\0\122\0\113\0\123\0\120\0\101\0\103\0\105) + /Parent 947 0 R + /A 197 0 R +>> endobj +950 0 obj +<< + /Title (\376\377\0\66\0\56\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 939 0 R + /First 951 0 R + /Last 951 0 R + /Prev 947 0 R + /Next 952 0 R + /Count -1 + /A 949 0 R +>> endobj +951 0 obj +<< + /Title (\376\377\0\66\0\56\0\64\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123) + /Parent 950 0 R + /A 201 0 R +>> endobj +952 0 obj +<< + /Title (\376\377\0\66\0\56\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 939 0 R + /Prev 950 0 R + /Next 953 0 R + /A 203 0 R +>> endobj +953 0 obj +<< + /Title (\376\377\0\66\0\56\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\117\0\126\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 939 0 R + /Prev 952 0 R + /Next 954 0 R + /A 205 0 R +>> endobj +954 0 obj +<< + /Title (\376\377\0\66\0\56\0\67\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\105\0\122\0\123\0\111\0\117\0\116\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 939 0 R + /First 955 0 R + /Last 955 0 R + /Prev 953 0 R + /Count -1 + /A 207 0 R +>> endobj +955 0 obj +<< + /Title (\376\377\0\66\0\56\0\67\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\126\0\105\0\122\0\123\0\111\0\117\0\116\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114\0\40\0\50\0\165\0\163\0\151\0\156\0\147\0\40\0\141\0\156\0\40\0\145\0\170\0\151\0\163\0\164\0\151\0\156\0\147\0\40\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\150\0\151\0\163\0\164\0\157\0\162\0\171\0\51) + /Parent 954 0 R + /A 209 0 R +>> endobj +956 0 obj +<< + /Title (\376\377\0\67\0\40\0\125\0\120\0\104\0\101\0\124\0\105\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 958 0 R + /Last 960 0 R + /Prev 939 0 R + /Next 962 0 R + /Count -3 + /A 211 0 R +>> endobj +958 0 obj +<< + /Title (\376\377\0\67\0\56\0\61\0\40\0\125\0\120\0\104\0\101\0\124\0\105\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 956 0 R + /First 959 0 R + /Last 959 0 R + /Next 960 0 R + /Count -1 + /A 957 0 R +>> endobj +959 0 obj +<< + /Title (\376\377\0\67\0\56\0\61\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\125\0\120\0\104\0\101\0\124\0\105) + /Parent 958 0 R + /A 215 0 R +>> endobj +960 0 obj +<< + /Title (\376\377\0\67\0\56\0\62\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 956 0 R + /Prev 958 0 R + /A 217 0 R +>> endobj +962 0 obj +<< + /Title (\376\377\0\70\0\40\0\114\0\141\0\142\0\145\0\154\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 963 0 R + /Last 976 0 R + /Prev 956 0 R + /Next 977 0 R + /Count -11 + /A 961 0 R +>> endobj +963 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 962 0 R + /First 965 0 R + /Last 965 0 R + /Next 967 0 R + /Count -1 + /A 221 0 R +>> endobj +965 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\154\0\141\0\142\0\145\0\154\0\55\0\156\0\141\0\155\0\145\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 963 0 R + /A 964 0 R +>> endobj +967 0 obj +<< + /Title (\376\377\0\70\0\56\0\62\0\40\0\114\0\101\0\102\0\105\0\114\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 962 0 R + /First 968 0 R + /Last 968 0 R + /Prev 963 0 R + /Next 970 0 R + /Count -1 + /A 966 0 R +>> endobj +968 0 obj +<< + /Title (\376\377\0\70\0\56\0\62\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\123\0\145\0\164\0\164\0\151\0\156\0\147\0\40\0\141\0\40\0\154\0\141\0\142\0\145\0\154) + /Parent 967 0 R + /A 227 0 R +>> endobj +970 0 obj +<< + /Title (\376\377\0\70\0\56\0\63\0\40\0\114\0\141\0\142\0\145\0\154\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 962 0 R + /Prev 967 0 R + /Next 971 0 R + /A 969 0 R +>> endobj +971 0 obj +<< + /Title (\376\377\0\70\0\56\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 962 0 R + /Prev 970 0 R + /Next 972 0 R + /A 231 0 R +>> endobj +972 0 obj +<< + /Title (\376\377\0\70\0\56\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\107\0\105\0\124\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 962 0 R + /Prev 971 0 R + /Next 973 0 R + /A 233 0 R +>> endobj +973 0 obj +<< + /Title (\376\377\0\70\0\56\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\120\0\122\0\117\0\120\0\106\0\111\0\116\0\104\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 962 0 R + /Prev 972 0 R + /Next 974 0 R + /A 235 0 R +>> endobj +974 0 obj +<< + /Title (\376\377\0\70\0\56\0\67\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\117\0\120\0\131\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 962 0 R + /Prev 973 0 R + /Next 975 0 R + /A 237 0 R +>> endobj +975 0 obj +<< + /Title (\376\377\0\70\0\56\0\70\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 962 0 R + /Prev 974 0 R + /Next 976 0 R + /A 239 0 R +>> endobj +976 0 obj +<< + /Title (\376\377\0\70\0\56\0\71\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\125\0\120\0\104\0\101\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 962 0 R + /Prev 975 0 R + /A 241 0 R +>> endobj +977 0 obj +<< + /Title (\376\377\0\71\0\40\0\127\0\157\0\162\0\153\0\151\0\156\0\147\0\55\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 978 0 R + /Last 992 0 R + /Prev 962 0 R + /Next 994 0 R + /Count -14 + /A 243 0 R +>> endobj +978 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 977 0 R + /First 979 0 R + /Last 980 0 R + /Next 981 0 R + /Count -2 + /A 245 0 R +>> endobj +979 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\146\0\157\0\162\0\153) + /Parent 978 0 R + /Next 980 0 R + /A 247 0 R +>> endobj +980 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\151\0\156\0\55\0\146\0\157\0\162\0\153) + /Parent 978 0 R + /Prev 979 0 R + /A 249 0 R +>> endobj +981 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\40\0\127\0\157\0\162\0\153\0\151\0\156\0\147\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 977 0 R + /First 983 0 R + /Last 985 0 R + /Prev 978 0 R + /Next 986 0 R + /Count -3 + /A 251 0 R +>> endobj +983 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\141\0\165\0\164\0\157\0\55\0\165\0\160\0\144\0\141\0\164\0\145\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 981 0 R + /Next 984 0 R + /A 982 0 R +>> endobj +984 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\146\0\157\0\162\0\153) + /Parent 981 0 R + /Prev 983 0 R + /Next 985 0 R + /A 255 0 R +>> endobj +985 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\56\0\63\0\40\0\104\0\101\0\126\0\72\0\143\0\150\0\145\0\143\0\153\0\151\0\156\0\55\0\146\0\157\0\162\0\153) + /Parent 981 0 R + /Prev 984 0 R + /A 257 0 R +>> endobj +986 0 obj +<< + /Title (\376\377\0\71\0\56\0\63\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\40\0\50\0\141\0\160\0\160\0\154\0\151\0\145\0\144\0\40\0\164\0\157\0\40\0\141\0\40\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\51) + /Parent 977 0 R + /First 987 0 R + /Last 987 0 R + /Prev 981 0 R + /Next 988 0 R + /Count -1 + /A 259 0 R +>> endobj +987 0 obj +<< + /Title (\376\377\0\71\0\56\0\63\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\157\0\146\0\40\0\141\0\40\0\166\0\145\0\162\0\163\0\151\0\157\0\156) + /Parent 986 0 R + /A 261 0 R +>> endobj +988 0 obj +<< + /Title (\376\377\0\71\0\56\0\64\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\40\0\50\0\141\0\160\0\160\0\154\0\151\0\145\0\144\0\40\0\164\0\157\0\40\0\141\0\40\0\167\0\157\0\162\0\153\0\151\0\156\0\147\0\40\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\51) + /Parent 977 0 R + /First 989 0 R + /Last 989 0 R + /Prev 986 0 R + /Next 990 0 R + /Count -1 + /A 263 0 R +>> endobj +989 0 obj +<< + /Title (\376\377\0\71\0\56\0\64\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\157\0\146\0\40\0\141\0\40\0\167\0\157\0\162\0\153\0\151\0\156\0\147\0\40\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 988 0 R + /A 265 0 R +>> endobj +990 0 obj +<< + /Title (\376\377\0\71\0\56\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 977 0 R + /Prev 988 0 R + /Next 991 0 R + /A 267 0 R +>> endobj +991 0 obj +<< + /Title (\376\377\0\71\0\56\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\117\0\120\0\131\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 977 0 R + /Prev 990 0 R + /Next 992 0 R + /A 269 0 R +>> endobj +992 0 obj +<< + /Title (\376\377\0\71\0\56\0\67\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\117\0\126\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 977 0 R + /Prev 991 0 R + /A 271 0 R +>> endobj +994 0 obj +<< + /Title (\376\377\0\61\0\60\0\40\0\101\0\144\0\166\0\141\0\156\0\143\0\145\0\144\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\151\0\156\0\147\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145\0\163) + /Parent 810 0 R + /First 995 0 R + /Last 996 0 R + /Prev 977 0 R + /Next 998 0 R + /Count -2 + /A 993 0 R +>> endobj +995 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\61\0\40\0\101\0\144\0\166\0\141\0\156\0\143\0\145\0\144\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\151\0\156\0\147\0\40\0\120\0\141\0\143\0\153\0\141\0\147\0\145\0\163) + /Parent 994 0 R + /Next 996 0 R + /A 275 0 R +>> endobj +996 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\62\0\40\0\101\0\144\0\166\0\141\0\156\0\143\0\145\0\144\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\151\0\156\0\147\0\40\0\124\0\145\0\162\0\155\0\163) + /Parent 994 0 R + /Prev 995 0 R + /A 277 0 R +>> endobj +998 0 obj +<< + /Title (\376\377\0\61\0\61\0\40\0\115\0\145\0\162\0\147\0\145\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 999 0 R + /Last 1011 0 R + /Prev 994 0 R + /Next 1013 0 R + /Count -10 + /A 997 0 R +>> endobj +999 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 998 0 R + /First 1001 0 R + /Last 1003 0 R + /Next 1005 0 R + /Count -2 + /A 284 0 R +>> endobj +1001 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\155\0\145\0\162\0\147\0\145\0\55\0\163\0\145\0\164) + /Parent 999 0 R + /Next 1003 0 R + /A 1000 0 R +>> endobj +1003 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\61\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\141\0\165\0\164\0\157\0\55\0\155\0\145\0\162\0\147\0\145\0\55\0\163\0\145\0\164) + /Parent 999 0 R + /Prev 1001 0 R + /A 1002 0 R +>> endobj +1005 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\62\0\40\0\115\0\105\0\122\0\107\0\105\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 998 0 R + /First 1006 0 R + /Last 1006 0 R + /Prev 999 0 R + /Next 1007 0 R + /Count -1 + /A 1004 0 R +>> endobj +1006 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\62\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\105\0\122\0\107\0\105) + /Parent 1005 0 R + /A 292 0 R +>> endobj +1007 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\63\0\40\0\104\0\101\0\126\0\72\0\155\0\145\0\162\0\147\0\145\0\55\0\160\0\162\0\145\0\166\0\151\0\145\0\167\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 998 0 R + /First 1008 0 R + /Last 1008 0 R + /Prev 1005 0 R + /Next 1009 0 R + /Count -1 + /A 294 0 R +>> endobj +1008 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\63\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\104\0\101\0\126\0\72\0\155\0\145\0\162\0\147\0\145\0\55\0\160\0\162\0\145\0\166\0\151\0\145\0\167\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 1007 0 R + /A 296 0 R +>> endobj +1009 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 998 0 R + /Prev 1007 0 R + /Next 1010 0 R + /A 298 0 R +>> endobj +1010 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 998 0 R + /Prev 1009 0 R + /Next 1011 0 R + /A 300 0 R +>> endobj +1011 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 998 0 R + /Prev 1010 0 R + /A 302 0 R +>> endobj +1013 0 obj +<< + /Title (\376\377\0\61\0\62\0\40\0\102\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 1014 0 R + /Last 1042 0 R + /Prev 998 0 R + /Next 1044 0 R + /Count -22 + /A 1012 0 R +>> endobj +1014 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\103\0\157\0\156\0\146\0\151\0\147\0\165\0\162\0\141\0\164\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1013 0 R + /First 1016 0 R + /Last 1016 0 R + /Next 1017 0 R + /Count -1 + /A 306 0 R +>> endobj +1016 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 1014 0 R + /A 1015 0 R +>> endobj +1017 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\62\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\103\0\157\0\156\0\146\0\151\0\147\0\165\0\162\0\141\0\164\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1013 0 R + /First 1019 0 R + /Last 1019 0 R + /Prev 1014 0 R + /Next 1020 0 R + /Count -1 + /A 310 0 R +>> endobj +1019 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\142\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\55\0\163\0\145\0\164) + /Parent 1017 0 R + /A 1018 0 R +>> endobj +1020 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\63\0\40\0\102\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1013 0 R + /First 1022 0 R + /Last 1023 0 R + /Prev 1017 0 R + /Next 1024 0 R + /Count -2 + /A 314 0 R +>> endobj +1022 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\63\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 1020 0 R + /Next 1023 0 R + /A 1021 0 R +>> endobj +1023 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\63\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\142\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 1020 0 R + /Prev 1022 0 R + /A 318 0 R +>> endobj +1024 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1013 0 R + /First 1026 0 R + /Last 1026 0 R + /Prev 1020 0 R + /Next 1027 0 R + /Count -1 + /A 320 0 R +>> endobj +1026 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\64\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\55\0\143\0\157\0\156\0\146\0\151\0\147\0\165\0\162\0\141\0\164\0\151\0\157\0\156\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 1024 0 R + /A 1025 0 R +>> endobj +1027 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\127\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1013 0 R + /First 1029 0 R + /Last 1029 0 R + /Prev 1024 0 R + /Next 1031 0 R + /Count -1 + /A 324 0 R +>> endobj +1029 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\65\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 1027 0 R + /A 1028 0 R +>> endobj +1031 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\66\0\40\0\102\0\101\0\123\0\105\0\114\0\111\0\116\0\105\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1013 0 R + /First 1032 0 R + /Last 1032 0 R + /Prev 1027 0 R + /Next 1033 0 R + /Count -1 + /A 1030 0 R +>> endobj +1032 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\66\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\102\0\101\0\123\0\105\0\114\0\111\0\116\0\105\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114) + /Parent 1031 0 R + /A 330 0 R +>> endobj +1033 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\67\0\40\0\104\0\101\0\126\0\72\0\143\0\157\0\155\0\160\0\141\0\162\0\145\0\55\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 1013 0 R + /First 1034 0 R + /Last 1034 0 R + /Prev 1031 0 R + /Next 1035 0 R + /Count -1 + /A 332 0 R +>> endobj +1034 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\67\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\104\0\101\0\126\0\72\0\143\0\157\0\155\0\160\0\141\0\162\0\145\0\55\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 1033 0 R + /A 334 0 R +>> endobj +1035 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\70\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1013 0 R + /Prev 1033 0 R + /Next 1036 0 R + /A 336 0 R +>> endobj +1036 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\71\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\113\0\103\0\117\0\114\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1013 0 R + /Prev 1035 0 R + /Next 1037 0 R + /A 338 0 R +>> endobj +1037 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\60\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\117\0\120\0\131\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1013 0 R + /Prev 1036 0 R + /Next 1038 0 R + /A 340 0 R +>> endobj +1038 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1013 0 R + /Prev 1037 0 R + /Next 1039 0 R + /A 342 0 R +>> endobj +1039 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\62\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1013 0 R + /Prev 1038 0 R + /Next 1041 0 R + /A 344 0 R +>> endobj +1041 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\63\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\125\0\120\0\104\0\101\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1013 0 R + /Prev 1039 0 R + /Next 1042 0 R + /A 1040 0 R +>> endobj +1042 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\105\0\122\0\107\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1013 0 R + /Prev 1041 0 R + /A 348 0 R +>> endobj +1044 0 obj +<< + /Title (\376\377\0\61\0\63\0\40\0\101\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 1045 0 R + /Last 1074 0 R + /Prev 1013 0 R + /Next 1076 0 R + /Count -22 + /A 1043 0 R +>> endobj +1045 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\40\0\101\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1044 0 R + /First 1047 0 R + /Last 1053 0 R + /Next 1054 0 R + /Count -4 + /A 352 0 R +>> endobj +1047 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\55\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 1045 0 R + /Next 1049 0 R + /A 1046 0 R +>> endobj +1049 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\55\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 1045 0 R + /Prev 1047 0 R + /Next 1051 0 R + /A 1048 0 R +>> endobj +1051 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\56\0\63\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\142\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\55\0\163\0\145\0\164) + /Parent 1045 0 R + /Prev 1049 0 R + /Next 1053 0 R + /A 1050 0 R +>> endobj +1053 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\56\0\64\0\40\0\104\0\101\0\126\0\72\0\143\0\165\0\162\0\162\0\145\0\156\0\164\0\55\0\167\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 1045 0 R + /Prev 1051 0 R + /A 1052 0 R +>> endobj +1054 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\62\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1044 0 R + /First 1056 0 R + /Last 1056 0 R + /Prev 1045 0 R + /Next 1057 0 R + /Count -1 + /A 362 0 R +>> endobj +1056 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\55\0\163\0\145\0\164) + /Parent 1054 0 R + /A 1055 0 R +>> endobj +1057 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\63\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1044 0 R + /First 1059 0 R + /Last 1060 0 R + /Prev 1054 0 R + /Next 1061 0 R + /Count -2 + /A 366 0 R +>> endobj +1059 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\63\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\165\0\156\0\162\0\145\0\163\0\145\0\162\0\166\0\145\0\144) + /Parent 1057 0 R + /Next 1060 0 R + /A 1058 0 R +>> endobj +1060 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\63\0\56\0\62\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\55\0\163\0\145\0\164) + /Parent 1057 0 R + /Prev 1059 0 R + /A 370 0 R +>> endobj +1061 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\127\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1044 0 R + /First 1063 0 R + /Last 1063 0 R + /Prev 1057 0 R + /Next 1065 0 R + /Count -1 + /A 375 0 R +>> endobj +1063 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\64\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\143\0\165\0\162\0\162\0\145\0\156\0\164\0\55\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\55\0\163\0\145\0\164) + /Parent 1061 0 R + /A 1062 0 R +>> endobj +1065 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\65\0\40\0\115\0\113\0\101\0\103\0\124\0\111\0\126\0\111\0\124\0\131\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 1044 0 R + /First 1066 0 R + /Last 1066 0 R + /Prev 1061 0 R + /Next 1067 0 R + /Count -1 + /A 1064 0 R +>> endobj +1066 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\65\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\115\0\113\0\101\0\103\0\124\0\111\0\126\0\111\0\124\0\131) + /Parent 1065 0 R + /A 381 0 R +>> endobj +1067 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\66\0\40\0\104\0\101\0\126\0\72\0\154\0\141\0\164\0\145\0\163\0\164\0\55\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\55\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 1044 0 R + /Prev 1065 0 R + /Next 1068 0 R + /A 383 0 R +>> endobj +1068 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\67\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1044 0 R + /Prev 1067 0 R + /Next 1069 0 R + /A 385 0 R +>> endobj +1069 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\70\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1044 0 R + /Prev 1068 0 R + /Next 1070 0 R + /A 387 0 R +>> endobj +1070 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\71\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\117\0\126\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1044 0 R + /Prev 1069 0 R + /Next 1071 0 R + /A 389 0 R +>> endobj +1071 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\60\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1044 0 R + /First 1072 0 R + /Last 1072 0 R + /Prev 1070 0 R + /Next 1073 0 R + /Count -1 + /A 391 0 R +>> endobj +1072 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\60\0\56\0\61\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\167\0\151\0\164\0\150\0\40\0\141\0\156\0\40\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171) + /Parent 1071 0 R + /A 393 0 R +>> endobj +1073 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1044 0 R + /Prev 1071 0 R + /Next 1074 0 R + /A 395 0 R +>> endobj +1074 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\62\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\105\0\122\0\107\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1044 0 R + /Prev 1073 0 R + /A 397 0 R +>> endobj +1076 0 obj +<< + /Title (\376\377\0\61\0\64\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\55\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\106\0\145\0\141\0\164\0\165\0\162\0\145) + /Parent 810 0 R + /First 1077 0 R + /Last 1091 0 R + /Prev 1044 0 R + /Next 1092 0 R + /Count -13 + /A 1075 0 R +>> endobj +1077 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1076 0 R + /First 1079 0 R + /Last 1079 0 R + /Next 1080 0 R + /Count -1 + /A 401 0 R +>> endobj +1079 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\145\0\143\0\154\0\151\0\160\0\163\0\145\0\144\0\55\0\163\0\145\0\164\0\40\0\50\0\143\0\157\0\155\0\160\0\165\0\164\0\145\0\144\0\51) + /Parent 1077 0 R + /A 1078 0 R +>> endobj +1080 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 1076 0 R + /First 1082 0 R + /Last 1082 0 R + /Prev 1077 0 R + /Next 1083 0 R + /Count -1 + /A 405 0 R +>> endobj +1082 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\62\0\56\0\61\0\40\0\104\0\101\0\126\0\72\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\55\0\142\0\151\0\156\0\144\0\151\0\156\0\147\0\55\0\163\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 1080 0 R + /A 1081 0 R +>> endobj +1083 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\63\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1080 0 R + /Next 1084 0 R + /A 409 0 R +>> endobj +1084 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\64\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\104\0\105\0\114\0\105\0\124\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1083 0 R + /Next 1085 0 R + /A 411 0 R +>> endobj +1085 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\65\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\113\0\103\0\117\0\114\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1084 0 R + /Next 1086 0 R + /A 413 0 R +>> endobj +1086 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\66\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\117\0\120\0\131\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1085 0 R + /Next 1087 0 R + /A 415 0 R +>> endobj +1087 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\67\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\115\0\117\0\126\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1086 0 R + /Next 1088 0 R + /A 417 0 R +>> endobj +1088 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\70\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\126\0\105\0\122\0\123\0\111\0\117\0\116\0\55\0\103\0\117\0\116\0\124\0\122\0\117\0\114\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1087 0 R + /Next 1089 0 R + /A 419 0 R +>> endobj +1089 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\71\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\117\0\125\0\124\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1088 0 R + /Next 1090 0 R + /A 421 0 R +>> endobj +1090 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\60\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\110\0\105\0\103\0\113\0\111\0\116\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1089 0 R + /Next 1091 0 R + /A 423 0 R +>> endobj +1091 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\61\0\40\0\101\0\144\0\144\0\151\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\125\0\120\0\104\0\101\0\124\0\105\0\40\0\141\0\156\0\144\0\40\0\115\0\105\0\122\0\107\0\105\0\40\0\123\0\145\0\155\0\141\0\156\0\164\0\151\0\143\0\163) + /Parent 1076 0 R + /Prev 1090 0 R + /A 425 0 R +>> endobj +1092 0 obj +<< + /Title (\376\377\0\61\0\65\0\40\0\111\0\156\0\164\0\145\0\162\0\156\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\151\0\172\0\141\0\164\0\151\0\157\0\156\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 810 0 R + /Prev 1076 0 R + /Next 1093 0 R + /A 427 0 R +>> endobj +1093 0 obj +<< + /Title (\376\377\0\61\0\66\0\40\0\123\0\145\0\143\0\165\0\162\0\151\0\164\0\171\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 810 0 R + /First 1094 0 R + /Last 1097 0 R + /Prev 1092 0 R + /Next 1098 0 R + /Count -4 + /A 429 0 R +>> endobj +1094 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\61\0\40\0\101\0\165\0\144\0\151\0\164\0\151\0\156\0\147\0\40\0\141\0\156\0\144\0\40\0\124\0\162\0\141\0\143\0\145\0\141\0\142\0\151\0\154\0\151\0\164\0\171) + /Parent 1093 0 R + /Next 1095 0 R + /A 431 0 R +>> endobj +1095 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\62\0\40\0\111\0\156\0\143\0\162\0\145\0\141\0\163\0\145\0\144\0\40\0\116\0\145\0\145\0\144\0\40\0\146\0\157\0\162\0\40\0\101\0\143\0\143\0\145\0\163\0\163\0\40\0\103\0\157\0\156\0\164\0\162\0\157\0\154) + /Parent 1093 0 R + /Prev 1094 0 R + /Next 1096 0 R + /A 433 0 R +>> endobj +1096 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\63\0\40\0\123\0\145\0\143\0\165\0\162\0\151\0\164\0\171\0\40\0\124\0\150\0\162\0\157\0\165\0\147\0\150\0\40\0\117\0\142\0\163\0\143\0\165\0\162\0\151\0\164\0\171) + /Parent 1093 0 R + /Prev 1095 0 R + /Next 1097 0 R + /A 435 0 R +>> endobj +1097 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\64\0\40\0\104\0\145\0\156\0\151\0\141\0\154\0\40\0\157\0\146\0\40\0\123\0\145\0\162\0\166\0\151\0\143\0\145) + /Parent 1093 0 R + /Prev 1096 0 R + /A 437 0 R +>> endobj +1098 0 obj +<< + /Title (\376\377\0\61\0\67\0\40\0\111\0\101\0\116\0\101\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 810 0 R + /Prev 1093 0 R + /Next 1099 0 R + /A 439 0 R +>> endobj +1099 0 obj +<< + /Title (\376\377\0\61\0\70\0\40\0\111\0\156\0\164\0\145\0\154\0\154\0\145\0\143\0\164\0\165\0\141\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171) + /Parent 810 0 R + /Prev 1098 0 R + /Next 1100 0 R + /A 441 0 R +>> endobj +1100 0 obj +<< + /Title (\376\377\0\61\0\71\0\40\0\101\0\143\0\153\0\156\0\157\0\167\0\154\0\145\0\144\0\147\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 810 0 R + /Prev 1099 0 R + /Next 1101 0 R + /A 443 0 R +>> endobj +1101 0 obj +<< + /Title (\376\377\0\62\0\60\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 810 0 R + /Prev 1100 0 R + /Next 1102 0 R + /A 445 0 R +>> endobj +1102 0 obj +<< + /Title (\376\377\0\101\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\103\0\154\0\141\0\163\0\163\0\151\0\146\0\151\0\143\0\141\0\164\0\151\0\157\0\156) + /Parent 810 0 R + /First 1103 0 R + /Last 1120 0 R + /Prev 1101 0 R + /Next 1121 0 R + /Count -18 + /A 447 0 R +>> endobj +1103 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\40\0\104\0\145\0\154\0\164\0\141\0\126\0\55\0\103\0\157\0\155\0\160\0\154\0\151\0\141\0\156\0\164\0\40\0\125\0\156\0\155\0\141\0\160\0\160\0\145\0\144\0\40\0\125\0\122\0\114\0\40\0\50\0\141\0\40\0\125\0\122\0\114\0\40\0\164\0\150\0\141\0\164\0\40\0\151\0\144\0\145\0\156\0\164\0\151\0\146\0\151\0\145\0\163\0\40\0\156\0\157\0\40\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\51) + /Parent 1102 0 R + /Next 1104 0 R + /A 449 0 R +>> endobj +1104 0 obj +<< + /Title (\376\377\0\101\0\56\0\62\0\40\0\104\0\145\0\154\0\164\0\141\0\126\0\55\0\103\0\157\0\155\0\160\0\154\0\151\0\141\0\156\0\164\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 1102 0 R + /Prev 1103 0 R + /Next 1105 0 R + /A 451 0 R +>> endobj +1105 0 obj +<< + /Title (\376\377\0\101\0\56\0\63\0\40\0\104\0\145\0\154\0\164\0\141\0\126\0\55\0\103\0\157\0\155\0\160\0\154\0\151\0\141\0\156\0\164\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156) + /Parent 1102 0 R + /Prev 1104 0 R + /Next 1106 0 R + /A 456 0 R +>> endobj +1106 0 obj +<< + /Title (\376\377\0\101\0\56\0\64\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\141\0\142\0\154\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 1102 0 R + /Prev 1105 0 R + /Next 1107 0 R + /A 458 0 R +>> endobj +1107 0 obj +<< + /Title (\376\377\0\101\0\56\0\65\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 1102 0 R + /Prev 1106 0 R + /Next 1108 0 R + /A 460 0 R +>> endobj +1108 0 obj +<< + /Title (\376\377\0\101\0\56\0\66\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156) + /Parent 1102 0 R + /Prev 1107 0 R + /Next 1109 0 R + /A 462 0 R +>> endobj +1109 0 obj +<< + /Title (\376\377\0\101\0\56\0\67\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\111\0\156\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 1102 0 R + /Prev 1108 0 R + /Next 1110 0 R + /A 464 0 R +>> endobj +1110 0 obj +<< + /Title (\376\377\0\101\0\56\0\70\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 1102 0 R + /Prev 1109 0 R + /Next 1111 0 R + /A 466 0 R +>> endobj +1111 0 obj +<< + /Title (\376\377\0\101\0\56\0\71\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\50\0\143\0\150\0\145\0\143\0\153\0\157\0\165\0\164\0\55\0\151\0\156\0\55\0\160\0\154\0\141\0\143\0\145\0\51) + /Parent 1102 0 R + /Prev 1110 0 R + /Next 1112 0 R + /A 468 0 R +>> endobj +1112 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\60\0\40\0\127\0\157\0\162\0\153\0\151\0\156\0\147\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\50\0\167\0\157\0\162\0\153\0\151\0\156\0\147\0\55\0\162\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\51) + /Parent 1102 0 R + /Prev 1111 0 R + /Next 1113 0 R + /A 470 0 R +>> endobj +1113 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\61\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\110\0\151\0\163\0\164\0\157\0\162\0\171\0\40\0\50\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\150\0\151\0\163\0\164\0\157\0\162\0\171\0\51) + /Parent 1102 0 R + /Prev 1112 0 R + /Next 1114 0 R + /A 472 0 R +>> endobj +1114 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\62\0\40\0\127\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\40\0\50\0\167\0\157\0\162\0\153\0\163\0\160\0\141\0\143\0\145\0\51) + /Parent 1102 0 R + /Prev 1113 0 R + /Next 1115 0 R + /A 474 0 R +>> endobj +1115 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\63\0\40\0\101\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\40\0\50\0\141\0\143\0\164\0\151\0\166\0\151\0\164\0\171\0\51) + /Parent 1102 0 R + /Prev 1114 0 R + /Next 1116 0 R + /A 476 0 R +>> endobj +1116 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\64\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\50\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\51) + /Parent 1102 0 R + /Prev 1115 0 R + /Next 1117 0 R + /A 478 0 R +>> endobj +1117 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\65\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\40\0\50\0\166\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\51) + /Parent 1102 0 R + /Prev 1116 0 R + /Next 1118 0 R + /A 480 0 R +>> endobj +1118 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\66\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\103\0\157\0\156\0\146\0\151\0\147\0\165\0\162\0\141\0\164\0\151\0\157\0\156\0\40\0\50\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\51) + /Parent 1102 0 R + /Prev 1117 0 R + /Next 1119 0 R + /A 482 0 R +>> endobj +1119 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\67\0\40\0\102\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\40\0\50\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\51) + /Parent 1102 0 R + /Prev 1118 0 R + /Next 1120 0 R + /A 484 0 R +>> endobj +1120 0 obj +<< + /Title (\376\377\0\101\0\56\0\61\0\70\0\40\0\103\0\150\0\145\0\143\0\153\0\145\0\144\0\55\0\117\0\165\0\164\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\55\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\154\0\145\0\144\0\40\0\103\0\157\0\156\0\146\0\151\0\147\0\165\0\162\0\141\0\164\0\151\0\157\0\156\0\40\0\50\0\142\0\141\0\163\0\145\0\154\0\151\0\156\0\145\0\51) + /Parent 1102 0 R + /Prev 1119 0 R + /A 486 0 R +>> endobj +1121 0 obj +<< + /Title (\376\377\0\101\0\165\0\164\0\150\0\157\0\162\0\163\0\47\0\40\0\101\0\144\0\144\0\162\0\145\0\163\0\163\0\145\0\163) + /Parent 810 0 R + /Prev 1102 0 R + /Next 1122 0 R + /A 488 0 R +>> endobj +1122 0 obj +<< + /Title (\376\377\0\111\0\156\0\164\0\145\0\154\0\154\0\145\0\143\0\164\0\165\0\141\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\141\0\156\0\144\0\40\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\123\0\164\0\141\0\164\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 810 0 R + /Prev 1121 0 R + /Next 1123 0 R + /A 490 0 R +>> endobj +1123 0 obj +<< + /Title (\376\377\0\111\0\156\0\144\0\145\0\170) + /Parent 810 0 R + /Prev 1122 0 R + /A 492 0 R +>> endobj +1124 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F5 +/BaseFont /Times-Roman +/Encoding /WinAnsiEncoding >> +endobj +1125 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F6 +/BaseFont /Times-Italic +/Encoding /WinAnsiEncoding >> +endobj +1126 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F9 +/BaseFont /Courier +/Encoding /WinAnsiEncoding >> +endobj +1127 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F7 +/BaseFont /Times-Bold +/Encoding /WinAnsiEncoding >> +endobj +1 0 obj +<< /Type /Pages +/Count 100 +/Kids [6 0 R 8 0 R 101 0 R 192 0 R 279 0 R 372 0 R 453 0 R 494 0 R 508 0 R 513 0 R 517 0 R 519 0 R 521 0 R 525 0 R 529 0 R 535 0 R 541 0 R 545 0 R 547 0 R 549 0 R 551 0 R 553 0 R 555 0 R 559 0 R 561 0 R 566 0 R 573 0 R 575 0 R 577 0 R 579 0 R 581 0 R 583 0 R 585 0 R 589 0 R 591 0 R 595 0 R 597 0 R 603 0 R 607 0 R 609 0 R 611 0 R 613 0 R 617 0 R 619 0 R 623 0 R 625 0 R 627 0 R 629 0 R 631 0 R 638 0 R 645 0 R 654 0 R 656 0 R 661 0 R 667 0 R 669 0 R 671 0 R 673 0 R 675 0 R 677 0 R 679 0 R 681 0 R 683 0 R 685 0 R 687 0 R 689 0 R 691 0 R 693 0 R 700 0 R 702 0 R 704 0 R 706 0 R 708 0 R 710 0 R 712 0 R 714 0 R 716 0 R 718 0 R 720 0 R 722 0 R 724 0 R 738 0 R 740 0 R 742 0 R 747 0 R 749 0 R 763 0 R 773 0 R 775 0 R 777 0 R 779 0 R 781 0 R 789 0 R 791 0 R 793 0 R 796 0 R 799 0 R 802 0 R 805 0 R 808 0 R ] >> +endobj +2 0 obj +<< /Type /Catalog +/Pages 1 0 R + /Outlines 810 0 R + /PageMode /UseOutlines + /Names << /Dests << /Names [ (rfc.status) [ 6 0 R /XYZ 67.0 454.084 null ] (rfc.copyrightnotice) [ 6 0 R /XYZ 67.0 363.95 null ] (rfc.abstract) [ 6 0 R /XYZ 67.0 306.816 null ] (rfc.toc) [ 8 0 R /XYZ 67.0 725.0 null ] (rfc.section.1) [ 494 0 R /XYZ 67.0 725.0 null ] (rfc.section.1.1) [ 494 0 R /XYZ 67.0 336.866 null ] (rfc.xref.RFC2518.1) [ 494 0 R /XYZ 67.0 301.894 null ] (rfc.xref.RFC2616.1) [ 494 0 R /XYZ 67.0 301.894 null ] (rfc.section.1.2) [ 494 0 R /XYZ 67.0 229.894 null ] (rfc.xref.RFC2119.1) [ 494 0 R /XYZ 67.0 194.922 null ] (rfc.section.1.3) [ 508 0 R /XYZ 67.0 638.0 null ] (rfc.iref.1) [ 508 0 R /XYZ 67.0 582.028 null ] (rfc.iref.2) [ 508 0 R /XYZ 67.0 582.028 null ] (rfc.iref.3) [ 508 0 R /XYZ 67.0 582.028 null ] (rfc.iref.4) [ 508 0 R /XYZ 67.0 523.028 null ] (rfc.iref.5) [ 508 0 R /XYZ 67.0 486.028 null ] (rfc.iref.6) [ 508 0 R /XYZ 67.0 427.028 null ] (rfc.iref.7) [ 508 0 R /XYZ 67.0 390.028 null ] (rfc.iref.8) [ 508 0 R /XYZ 67.0 320.028 null ] (rfc.iref.9) [ 508 0 R /XYZ 67.0 272.028 null ] (rfc.iref.10) [ 508 0 R /XYZ 67.0 213.028 null ] (rfc.iref.11) [ 508 0 R /XYZ 67.0 213.028 null ] (rfc.iref.12) [ 508 0 R /XYZ 67.0 213.028 null ] (rfc.iref.13) [ 508 0 R /XYZ 67.0 213.028 null ] (rfc.iref.14) [ 508 0 R /XYZ 67.0 110.028 null ] (rfc.iref.15) [ 513 0 R /XYZ 67.0 699.0 null ] (rfc.iref.16) [ 513 0 R /XYZ 67.0 651.0 null ] (rfc.iref.17) [ 513 0 R /XYZ 67.0 592.0 null ] (rfc.iref.18) [ 513 0 R /XYZ 67.0 592.0 null ] (rfc.figure.u.1) [ 513 0 R /XYZ 67.0 468.0 null ] (rfc.iref.19) [ 513 0 R /XYZ 67.0 211.15 null ] (rfc.section.1.4) [ 513 0 R /XYZ 67.0 156.15 null ] (rfc.section.1.4.1) [ 513 0 R /XYZ 67.0 126.178 null ] (rfc.section.1.4.2) [ 513 0 R /XYZ 67.0 68.287 null ] (protected.property.value) [ 517 0 R /XYZ 67.0 725.0 null ] (rfc.section.1.4.3) [ 517 0 R /XYZ 67.0 645.109 null ] (computed.property.value) [ 517 0 R /XYZ 67.0 645.109 null ] (rfc.section.1.4.4) [ 517 0 R /XYZ 67.0 554.218 null ] (rfc.section.1.4.5) [ 517 0 R /XYZ 67.0 507.327 null ] (rfc.xref.RFC2518.2) [ 517 0 R /XYZ 67.0 487.436 null ] (rfc.section.1.5) [ 517 0 R /XYZ 67.0 459.436 null ] (rfc.section.1.6) [ 517 0 R /XYZ 67.0 385.464 null ] (method.preconditions.and.postconditions) [ 517 0 R /XYZ 67.0 385.464 null ] (rfc.section.1.6.1) [ 517 0 R /XYZ 67.0 214.492 null ] (rfc.figure.u.2) [ 517 0 R /XYZ 67.0 194.601 null ] (rfc.figure.u.3) [ 517 0 R /XYZ 67.0 137.881 null ] (rfc.section.1.7) [ 519 0 R /XYZ 67.0 636.56 null ] (clarification.of.copy.semantics.with.overwrite-t) [ 519 0 R /XYZ 67.0 636.56 null ] (rfc.xref.RFC2518.3) [ 519 0 R /XYZ 67.0 612.588 null ] (rfc.section.1.8) [ 519 0 R /XYZ 67.0 379.588 null ] (rfc.section.2) [ 521 0 R /XYZ 67.0 725.0 null ] (rfc.section.2.1) [ 521 0 R /XYZ 67.0 658.866 null ] (rfc.section.2.2) [ 521 0 R /XYZ 67.0 234.894 null ] (basic.versioning.semantics) [ 521 0 R /XYZ 67.0 234.894 null ] (rfc.section.2.2.1) [ 521 0 R /XYZ 67.0 204.922 null ] (creating.a.version-controlled.resource) [ 521 0 R /XYZ 67.0 204.922 null ] (rfc.figure.u.4) [ 525 0 R /XYZ 67.0 498.0 null ] (rfc.section.2.2.2) [ 525 0 R /XYZ 67.0 190.386 null ] (rfc.figure.u.5) [ 529 0 R /XYZ 67.0 422.0 null ] (rfc.section.2.2.3) [ 529 0 R /XYZ 67.0 134.276 null ] (rfc.section.3) [ 541 0 R /XYZ 67.0 725.0 null ] (rfc.section.3.1) [ 541 0 R /XYZ 67.0 625.866 null ] (rfc.section.3.1.1) [ 541 0 R /XYZ 67.0 574.894 null ] (PROPERTY_comment) [ 541 0 R /XYZ 67.0 574.894 null ] (rfc.figure.u.6) [ 541 0 R /XYZ 67.0 523.003 null ] (rfc.section.3.1.2) [ 541 0 R /XYZ 67.0 477.283 null ] (PROPERTY_creator-displayname) [ 541 0 R /XYZ 67.0 477.283 null ] (rfc.figure.u.7) [ 541 0 R /XYZ 67.0 425.392 null ] (rfc.section.3.1.3) [ 541 0 R /XYZ 67.0 379.672 null ] (PROPERTY_supported-method-set) [ 541 0 R /XYZ 67.0 379.672 null ] (rfc.figure.u.8) [ 541 0 R /XYZ 67.0 305.781 null ] (rfc.section.3.1.4) [ 541 0 R /XYZ 67.0 240.341 null ] (PROPERTY_supported-live-property-set) [ 541 0 R /XYZ 67.0 240.341 null ] (rfc.figure.u.9) [ 541 0 R /XYZ 67.0 166.45 null ] (rfc.section.3.1.5) [ 541 0 R /XYZ 67.0 101.01 null ] (PROPERTY_supported-report-set) [ 541 0 R /XYZ 67.0 101.01 null ] (rfc.figure.u.10) [ 545 0 R /XYZ 67.0 699.0 null ] (rfc.section.3.2) [ 545 0 R /XYZ 67.0 632.56 null ] (rfc.section.3.2.1) [ 545 0 R /XYZ 67.0 581.588 null ] (PROPERTY_checked-in) [ 545 0 R /XYZ 67.0 581.588 null ] (rfc.figure.u.11) [ 545 0 R /XYZ 67.0 518.697 null ] (rfc.section.3.2.2) [ 545 0 R /XYZ 67.0 482.837 null ] (PROPERTY_auto-version) [ 545 0 R /XYZ 67.0 482.837 null ] (rfc.figure.u.12) [ 545 0 R /XYZ 67.0 215.946 null ] (rfc.section.3.3) [ 545 0 R /XYZ 67.0 129.786 null ] (rfc.section.3.3.1) [ 545 0 R /XYZ 67.0 78.814 null ] (PROPERTY_checked-out) [ 547 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.13) [ 547 0 R /XYZ 67.0 673.109 null ] (rfc.section.3.3.2) [ 547 0 R /XYZ 67.0 637.249 null ] (PROPERTY_predecessor-set) [ 547 0 R /XYZ 67.0 637.249 null ] (rfc.figure.u.14) [ 547 0 R /XYZ 67.0 564.358 null ] (rfc.section.3.4) [ 547 0 R /XYZ 67.0 527.498 null ] (rfc.section.3.4.1) [ 547 0 R /XYZ 67.0 476.526 null ] (rfc.figure.u.15) [ 547 0 R /XYZ 67.0 424.635 null ] (rfc.section.3.4.2) [ 547 0 R /XYZ 67.0 388.775 null ] (PROPERTY_successor-set) [ 547 0 R /XYZ 67.0 388.775 null ] (rfc.figure.u.16) [ 547 0 R /XYZ 67.0 347.884 null ] (rfc.section.3.4.3) [ 547 0 R /XYZ 67.0 312.024 null ] (PROPERTY_checkout-set) [ 547 0 R /XYZ 67.0 312.024 null ] (rfc.figure.u.17) [ 547 0 R /XYZ 67.0 271.133 null ] (rfc.section.3.4.4) [ 547 0 R /XYZ 67.0 235.273 null ] (PROPERTY_version-name) [ 547 0 R /XYZ 67.0 235.273 null ] (rfc.figure.u.18) [ 547 0 R /XYZ 67.0 172.382 null ] (rfc.section.3.5) [ 547 0 R /XYZ 67.0 125.662 null ] (METHOD_VERSION-CONTROL) [ 547 0 R /XYZ 67.0 125.662 null ] (rfc.iref.50) [ 549 0 R /XYZ 67.0 613.0 null ] (rfc.figure.u.19) [ 549 0 R /XYZ 67.0 583.5 null ] (rfc.figure.u.20) [ 549 0 R /XYZ 67.0 504.64 null ] (rfc.iref.51) [ 549 0 R /XYZ 67.0 467.28 null ] (rfc.iref.52) [ 549 0 R /XYZ 67.0 453.78 null ] (rfc.iref.53) [ 549 0 R /XYZ 67.0 453.78 null ] (rfc.iref.54) [ 549 0 R /XYZ 67.0 360.78 null ] (rfc.iref.55) [ 549 0 R /XYZ 67.0 360.78 null ] (rfc.section.3.5.1) [ 549 0 R /XYZ 67.0 309.28 null ] (rfc.figure.u.21) [ 549 0 R /XYZ 67.0 289.389 null ] (rfc.figure.u.22) [ 549 0 R /XYZ 67.0 222.809 null ] (rfc.section.3.6) [ 549 0 R /XYZ 67.0 125.949 null ] (METHOD_REPORT) [ 549 0 R /XYZ 67.0 125.949 null ] (rfc.iref.58) [ 551 0 R /XYZ 67.0 699.0 null ] (rfc.iref.59) [ 551 0 R /XYZ 67.0 570.0 null ] (rfc.iref.60) [ 551 0 R /XYZ 67.0 556.5 null ] (rfc.iref.61) [ 551 0 R /XYZ 67.0 556.5 null ] (rfc.iref.62) [ 551 0 R /XYZ 67.0 522.0 null ] (rfc.iref.63) [ 551 0 R /XYZ 67.0 508.5 null ] (rfc.iref.64) [ 551 0 R /XYZ 67.0 508.5 null ] (rfc.section.3.7) [ 551 0 R /XYZ 67.0 467.0 null ] (REPORT_version-tree) [ 551 0 R /XYZ 67.0 467.0 null ] (rfc.iref.67) [ 551 0 R /XYZ 67.0 379.028 null ] (rfc.figure.u.23) [ 551 0 R /XYZ 67.0 349.528 null ] (rfc.xref.RFC2518.4) [ 551 0 R /XYZ 67.0 314.948 null ] (rfc.figure.u.24) [ 551 0 R /XYZ 67.0 234.648 null ] (rfc.xref.RFC2518.5) [ 551 0 R /XYZ 67.0 229.648 null ] (rfc.section.3.7.1) [ 551 0 R /XYZ 67.0 124.848 null ] (rfc.figure.u.25) [ 551 0 R /XYZ 67.0 83.957 null ] (rfc.figure.u.26) [ 553 0 R /XYZ 67.0 617.26 null ] (rfc.figure.u.27) [ 553 0 R /XYZ 67.0 452.08 null ] (rfc.section.3.8) [ 555 0 R /XYZ 67.0 627.98 null ] (REPORT_expand-property) [ 555 0 R /XYZ 67.0 627.98 null ] (rfc.iref.70) [ 555 0 R /XYZ 67.0 529.008 null ] (rfc.figure.u.28) [ 555 0 R /XYZ 67.0 499.508 null ] (rfc.figure.u.29) [ 555 0 R /XYZ 67.0 404.348 null ] (rfc.xref.RFC2518.6) [ 555 0 R /XYZ 67.0 399.348 null ] (rfc.section.3.8.1) [ 555 0 R /XYZ 67.0 223.548 null ] (rfc.figure.u.30) [ 555 0 R /XYZ 67.0 160.657 null ] (rfc.figure.u.31) [ 559 0 R /XYZ 67.0 625.12 null ] (rfc.section.3.9) [ 559 0 R /XYZ 67.0 125.14 null ] (rfc.section.3.10) [ 561 0 R /XYZ 67.0 692.0 null ] (additional.put.semantics) [ 561 0 R /XYZ 67.0 692.0 null ] (rfc.iref.74) [ 561 0 R /XYZ 67.0 668.028 null ] (rfc.iref.75) [ 561 0 R /XYZ 67.0 654.528 null ] (rfc.iref.76) [ 561 0 R /XYZ 67.0 654.528 null ] (rfc.iref.77) [ 561 0 R /XYZ 67.0 616.528 null ] (rfc.iref.78) [ 561 0 R /XYZ 67.0 616.528 null ] (rfc.iref.79) [ 561 0 R /XYZ 67.0 566.028 null ] (rfc.iref.80) [ 561 0 R /XYZ 67.0 552.528 null ] (rfc.iref.81) [ 561 0 R /XYZ 67.0 552.528 null ] (rfc.iref.82) [ 561 0 R /XYZ 67.0 448.528 null ] (rfc.iref.83) [ 561 0 R /XYZ 67.0 448.528 null ] (rfc.section.3.11) [ 561 0 R /XYZ 67.0 303.028 null ] (rfc.iref.85) [ 561 0 R /XYZ 67.0 236.056 null ] (rfc.iref.86) [ 561 0 R /XYZ 67.0 222.556 null ] (rfc.iref.87) [ 561 0 R /XYZ 67.0 222.556 null ] (rfc.section.3.12) [ 561 0 R /XYZ 67.0 181.056 null ] (rfc.iref.89) [ 561 0 R /XYZ 67.0 157.084 null ] (rfc.iref.90) [ 561 0 R /XYZ 67.0 143.584 null ] (rfc.iref.91) [ 561 0 R /XYZ 67.0 143.584 null ] (rfc.iref.92) [ 561 0 R /XYZ 67.0 116.584 null ] (rfc.iref.93) [ 561 0 R /XYZ 67.0 116.584 null ] (rfc.iref.94) [ 561 0 R /XYZ 67.0 89.584 null ] (rfc.iref.95) [ 561 0 R /XYZ 67.0 89.584 null ] (rfc.iref.96) [ 566 0 R /XYZ 67.0 706.5 null ] (rfc.iref.97) [ 566 0 R /XYZ 67.0 706.5 null ] (rfc.iref.98) [ 566 0 R /XYZ 67.0 661.0 null ] (rfc.iref.99) [ 566 0 R /XYZ 67.0 647.5 null ] (rfc.iref.100) [ 566 0 R /XYZ 67.0 647.5 null ] (rfc.iref.101) [ 566 0 R /XYZ 67.0 631.5 null ] (rfc.iref.102) [ 566 0 R /XYZ 67.0 631.5 null ] (rfc.section.3.13) [ 566 0 R /XYZ 67.0 590.0 null ] (additional.delete.semantics) [ 566 0 R /XYZ 67.0 590.0 null ] (rfc.iref.104) [ 566 0 R /XYZ 67.0 566.028 null ] (rfc.iref.105) [ 566 0 R /XYZ 67.0 552.528 null ] (rfc.iref.106) [ 566 0 R /XYZ 67.0 552.528 null ] (rfc.iref.107) [ 566 0 R /XYZ 67.0 529.028 null ] (rfc.iref.108) [ 566 0 R /XYZ 67.0 515.528 null ] (rfc.iref.109) [ 566 0 R /XYZ 67.0 515.528 null ] (rfc.section.3.14) [ 566 0 R /XYZ 67.0 474.028 null ] (rfc.iref.111) [ 566 0 R /XYZ 67.0 450.056 null ] (rfc.iref.112) [ 566 0 R /XYZ 67.0 402.056 null ] (rfc.iref.113) [ 566 0 R /XYZ 67.0 388.556 null ] (rfc.iref.114) [ 566 0 R /XYZ 67.0 388.556 null ] (rfc.iref.115) [ 566 0 R /XYZ 67.0 339.556 null ] (rfc.iref.116) [ 566 0 R /XYZ 67.0 339.556 null ] (rfc.iref.117) [ 566 0 R /XYZ 67.0 312.556 null ] (rfc.iref.118) [ 566 0 R /XYZ 67.0 312.556 null ] (rfc.iref.119) [ 566 0 R /XYZ 67.0 285.556 null ] (rfc.iref.120) [ 566 0 R /XYZ 67.0 285.556 null ] (rfc.section.3.15) [ 566 0 R /XYZ 67.0 200.056 null ] (rfc.iref.122) [ 566 0 R /XYZ 67.0 176.084 null ] (rfc.iref.123) [ 566 0 R /XYZ 67.0 162.584 null ] (rfc.iref.124) [ 566 0 R /XYZ 67.0 162.584 null ] (rfc.iref.125) [ 566 0 R /XYZ 67.0 139.084 null ] (rfc.iref.126) [ 566 0 R /XYZ 67.0 125.584 null ] (rfc.iref.127) [ 566 0 R /XYZ 67.0 125.584 null ] (rfc.section.3.16) [ 566 0 R /XYZ 67.0 73.084 null ] (rfc.iref.129) [ 573 0 R /XYZ 67.0 658.028 null ] (rfc.iref.130) [ 573 0 R /XYZ 67.0 644.528 null ] (rfc.iref.131) [ 573 0 R /XYZ 67.0 644.528 null ] (rfc.iref.132) [ 573 0 R /XYZ 67.0 588.028 null ] (rfc.iref.133) [ 573 0 R /XYZ 67.0 574.528 null ] (rfc.iref.134) [ 573 0 R /XYZ 67.0 574.528 null ] (rfc.section.4) [ 575 0 R /XYZ 67.0 725.0 null ] (checkout-in-place.feature) [ 575 0 R /XYZ 67.0 725.0 null ] (rfc.section.4.1) [ 575 0 R /XYZ 67.0 636.866 null ] (rfc.section.4.1.1) [ 575 0 R /XYZ 67.0 585.894 null ] (PROPERTY_checkout-fork) [ 575 0 R /XYZ 67.0 585.894 null ] (rfc.figure.u.32) [ 575 0 R /XYZ 67.0 485.003 null ] (rfc.section.4.1.2) [ 575 0 R /XYZ 67.0 404.703 null ] (PROPERTY_checkin-fork) [ 575 0 R /XYZ 67.0 404.703 null ] (rfc.figure.u.33) [ 575 0 R /XYZ 67.0 303.812 null ] (rfc.section.4.2) [ 575 0 R /XYZ 67.0 222.512 null ] (rfc.section.4.2.1) [ 575 0 R /XYZ 67.0 171.54 null ] (rfc.section.4.2.2) [ 575 0 R /XYZ 67.0 113.649 null ] (rfc.section.4.3) [ 577 0 R /XYZ 67.0 692.0 null ] (checkout.method.applied.to.a.version-controlled.resource) [ 577 0 R /XYZ 67.0 692.0 null ] (rfc.iref.145) [ 577 0 R /XYZ 67.0 615.028 null ] (rfc.figure.u.34) [ 577 0 R /XYZ 67.0 585.528 null ] (rfc.figure.u.35) [ 577 0 R /XYZ 67.0 539.668 null ] (rfc.figure.u.36) [ 577 0 R /XYZ 67.0 482.808 null ] (rfc.iref.146) [ 577 0 R /XYZ 67.0 429.448 null ] (rfc.iref.147) [ 577 0 R /XYZ 67.0 415.948 null ] (rfc.iref.148) [ 577 0 R /XYZ 67.0 415.948 null ] (rfc.iref.149) [ 577 0 R /XYZ 67.0 388.948 null ] (rfc.iref.150) [ 577 0 R /XYZ 67.0 388.948 null ] (rfc.iref.151) [ 577 0 R /XYZ 67.0 350.948 null ] (rfc.iref.152) [ 577 0 R /XYZ 67.0 350.948 null ] (rfc.iref.153) [ 577 0 R /XYZ 67.0 312.948 null ] (rfc.iref.154) [ 577 0 R /XYZ 67.0 312.948 null ] (rfc.iref.155) [ 577 0 R /XYZ 67.0 274.948 null ] (rfc.iref.156) [ 577 0 R /XYZ 67.0 274.948 null ] (rfc.iref.157) [ 577 0 R /XYZ 67.0 229.448 null ] (rfc.iref.158) [ 577 0 R /XYZ 67.0 215.948 null ] (rfc.iref.159) [ 577 0 R /XYZ 67.0 215.948 null ] (rfc.iref.160) [ 577 0 R /XYZ 67.0 177.948 null ] (rfc.iref.161) [ 577 0 R /XYZ 67.0 177.948 null ] (rfc.section.4.3.1) [ 577 0 R /XYZ 67.0 137.448 null ] (rfc.figure.u.37) [ 577 0 R /XYZ 67.0 117.557 null ] (rfc.figure.u.38) [ 579 0 R /XYZ 67.0 694.14 null ] (rfc.section.4.4) [ 579 0 R /XYZ 67.0 609.42 null ] (checkin.method.applied.to.a.version-controlled.resource) [ 579 0 R /XYZ 67.0 609.42 null ] (rfc.iref.164) [ 579 0 R /XYZ 67.0 532.448 null ] (rfc.figure.u.39) [ 579 0 R /XYZ 67.0 502.948 null ] (rfc.figure.u.40) [ 579 0 R /XYZ 67.0 396.788 null ] (rfc.iref.165) [ 579 0 R /XYZ 67.0 343.428 null ] (rfc.iref.166) [ 579 0 R /XYZ 67.0 329.928 null ] (rfc.iref.167) [ 579 0 R /XYZ 67.0 329.928 null ] (rfc.iref.168) [ 579 0 R /XYZ 67.0 302.928 null ] (rfc.iref.169) [ 579 0 R /XYZ 67.0 302.928 null ] (rfc.iref.170) [ 579 0 R /XYZ 67.0 264.928 null ] (rfc.iref.171) [ 579 0 R /XYZ 67.0 264.928 null ] (rfc.iref.172) [ 579 0 R /XYZ 67.0 237.928 null ] (rfc.iref.173) [ 579 0 R /XYZ 67.0 237.928 null ] (rfc.iref.174) [ 579 0 R /XYZ 67.0 192.428 null ] (rfc.iref.175) [ 579 0 R /XYZ 67.0 178.928 null ] (rfc.iref.176) [ 579 0 R /XYZ 67.0 178.928 null ] (rfc.iref.177) [ 579 0 R /XYZ 67.0 129.928 null ] (rfc.iref.178) [ 579 0 R /XYZ 67.0 129.928 null ] (rfc.iref.179) [ 581 0 R /XYZ 67.0 722.5 null ] (rfc.iref.180) [ 581 0 R /XYZ 67.0 722.5 null ] (rfc.iref.181) [ 581 0 R /XYZ 67.0 673.5 null ] (rfc.iref.182) [ 581 0 R /XYZ 67.0 673.5 null ] (rfc.section.4.4.1) [ 581 0 R /XYZ 67.0 622.0 null ] (rfc.figure.u.41) [ 581 0 R /XYZ 67.0 602.109 null ] (rfc.figure.u.42) [ 581 0 R /XYZ 67.0 535.529 null ] (rfc.section.4.5) [ 581 0 R /XYZ 67.0 429.949 null ] (METHOD_UNCHECKOUT) [ 581 0 R /XYZ 67.0 429.949 null ] (rfc.iref.185) [ 581 0 R /XYZ 67.0 352.977 null ] (rfc.figure.u.43) [ 581 0 R /XYZ 67.0 323.477 null ] (rfc.figure.u.44) [ 581 0 R /XYZ 67.0 266.617 null ] (rfc.iref.186) [ 581 0 R /XYZ 67.0 213.257 null ] (rfc.iref.187) [ 581 0 R /XYZ 67.0 199.757 null ] (rfc.iref.188) [ 581 0 R /XYZ 67.0 199.757 null ] (rfc.iref.189) [ 581 0 R /XYZ 67.0 165.257 null ] (rfc.iref.190) [ 581 0 R /XYZ 67.0 151.757 null ] (rfc.iref.191) [ 581 0 R /XYZ 67.0 151.757 null ] (rfc.iref.192) [ 581 0 R /XYZ 67.0 124.757 null ] (rfc.iref.193) [ 581 0 R /XYZ 67.0 124.757 null ] (rfc.section.4.5.1) [ 581 0 R /XYZ 67.0 84.257 null ] (rfc.figure.u.45) [ 583 0 R /XYZ 67.0 705.109 null ] (rfc.figure.u.46) [ 583 0 R /XYZ 67.0 638.529 null ] (rfc.section.4.6) [ 583 0 R /XYZ 67.0 531.809 null ] (rfc.section.5) [ 585 0 R /XYZ 67.0 725.0 null ] (version-history.feature) [ 585 0 R /XYZ 67.0 725.0 null ] (rfc.section.5.1) [ 585 0 R /XYZ 67.0 603.866 null ] (rfc.section.5.1.1) [ 585 0 R /XYZ 67.0 531.894 null ] (PROPERTY_version-set) [ 585 0 R /XYZ 67.0 531.894 null ] (rfc.figure.u.47) [ 585 0 R /XYZ 67.0 496.003 null ] (rfc.section.5.1.2) [ 585 0 R /XYZ 67.0 455.143 null ] (PROPERTY_root-version) [ 585 0 R /XYZ 67.0 455.143 null ] (rfc.figure.u.48) [ 585 0 R /XYZ 67.0 419.252 null ] (rfc.section.5.2) [ 585 0 R /XYZ 67.0 377.392 null ] (rfc.section.5.2.1) [ 585 0 R /XYZ 67.0 326.42 null ] (PROPERTY_version-history) [ 585 0 R /XYZ 67.0 326.42 null ] (rfc.figure.u.49) [ 585 0 R /XYZ 67.0 279.529 null ] (rfc.section.5.3) [ 585 0 R /XYZ 67.0 237.669 null ] (rfc.section.5.3.1) [ 585 0 R /XYZ 67.0 186.697 null ] (rfc.figure.u.50) [ 585 0 R /XYZ 67.0 150.806 null ] (rfc.section.5.4) [ 585 0 R /XYZ 67.0 108.946 null ] (REPORT_locate-by-history) [ 585 0 R /XYZ 67.0 108.946 null ] (rfc.iref.210) [ 589 0 R /XYZ 67.0 666.0 null ] (rfc.figure.u.51) [ 589 0 R /XYZ 67.0 636.5 null ] (rfc.xref.RFC2518.7) [ 589 0 R /XYZ 67.0 611.78 null ] (rfc.iref.211) [ 589 0 R /XYZ 67.0 479.98 null ] (rfc.iref.212) [ 589 0 R /XYZ 67.0 466.48 null ] (rfc.iref.213) [ 589 0 R /XYZ 67.0 466.48 null ] (rfc.section.5.4.1) [ 589 0 R /XYZ 67.0 425.98 null ] (rfc.figure.u.52) [ 589 0 R /XYZ 67.0 406.089 null ] (rfc.figure.u.53) [ 589 0 R /XYZ 67.0 211.329 null ] (rfc.section.5.5) [ 591 0 R /XYZ 67.0 584.98 null ] (rfc.iref.216) [ 591 0 R /XYZ 67.0 486.008 null ] (rfc.figure.u.54) [ 591 0 R /XYZ 67.0 456.508 null ] (rfc.figure.u.55) [ 591 0 R /XYZ 67.0 379.928 null ] (rfc.section.5.6) [ 591 0 R /XYZ 67.0 225.128 null ] (rfc.iref.218) [ 591 0 R /XYZ 67.0 201.156 null ] (rfc.iref.219) [ 591 0 R /XYZ 67.0 187.656 null ] (rfc.iref.220) [ 591 0 R /XYZ 67.0 187.656 null ] (rfc.iref.221) [ 591 0 R /XYZ 67.0 149.656 null ] (rfc.iref.222) [ 591 0 R /XYZ 67.0 149.656 null ] (rfc.section.5.7) [ 591 0 R /XYZ 67.0 75.156 null ] (rfc.iref.224) [ 595 0 R /XYZ 67.0 701.028 null ] (rfc.iref.225) [ 595 0 R /XYZ 67.0 687.528 null ] (rfc.iref.226) [ 595 0 R /XYZ 67.0 687.528 null ] (rfc.section.5.8) [ 595 0 R /XYZ 67.0 624.028 null ] (rfc.iref.228) [ 595 0 R /XYZ 67.0 600.056 null ] (rfc.iref.229) [ 595 0 R /XYZ 67.0 586.556 null ] (rfc.iref.230) [ 595 0 R /XYZ 67.0 586.556 null ] (rfc.section.5.9) [ 595 0 R /XYZ 67.0 556.056 null ] (rfc.iref.232) [ 595 0 R /XYZ 67.0 532.084 null ] (rfc.iref.233) [ 595 0 R /XYZ 67.0 518.584 null ] (rfc.iref.234) [ 595 0 R /XYZ 67.0 518.584 null ] (rfc.section.5.10) [ 595 0 R /XYZ 67.0 466.084 null ] (rfc.iref.236) [ 595 0 R /XYZ 67.0 442.112 null ] (rfc.iref.237) [ 595 0 R /XYZ 67.0 428.612 null ] (rfc.iref.238) [ 595 0 R /XYZ 67.0 428.612 null ] (rfc.section.6) [ 597 0 R /XYZ 67.0 725.0 null ] (workspace.feature) [ 597 0 R /XYZ 67.0 725.0 null ] (rfc.section.6.1) [ 597 0 R /XYZ 67.0 397.866 null ] (rfc.section.6.1.1) [ 597 0 R /XYZ 67.0 346.894 null ] (PROPERTY_workspace-checkout-set) [ 597 0 R /XYZ 67.0 346.894 null ] (rfc.figure.u.56) [ 597 0 R /XYZ 67.0 311.003 null ] (rfc.section.6.2) [ 597 0 R /XYZ 67.0 269.143 null ] (rfc.section.6.2.1) [ 597 0 R /XYZ 67.0 218.171 null ] (PROPERTY_workspace) [ 597 0 R /XYZ 67.0 218.171 null ] (rfc.figure.u.57) [ 597 0 R /XYZ 67.0 171.28 null ] (rfc.section.6.3) [ 597 0 R /XYZ 67.0 129.42 null ] (METHOD_MKWORKSPACE) [ 597 0 R /XYZ 67.0 129.42 null ] (rfc.iref.247) [ 603 0 R /XYZ 67.0 678.0 null ] (rfc.figure.u.58) [ 603 0 R /XYZ 67.0 648.5 null ] (rfc.figure.u.59) [ 603 0 R /XYZ 67.0 591.64 null ] (rfc.iref.248) [ 603 0 R /XYZ 67.0 538.28 null ] (rfc.iref.249) [ 603 0 R /XYZ 67.0 524.78 null ] (rfc.iref.250) [ 603 0 R /XYZ 67.0 524.78 null ] (rfc.iref.251) [ 603 0 R /XYZ 67.0 508.78 null ] (rfc.iref.252) [ 603 0 R /XYZ 67.0 508.78 null ] (rfc.iref.253) [ 603 0 R /XYZ 67.0 474.28 null ] (rfc.iref.254) [ 603 0 R /XYZ 67.0 460.78 null ] (rfc.iref.255) [ 603 0 R /XYZ 67.0 460.78 null ] (rfc.section.6.3.1) [ 603 0 R /XYZ 67.0 409.28 null ] (rfc.figure.u.60) [ 603 0 R /XYZ 67.0 389.389 null ] (rfc.figure.u.61) [ 603 0 R /XYZ 67.0 322.809 null ] (rfc.section.6.4) [ 603 0 R /XYZ 67.0 238.089 null ] (additional.options.semantics.with.workspace.feature) [ 603 0 R /XYZ 67.0 238.089 null ] (rfc.iref.258) [ 603 0 R /XYZ 67.0 118.117 null ] (rfc.figure.u.62) [ 603 0 R /XYZ 67.0 88.617 null ] (rfc.figure.u.63) [ 607 0 R /XYZ 67.0 645.92 null ] (rfc.section.6.4.1) [ 607 0 R /XYZ 67.0 492.12 null ] (rfc.figure.u.64) [ 607 0 R /XYZ 67.0 472.229 null ] (rfc.figure.u.65) [ 607 0 R /XYZ 67.0 336.629 null ] (rfc.section.6.5) [ 607 0 R /XYZ 67.0 91.869 null ] (rfc.iref.260) [ 609 0 R /XYZ 67.0 720.0 null ] (rfc.iref.261) [ 609 0 R /XYZ 67.0 706.5 null ] (rfc.iref.262) [ 609 0 R /XYZ 67.0 706.5 null ] (rfc.section.6.6) [ 609 0 R /XYZ 67.0 665.0 null ] (rfc.iref.264) [ 609 0 R /XYZ 67.0 641.028 null ] (rfc.iref.265) [ 609 0 R /XYZ 67.0 627.528 null ] (rfc.iref.266) [ 609 0 R /XYZ 67.0 627.528 null ] (rfc.iref.267) [ 609 0 R /XYZ 67.0 589.528 null ] (rfc.iref.268) [ 609 0 R /XYZ 67.0 589.528 null ] (rfc.section.6.7) [ 609 0 R /XYZ 67.0 548.028 null ] (rfc.iref.270) [ 609 0 R /XYZ 67.0 481.056 null ] (rfc.figure.u.66) [ 609 0 R /XYZ 67.0 462.556 null ] (rfc.iref.271) [ 609 0 R /XYZ 67.0 385.756 null ] (rfc.iref.272) [ 609 0 R /XYZ 67.0 372.256 null ] (rfc.iref.273) [ 609 0 R /XYZ 67.0 372.256 null ] (rfc.iref.274) [ 609 0 R /XYZ 67.0 345.256 null ] (rfc.iref.275) [ 609 0 R /XYZ 67.0 345.256 null ] (rfc.iref.276) [ 609 0 R /XYZ 67.0 329.256 null ] (rfc.iref.277) [ 609 0 R /XYZ 67.0 329.256 null ] (rfc.iref.278) [ 609 0 R /XYZ 67.0 272.756 null ] (rfc.iref.279) [ 609 0 R /XYZ 67.0 259.256 null ] (rfc.iref.280) [ 609 0 R /XYZ 67.0 259.256 null ] (rfc.section.6.7.1) [ 609 0 R /XYZ 67.0 207.756 null ] (rfc.figure.u.67) [ 609 0 R /XYZ 67.0 187.865 null ] (rfc.figure.u.68) [ 611 0 R /XYZ 67.0 684.28 null ] (rfc.section.7) [ 613 0 R /XYZ 67.0 725.0 null ] (rfc.section.7.1) [ 613 0 R /XYZ 67.0 658.866 null ] (METHOD_UPDATE) [ 613 0 R /XYZ 67.0 658.866 null ] (rfc.iref.285) [ 613 0 R /XYZ 67.0 548.894 null ] (rfc.figure.u.69) [ 613 0 R /XYZ 67.0 519.394 null ] (rfc.xref.RFC2518.8) [ 613 0 R /XYZ 67.0 474.954 null ] (rfc.figure.u.70) [ 613 0 R /XYZ 67.0 383.654 null ] (rfc.xref.RFC2518.9) [ 613 0 R /XYZ 67.0 378.654 null ] (rfc.iref.286) [ 613 0 R /XYZ 67.0 290.854 null ] (rfc.iref.287) [ 613 0 R /XYZ 67.0 277.354 null ] (rfc.iref.288) [ 613 0 R /XYZ 67.0 277.354 null ] (rfc.iref.289) [ 613 0 R /XYZ 67.0 206.354 null ] (rfc.iref.290) [ 613 0 R /XYZ 67.0 206.354 null ] (rfc.section.7.1.1) [ 613 0 R /XYZ 67.0 165.854 null ] (rfc.figure.u.71) [ 613 0 R /XYZ 67.0 145.963 null ] (rfc.figure.u.72) [ 617 0 R /XYZ 67.0 644.84 null ] (rfc.section.7.2) [ 617 0 R /XYZ 67.0 449.38 null ] (rfc.section.8) [ 619 0 R /XYZ 67.0 725.0 null ] (label.feature) [ 619 0 R /XYZ 67.0 725.0 null ] (rfc.section.8.1) [ 619 0 R /XYZ 67.0 571.866 null ] (rfc.section.8.1.1) [ 619 0 R /XYZ 67.0 520.894 null ] (PROPERTY_label-name-set) [ 619 0 R /XYZ 67.0 520.894 null ] (rfc.figure.u.73) [ 619 0 R /XYZ 67.0 485.003 null ] (rfc.section.8.2) [ 619 0 R /XYZ 67.0 423.423 null ] (METHOD_LABEL) [ 619 0 R /XYZ 67.0 423.423 null ] (rfc.iref.299) [ 619 0 R /XYZ 67.0 313.451 null ] (rfc.figure.u.74) [ 619 0 R /XYZ 67.0 283.951 null ] (rfc.figure.u.75) [ 619 0 R /XYZ 67.0 83.211 null ] (rfc.iref.300) [ 623 0 R /XYZ 67.0 669.14 null ] (rfc.iref.301) [ 623 0 R /XYZ 67.0 655.64 null ] (rfc.iref.302) [ 623 0 R /XYZ 67.0 655.64 null ] (rfc.iref.303) [ 623 0 R /XYZ 67.0 628.64 null ] (rfc.iref.304) [ 623 0 R /XYZ 67.0 628.64 null ] (rfc.iref.305) [ 623 0 R /XYZ 67.0 590.64 null ] (rfc.iref.306) [ 623 0 R /XYZ 67.0 590.64 null ] (rfc.iref.307) [ 623 0 R /XYZ 67.0 552.64 null ] (rfc.iref.308) [ 623 0 R /XYZ 67.0 552.64 null ] (rfc.iref.309) [ 623 0 R /XYZ 67.0 518.14 null ] (rfc.iref.310) [ 623 0 R /XYZ 67.0 504.64 null ] (rfc.iref.311) [ 623 0 R /XYZ 67.0 504.64 null ] (rfc.iref.312) [ 623 0 R /XYZ 67.0 466.64 null ] (rfc.iref.313) [ 623 0 R /XYZ 67.0 466.64 null ] (rfc.section.8.2.1) [ 623 0 R /XYZ 67.0 426.14 null ] (rfc.figure.u.76) [ 623 0 R /XYZ 67.0 406.249 null ] (rfc.figure.u.77) [ 623 0 R /XYZ 67.0 260.789 null ] (rfc.section.8.3) [ 623 0 R /XYZ 67.0 176.069 null ] (label.header) [ 623 0 R /XYZ 67.0 176.069 null ] (rfc.figure.u.78) [ 623 0 R /XYZ 67.0 77.097 null ] (rfc.section.8.4) [ 625 0 R /XYZ 67.0 612.14 null ] (rfc.section.8.5) [ 625 0 R /XYZ 67.0 549.168 null ] (rfc.iref.319) [ 625 0 R /XYZ 67.0 525.196 null ] (rfc.iref.320) [ 625 0 R /XYZ 67.0 488.196 null ] (rfc.iref.321) [ 625 0 R /XYZ 67.0 474.696 null ] (rfc.iref.322) [ 625 0 R /XYZ 67.0 474.696 null ] (rfc.iref.323) [ 625 0 R /XYZ 67.0 429.196 null ] (rfc.iref.324) [ 625 0 R /XYZ 67.0 415.696 null ] (rfc.iref.325) [ 625 0 R /XYZ 67.0 415.696 null ] (rfc.section.8.6) [ 625 0 R /XYZ 67.0 363.196 null ] (rfc.iref.327) [ 625 0 R /XYZ 67.0 339.224 null ] (rfc.iref.328) [ 625 0 R /XYZ 67.0 302.224 null ] (rfc.iref.329) [ 625 0 R /XYZ 67.0 288.724 null ] (rfc.iref.330) [ 625 0 R /XYZ 67.0 288.724 null ] (rfc.iref.331) [ 625 0 R /XYZ 67.0 243.224 null ] (rfc.iref.332) [ 625 0 R /XYZ 67.0 229.724 null ] (rfc.iref.333) [ 625 0 R /XYZ 67.0 229.724 null ] (rfc.section.8.7) [ 625 0 R /XYZ 67.0 177.224 null ] (rfc.iref.335) [ 625 0 R /XYZ 67.0 153.252 null ] (rfc.iref.336) [ 625 0 R /XYZ 67.0 116.252 null ] (rfc.iref.337) [ 625 0 R /XYZ 67.0 102.752 null ] (rfc.iref.338) [ 625 0 R /XYZ 67.0 102.752 null ] (rfc.iref.339) [ 627 0 R /XYZ 67.0 699.0 null ] (rfc.iref.340) [ 627 0 R /XYZ 67.0 685.5 null ] (rfc.iref.341) [ 627 0 R /XYZ 67.0 685.5 null ] (rfc.section.8.8) [ 627 0 R /XYZ 67.0 633.0 null ] (rfc.iref.344) [ 627 0 R /XYZ 67.0 577.028 null ] (rfc.iref.345) [ 627 0 R /XYZ 67.0 540.028 null ] (rfc.iref.346) [ 627 0 R /XYZ 67.0 526.528 null ] (rfc.iref.347) [ 627 0 R /XYZ 67.0 526.528 null ] (rfc.iref.348) [ 627 0 R /XYZ 67.0 488.528 null ] (rfc.iref.349) [ 627 0 R /XYZ 67.0 488.528 null ] (rfc.iref.350) [ 627 0 R /XYZ 67.0 454.028 null ] (rfc.iref.351) [ 627 0 R /XYZ 67.0 440.528 null ] (rfc.iref.352) [ 627 0 R /XYZ 67.0 440.528 null ] (rfc.section.8.9) [ 627 0 R /XYZ 67.0 388.028 null ] (rfc.iref.354) [ 627 0 R /XYZ 67.0 321.056 null ] (rfc.figure.u.79) [ 627 0 R /XYZ 67.0 302.556 null ] (rfc.iref.355) [ 627 0 R /XYZ 67.0 170.756 null ] (rfc.iref.356) [ 627 0 R /XYZ 67.0 157.256 null ] (rfc.iref.357) [ 627 0 R /XYZ 67.0 157.256 null ] (rfc.iref.358) [ 627 0 R /XYZ 67.0 119.256 null ] (rfc.iref.359) [ 627 0 R /XYZ 67.0 119.256 null ] (rfc.iref.360) [ 629 0 R /XYZ 67.0 688.0 null ] (rfc.iref.361) [ 629 0 R /XYZ 67.0 674.5 null ] (rfc.iref.362) [ 629 0 R /XYZ 67.0 674.5 null ] (rfc.section.9) [ 631 0 R /XYZ 67.0 725.0 null ] (rfc.section.9.1) [ 631 0 R /XYZ 67.0 538.866 null ] (rfc.section.9.1.1) [ 631 0 R /XYZ 67.0 487.894 null ] (rfc.section.9.1.2) [ 631 0 R /XYZ 67.0 441.003 null ] (rfc.section.9.2) [ 631 0 R /XYZ 67.0 393.112 null ] (rfc.section.9.2.1) [ 631 0 R /XYZ 67.0 320.14 null ] (PROPERTY_auto-update) [ 631 0 R /XYZ 67.0 320.14 null ] (rfc.figure.u.80) [ 631 0 R /XYZ 67.0 273.249 null ] (rfc.section.9.2.2) [ 631 0 R /XYZ 67.0 232.389 null ] (rfc.section.9.2.3) [ 631 0 R /XYZ 67.0 185.498 null ] (rfc.section.9.3) [ 631 0 R /XYZ 67.0 137.607 null ] (rfc.iref.376) [ 638 0 R /XYZ 67.0 720.0 null ] (rfc.figure.u.81) [ 638 0 R /XYZ 67.0 690.5 null ] (rfc.figure.u.82) [ 638 0 R /XYZ 67.0 584.34 null ] (rfc.iref.377) [ 638 0 R /XYZ 67.0 514.98 null ] (rfc.iref.378) [ 638 0 R /XYZ 67.0 501.48 null ] (rfc.iref.379) [ 638 0 R /XYZ 67.0 501.48 null ] (rfc.iref.380) [ 638 0 R /XYZ 67.0 485.48 null ] (rfc.iref.381) [ 638 0 R /XYZ 67.0 485.48 null ] (rfc.iref.382) [ 638 0 R /XYZ 67.0 469.48 null ] (rfc.iref.383) [ 638 0 R /XYZ 67.0 469.48 null ] (rfc.iref.384) [ 638 0 R /XYZ 67.0 453.48 null ] (rfc.iref.385) [ 638 0 R /XYZ 67.0 453.48 null ] (rfc.iref.386) [ 638 0 R /XYZ 67.0 429.98 null ] (rfc.iref.387) [ 638 0 R /XYZ 67.0 416.48 null ] (rfc.iref.388) [ 638 0 R /XYZ 67.0 416.48 null ] (rfc.iref.389) [ 638 0 R /XYZ 67.0 345.48 null ] (rfc.iref.390) [ 638 0 R /XYZ 67.0 345.48 null ] (rfc.section.9.3.1) [ 638 0 R /XYZ 67.0 260.98 null ] (rfc.figure.u.83) [ 638 0 R /XYZ 67.0 241.089 null ] (rfc.figure.u.84) [ 638 0 R /XYZ 67.0 174.509 null ] (rfc.section.9.4) [ 638 0 R /XYZ 67.0 68.929 null ] (rfc.iref.393) [ 645 0 R /XYZ 67.0 636.028 null ] (rfc.figure.u.85) [ 645 0 R /XYZ 67.0 606.528 null ] (rfc.figure.u.86) [ 645 0 R /XYZ 67.0 510.228 null ] (rfc.iref.394) [ 645 0 R /XYZ 67.0 461.868 null ] (rfc.iref.395) [ 645 0 R /XYZ 67.0 448.368 null ] (rfc.iref.396) [ 645 0 R /XYZ 67.0 448.368 null ] (rfc.iref.397) [ 645 0 R /XYZ 67.0 432.368 null ] (rfc.iref.398) [ 645 0 R /XYZ 67.0 432.368 null ] (rfc.iref.399) [ 645 0 R /XYZ 67.0 416.368 null ] (rfc.iref.400) [ 645 0 R /XYZ 67.0 416.368 null ] (rfc.iref.401) [ 645 0 R /XYZ 67.0 400.368 null ] (rfc.iref.402) [ 645 0 R /XYZ 67.0 400.368 null ] (rfc.iref.403) [ 645 0 R /XYZ 67.0 384.368 null ] (rfc.iref.404) [ 645 0 R /XYZ 67.0 384.368 null ] (rfc.iref.405) [ 645 0 R /XYZ 67.0 327.868 null ] (rfc.iref.406) [ 645 0 R /XYZ 67.0 314.368 null ] (rfc.iref.407) [ 645 0 R /XYZ 67.0 314.368 null ] (rfc.iref.408) [ 645 0 R /XYZ 67.0 298.368 null ] (rfc.iref.409) [ 645 0 R /XYZ 67.0 298.368 null ] (rfc.iref.410) [ 645 0 R /XYZ 67.0 282.368 null ] (rfc.iref.411) [ 645 0 R /XYZ 67.0 282.368 null ] (rfc.iref.412) [ 645 0 R /XYZ 67.0 244.368 null ] (rfc.iref.413) [ 645 0 R /XYZ 67.0 244.368 null ] (rfc.section.9.4.1) [ 645 0 R /XYZ 67.0 203.868 null ] (rfc.figure.u.87) [ 645 0 R /XYZ 67.0 183.977 null ] (rfc.figure.u.88) [ 645 0 R /XYZ 67.0 117.397 null ] (rfc.section.9.5) [ 654 0 R /XYZ 67.0 655.14 null ] (rfc.section.9.6) [ 654 0 R /XYZ 67.0 581.168 null ] (rfc.iref.417) [ 654 0 R /XYZ 67.0 557.196 null ] (rfc.iref.418) [ 654 0 R /XYZ 67.0 543.696 null ] (rfc.iref.419) [ 654 0 R /XYZ 67.0 543.696 null ] (rfc.section.9.7) [ 654 0 R /XYZ 67.0 480.196 null ] (rfc.iref.421) [ 654 0 R /XYZ 67.0 456.224 null ] (rfc.iref.422) [ 654 0 R /XYZ 67.0 442.724 null ] (rfc.iref.423) [ 654 0 R /XYZ 67.0 442.724 null ] (rfc.iref.424) [ 654 0 R /XYZ 67.0 408.224 null ] (rfc.iref.425) [ 654 0 R /XYZ 67.0 394.724 null ] (rfc.iref.426) [ 654 0 R /XYZ 67.0 394.724 null ] (rfc.section.10) [ 656 0 R /XYZ 67.0 725.0 null ] (advanced.versioning.features) [ 656 0 R /XYZ 67.0 725.0 null ] (rfc.section.10.1) [ 656 0 R /XYZ 67.0 603.866 null ] (rfc.section.10.2) [ 656 0 R /XYZ 67.0 433.894 null ] (rfc.iref.427) [ 656 0 R /XYZ 67.0 388.922 null ] (rfc.iref.428) [ 656 0 R /XYZ 67.0 318.922 null ] (rfc.iref.429) [ 656 0 R /XYZ 67.0 226.922 null ] (rfc.iref.430) [ 656 0 R /XYZ 67.0 145.922 null ] (rfc.iref.431) [ 661 0 R /XYZ 67.0 699.0 null ] (rfc.iref.432) [ 661 0 R /XYZ 67.0 662.0 null ] (rfc.iref.433) [ 661 0 R /XYZ 67.0 570.0 null ] (rfc.section.11) [ 667 0 R /XYZ 67.0 725.0 null ] (merge.feature) [ 667 0 R /XYZ 67.0 725.0 null ] (rfc.section.11.1) [ 667 0 R /XYZ 67.0 484.866 null ] (rfc.section.11.1.1) [ 667 0 R /XYZ 67.0 433.894 null ] (PROPERTY_merge-set) [ 667 0 R /XYZ 67.0 433.894 null ] (rfc.figure.u.89) [ 667 0 R /XYZ 67.0 398.003 null ] (rfc.section.11.1.2) [ 667 0 R /XYZ 67.0 357.143 null ] (PROPERTY_auto-merge-set) [ 667 0 R /XYZ 67.0 357.143 null ] (rfc.figure.u.90) [ 667 0 R /XYZ 67.0 299.252 null ] (rfc.section.11.2) [ 667 0 R /XYZ 67.0 257.392 null ] (METHOD_MERGE) [ 667 0 R /XYZ 67.0 257.392 null ] (rfc.iref.442) [ 669 0 R /XYZ 67.0 446.0 null ] (rfc.figure.u.91) [ 669 0 R /XYZ 67.0 400.5 null ] (rfc.xref.RFC2518.10) [ 669 0 R /XYZ 67.0 316.62 null ] (rfc.figure.u.92) [ 669 0 R /XYZ 67.0 225.32 null ] (rfc.xref.RFC2518.11) [ 669 0 R /XYZ 67.0 220.32 null ] (rfc.iref.443) [ 669 0 R /XYZ 67.0 105.52 null ] (rfc.iref.444) [ 669 0 R /XYZ 67.0 92.02 null ] (rfc.iref.445) [ 669 0 R /XYZ 67.0 92.02 null ] (rfc.iref.446) [ 671 0 R /XYZ 67.0 695.5 null ] (rfc.iref.447) [ 671 0 R /XYZ 67.0 695.5 null ] (rfc.iref.448) [ 671 0 R /XYZ 67.0 645.0 null ] (rfc.iref.449) [ 671 0 R /XYZ 67.0 631.5 null ] (rfc.iref.450) [ 671 0 R /XYZ 67.0 631.5 null ] (rfc.iref.451) [ 671 0 R /XYZ 67.0 593.5 null ] (rfc.iref.452) [ 671 0 R /XYZ 67.0 593.5 null ] (rfc.iref.453) [ 671 0 R /XYZ 67.0 511.5 null ] (rfc.iref.454) [ 671 0 R /XYZ 67.0 511.5 null ] (rfc.iref.455) [ 671 0 R /XYZ 67.0 451.5 null ] (rfc.iref.456) [ 671 0 R /XYZ 67.0 451.5 null ] (rfc.iref.457) [ 671 0 R /XYZ 67.0 342.5 null ] (rfc.iref.458) [ 671 0 R /XYZ 67.0 342.5 null ] (rfc.section.11.2.1) [ 671 0 R /XYZ 67.0 302.0 null ] (rfc.figure.u.93) [ 671 0 R /XYZ 67.0 282.109 null ] (rfc.figure.u.94) [ 671 0 R /XYZ 67.0 136.649 null ] (rfc.section.11.3) [ 673 0 R /XYZ 67.0 535.68 null ] (rfc.iref.461) [ 673 0 R /XYZ 67.0 468.708 null ] (rfc.figure.u.95) [ 673 0 R /XYZ 67.0 439.208 null ] (rfc.figure.u.96) [ 673 0 R /XYZ 67.0 383.488 null ] (rfc.figure.u.97) [ 673 0 R /XYZ 67.0 316.768 null ] (rfc.figure.u.98) [ 673 0 R /XYZ 67.0 251.188 null ] (rfc.figure.u.99) [ 673 0 R /XYZ 67.0 194.328 null ] (rfc.figure.u.100) [ 673 0 R /XYZ 67.0 137.468 null ] (rfc.section.11.3.1) [ 673 0 R /XYZ 67.0 94.108 null ] (rfc.figure.u.101) [ 673 0 R /XYZ 67.0 74.217 null ] (rfc.figure.u.102) [ 675 0 R /XYZ 67.0 579.54 null ] (rfc.section.11.4) [ 675 0 R /XYZ 67.0 236.18 null ] (rfc.section.11.5) [ 675 0 R /XYZ 67.0 173.208 null ] (rfc.iref.465) [ 675 0 R /XYZ 67.0 149.236 null ] (rfc.iref.466) [ 675 0 R /XYZ 67.0 135.736 null ] (rfc.iref.467) [ 675 0 R /XYZ 67.0 135.736 null ] (rfc.section.11.6) [ 675 0 R /XYZ 67.0 94.236 null ] (rfc.iref.469) [ 677 0 R /XYZ 67.0 720.0 null ] (rfc.iref.470) [ 677 0 R /XYZ 67.0 706.5 null ] (rfc.iref.471) [ 677 0 R /XYZ 67.0 706.5 null ] (rfc.section.12) [ 679 0 R /XYZ 67.0 725.0 null ] (baseline.feature) [ 679 0 R /XYZ 67.0 725.0 null ] (rfc.section.12.1) [ 679 0 R /XYZ 67.0 398.866 null ] (rfc.section.12.1.1) [ 679 0 R /XYZ 67.0 325.894 null ] (PROPERTY_baseline-controlled-collection) [ 679 0 R /XYZ 67.0 325.894 null ] (rfc.figure.u.103) [ 679 0 R /XYZ 67.0 257.003 null ] (rfc.section.12.2) [ 679 0 R /XYZ 67.0 215.143 null ] (rfc.section.12.2.1) [ 679 0 R /XYZ 67.0 153.171 null ] (PROPERTY_subbaseline-set) [ 679 0 R /XYZ 67.0 153.171 null ] (rfc.figure.u.104) [ 679 0 R /XYZ 67.0 85.28 null ] (rfc.section.12.3) [ 681 0 R /XYZ 67.0 683.14 null ] (rfc.section.12.3.1) [ 681 0 R /XYZ 67.0 610.168 null ] (PROPERTY_baseline-collection) [ 681 0 R /XYZ 67.0 610.168 null ] (rfc.figure.u.105) [ 681 0 R /XYZ 67.0 541.277 null ] (rfc.section.12.3.2) [ 681 0 R /XYZ 67.0 500.417 null ] (rfc.figure.u.106) [ 681 0 R /XYZ 67.0 442.526 null ] (rfc.section.12.4) [ 681 0 R /XYZ 67.0 400.666 null ] (rfc.section.12.4.1) [ 681 0 R /XYZ 67.0 349.694 null ] (PROPERTY_version-controlled-configuration) [ 681 0 R /XYZ 67.0 349.694 null ] (rfc.figure.u.107) [ 681 0 R /XYZ 67.0 291.803 null ] (rfc.section.12.5) [ 681 0 R /XYZ 67.0 249.943 null ] (rfc.section.12.5.1) [ 681 0 R /XYZ 67.0 198.971 null ] (PROPERTY_baseline-controlled-collection-set) [ 681 0 R /XYZ 67.0 198.971 null ] (rfc.figure.u.108) [ 681 0 R /XYZ 67.0 152.08 null ] (rfc.section.12.6) [ 681 0 R /XYZ 67.0 110.22 null ] (METHOD_BASELINE-CONTROL) [ 681 0 R /XYZ 67.0 110.22 null ] (rfc.iref.490) [ 683 0 R /XYZ 67.0 580.0 null ] (rfc.figure.u.109) [ 683 0 R /XYZ 67.0 550.5 null ] (rfc.figure.u.110) [ 683 0 R /XYZ 67.0 454.2 null ] (rfc.iref.491) [ 683 0 R /XYZ 67.0 400.84 null ] (rfc.iref.492) [ 683 0 R /XYZ 67.0 387.34 null ] (rfc.iref.493) [ 683 0 R /XYZ 67.0 387.34 null ] (rfc.iref.494) [ 683 0 R /XYZ 67.0 360.34 null ] (rfc.iref.495) [ 683 0 R /XYZ 67.0 360.34 null ] (rfc.iref.496) [ 683 0 R /XYZ 67.0 333.34 null ] (rfc.iref.497) [ 683 0 R /XYZ 67.0 333.34 null ] (rfc.iref.498) [ 683 0 R /XYZ 67.0 306.34 null ] (rfc.iref.499) [ 683 0 R /XYZ 67.0 306.34 null ] (rfc.iref.500) [ 683 0 R /XYZ 67.0 238.84 null ] (rfc.iref.501) [ 683 0 R /XYZ 67.0 225.34 null ] (rfc.iref.502) [ 683 0 R /XYZ 67.0 225.34 null ] (rfc.iref.503) [ 683 0 R /XYZ 67.0 198.34 null ] (rfc.iref.504) [ 683 0 R /XYZ 67.0 198.34 null ] (rfc.iref.505) [ 683 0 R /XYZ 67.0 171.34 null ] (rfc.iref.506) [ 683 0 R /XYZ 67.0 171.34 null ] (rfc.iref.507) [ 683 0 R /XYZ 67.0 89.34 null ] (rfc.iref.508) [ 683 0 R /XYZ 67.0 89.34 null ] (rfc.section.12.6.1) [ 685 0 R /XYZ 67.0 649.0 null ] (rfc.figure.u.111) [ 685 0 R /XYZ 67.0 629.109 null ] (rfc.figure.u.112) [ 685 0 R /XYZ 67.0 503.369 null ] (rfc.figure.u.113) [ 685 0 R /XYZ 67.0 371.789 null ] (rfc.section.12.7) [ 687 0 R /XYZ 67.0 638.252 null ] (rfc.iref.511) [ 687 0 R /XYZ 67.0 582.28 null ] (rfc.figure.u.114) [ 687 0 R /XYZ 67.0 552.78 null ] (rfc.figure.u.115) [ 687 0 R /XYZ 67.0 506.92 null ] (rfc.figure.u.116) [ 687 0 R /XYZ 67.0 429.2 null ] (rfc.figure.u.117) [ 687 0 R /XYZ 67.0 361.34 null ] (rfc.figure.u.118) [ 687 0 R /XYZ 67.0 293.48 null ] (rfc.iref.512) [ 687 0 R /XYZ 67.0 256.12 null ] (rfc.iref.513) [ 687 0 R /XYZ 67.0 242.62 null ] (rfc.iref.514) [ 687 0 R /XYZ 67.0 242.62 null ] (rfc.iref.515) [ 687 0 R /XYZ 67.0 226.62 null ] (rfc.iref.516) [ 687 0 R /XYZ 67.0 226.62 null ] (rfc.section.12.7.1) [ 687 0 R /XYZ 67.0 186.12 null ] (rfc.figure.u.119) [ 687 0 R /XYZ 67.0 166.229 null ] (rfc.figure.u.120) [ 689 0 R /XYZ 67.0 684.28 null ] (rfc.section.12.8) [ 689 0 R /XYZ 67.0 440.66 null ] (rfc.section.12.9) [ 689 0 R /XYZ 67.0 377.688 null ] (rfc.iref.520) [ 689 0 R /XYZ 67.0 353.716 null ] (rfc.section.12.10) [ 689 0 R /XYZ 67.0 298.716 null ] (rfc.iref.522) [ 689 0 R /XYZ 67.0 274.744 null ] (rfc.section.12.11) [ 689 0 R /XYZ 67.0 219.744 null ] (rfc.iref.524) [ 689 0 R /XYZ 67.0 195.772 null ] (rfc.iref.525) [ 689 0 R /XYZ 67.0 182.272 null ] (rfc.iref.526) [ 689 0 R /XYZ 67.0 182.272 null ] (rfc.section.12.12) [ 689 0 R /XYZ 67.0 140.772 null ] (rfc.iref.528) [ 689 0 R /XYZ 67.0 116.8 null ] (rfc.iref.529) [ 689 0 R /XYZ 67.0 103.3 null ] (rfc.iref.530) [ 689 0 R /XYZ 67.0 103.3 null ] (rfc.iref.531) [ 691 0 R /XYZ 67.0 706.5 null ] (rfc.iref.532) [ 691 0 R /XYZ 67.0 706.5 null ] (rfc.iref.533) [ 691 0 R /XYZ 67.0 624.5 null ] (rfc.iref.534) [ 691 0 R /XYZ 67.0 624.5 null ] (rfc.iref.535) [ 691 0 R /XYZ 67.0 568.0 null ] (rfc.iref.536) [ 691 0 R /XYZ 67.0 554.5 null ] (rfc.iref.537) [ 691 0 R /XYZ 67.0 554.5 null ] (rfc.iref.538) [ 691 0 R /XYZ 67.0 505.5 null ] (rfc.iref.539) [ 691 0 R /XYZ 67.0 505.5 null ] (rfc.section.12.13) [ 691 0 R /XYZ 67.0 453.0 null ] (additional.update.semantics.with.baseline.feature) [ 691 0 R /XYZ 67.0 453.0 null ] (rfc.iref.541) [ 691 0 R /XYZ 67.0 429.028 null ] (rfc.iref.542) [ 691 0 R /XYZ 67.0 415.528 null ] (rfc.iref.543) [ 691 0 R /XYZ 67.0 415.528 null ] (rfc.iref.544) [ 691 0 R /XYZ 67.0 377.528 null ] (rfc.iref.545) [ 691 0 R /XYZ 67.0 377.528 null ] (rfc.iref.546) [ 691 0 R /XYZ 67.0 350.528 null ] (rfc.iref.547) [ 691 0 R /XYZ 67.0 350.528 null ] (rfc.iref.548) [ 691 0 R /XYZ 67.0 283.028 null ] (rfc.iref.549) [ 691 0 R /XYZ 67.0 269.528 null ] (rfc.iref.550) [ 691 0 R /XYZ 67.0 269.528 null ] (rfc.iref.551) [ 693 0 R /XYZ 67.0 701.5 null ] (rfc.iref.552) [ 693 0 R /XYZ 67.0 701.5 null ] (rfc.iref.553) [ 693 0 R /XYZ 67.0 608.5 null ] (rfc.iref.554) [ 693 0 R /XYZ 67.0 608.5 null ] (rfc.section.12.14) [ 693 0 R /XYZ 67.0 523.0 null ] (rfc.iref.556) [ 693 0 R /XYZ 67.0 456.028 null ] (rfc.iref.557) [ 693 0 R /XYZ 67.0 442.528 null ] (rfc.iref.558) [ 693 0 R /XYZ 67.0 442.528 null ] (rfc.iref.559) [ 693 0 R /XYZ 67.0 426.528 null ] (rfc.iref.560) [ 693 0 R /XYZ 67.0 426.528 null ] (rfc.iref.561) [ 693 0 R /XYZ 67.0 403.028 null ] (rfc.iref.562) [ 693 0 R /XYZ 67.0 389.528 null ] (rfc.iref.563) [ 693 0 R /XYZ 67.0 389.528 null ] (rfc.iref.564) [ 693 0 R /XYZ 67.0 329.528 null ] (rfc.iref.565) [ 693 0 R /XYZ 67.0 329.528 null ] (rfc.iref.566) [ 693 0 R /XYZ 67.0 247.528 null ] (rfc.iref.567) [ 693 0 R /XYZ 67.0 247.528 null ] (rfc.iref.568) [ 693 0 R /XYZ 67.0 231.528 null ] (rfc.iref.569) [ 693 0 R /XYZ 67.0 231.528 null ] (rfc.section.13) [ 700 0 R /XYZ 67.0 725.0 null ] (activity.feature) [ 700 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.121) [ 700 0 R /XYZ 67.0 437.866 null ] (rfc.section.13.1) [ 700 0 R /XYZ 67.0 169.386 null ] (rfc.section.13.1.1) [ 700 0 R /XYZ 67.0 97.414 null ] (PROPERTY_activity-version-set) [ 700 0 R /XYZ 67.0 97.414 null ] (rfc.figure.u.122) [ 702 0 R /XYZ 67.0 671.0 null ] (rfc.section.13.1.2) [ 702 0 R /XYZ 67.0 630.14 null ] (PROPERTY_activity-checkout-set) [ 702 0 R /XYZ 67.0 630.14 null ] (rfc.figure.u.123) [ 702 0 R /XYZ 67.0 594.249 null ] (rfc.section.13.1.3) [ 702 0 R /XYZ 67.0 553.389 null ] (PROPERTY_subactivity-set) [ 702 0 R /XYZ 67.0 553.389 null ] (rfc.figure.u.124) [ 702 0 R /XYZ 67.0 452.498 null ] (rfc.section.13.1.4) [ 702 0 R /XYZ 67.0 411.638 null ] (PROPERTY_current-workspace-set) [ 702 0 R /XYZ 67.0 411.638 null ] (rfc.figure.u.125) [ 702 0 R /XYZ 67.0 375.747 null ] (rfc.section.13.2) [ 702 0 R /XYZ 67.0 333.887 null ] (rfc.section.13.2.1) [ 702 0 R /XYZ 67.0 282.915 null ] (PROPERTY_activity-set) [ 702 0 R /XYZ 67.0 282.915 null ] (rfc.figure.u.126) [ 702 0 R /XYZ 67.0 225.024 null ] (rfc.section.13.3) [ 702 0 R /XYZ 67.0 183.164 null ] (rfc.section.13.3.1) [ 702 0 R /XYZ 67.0 132.192 null ] (PROPERTY_unreserved) [ 702 0 R /XYZ 67.0 132.192 null ] (rfc.figure.u.127) [ 704 0 R /XYZ 67.0 649.0 null ] (rfc.section.13.3.2) [ 704 0 R /XYZ 67.0 598.28 null ] (rfc.section.13.4) [ 704 0 R /XYZ 67.0 539.389 null ] (rfc.section.13.4.1) [ 704 0 R /XYZ 67.0 488.417 null ] (PROPERTY_current-activity-set) [ 704 0 R /XYZ 67.0 488.417 null ] (rfc.figure.u.128) [ 704 0 R /XYZ 67.0 419.526 null ] (rfc.section.13.5) [ 704 0 R /XYZ 67.0 377.666 null ] (METHOD_MKACTIVITY) [ 704 0 R /XYZ 67.0 377.666 null ] (rfc.iref.592) [ 704 0 R /XYZ 67.0 310.694 null ] (rfc.figure.u.129) [ 704 0 R /XYZ 67.0 281.194 null ] (rfc.figure.u.130) [ 704 0 R /XYZ 67.0 224.334 null ] (rfc.iref.593) [ 704 0 R /XYZ 67.0 170.974 null ] (rfc.iref.594) [ 704 0 R /XYZ 67.0 157.474 null ] (rfc.iref.595) [ 704 0 R /XYZ 67.0 157.474 null ] (rfc.iref.596) [ 704 0 R /XYZ 67.0 141.474 null ] (rfc.iref.597) [ 704 0 R /XYZ 67.0 141.474 null ] (rfc.iref.598) [ 704 0 R /XYZ 67.0 117.974 null ] (rfc.iref.599) [ 704 0 R /XYZ 67.0 104.474 null ] (rfc.iref.600) [ 704 0 R /XYZ 67.0 104.474 null ] (rfc.section.13.5.1) [ 706 0 R /XYZ 67.0 709.0 null ] (rfc.figure.u.131) [ 706 0 R /XYZ 67.0 689.109 null ] (rfc.figure.u.132) [ 706 0 R /XYZ 67.0 622.529 null ] (rfc.section.13.6) [ 706 0 R /XYZ 67.0 537.809 null ] (rfc.iref.603) [ 706 0 R /XYZ 67.0 481.837 null ] (rfc.figure.u.133) [ 706 0 R /XYZ 67.0 452.337 null ] (rfc.figure.u.134) [ 706 0 R /XYZ 67.0 406.477 null ] (rfc.iref.604) [ 706 0 R /XYZ 67.0 331.117 null ] (rfc.iref.605) [ 706 0 R /XYZ 67.0 317.617 null ] (rfc.iref.606) [ 706 0 R /XYZ 67.0 317.617 null ] (rfc.section.13.7) [ 706 0 R /XYZ 67.0 287.117 null ] (rfc.iref.609) [ 706 0 R /XYZ 67.0 199.145 null ] (rfc.figure.u.135) [ 706 0 R /XYZ 67.0 169.645 null ] (rfc.figure.u.136) [ 706 0 R /XYZ 67.0 93.065 null ] (rfc.section.13.8) [ 708 0 R /XYZ 67.0 577.56 null ] (rfc.iref.611) [ 708 0 R /XYZ 67.0 553.588 null ] (rfc.iref.612) [ 708 0 R /XYZ 67.0 540.088 null ] (rfc.iref.613) [ 708 0 R /XYZ 67.0 540.088 null ] (rfc.section.13.9) [ 708 0 R /XYZ 67.0 498.588 null ] (rfc.iref.615) [ 708 0 R /XYZ 67.0 474.616 null ] (rfc.iref.616) [ 708 0 R /XYZ 67.0 461.116 null ] (rfc.iref.617) [ 708 0 R /XYZ 67.0 461.116 null ] (rfc.iref.618) [ 708 0 R /XYZ 67.0 434.116 null ] (rfc.iref.619) [ 708 0 R /XYZ 67.0 434.116 null ] (rfc.iref.620) [ 708 0 R /XYZ 67.0 396.116 null ] (rfc.iref.621) [ 708 0 R /XYZ 67.0 396.116 null ] (rfc.section.13.10) [ 708 0 R /XYZ 67.0 343.616 null ] (rfc.iref.623) [ 708 0 R /XYZ 67.0 298.644 null ] (rfc.figure.u.137) [ 708 0 R /XYZ 67.0 280.144 null ] (rfc.iref.624) [ 708 0 R /XYZ 67.0 193.484 null ] (rfc.iref.625) [ 708 0 R /XYZ 67.0 179.984 null ] (rfc.iref.626) [ 708 0 R /XYZ 67.0 179.984 null ] (rfc.iref.627) [ 708 0 R /XYZ 67.0 141.984 null ] (rfc.iref.628) [ 708 0 R /XYZ 67.0 141.984 null ] (rfc.iref.629) [ 708 0 R /XYZ 67.0 107.484 null ] (rfc.iref.630) [ 708 0 R /XYZ 67.0 93.984 null ] (rfc.iref.631) [ 708 0 R /XYZ 67.0 93.984 null ] (rfc.iref.632) [ 710 0 R /XYZ 67.0 631.5 null ] (rfc.iref.633) [ 710 0 R /XYZ 67.0 631.5 null ] (rfc.section.13.10.1) [ 710 0 R /XYZ 67.0 591.0 null ] (rfc.figure.u.138) [ 710 0 R /XYZ 67.0 571.109 null ] (rfc.figure.u.139) [ 710 0 R /XYZ 67.0 425.649 null ] (rfc.section.13.11) [ 710 0 R /XYZ 67.0 340.929 null ] (rfc.iref.635) [ 710 0 R /XYZ 67.0 316.957 null ] (rfc.iref.636) [ 710 0 R /XYZ 67.0 303.457 null ] (rfc.iref.637) [ 710 0 R /XYZ 67.0 303.457 null ] (rfc.iref.638) [ 710 0 R /XYZ 67.0 265.457 null ] (rfc.iref.639) [ 710 0 R /XYZ 67.0 265.457 null ] (rfc.iref.640) [ 710 0 R /XYZ 67.0 219.957 null ] (rfc.iref.641) [ 710 0 R /XYZ 67.0 206.457 null ] (rfc.iref.642) [ 710 0 R /XYZ 67.0 206.457 null ] (rfc.iref.643) [ 710 0 R /XYZ 67.0 179.457 null ] (rfc.iref.644) [ 710 0 R /XYZ 67.0 179.457 null ] (rfc.section.13.12) [ 710 0 R /XYZ 67.0 126.957 null ] (rfc.iref.646) [ 712 0 R /XYZ 67.0 688.0 null ] (rfc.figure.u.140) [ 712 0 R /XYZ 67.0 669.5 null ] (rfc.iref.647) [ 712 0 R /XYZ 67.0 632.14 null ] (rfc.iref.648) [ 712 0 R /XYZ 67.0 618.64 null ] (rfc.iref.649) [ 712 0 R /XYZ 67.0 618.64 null ] (rfc.section.14) [ 714 0 R /XYZ 67.0 725.0 null ] (version-controlled-collection.feature) [ 714 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.141) [ 714 0 R /XYZ 67.0 370.866 null ] (rfc.section.14.1) [ 716 0 R /XYZ 67.0 388.252 null ] (rfc.section.14.1.1) [ 716 0 R /XYZ 67.0 315.28 null ] (PROPERTY_eclipsed-set) [ 716 0 R /XYZ 67.0 315.28 null ] (rfc.figure.u.142) [ 716 0 R /XYZ 67.0 268.389 null ] (rfc.section.14.2) [ 716 0 R /XYZ 67.0 141.809 null ] (rfc.section.14.2.1) [ 716 0 R /XYZ 67.0 79.837 null ] (PROPERTY_version-controlled-binding-set) [ 718 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.143) [ 718 0 R /XYZ 67.0 689.109 null ] (rfc.section.14.3) [ 718 0 R /XYZ 67.0 588.089 null ] (rfc.section.14.4) [ 718 0 R /XYZ 67.0 514.117 null ] (rfc.iref.659) [ 718 0 R /XYZ 67.0 490.145 null ] (rfc.iref.660) [ 718 0 R /XYZ 67.0 476.645 null ] (rfc.iref.661) [ 718 0 R /XYZ 67.0 476.645 null ] (rfc.section.14.5) [ 718 0 R /XYZ 67.0 413.145 null ] (rfc.iref.663) [ 718 0 R /XYZ 67.0 389.173 null ] (rfc.iref.664) [ 718 0 R /XYZ 67.0 341.173 null ] (rfc.section.14.6) [ 718 0 R /XYZ 67.0 286.173 null ] (rfc.iref.666) [ 718 0 R /XYZ 67.0 262.201 null ] (rfc.iref.667) [ 718 0 R /XYZ 67.0 248.701 null ] (rfc.iref.668) [ 718 0 R /XYZ 67.0 248.701 null ] (rfc.section.14.7) [ 718 0 R /XYZ 67.0 207.201 null ] (rfc.iref.670) [ 718 0 R /XYZ 67.0 183.229 null ] (rfc.iref.671) [ 718 0 R /XYZ 67.0 169.729 null ] (rfc.iref.672) [ 718 0 R /XYZ 67.0 169.729 null ] (rfc.iref.673) [ 718 0 R /XYZ 67.0 131.729 null ] (rfc.iref.674) [ 718 0 R /XYZ 67.0 131.729 null ] (rfc.section.14.8) [ 720 0 R /XYZ 67.0 708.0 null ] (rfc.iref.676) [ 720 0 R /XYZ 67.0 684.028 null ] (rfc.iref.677) [ 720 0 R /XYZ 67.0 670.528 null ] (rfc.iref.678) [ 720 0 R /XYZ 67.0 670.528 null ] (rfc.iref.679) [ 720 0 R /XYZ 67.0 625.028 null ] (rfc.iref.680) [ 720 0 R /XYZ 67.0 611.528 null ] (rfc.iref.681) [ 720 0 R /XYZ 67.0 611.528 null ] (rfc.section.14.9) [ 720 0 R /XYZ 67.0 493.028 null ] (rfc.iref.683) [ 720 0 R /XYZ 67.0 469.056 null ] (rfc.iref.684) [ 720 0 R /XYZ 67.0 455.556 null ] (rfc.iref.685) [ 720 0 R /XYZ 67.0 455.556 null ] (rfc.section.14.10) [ 720 0 R /XYZ 67.0 403.056 null ] (rfc.iref.687) [ 720 0 R /XYZ 67.0 379.084 null ] (rfc.iref.688) [ 720 0 R /XYZ 67.0 365.584 null ] (rfc.iref.689) [ 720 0 R /XYZ 67.0 365.584 null ] (rfc.iref.690) [ 720 0 R /XYZ 67.0 316.584 null ] (rfc.iref.691) [ 720 0 R /XYZ 67.0 316.584 null ] (rfc.section.14.11) [ 720 0 R /XYZ 67.0 198.084 null ] (rfc.iref.694) [ 720 0 R /XYZ 67.0 174.112 null ] (rfc.iref.695) [ 720 0 R /XYZ 67.0 174.112 null ] (rfc.iref.696) [ 720 0 R /XYZ 67.0 160.612 null ] (rfc.iref.697) [ 720 0 R /XYZ 67.0 160.612 null ] (rfc.section.15) [ 724 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2277.1) [ 724 0 R /XYZ 67.0 697.866 null ] (rfc.xref.RFC2279.1) [ 724 0 R /XYZ 67.0 610.866 null ] (rfc.xref.RFC3023.1) [ 724 0 R /XYZ 67.0 588.866 null ] (rfc.xref.RFC3066.1) [ 724 0 R /XYZ 67.0 566.866 null ] (rfc.xref.ISO639.1) [ 724 0 R /XYZ 67.0 566.866 null ] (rfc.xref.RFC2518.12) [ 724 0 R /XYZ 67.0 523.866 null ] (rfc.section.16) [ 738 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.13) [ 738 0 R /XYZ 67.0 697.866 null ] (rfc.section.16.1) [ 738 0 R /XYZ 67.0 647.866 null ] (rfc.section.16.2) [ 738 0 R /XYZ 67.0 518.894 null ] (rfc.section.16.3) [ 738 0 R /XYZ 67.0 357.922 null ] (rfc.section.16.4) [ 738 0 R /XYZ 67.0 261.95 null ] (rfc.section.17) [ 740 0 R /XYZ 67.0 725.0 null ] (rfc.section.18) [ 742 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2026.1) [ 742 0 R /XYZ 67.0 697.866 null ] (rfc.xref.RFC2026.2) [ 742 0 R /XYZ 67.0 697.866 null ] (rfc.section.19) [ 747 0 R /XYZ 67.0 725.0 null ] (rfc.references) [ 749 0 R /XYZ 67.0 725.0 null ] (ISO639) [ 749 0 R /XYZ 67.0 702.866 null ] (RFC2026) [ 749 0 R /XYZ 67.0 675.866 null ] (RFC2119) [ 749 0 R /XYZ 67.0 659.866 null ] (RFC2277) [ 749 0 R /XYZ 67.0 632.866 null ] (RFC2279) [ 749 0 R /XYZ 67.0 616.866 null ] (RFC2396) [ 749 0 R /XYZ 67.0 600.866 null ] (RFC2518) [ 749 0 R /XYZ 67.0 573.866 null ] (RFC2616) [ 749 0 R /XYZ 67.0 546.866 null ] (RFC3023) [ 749 0 R /XYZ 67.0 519.866 null ] (RFC3066) [ 749 0 R /XYZ 67.0 503.866 null ] (rfc.section.A) [ 763 0 R /XYZ 67.0 725.0 null ] (rfc.section.A.1) [ 763 0 R /XYZ 67.0 539.866 null ] (rfc.xref.RFC2616.2) [ 763 0 R /XYZ 67.0 502.394 null ] (rfc.xref.RFC2518.14) [ 763 0 R /XYZ 67.0 486.394 null ] (rfc.section.A.2) [ 763 0 R /XYZ 67.0 407.894 null ] (rfc.xref.RFC2518.15) [ 763 0 R /XYZ 67.0 290.422 null ] (rfc.xref.RFC2518.16) [ 763 0 R /XYZ 67.0 237.422 null ] (rfc.xref.RFC2616.3) [ 763 0 R /XYZ 67.0 221.422 null ] (rfc.section.A.3) [ 763 0 R /XYZ 67.0 190.922 null ] (rfc.section.A.4) [ 773 0 R /XYZ 67.0 708.0 null ] (rfc.section.A.5) [ 773 0 R /XYZ 67.0 555.028 null ] (rfc.section.A.6) [ 773 0 R /XYZ 67.0 354.056 null ] (rfc.section.A.7) [ 773 0 R /XYZ 67.0 73.084 null ] (rfc.section.A.8) [ 775 0 R /XYZ 67.0 572.028 null ] (rfc.section.A.9) [ 775 0 R /XYZ 67.0 339.056 null ] (rfc.section.A.10) [ 775 0 R /XYZ 67.0 186.084 null ] (rfc.section.A.11) [ 777 0 R /XYZ 67.0 692.0 null ] (rfc.section.A.12) [ 777 0 R /XYZ 67.0 555.028 null ] (rfc.section.A.13) [ 777 0 R /XYZ 67.0 402.056 null ] (rfc.section.A.14) [ 777 0 R /XYZ 67.0 233.084 null ] (rfc.section.A.15) [ 777 0 R /XYZ 67.0 112.112 null ] (rfc.section.A.16) [ 779 0 R /XYZ 67.0 623.0 null ] (rfc.section.A.17) [ 779 0 R /XYZ 67.0 502.028 null ] (rfc.section.A.18) [ 779 0 R /XYZ 67.0 365.056 null ] (rfc.authors) [ 781 0 R /XYZ 67.0 725.0 null ] (rfc.copyright) [ 781 0 R /XYZ 67.0 308.866 null ] (rfc.ipr) [ 791 0 R /XYZ 67.0 725.0 null ] (rfc.index) [ 793 0 R /XYZ 67.0 725.0 null ] ] >> >> + >> +endobj +3 0 obj +<< +/Font << /F5 1124 0 R /F6 1125 0 R /F9 1126 0 R /F7 1127 0 R >> +/ProcSet [ /PDF /ImageC /Text ] >> +endobj +11 0 obj +<< +/S /GoTo +/D [494 0 R /XYZ 67.0 725.0 null] +>> +endobj +13 0 obj +<< +/S /GoTo +/D [494 0 R /XYZ 67.0 336.866 null] +>> +endobj +15 0 obj +<< +/S /GoTo +/D [494 0 R /XYZ 67.0 229.894 null] +>> +endobj +17 0 obj +<< +/S /GoTo +/D [508 0 R /XYZ 67.0 638.0 null] +>> +endobj +19 0 obj +<< +/S /GoTo +/D [513 0 R /XYZ 67.0 156.15 null] +>> +endobj +21 0 obj +<< +/S /GoTo +/D [513 0 R /XYZ 67.0 126.178 null] +>> +endobj +23 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 725.0 null] +>> +endobj +25 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 645.109 null] +>> +endobj +27 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 554.218 null] +>> +endobj +29 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 507.327 null] +>> +endobj +31 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 459.436 null] +>> +endobj +33 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 385.464 null] +>> +endobj +35 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 214.492 null] +>> +endobj +37 0 obj +<< +/S /GoTo +/D [519 0 R /XYZ 67.0 636.56 null] +>> +endobj +39 0 obj +<< +/S /GoTo +/D [519 0 R /XYZ 67.0 379.588 null] +>> +endobj +41 0 obj +<< +/S /GoTo +/D [521 0 R /XYZ 67.0 725.0 null] +>> +endobj +43 0 obj +<< +/S /GoTo +/D [521 0 R /XYZ 67.0 658.866 null] +>> +endobj +45 0 obj +<< +/S /GoTo +/D [521 0 R /XYZ 67.0 234.894 null] +>> +endobj +47 0 obj +<< +/S /GoTo +/D [521 0 R /XYZ 67.0 204.922 null] +>> +endobj +49 0 obj +<< +/S /GoTo +/D [525 0 R /XYZ 67.0 190.386 null] +>> +endobj +51 0 obj +<< +/S /GoTo +/D [529 0 R /XYZ 67.0 134.276 null] +>> +endobj +53 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 725.0 null] +>> +endobj +55 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 625.866 null] +>> +endobj +57 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 574.894 null] +>> +endobj +59 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 477.283 null] +>> +endobj +61 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 379.672 null] +>> +endobj +63 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 240.341 null] +>> +endobj +65 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 101.01 null] +>> +endobj +67 0 obj +<< +/S /GoTo +/D [545 0 R /XYZ 67.0 632.56 null] +>> +endobj +69 0 obj +<< +/S /GoTo +/D [545 0 R /XYZ 67.0 581.588 null] +>> +endobj +71 0 obj +<< +/S /GoTo +/D [545 0 R /XYZ 67.0 482.837 null] +>> +endobj +73 0 obj +<< +/S /GoTo +/D [545 0 R /XYZ 67.0 129.786 null] +>> +endobj +75 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 725.0 null] +>> +endobj +77 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 637.249 null] +>> +endobj +79 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 527.498 null] +>> +endobj +81 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 476.526 null] +>> +endobj +83 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 388.775 null] +>> +endobj +85 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 312.024 null] +>> +endobj +87 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 235.273 null] +>> +endobj +89 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 125.662 null] +>> +endobj +91 0 obj +<< +/S /GoTo +/D [549 0 R /XYZ 67.0 309.28 null] +>> +endobj +93 0 obj +<< +/S /GoTo +/D [549 0 R /XYZ 67.0 125.949 null] +>> +endobj +95 0 obj +<< +/S /GoTo +/D [551 0 R /XYZ 67.0 467.0 null] +>> +endobj +97 0 obj +<< +/S /GoTo +/D [551 0 R /XYZ 67.0 124.848 null] +>> +endobj +99 0 obj +<< +/S /GoTo +/D [555 0 R /XYZ 67.0 627.98 null] +>> +endobj +104 0 obj +<< +/S /GoTo +/D [555 0 R /XYZ 67.0 223.548 null] +>> +endobj +106 0 obj +<< +/S /GoTo +/D [559 0 R /XYZ 67.0 125.14 null] +>> +endobj +108 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 692.0 null] +>> +endobj +110 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 303.028 null] +>> +endobj +112 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 181.056 null] +>> +endobj +114 0 obj +<< +/S /GoTo +/D [566 0 R /XYZ 67.0 590.0 null] +>> +endobj +116 0 obj +<< +/S /GoTo +/D [566 0 R /XYZ 67.0 474.028 null] +>> +endobj +118 0 obj +<< +/S /GoTo +/D [566 0 R /XYZ 67.0 200.056 null] +>> +endobj +120 0 obj +<< +/S /GoTo +/D [566 0 R /XYZ 67.0 73.084 null] +>> +endobj +122 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 725.0 null] +>> +endobj +124 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 636.866 null] +>> +endobj +126 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 585.894 null] +>> +endobj +128 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 404.703 null] +>> +endobj +130 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 222.512 null] +>> +endobj +132 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 171.54 null] +>> +endobj +134 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 113.649 null] +>> +endobj +136 0 obj +<< +/S /GoTo +/D [577 0 R /XYZ 67.0 692.0 null] +>> +endobj +138 0 obj +<< +/S /GoTo +/D [577 0 R /XYZ 67.0 137.448 null] +>> +endobj +140 0 obj +<< +/S /GoTo +/D [579 0 R /XYZ 67.0 609.42 null] +>> +endobj +142 0 obj +<< +/S /GoTo +/D [581 0 R /XYZ 67.0 622.0 null] +>> +endobj +144 0 obj +<< +/S /GoTo +/D [581 0 R /XYZ 67.0 429.949 null] +>> +endobj +146 0 obj +<< +/S /GoTo +/D [581 0 R /XYZ 67.0 84.257 null] +>> +endobj +148 0 obj +<< +/S /GoTo +/D [583 0 R /XYZ 67.0 531.809 null] +>> +endobj +150 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 725.0 null] +>> +endobj +152 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 603.866 null] +>> +endobj +154 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 531.894 null] +>> +endobj +156 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 455.143 null] +>> +endobj +158 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 377.392 null] +>> +endobj +160 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 326.42 null] +>> +endobj +162 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 237.669 null] +>> +endobj +164 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 186.697 null] +>> +endobj +166 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 108.946 null] +>> +endobj +168 0 obj +<< +/S /GoTo +/D [589 0 R /XYZ 67.0 425.98 null] +>> +endobj +170 0 obj +<< +/S /GoTo +/D [591 0 R /XYZ 67.0 584.98 null] +>> +endobj +172 0 obj +<< +/S /GoTo +/D [591 0 R /XYZ 67.0 225.128 null] +>> +endobj +174 0 obj +<< +/S /GoTo +/D [591 0 R /XYZ 67.0 75.156 null] +>> +endobj +176 0 obj +<< +/S /GoTo +/D [595 0 R /XYZ 67.0 624.028 null] +>> +endobj +178 0 obj +<< +/S /GoTo +/D [595 0 R /XYZ 67.0 556.056 null] +>> +endobj +180 0 obj +<< +/S /GoTo +/D [595 0 R /XYZ 67.0 466.084 null] +>> +endobj +182 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 725.0 null] +>> +endobj +184 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 397.866 null] +>> +endobj +186 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 346.894 null] +>> +endobj +188 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 269.143 null] +>> +endobj +190 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 218.171 null] +>> +endobj +195 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 129.42 null] +>> +endobj +197 0 obj +<< +/S /GoTo +/D [603 0 R /XYZ 67.0 409.28 null] +>> +endobj +199 0 obj +<< +/S /GoTo +/D [603 0 R /XYZ 67.0 238.089 null] +>> +endobj +201 0 obj +<< +/S /GoTo +/D [607 0 R /XYZ 67.0 492.12 null] +>> +endobj +203 0 obj +<< +/S /GoTo +/D [607 0 R /XYZ 67.0 91.869 null] +>> +endobj +205 0 obj +<< +/S /GoTo +/D [609 0 R /XYZ 67.0 665.0 null] +>> +endobj +207 0 obj +<< +/S /GoTo +/D [609 0 R /XYZ 67.0 548.028 null] +>> +endobj +209 0 obj +<< +/S /GoTo +/D [609 0 R /XYZ 67.0 207.756 null] +>> +endobj +211 0 obj +<< +/S /GoTo +/D [613 0 R /XYZ 67.0 725.0 null] +>> +endobj +213 0 obj +<< +/S /GoTo +/D [613 0 R /XYZ 67.0 658.866 null] +>> +endobj +215 0 obj +<< +/S /GoTo +/D [613 0 R /XYZ 67.0 165.854 null] +>> +endobj +217 0 obj +<< +/S /GoTo +/D [617 0 R /XYZ 67.0 449.38 null] +>> +endobj +219 0 obj +<< +/S /GoTo +/D [619 0 R /XYZ 67.0 725.0 null] +>> +endobj +221 0 obj +<< +/S /GoTo +/D [619 0 R /XYZ 67.0 571.866 null] +>> +endobj +223 0 obj +<< +/S /GoTo +/D [619 0 R /XYZ 67.0 520.894 null] +>> +endobj +225 0 obj +<< +/S /GoTo +/D [619 0 R /XYZ 67.0 423.423 null] +>> +endobj +227 0 obj +<< +/S /GoTo +/D [623 0 R /XYZ 67.0 426.14 null] +>> +endobj +229 0 obj +<< +/S /GoTo +/D [623 0 R /XYZ 67.0 176.069 null] +>> +endobj +231 0 obj +<< +/S /GoTo +/D [625 0 R /XYZ 67.0 612.14 null] +>> +endobj +233 0 obj +<< +/S /GoTo +/D [625 0 R /XYZ 67.0 549.168 null] +>> +endobj +235 0 obj +<< +/S /GoTo +/D [625 0 R /XYZ 67.0 363.196 null] +>> +endobj +237 0 obj +<< +/S /GoTo +/D [625 0 R /XYZ 67.0 177.224 null] +>> +endobj +239 0 obj +<< +/S /GoTo +/D [627 0 R /XYZ 67.0 633.0 null] +>> +endobj +241 0 obj +<< +/S /GoTo +/D [627 0 R /XYZ 67.0 388.028 null] +>> +endobj +243 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 725.0 null] +>> +endobj +245 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 538.866 null] +>> +endobj +247 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 487.894 null] +>> +endobj +249 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 441.003 null] +>> +endobj +251 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 393.112 null] +>> +endobj +253 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 320.14 null] +>> +endobj +255 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 232.389 null] +>> +endobj +257 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 185.498 null] +>> +endobj +259 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 137.607 null] +>> +endobj +261 0 obj +<< +/S /GoTo +/D [638 0 R /XYZ 67.0 260.98 null] +>> +endobj +263 0 obj +<< +/S /GoTo +/D [638 0 R /XYZ 67.0 68.929 null] +>> +endobj +265 0 obj +<< +/S /GoTo +/D [645 0 R /XYZ 67.0 203.868 null] +>> +endobj +267 0 obj +<< +/S /GoTo +/D [654 0 R /XYZ 67.0 655.14 null] +>> +endobj +269 0 obj +<< +/S /GoTo +/D [654 0 R /XYZ 67.0 581.168 null] +>> +endobj +271 0 obj +<< +/S /GoTo +/D [654 0 R /XYZ 67.0 480.196 null] +>> +endobj +273 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 725.0 null] +>> +endobj +275 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 603.866 null] +>> +endobj +277 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 433.894 null] +>> +endobj +282 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 725.0 null] +>> +endobj +284 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 484.866 null] +>> +endobj +286 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 433.894 null] +>> +endobj +288 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 357.143 null] +>> +endobj +290 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 257.392 null] +>> +endobj +292 0 obj +<< +/S /GoTo +/D [671 0 R /XYZ 67.0 302.0 null] +>> +endobj +294 0 obj +<< +/S /GoTo +/D [673 0 R /XYZ 67.0 535.68 null] +>> +endobj +296 0 obj +<< +/S /GoTo +/D [673 0 R /XYZ 67.0 94.108 null] +>> +endobj +298 0 obj +<< +/S /GoTo +/D [675 0 R /XYZ 67.0 236.18 null] +>> +endobj +300 0 obj +<< +/S /GoTo +/D [675 0 R /XYZ 67.0 173.208 null] +>> +endobj +302 0 obj +<< +/S /GoTo +/D [675 0 R /XYZ 67.0 94.236 null] +>> +endobj +304 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 725.0 null] +>> +endobj +306 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 398.866 null] +>> +endobj +308 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 325.894 null] +>> +endobj +310 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 215.143 null] +>> +endobj +312 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 153.171 null] +>> +endobj +314 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 683.14 null] +>> +endobj +316 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 610.168 null] +>> +endobj +318 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 500.417 null] +>> +endobj +320 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 400.666 null] +>> +endobj +322 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 349.694 null] +>> +endobj +324 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 249.943 null] +>> +endobj +326 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 198.971 null] +>> +endobj +328 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 110.22 null] +>> +endobj +330 0 obj +<< +/S /GoTo +/D [685 0 R /XYZ 67.0 649.0 null] +>> +endobj +332 0 obj +<< +/S /GoTo +/D [687 0 R /XYZ 67.0 638.252 null] +>> +endobj +334 0 obj +<< +/S /GoTo +/D [687 0 R /XYZ 67.0 186.12 null] +>> +endobj +336 0 obj +<< +/S /GoTo +/D [689 0 R /XYZ 67.0 440.66 null] +>> +endobj +338 0 obj +<< +/S /GoTo +/D [689 0 R /XYZ 67.0 377.688 null] +>> +endobj +340 0 obj +<< +/S /GoTo +/D [689 0 R /XYZ 67.0 298.716 null] +>> +endobj +342 0 obj +<< +/S /GoTo +/D [689 0 R /XYZ 67.0 219.744 null] +>> +endobj +344 0 obj +<< +/S /GoTo +/D [689 0 R /XYZ 67.0 140.772 null] +>> +endobj +346 0 obj +<< +/S /GoTo +/D [691 0 R /XYZ 67.0 453.0 null] +>> +endobj +348 0 obj +<< +/S /GoTo +/D [693 0 R /XYZ 67.0 523.0 null] +>> +endobj +350 0 obj +<< +/S /GoTo +/D [700 0 R /XYZ 67.0 725.0 null] +>> +endobj +352 0 obj +<< +/S /GoTo +/D [700 0 R /XYZ 67.0 169.386 null] +>> +endobj +354 0 obj +<< +/S /GoTo +/D [700 0 R /XYZ 67.0 97.414 null] +>> +endobj +356 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 630.14 null] +>> +endobj +358 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 553.389 null] +>> +endobj +360 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 411.638 null] +>> +endobj +362 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 333.887 null] +>> +endobj +364 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 282.915 null] +>> +endobj +366 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 183.164 null] +>> +endobj +368 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 132.192 null] +>> +endobj +370 0 obj +<< +/S /GoTo +/D [704 0 R /XYZ 67.0 598.28 null] +>> +endobj +375 0 obj +<< +/S /GoTo +/D [704 0 R /XYZ 67.0 539.389 null] +>> +endobj +377 0 obj +<< +/S /GoTo +/D [704 0 R /XYZ 67.0 488.417 null] +>> +endobj +379 0 obj +<< +/S /GoTo +/D [704 0 R /XYZ 67.0 377.666 null] +>> +endobj +381 0 obj +<< +/S /GoTo +/D [706 0 R /XYZ 67.0 709.0 null] +>> +endobj +383 0 obj +<< +/S /GoTo +/D [706 0 R /XYZ 67.0 537.809 null] +>> +endobj +385 0 obj +<< +/S /GoTo +/D [706 0 R /XYZ 67.0 287.117 null] +>> +endobj +387 0 obj +<< +/S /GoTo +/D [708 0 R /XYZ 67.0 577.56 null] +>> +endobj +389 0 obj +<< +/S /GoTo +/D [708 0 R /XYZ 67.0 498.588 null] +>> +endobj +391 0 obj +<< +/S /GoTo +/D [708 0 R /XYZ 67.0 343.616 null] +>> +endobj +393 0 obj +<< +/S /GoTo +/D [710 0 R /XYZ 67.0 591.0 null] +>> +endobj +395 0 obj +<< +/S /GoTo +/D [710 0 R /XYZ 67.0 340.929 null] +>> +endobj +397 0 obj +<< +/S /GoTo +/D [710 0 R /XYZ 67.0 126.957 null] +>> +endobj +399 0 obj +<< +/S /GoTo +/D [714 0 R /XYZ 67.0 725.0 null] +>> +endobj +401 0 obj +<< +/S /GoTo +/D [716 0 R /XYZ 67.0 388.252 null] +>> +endobj +403 0 obj +<< +/S /GoTo +/D [716 0 R /XYZ 67.0 315.28 null] +>> +endobj +405 0 obj +<< +/S /GoTo +/D [716 0 R /XYZ 67.0 141.809 null] +>> +endobj +407 0 obj +<< +/S /GoTo +/D [718 0 R /XYZ 67.0 725.0 null] +>> +endobj +409 0 obj +<< +/S /GoTo +/D [718 0 R /XYZ 67.0 588.089 null] +>> +endobj +411 0 obj +<< +/S /GoTo +/D [718 0 R /XYZ 67.0 514.117 null] +>> +endobj +413 0 obj +<< +/S /GoTo +/D [718 0 R /XYZ 67.0 413.145 null] +>> +endobj +415 0 obj +<< +/S /GoTo +/D [718 0 R /XYZ 67.0 286.173 null] +>> +endobj +417 0 obj +<< +/S /GoTo +/D [718 0 R /XYZ 67.0 207.201 null] +>> +endobj +419 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 708.0 null] +>> +endobj +421 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 493.028 null] +>> +endobj +423 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 403.056 null] +>> +endobj +425 0 obj +<< +/S /GoTo +/D [720 0 R /XYZ 67.0 198.084 null] +>> +endobj +427 0 obj +<< +/S /GoTo +/D [724 0 R /XYZ 67.0 725.0 null] +>> +endobj +429 0 obj +<< +/S /GoTo +/D [738 0 R /XYZ 67.0 725.0 null] +>> +endobj +431 0 obj +<< +/S /GoTo +/D [738 0 R /XYZ 67.0 647.866 null] +>> +endobj +433 0 obj +<< +/S /GoTo +/D [738 0 R /XYZ 67.0 518.894 null] +>> +endobj +435 0 obj +<< +/S /GoTo +/D [738 0 R /XYZ 67.0 357.922 null] +>> +endobj +437 0 obj +<< +/S /GoTo +/D [738 0 R /XYZ 67.0 261.95 null] +>> +endobj +439 0 obj +<< +/S /GoTo +/D [740 0 R /XYZ 67.0 725.0 null] +>> +endobj +441 0 obj +<< +/S /GoTo +/D [742 0 R /XYZ 67.0 725.0 null] +>> +endobj +443 0 obj +<< +/S /GoTo +/D [747 0 R /XYZ 67.0 725.0 null] +>> +endobj +445 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 725.0 null] +>> +endobj +447 0 obj +<< +/S /GoTo +/D [763 0 R /XYZ 67.0 725.0 null] +>> +endobj +449 0 obj +<< +/S /GoTo +/D [763 0 R /XYZ 67.0 539.866 null] +>> +endobj +451 0 obj +<< +/S /GoTo +/D [763 0 R /XYZ 67.0 407.894 null] +>> +endobj +456 0 obj +<< +/S /GoTo +/D [763 0 R /XYZ 67.0 190.922 null] +>> +endobj +458 0 obj +<< +/S /GoTo +/D [773 0 R /XYZ 67.0 708.0 null] +>> +endobj +460 0 obj +<< +/S /GoTo +/D [773 0 R /XYZ 67.0 555.028 null] +>> +endobj +462 0 obj +<< +/S /GoTo +/D [773 0 R /XYZ 67.0 354.056 null] +>> +endobj +464 0 obj +<< +/S /GoTo +/D [773 0 R /XYZ 67.0 73.084 null] +>> +endobj +466 0 obj +<< +/S /GoTo +/D [775 0 R /XYZ 67.0 572.028 null] +>> +endobj +468 0 obj +<< +/S /GoTo +/D [775 0 R /XYZ 67.0 339.056 null] +>> +endobj +470 0 obj +<< +/S /GoTo +/D [775 0 R /XYZ 67.0 186.084 null] +>> +endobj +472 0 obj +<< +/S /GoTo +/D [777 0 R /XYZ 67.0 692.0 null] +>> +endobj +474 0 obj +<< +/S /GoTo +/D [777 0 R /XYZ 67.0 555.028 null] +>> +endobj +476 0 obj +<< +/S /GoTo +/D [777 0 R /XYZ 67.0 402.056 null] +>> +endobj +478 0 obj +<< +/S /GoTo +/D [777 0 R /XYZ 67.0 233.084 null] +>> +endobj +480 0 obj +<< +/S /GoTo +/D [777 0 R /XYZ 67.0 112.112 null] +>> +endobj +482 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 623.0 null] +>> +endobj +484 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 502.028 null] +>> +endobj +486 0 obj +<< +/S /GoTo +/D [779 0 R /XYZ 67.0 365.056 null] +>> +endobj +488 0 obj +<< +/S /GoTo +/D [781 0 R /XYZ 67.0 725.0 null] +>> +endobj +490 0 obj +<< +/S /GoTo +/D [791 0 R /XYZ 67.0 725.0 null] +>> +endobj +492 0 obj +<< +/S /GoTo +/D [793 0 R /XYZ 67.0 725.0 null] +>> +endobj +499 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 573.866 null] +>> +endobj +501 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 546.866 null] +>> +endobj +504 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 659.866 null] +>> +endobj +727 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 632.866 null] +>> +endobj +729 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 616.866 null] +>> +endobj +731 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 519.866 null] +>> +endobj +734 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 503.866 null] +>> +endobj +736 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 702.866 null] +>> +endobj +745 0 obj +<< +/S /GoTo +/D [749 0 R /XYZ 67.0 675.866 null] +>> +endobj +810 0 obj +<< + /First 812 0 R + /Last 1123 0 R +>> endobj +811 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 454.084 null] +>> +endobj +813 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 363.95 null] +>> +endobj +815 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 306.816 null] +>> +endobj +817 0 obj +<< +/S /GoTo +/D [8 0 R /XYZ 67.0 725.0 null] +>> +endobj +825 0 obj +<< +/S /GoTo +/D [513 0 R /XYZ 67.0 68.287 null] +>> +endobj +827 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 645.109 null] +>> +endobj +832 0 obj +<< +/S /GoTo +/D [517 0 R /XYZ 67.0 385.464 null] +>> +endobj +835 0 obj +<< +/S /GoTo +/D [519 0 R /XYZ 67.0 636.56 null] +>> +endobj +840 0 obj +<< +/S /GoTo +/D [521 0 R /XYZ 67.0 234.894 null] +>> +endobj +842 0 obj +<< +/S /GoTo +/D [521 0 R /XYZ 67.0 204.922 null] +>> +endobj +848 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 574.894 null] +>> +endobj +850 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 477.283 null] +>> +endobj +852 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 379.672 null] +>> +endobj +854 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 240.341 null] +>> +endobj +856 0 obj +<< +/S /GoTo +/D [541 0 R /XYZ 67.0 101.01 null] +>> +endobj +859 0 obj +<< +/S /GoTo +/D [545 0 R /XYZ 67.0 581.588 null] +>> +endobj +861 0 obj +<< +/S /GoTo +/D [545 0 R /XYZ 67.0 482.837 null] +>> +endobj +864 0 obj +<< +/S /GoTo +/D [545 0 R /XYZ 67.0 78.814 null] +>> +endobj +866 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 637.249 null] +>> +endobj +870 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 388.775 null] +>> +endobj +872 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 312.024 null] +>> +endobj +874 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 235.273 null] +>> +endobj +876 0 obj +<< +/S /GoTo +/D [547 0 R /XYZ 67.0 125.662 null] +>> +endobj +879 0 obj +<< +/S /GoTo +/D [549 0 R /XYZ 67.0 125.949 null] +>> +endobj +881 0 obj +<< +/S /GoTo +/D [551 0 R /XYZ 67.0 467.0 null] +>> +endobj +884 0 obj +<< +/S /GoTo +/D [555 0 R /XYZ 67.0 627.98 null] +>> +endobj +888 0 obj +<< +/S /GoTo +/D [561 0 R /XYZ 67.0 692.0 null] +>> +endobj +892 0 obj +<< +/S /GoTo +/D [566 0 R /XYZ 67.0 590.0 null] +>> +endobj +897 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 725.0 null] +>> +endobj +900 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 585.894 null] +>> +endobj +902 0 obj +<< +/S /GoTo +/D [575 0 R /XYZ 67.0 404.703 null] +>> +endobj +907 0 obj +<< +/S /GoTo +/D [577 0 R /XYZ 67.0 692.0 null] +>> +endobj +910 0 obj +<< +/S /GoTo +/D [579 0 R /XYZ 67.0 609.42 null] +>> +endobj +913 0 obj +<< +/S /GoTo +/D [581 0 R /XYZ 67.0 429.949 null] +>> +endobj +917 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 725.0 null] +>> +endobj +920 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 531.894 null] +>> +endobj +922 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 455.143 null] +>> +endobj +925 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 326.42 null] +>> +endobj +929 0 obj +<< +/S /GoTo +/D [585 0 R /XYZ 67.0 108.946 null] +>> +endobj +938 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 725.0 null] +>> +endobj +941 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 346.894 null] +>> +endobj +944 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 218.171 null] +>> +endobj +946 0 obj +<< +/S /GoTo +/D [597 0 R /XYZ 67.0 129.42 null] +>> +endobj +949 0 obj +<< +/S /GoTo +/D [603 0 R /XYZ 67.0 238.089 null] +>> +endobj +957 0 obj +<< +/S /GoTo +/D [613 0 R /XYZ 67.0 658.866 null] +>> +endobj +961 0 obj +<< +/S /GoTo +/D [619 0 R /XYZ 67.0 725.0 null] +>> +endobj +964 0 obj +<< +/S /GoTo +/D [619 0 R /XYZ 67.0 520.894 null] +>> +endobj +966 0 obj +<< +/S /GoTo +/D [619 0 R /XYZ 67.0 423.423 null] +>> +endobj +969 0 obj +<< +/S /GoTo +/D [623 0 R /XYZ 67.0 176.069 null] +>> +endobj +982 0 obj +<< +/S /GoTo +/D [631 0 R /XYZ 67.0 320.14 null] +>> +endobj +993 0 obj +<< +/S /GoTo +/D [656 0 R /XYZ 67.0 725.0 null] +>> +endobj +997 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 725.0 null] +>> +endobj +1000 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 433.894 null] +>> +endobj +1002 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 357.143 null] +>> +endobj +1004 0 obj +<< +/S /GoTo +/D [667 0 R /XYZ 67.0 257.392 null] +>> +endobj +1012 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 725.0 null] +>> +endobj +1015 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 325.894 null] +>> +endobj +1018 0 obj +<< +/S /GoTo +/D [679 0 R /XYZ 67.0 153.171 null] +>> +endobj +1021 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 610.168 null] +>> +endobj +1025 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 349.694 null] +>> +endobj +1028 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 198.971 null] +>> +endobj +1030 0 obj +<< +/S /GoTo +/D [681 0 R /XYZ 67.0 110.22 null] +>> +endobj +1040 0 obj +<< +/S /GoTo +/D [691 0 R /XYZ 67.0 453.0 null] +>> +endobj +1043 0 obj +<< +/S /GoTo +/D [700 0 R /XYZ 67.0 725.0 null] +>> +endobj +1046 0 obj +<< +/S /GoTo +/D [700 0 R /XYZ 67.0 97.414 null] +>> +endobj +1048 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 630.14 null] +>> +endobj +1050 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 553.389 null] +>> +endobj +1052 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 411.638 null] +>> +endobj +1055 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 282.915 null] +>> +endobj +1058 0 obj +<< +/S /GoTo +/D [702 0 R /XYZ 67.0 132.192 null] +>> +endobj +1062 0 obj +<< +/S /GoTo +/D [704 0 R /XYZ 67.0 488.417 null] +>> +endobj +1064 0 obj +<< +/S /GoTo +/D [704 0 R /XYZ 67.0 377.666 null] +>> +endobj +1075 0 obj +<< +/S /GoTo +/D [714 0 R /XYZ 67.0 725.0 null] +>> +endobj +1078 0 obj +<< +/S /GoTo +/D [716 0 R /XYZ 67.0 315.28 null] +>> +endobj +1081 0 obj +<< +/S /GoTo +/D [716 0 R /XYZ 67.0 79.837 null] +>> +endobj +xref +0 1128 +0000000000 65535 f +0000326507 00000 n +0000327357 00000 n +0000377446 00000 n +0000000015 00000 n +0000000071 00000 n +0000001596 00000 n +0000001702 00000 n +0000004211 00000 n +0000004331 00000 n +0000004665 00000 n +0000377566 00000 n +0000004800 00000 n +0000377631 00000 n +0000004935 00000 n +0000377698 00000 n +0000005070 00000 n +0000377765 00000 n +0000005205 00000 n +0000377830 00000 n +0000005340 00000 n +0000377896 00000 n +0000005475 00000 n +0000377963 00000 n +0000005610 00000 n +0000378028 00000 n +0000005745 00000 n +0000378095 00000 n +0000005880 00000 n +0000378162 00000 n +0000006015 00000 n +0000378229 00000 n +0000006150 00000 n +0000378296 00000 n +0000006285 00000 n +0000378363 00000 n +0000006420 00000 n +0000378430 00000 n +0000006555 00000 n +0000378496 00000 n +0000006690 00000 n +0000378563 00000 n +0000006825 00000 n +0000378628 00000 n +0000006960 00000 n +0000378695 00000 n +0000007095 00000 n +0000378762 00000 n +0000007230 00000 n +0000378829 00000 n +0000007364 00000 n +0000378896 00000 n +0000007499 00000 n +0000378963 00000 n +0000007634 00000 n +0000379028 00000 n +0000007769 00000 n +0000379095 00000 n +0000007905 00000 n +0000379162 00000 n +0000008041 00000 n +0000379229 00000 n +0000008176 00000 n +0000379296 00000 n +0000008311 00000 n +0000379363 00000 n +0000008445 00000 n +0000379429 00000 n +0000008580 00000 n +0000379495 00000 n +0000008715 00000 n +0000379562 00000 n +0000008851 00000 n +0000379629 00000 n +0000008986 00000 n +0000379696 00000 n +0000009121 00000 n +0000379761 00000 n +0000009257 00000 n +0000379828 00000 n +0000009392 00000 n +0000379895 00000 n +0000009527 00000 n +0000379962 00000 n +0000009661 00000 n +0000380029 00000 n +0000009796 00000 n +0000380096 00000 n +0000009931 00000 n +0000380163 00000 n +0000010066 00000 n +0000380230 00000 n +0000010201 00000 n +0000380296 00000 n +0000010336 00000 n +0000380363 00000 n +0000010471 00000 n +0000380428 00000 n +0000010604 00000 n +0000380495 00000 n +0000010737 00000 n +0000012998 00000 n +0000013124 00000 n +0000013497 00000 n +0000380561 00000 n +0000013630 00000 n +0000380629 00000 n +0000013763 00000 n +0000380696 00000 n +0000013896 00000 n +0000380762 00000 n +0000014029 00000 n +0000380830 00000 n +0000014162 00000 n +0000380898 00000 n +0000014295 00000 n +0000380964 00000 n +0000014428 00000 n +0000381032 00000 n +0000014561 00000 n +0000381100 00000 n +0000014694 00000 n +0000381167 00000 n +0000014827 00000 n +0000381233 00000 n +0000014962 00000 n +0000381301 00000 n +0000015098 00000 n +0000381369 00000 n +0000015234 00000 n +0000381437 00000 n +0000015369 00000 n +0000381505 00000 n +0000015505 00000 n +0000381572 00000 n +0000015641 00000 n +0000381640 00000 n +0000015776 00000 n +0000381706 00000 n +0000015911 00000 n +0000381774 00000 n +0000016046 00000 n +0000381841 00000 n +0000016181 00000 n +0000381907 00000 n +0000016316 00000 n +0000381975 00000 n +0000016451 00000 n +0000382042 00000 n +0000016586 00000 n +0000382110 00000 n +0000016720 00000 n +0000382176 00000 n +0000016855 00000 n +0000382244 00000 n +0000016990 00000 n +0000382312 00000 n +0000017125 00000 n +0000382380 00000 n +0000017260 00000 n +0000382448 00000 n +0000017395 00000 n +0000382515 00000 n +0000017530 00000 n +0000382583 00000 n +0000017665 00000 n +0000382651 00000 n +0000017800 00000 n +0000382719 00000 n +0000017935 00000 n +0000382786 00000 n +0000018070 00000 n +0000382853 00000 n +0000018205 00000 n +0000382921 00000 n +0000018340 00000 n +0000382988 00000 n +0000018475 00000 n +0000383056 00000 n +0000018610 00000 n +0000383124 00000 n +0000018745 00000 n +0000383192 00000 n +0000018880 00000 n +0000383258 00000 n +0000019015 00000 n +0000383326 00000 n +0000019150 00000 n +0000383394 00000 n +0000019283 00000 n +0000383462 00000 n +0000019416 00000 n +0000021622 00000 n +0000021748 00000 n +0000022105 00000 n +0000383530 00000 n +0000022238 00000 n +0000383597 00000 n +0000022371 00000 n +0000383664 00000 n +0000022504 00000 n +0000383732 00000 n +0000022636 00000 n +0000383799 00000 n +0000022769 00000 n +0000383866 00000 n +0000022902 00000 n +0000383932 00000 n +0000023035 00000 n +0000384000 00000 n +0000023168 00000 n +0000384068 00000 n +0000023301 00000 n +0000384134 00000 n +0000023436 00000 n +0000384202 00000 n +0000023571 00000 n +0000384270 00000 n +0000023706 00000 n +0000384337 00000 n +0000023841 00000 n +0000384403 00000 n +0000023976 00000 n +0000384471 00000 n +0000024111 00000 n +0000384539 00000 n +0000024246 00000 n +0000384607 00000 n +0000024380 00000 n +0000384674 00000 n +0000024515 00000 n +0000384742 00000 n +0000024650 00000 n +0000384809 00000 n +0000024785 00000 n +0000384877 00000 n +0000024920 00000 n +0000384945 00000 n +0000025055 00000 n +0000385013 00000 n +0000025190 00000 n +0000385079 00000 n +0000025325 00000 n +0000385147 00000 n +0000025460 00000 n +0000385213 00000 n +0000025595 00000 n +0000385281 00000 n +0000025731 00000 n +0000385349 00000 n +0000025867 00000 n +0000385417 00000 n +0000026002 00000 n +0000385485 00000 n +0000026137 00000 n +0000385552 00000 n +0000026273 00000 n +0000385620 00000 n +0000026409 00000 n +0000385688 00000 n +0000026544 00000 n +0000385756 00000 n +0000026679 00000 n +0000385823 00000 n +0000026814 00000 n +0000385890 00000 n +0000026949 00000 n +0000385958 00000 n +0000027084 00000 n +0000386025 00000 n +0000027219 00000 n +0000386093 00000 n +0000027354 00000 n +0000386161 00000 n +0000027489 00000 n +0000386227 00000 n +0000027624 00000 n +0000386295 00000 n +0000027758 00000 n +0000030079 00000 n +0000030205 00000 n +0000030586 00000 n +0000386363 00000 n +0000030719 00000 n +0000386429 00000 n +0000030854 00000 n +0000386497 00000 n +0000030990 00000 n +0000386565 00000 n +0000031126 00000 n +0000386633 00000 n +0000031261 00000 n +0000386701 00000 n +0000031396 00000 n +0000386767 00000 n +0000031531 00000 n +0000386834 00000 n +0000031666 00000 n +0000386901 00000 n +0000031801 00000 n +0000386968 00000 n +0000031936 00000 n +0000387036 00000 n +0000032071 00000 n +0000387103 00000 n +0000032206 00000 n +0000387169 00000 n +0000032341 00000 n +0000387237 00000 n +0000032476 00000 n +0000387305 00000 n +0000032611 00000 n +0000387373 00000 n +0000032747 00000 n +0000387441 00000 n +0000032882 00000 n +0000387508 00000 n +0000033017 00000 n +0000387576 00000 n +0000033152 00000 n +0000387644 00000 n +0000033287 00000 n +0000387712 00000 n +0000033422 00000 n +0000387780 00000 n +0000033557 00000 n +0000387848 00000 n +0000033691 00000 n +0000387916 00000 n +0000033826 00000 n +0000387983 00000 n +0000033961 00000 n +0000388049 00000 n +0000034096 00000 n +0000388117 00000 n +0000034231 00000 n +0000388184 00000 n +0000034366 00000 n +0000388251 00000 n +0000034500 00000 n +0000388319 00000 n +0000034635 00000 n +0000388387 00000 n +0000034770 00000 n +0000388455 00000 n +0000034905 00000 n +0000388523 00000 n +0000035040 00000 n +0000388589 00000 n +0000035175 00000 n +0000388655 00000 n +0000035309 00000 n +0000388721 00000 n +0000035444 00000 n +0000388789 00000 n +0000035579 00000 n +0000388856 00000 n +0000035714 00000 n +0000388923 00000 n +0000035850 00000 n +0000388991 00000 n +0000035985 00000 n +0000389059 00000 n +0000036120 00000 n +0000389127 00000 n +0000036256 00000 n +0000389195 00000 n +0000036391 00000 n +0000389263 00000 n +0000036526 00000 n +0000389331 00000 n +0000036660 00000 n +0000038949 00000 n +0000039075 00000 n +0000039408 00000 n +0000389398 00000 n +0000039541 00000 n +0000389466 00000 n +0000039675 00000 n +0000389534 00000 n +0000039808 00000 n +0000389602 00000 n +0000039940 00000 n +0000389668 00000 n +0000040073 00000 n +0000389736 00000 n +0000040206 00000 n +0000389804 00000 n +0000040339 00000 n +0000389871 00000 n +0000040472 00000 n +0000389939 00000 n +0000040605 00000 n +0000390007 00000 n +0000040738 00000 n +0000390073 00000 n +0000040871 00000 n +0000390141 00000 n +0000041004 00000 n +0000390209 00000 n +0000041137 00000 n +0000390275 00000 n +0000041272 00000 n +0000390343 00000 n +0000041407 00000 n +0000390410 00000 n +0000041542 00000 n +0000390478 00000 n +0000041677 00000 n +0000390544 00000 n +0000041812 00000 n +0000390612 00000 n +0000041947 00000 n +0000390680 00000 n +0000042081 00000 n +0000390748 00000 n +0000042216 00000 n +0000390816 00000 n +0000042351 00000 n +0000390884 00000 n +0000042486 00000 n +0000390950 00000 n +0000042621 00000 n +0000391018 00000 n +0000042756 00000 n +0000391086 00000 n +0000042891 00000 n +0000391154 00000 n +0000043026 00000 n +0000391220 00000 n +0000043160 00000 n +0000391286 00000 n +0000043295 00000 n +0000391354 00000 n +0000043430 00000 n +0000391422 00000 n +0000043565 00000 n +0000391490 00000 n +0000043700 00000 n +0000391557 00000 n +0000043834 00000 n +0000391623 00000 n +0000043969 00000 n +0000391689 00000 n +0000044104 00000 n +0000391755 00000 n +0000044239 00000 n +0000391821 00000 n +0000044374 00000 n +0000391887 00000 n +0000044508 00000 n +0000391955 00000 n +0000044641 00000 n +0000045955 00000 n +0000046081 00000 n +0000046254 00000 n +0000392023 00000 n +0000046387 00000 n +0000392091 00000 n +0000046520 00000 n +0000392157 00000 n +0000046652 00000 n +0000392225 00000 n +0000046786 00000 n +0000392293 00000 n +0000046919 00000 n +0000392360 00000 n +0000047052 00000 n +0000392428 00000 n +0000047185 00000 n +0000392496 00000 n +0000047318 00000 n +0000392564 00000 n +0000047451 00000 n +0000392630 00000 n +0000047584 00000 n +0000392698 00000 n +0000047717 00000 n +0000392766 00000 n +0000047850 00000 n +0000392834 00000 n +0000047982 00000 n +0000392902 00000 n +0000048115 00000 n +0000392968 00000 n +0000048248 00000 n +0000393036 00000 n +0000048381 00000 n +0000393104 00000 n +0000048514 00000 n +0000393170 00000 n +0000048649 00000 n +0000393236 00000 n +0000048783 00000 n +0000051568 00000 n +0000051694 00000 n +0000051779 00000 n +0000051918 00000 n +0000052054 00000 n +0000393302 00000 n +0000052193 00000 n +0000393370 00000 n +0000052332 00000 n +0000052470 00000 n +0000393438 00000 n +0000052606 00000 n +0000052744 00000 n +0000052879 00000 n +0000055334 00000 n +0000055460 00000 n +0000055497 00000 n +0000055631 00000 n +0000055769 00000 n +0000058048 00000 n +0000058174 00000 n +0000058203 00000 n +0000058338 00000 n +0000060942 00000 n +0000061052 00000 n +0000063274 00000 n +0000063384 00000 n +0000066014 00000 n +0000066140 00000 n +0000066169 00000 n +0000066306 00000 n +0000069193 00000 n +0000069319 00000 n +0000069348 00000 n +0000069486 00000 n +0000072038 00000 n +0000072164 00000 n +0000072209 00000 n +0000072344 00000 n +0000072479 00000 n +0000072614 00000 n +0000073438 00000 n +0000073564 00000 n +0000073609 00000 n +0000073743 00000 n +0000073877 00000 n +0000074012 00000 n +0000075932 00000 n +0000076058 00000 n +0000076087 00000 n +0000076225 00000 n +0000078000 00000 n +0000078110 00000 n +0000079711 00000 n +0000079821 00000 n +0000082257 00000 n +0000082367 00000 n +0000084119 00000 n +0000084229 00000 n +0000085813 00000 n +0000085923 00000 n +0000088327 00000 n +0000088453 00000 n +0000088482 00000 n +0000088619 00000 n +0000090431 00000 n +0000090541 00000 n +0000092700 00000 n +0000092826 00000 n +0000092863 00000 n +0000093002 00000 n +0000093137 00000 n +0000095183 00000 n +0000095309 00000 n +0000095362 00000 n +0000095497 00000 n +0000095630 00000 n +0000095767 00000 n +0000095904 00000 n +0000097273 00000 n +0000097383 00000 n +0000099149 00000 n +0000099259 00000 n +0000101073 00000 n +0000101183 00000 n +0000103307 00000 n +0000103417 00000 n +0000105307 00000 n +0000105417 00000 n +0000106598 00000 n +0000106708 00000 n +0000108252 00000 n +0000108378 00000 n +0000108407 00000 n +0000108542 00000 n +0000110476 00000 n +0000110586 00000 n +0000112848 00000 n +0000112974 00000 n +0000113003 00000 n +0000113142 00000 n +0000114272 00000 n +0000114382 00000 n +0000116888 00000 n +0000117014 00000 n +0000117059 00000 n +0000117196 00000 n +0000117335 00000 n +0000117474 00000 n +0000119371 00000 n +0000119497 00000 n +0000119526 00000 n +0000119661 00000 n +0000121692 00000 n +0000121802 00000 n +0000123982 00000 n +0000124092 00000 n +0000124945 00000 n +0000125055 00000 n +0000127132 00000 n +0000127258 00000 n +0000127287 00000 n +0000127426 00000 n +0000128798 00000 n +0000128908 00000 n +0000131209 00000 n +0000131335 00000 n +0000131364 00000 n +0000131501 00000 n +0000133482 00000 n +0000133592 00000 n +0000135171 00000 n +0000135281 00000 n +0000137243 00000 n +0000137353 00000 n +0000138059 00000 n +0000138169 00000 n +0000140158 00000 n +0000140284 00000 n +0000140337 00000 n +0000140474 00000 n +0000140611 00000 n +0000140748 00000 n +0000140885 00000 n +0000142940 00000 n +0000143066 00000 n +0000143119 00000 n +0000143256 00000 n +0000143393 00000 n +0000143530 00000 n +0000143665 00000 n +0000145934 00000 n +0000146060 00000 n +0000146129 00000 n +0000146268 00000 n +0000146407 00000 n +0000146546 00000 n +0000146685 00000 n +0000146824 00000 n +0000146963 00000 n +0000148384 00000 n +0000148494 00000 n +0000151084 00000 n +0000151210 00000 n +0000151247 00000 n +0000151386 00000 n +0000151525 00000 n +0000152622 00000 n +0000152748 00000 n +0000152793 00000 n +0000152928 00000 n +0000153063 00000 n +0000153198 00000 n +0000155787 00000 n +0000155897 00000 n +0000157933 00000 n +0000158043 00000 n +0000160398 00000 n +0000160508 00000 n +0000162340 00000 n +0000162450 00000 n +0000164310 00000 n +0000164420 00000 n +0000164946 00000 n +0000165056 00000 n +0000167398 00000 n +0000167508 00000 n +0000169078 00000 n +0000169188 00000 n +0000171294 00000 n +0000171404 00000 n +0000173633 00000 n +0000173743 00000 n +0000175718 00000 n +0000175828 00000 n +0000177703 00000 n +0000177813 00000 n +0000179755 00000 n +0000179865 00000 n +0000181567 00000 n +0000181693 00000 n +0000181746 00000 n +0000181885 00000 n +0000182024 00000 n +0000182163 00000 n +0000182302 00000 n +0000184916 00000 n +0000185026 00000 n +0000186767 00000 n +0000186877 00000 n +0000188943 00000 n +0000189053 00000 n +0000190919 00000 n +0000191029 00000 n +0000192963 00000 n +0000193073 00000 n +0000195223 00000 n +0000195333 00000 n +0000196141 00000 n +0000196251 00000 n +0000198842 00000 n +0000198952 00000 n +0000201334 00000 n +0000201444 00000 n +0000203113 00000 n +0000203223 00000 n +0000205159 00000 n +0000205269 00000 n +0000206227 00000 n +0000206337 00000 n +0000208273 00000 n +0000208399 00000 n +0000208468 00000 n +0000393506 00000 n +0000208605 00000 n +0000393574 00000 n +0000208744 00000 n +0000393642 00000 n +0000208883 00000 n +0000209019 00000 n +0000393710 00000 n +0000209158 00000 n +0000393778 00000 n +0000209296 00000 n +0000212108 00000 n +0000212218 00000 n +0000212761 00000 n +0000212871 00000 n +0000214267 00000 n +0000214393 00000 n +0000214422 00000 n +0000393846 00000 n +0000214561 00000 n +0000215585 00000 n +0000215695 00000 n +0000218896 00000 n +0000219022 00000 n +0000219131 00000 n +0000219322 00000 n +0000219513 00000 n +0000219704 00000 n +0000219895 00000 n +0000220086 00000 n +0000220276 00000 n +0000220467 00000 n +0000220657 00000 n +0000220847 00000 n +0000221038 00000 n +0000221229 00000 n +0000222970 00000 n +0000223096 00000 n +0000223173 00000 n +0000223311 00000 n +0000223449 00000 n +0000223588 00000 n +0000223727 00000 n +0000223865 00000 n +0000224003 00000 n +0000224141 00000 n +0000225174 00000 n +0000225284 00000 n +0000226332 00000 n +0000226442 00000 n +0000227440 00000 n +0000227550 00000 n +0000228350 00000 n +0000228460 00000 n +0000230737 00000 n +0000230863 00000 n +0000230924 00000 n +0000231111 00000 n +0000231289 00000 n +0000231471 00000 n +0000231651 00000 n +0000231827 00000 n +0000232251 00000 n +0000232361 00000 n +0000233733 00000 n +0000233843 00000 n +0000238078 00000 n +0000238204 00000 n +0000238225 00000 n +0000242612 00000 n +0000242738 00000 n +0000242759 00000 n +0000246950 00000 n +0000247076 00000 n +0000247097 00000 n +0000250784 00000 n +0000250910 00000 n +0000250931 00000 n +0000254978 00000 n +0000255104 00000 n +0000255125 00000 n +0000256414 00000 n +0000256540 00000 n +0000393914 00000 n +0000393969 00000 n +0000256561 00000 n +0000394035 00000 n +0000256758 00000 n +0000394100 00000 n +0000256954 00000 n +0000394166 00000 n +0000257103 00000 n +0000257304 00000 n +0000257529 00000 n +0000257764 00000 n +0000258015 00000 n +0000258165 00000 n +0000258416 00000 n +0000394230 00000 n +0000258661 00000 n +0000394297 00000 n +0000258934 00000 n +0000259201 00000 n +0000259461 00000 n +0000259711 00000 n +0000394365 00000 n +0000260165 00000 n +0000260559 00000 n +0000394433 00000 n +0000261026 00000 n +0000261429 00000 n +0000261734 00000 n +0000262034 00000 n +0000394500 00000 n +0000262287 00000 n +0000394568 00000 n +0000262589 00000 n +0000262929 00000 n +0000263289 00000 n +0000263458 00000 n +0000263747 00000 n +0000394636 00000 n +0000264072 00000 n +0000394704 00000 n +0000264253 00000 n +0000394772 00000 n +0000264520 00000 n +0000394840 00000 n +0000264861 00000 n +0000394908 00000 n +0000265243 00000 n +0000265569 00000 n +0000394975 00000 n +0000265956 00000 n +0000395043 00000 n +0000266223 00000 n +0000266433 00000 n +0000395111 00000 n +0000266778 00000 n +0000395178 00000 n +0000267051 00000 n +0000267279 00000 n +0000267548 00000 n +0000395246 00000 n +0000267844 00000 n +0000395314 00000 n +0000268138 00000 n +0000395382 00000 n +0000268426 00000 n +0000395450 00000 n +0000268705 00000 n +0000268998 00000 n +0000395518 00000 n +0000269244 00000 n +0000395586 00000 n +0000269442 00000 n +0000269740 00000 n +0000395652 00000 n +0000270032 00000 n +0000270348 00000 n +0000270618 00000 n +0000395719 00000 n +0000270905 00000 n +0000271173 00000 n +0000271471 00000 n +0000395785 00000 n +0000271775 00000 n +0000272061 00000 n +0000272335 00000 n +0000272609 00000 n +0000395851 00000 n +0000272880 00000 n +0000273181 00000 n +0000395917 00000 n +0000273501 00000 n +0000395985 00000 n +0000273717 00000 n +0000273927 00000 n +0000274273 00000 n +0000274489 00000 n +0000396053 00000 n +0000274699 00000 n +0000275201 00000 n +0000396119 00000 n +0000275600 00000 n +0000276096 00000 n +0000396186 00000 n +0000276296 00000 n +0000276560 00000 n +0000276778 00000 n +0000396254 00000 n +0000277050 00000 n +0000277340 00000 n +0000396320 00000 n +0000277642 00000 n +0000396388 00000 n +0000277915 00000 n +0000278188 00000 n +0000396456 00000 n +0000278641 00000 n +0000278917 00000 n +0000279252 00000 n +0000396523 00000 n +0000279528 00000 n +0000279855 00000 n +0000280177 00000 n +0000280464 00000 n +0000280745 00000 n +0000281014 00000 n +0000281283 00000 n +0000281617 00000 n +0000396591 00000 n +0000281894 00000 n +0000282149 00000 n +0000396657 00000 n +0000282416 00000 n +0000282733 00000 n +0000396725 00000 n +0000283074 00000 n +0000396793 00000 n +0000283321 00000 n +0000283591 00000 n +0000396860 00000 n +0000283815 00000 n +0000284144 00000 n +0000284344 00000 n +0000284625 00000 n +0000284894 00000 n +0000285255 00000 n +0000285711 00000 n +0000396928 00000 n +0000285947 00000 n +0000286172 00000 n +0000286366 00000 n +0000396996 00000 n +0000286638 00000 n +0000286869 00000 n +0000397062 00000 n +0000287189 00000 n +0000397130 00000 n +0000287464 00000 n +0000287698 00000 n +0000397198 00000 n +0000287944 00000 n +0000288136 00000 n +0000288423 00000 n +0000288686 00000 n +0000288979 00000 n +0000289248 00000 n +0000289541 00000 n +0000289807 00000 n +0000290103 00000 n +0000290423 00000 n +0000290639 00000 n +0000290849 00000 n +0000397266 00000 n +0000291172 00000 n +0000291445 00000 n +0000291676 00000 n +0000291886 00000 n +0000292270 00000 n +0000292551 00000 n +0000292982 00000 n +0000293310 00000 n +0000293597 00000 n +0000293866 00000 n +0000397333 00000 n +0000294120 00000 n +0000294444 00000 n +0000294721 00000 n +0000397399 00000 n +0000294980 00000 n +0000295218 00000 n +0000397465 00000 n +0000295622 00000 n +0000397534 00000 n +0000295822 00000 n +0000397603 00000 n +0000296051 00000 n +0000296295 00000 n +0000296490 00000 n +0000296804 00000 n +0000297110 00000 n +0000297405 00000 n +0000297694 00000 n +0000397672 00000 n +0000297973 00000 n +0000298232 00000 n +0000397739 00000 n +0000298645 00000 n +0000299024 00000 n +0000397808 00000 n +0000299411 00000 n +0000299632 00000 n +0000397877 00000 n +0000299919 00000 n +0000300249 00000 n +0000300554 00000 n +0000397946 00000 n +0000300906 00000 n +0000301291 00000 n +0000398015 00000 n +0000301649 00000 n +0000398084 00000 n +0000302045 00000 n +0000302356 00000 n +0000302616 00000 n +0000302949 00000 n +0000303273 00000 n +0000303569 00000 n +0000303853 00000 n +0000304136 00000 n +0000304443 00000 n +0000398152 00000 n +0000304744 00000 n +0000305040 00000 n +0000398219 00000 n +0000305313 00000 n +0000305573 00000 n +0000398286 00000 n +0000305844 00000 n +0000398354 00000 n +0000306173 00000 n +0000398422 00000 n +0000306524 00000 n +0000398491 00000 n +0000306777 00000 n +0000307112 00000 n +0000398560 00000 n +0000307458 00000 n +0000307661 00000 n +0000398629 00000 n +0000308083 00000 n +0000308291 00000 n +0000308509 00000 n +0000398698 00000 n +0000308867 00000 n +0000398767 00000 n +0000309117 00000 n +0000309393 00000 n +0000309618 00000 n +0000309948 00000 n +0000310244 00000 n +0000310534 00000 n +0000310812 00000 n +0000311163 00000 n +0000311480 00000 n +0000311781 00000 n +0000398836 00000 n +0000312054 00000 n +0000312438 00000 n +0000398903 00000 n +0000312833 00000 n +0000313099 00000 n +0000398971 00000 n +0000313445 00000 n +0000313823 00000 n +0000314119 00000 n +0000314409 00000 n +0000314693 00000 n +0000314971 00000 n +0000315249 00000 n +0000315592 00000 n +0000315894 00000 n +0000316195 00000 n +0000316532 00000 n +0000316860 00000 n +0000317160 00000 n +0000317422 00000 n +0000317746 00000 n +0000318030 00000 n +0000318244 00000 n +0000318476 00000 n +0000318720 00000 n +0000318935 00000 n +0000319114 00000 n +0000319411 00000 n +0000319900 00000 n +0000320174 00000 n +0000320460 00000 n +0000320705 00000 n +0000320991 00000 n +0000321159 00000 n +0000321509 00000 n +0000321753 00000 n +0000322224 00000 n +0000322560 00000 n +0000322884 00000 n +0000323138 00000 n +0000323380 00000 n +0000323870 00000 n +0000324295 00000 n +0000324679 00000 n +0000324921 00000 n +0000325359 00000 n +0000325569 00000 n +0000325945 00000 n +0000326063 00000 n +0000326175 00000 n +0000326288 00000 n +0000326396 00000 n +trailer +<< +/Size 1128 +/Root 2 0 R +/Info 4 0 R +>> +startxref +399039 +%%EOF diff --git a/dav/SabreDAV/docs/rfc3744.pdf b/dav/SabreDAV/docs/rfc3744.pdf new file mode 100644 index 000000000..45e8560d4 --- /dev/null +++ b/dav/SabreDAV/docs/rfc3744.pdf @@ -0,0 +1,6295 @@ +%PDF-1.3 +%ª«¬­ +4 0 obj +<< /Type /Info +/Producer (FOP 0.20.5) >> +endobj +5 0 obj +<< /Length 1466 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU3>B?8p&:Vs/i%^YATce6MDf4!n[PMV\$)`'Iq]LN-1^1g[6rNd2T>_UK!I!0WPF37'm:uCMh/B-6ai0a]^iCIMZ'+e!mO,\eXNcH73Ul38&HQ%q'&/DIUpRjAIV/FFa<+L7?R0=:?Tu:FY;X)PD^h+n?9Jo4c]7$'M!Y-49u5!Cb5-!%T*DneDR[dkqmU!+pKD>UOU4mNXO?BN0Cg=E*uGFmfQ.M.+5bmMm2X,@\)`9Y[LmP6A/I%d[q,&[;KHe&rcOW^Gq%#C9eW3'ZS6987GPc8'A(>=pR2Uh=Gdcd>@6!T(^.0,)ZE0^>BKmbc5ccYq3h7q^EPX>`Z5/UdKsJCoA-F[NAVF(NaElfJhG]>%rj6ONjs@/3>s\icca;l*aY:NcRnbS%Ha^BD\ttWlN&<^k3d!Cr[cIr?o&=ol%ChP-5eKZJ%B9YKNt4"N9s5GjrP%hoO^P0MbWE'Sa?M;A?fP+b^\G/*UR8c.I2^lMIh\%Y6oCl38+@li(&M*.>A1VYr3=[DN5<$*_V+bU1QCS6*jMRs)uL+lNq-qlhrX'@2!502$QSmr',ddf/H^G945c`I]\OA+L[hDGSiB6hFbN(;2ROFbu==>a*O=qa(MJ)8`6=gFhWJL7EV%TiiKRn]=^H1c3mS[&+*sWH5L]s56"CI0TG,NJDYaN=sD=s`CclGSd#.HT$UOY2U6cn?jpQQ;+Ne/JGZP?)PuX%+h84'CijM.?Ho9(K``rZZ&>=)3CPQ3Cs=K;_f9jY$^$*N&'AR]Yb.@aas=!N/@-F1Im,4rj#0OWq[c>dKJ&\<9j2K\PLs6Q4)c$m:-]Ep4X;?7ArTEO%]42+KWO+sFC'1qO>Nr.FF70eM!A;e.&Kr@:5_HOTGj%Sq%[q[XX#rp2,.r3-B*uuND)),_mE$W[!90>I(tI'_\T[STQH-FDP`:p24b6"b7@1M^r4!+-B-ff8GBCIAZP3//0"T4:mnj19j3k156ft=%tjAE"79-OV=<[`;[V0Hq^1JVAlVc.%HNb<^9>*&[kB.\;$Sdb.'-%g"++F%#"7,9NBuCFPGggS96+&,,RD?MUtRB-L`Ic)eEt^hN[eW=j_7+c1hoA*'8,#o<>o<+pQ&_^TSm6D_H"ufuop*7M0TUH\).(0R+ZdlgFNE_3+K6`ID;&`cM~> +endstream +endobj +6 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 5 0 R +>> +endobj +7 0 obj +<< /Length 2193 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$JbAu>q'Sc@2$LXq<<%%`u!19>C,b9&*NlP/R74&CGJe_$,;F=;VoC`>QR`(=R&^J?PZA;$0Iggce3&`V-/hVTu,9ju9NYZgT3*K[N#jX]#&la?-E/I31UW#.f0O&(s)tbDDdfeudR[&*qcW=[TX.nIcLd":dh]&&pYmKc:M%RS+4bDD2blnq1R%uj%Wccg*,XVmf]IiEK>TK,Q?5B*Ch_\aEYp0R$`_-sj3&(Q"%UCi?N\a(;LMe)<.ZRL:b;qG>Q>M/MheQ$>T6a)+$jd6B9[0PBjq\AlR2A)_<$7X<]-$4e*)]jZ?00$\hdm`[U)9Ym6,D&e`g1%'\:H$@Y(erGZFuSa&=BNG_!^fR=tBX;P_aB/Ql='014_#8=_3nZ"h#\qLuXM%?sj):=-nEOXAlXnO(h,6#?7KiE+iZp?=;I-k-BWP"EShl"u`,0]KTDBXTF&FrO"lu;^6=KEO?Jkj%?,*C]&E8\6N$hCVHjlSZ:u1*7PO?3XHouTsZ=0@;0mYYSS"K/gqJ@b#S,-Q;'X^8;ST_$>=0(8e7_BI"mmb?>AaL^R5umMd0o"B_UFtRaF38U81=XW1+]"[qt8pB,Kkek:d6ZW.E)F>$'fo2rCY&XY*D)gdiPZTQFp0W!/EW!CTj&:gbLW<6'O_TCSO+H=MBM)3dljVbMmC-n#OYF=`R8mFK;skpE4nWSnTuN!ZIFCO2QK0p-u$3G_1R6.gueh=o[NoHs[DlR!h'LS^n&@Nhl0u]i"[t:b@))IOmH&%.=!3_*2t,I`EZU*<_[B5\1r(gmHX`4=ELPJ4@8>,CL#nZoOL$&'jO4@LW&8HQGA?"+6\AIrZT!^=OW[E_Euun3D#5IIhKoce%-nO'%e$1uNht&ZDooJU3+K7"Up?pt,kNC]eRpet1JC'#`bJhp#)K_!3#qT!GYG/RldNeG4Xao'/)OKnnWJB@)8C-Vuo_,.J%8Bq%kD11.E(3/,.7.lJLQ^j`=>pXEdB9`())^'sX4Z%+a1-AU@]\c_F7!o],?+Y>fXGiW^QXTi-2'T$tAj;F`ohf>6HH?h4RH&X2tQBs*)]\XEr<0c'ga=8Vl"'K$C9PH*#@HJTYmk1'3@4hUPar4)m1!P40a/q"Ho)F=S+&WqMiB[BlU$@lc.$9oj]!%&81B+K6??^a<;RKn4>6,&j^^poeF`"RSsJJl[hjkf.m<.(3M>]`.J&[b(m55#N3:Hf>3U?AI]MnZ9$Ps^?/_2`9>k<_SH-_C*"iWD*W+_=\.4:nB_HcDk,SIDA&4O0;tHf87C.KnXiBjd3,Il8EbRbc7W`F97'f+`0Du'cC[phLl>MB`poZMi]D]:l\:%;aZ+%aVe,?`CWH@E2A_"rVe"Y"OlY2"[rVL.UUY1FgD!c=Z3`+SG5IMMV]t!hZ47FWk~> +endstream +endobj +8 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 7 0 R +/Annots 9 0 R +>> +endobj +9 0 obj +[ +10 0 R +12 0 R +14 0 R +16 0 R +18 0 R +20 0 R +22 0 R +24 0 R +26 0 R +28 0 R +30 0 R +32 0 R +34 0 R +36 0 R +38 0 R +40 0 R +42 0 R +44 0 R +46 0 R +48 0 R +50 0 R +52 0 R +54 0 R +56 0 R +58 0 R +60 0 R +62 0 R +64 0 R +66 0 R +68 0 R +70 0 R +72 0 R +74 0 R +76 0 R +78 0 R +80 0 R +82 0 R +84 0 R +86 0 R +88 0 R +90 0 R +] +endobj +10 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 84.5 686.866 138.95 676.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 11 0 R +/H /I +>> +endobj +12 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 671.056 122.55 661.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 13 0 R +/H /I +>> +endobj +14 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 655.056 192.28 645.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 15 0 R +/H /I +>> +endobj +16 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 84.5 634.056 127.84 624.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 17 0 R +/H /I +>> +endobj +18 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 84.5 613.246 126.16 603.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +20 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 597.436 177.26 587.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +22 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 581.436 180.6 571.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 23 0 R +/H /I +>> +endobj +24 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 565.436 223.92 555.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +26 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 549.436 213.37 539.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +28 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 533.436 187.27 523.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 29 0 R +/H /I +>> +endobj +30 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 517.436 192.25 507.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 31 0 R +/H /I +>> +endobj +32 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 501.436 282.22 491.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +34 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 485.436 195.59 475.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +36 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 469.436 177.83 459.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +38 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 453.436 192.83 443.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +40 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 437.436 175.05 427.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 41 0 R +/H /I +>> +endobj +42 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 421.436 251.14 411.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 43 0 R +/H /I +>> +endobj +44 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 400.436 170.88 390.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +46 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 384.626 190.86 374.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +48 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 368.626 180.32 358.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 49 0 R +/H /I +>> +endobj +50 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 352.626 195.31 342.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 51 0 R +/H /I +>> +endobj +52 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 336.626 197.54 326.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 53 0 R +/H /I +>> +endobj +54 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 315.626 195.58 305.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 55 0 R +/H /I +>> +endobj +56 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 299.816 146.43 289.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +58 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 288.816 244.48 278.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 59 0 R +/H /I +>> +endobj +60 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 277.816 275.32 267.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 61 0 R +/H /I +>> +endobj +62 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 261.816 144.77 251.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +64 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 245.816 214.2 235.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +66 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 234.816 373.64 224.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +68 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 218.816 223.07 208.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 69 0 R +/H /I +>> +endobj +70 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 207.816 375.72 197.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 71 0 R +/H /I +>> +endobj +72 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 191.816 133.1 181.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 73 0 R +/H /I +>> +endobj +74 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 180.816 168.11 170.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 75 0 R +/H /I +>> +endobj +76 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 169.816 195.87 159.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 77 0 R +/H /I +>> +endobj +78 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 158.816 173.11 148.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 79 0 R +/H /I +>> +endobj +80 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 147.816 176.98 137.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 81 0 R +/H /I +>> +endobj +82 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 136.816 326.83 126.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 83 0 R +/H /I +>> +endobj +84 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 120.816 180.87 110.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 85 0 R +/H /I +>> +endobj +86 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 109.5 109.816 175.6 99.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 87 0 R +/H /I +>> +endobj +88 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 98.816 237.27 88.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 89 0 R +/H /I +>> +endobj +90 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 109.5 87.816 206.13 77.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 91 0 R +/H /I +>> +endobj +92 0 obj +<< /Length 2423 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$JgN)%,&;KZP'YJO".YKeOO?;!tVf!GXj/?ZRR9Zs1Ci_p?.Rg1*T=t:rM$jV(BL`hrbTmh?*s=b[(5i@ej7M^2(&+qnHuF=\k'IVOf^n2Hm"M.G(1Ml7kY:6sa)d7B`6^%'P;iI99\n_Rmc-:r]^innb-2s4DWpN,hs7?XZWH">E:omq8XU<-[S68B[^Pn?35u4/7LeR2;.TPYAZG]^2=3;)q:K)oqm$:7bHK$8R>X!UX#j1DRJ;p[&+-d6it$'k1`qul-%AUhCf/hUOpE:%N;?=;acGeQLqF]q*)I!5N8eeHUcP+_O.siJ0cMPM.oG!d.#Lhp%-bt0^/k>k\.qjP"%6!^6pROZ/N70UPV2N3BHuaa7nGps#SB9USjQcU(D0r*_[%GM[SS52PBe!/ap3XA!WcF62(RF/l.EX+R2QoOu7?e`c3]92Be,Y-`nZT.5m1a6N,T%+o2^348N0pn,+;nPag33>`99QIlAO&.d`C!%&6[[BY+/u3q@+?.[>A_^\&YjL?h4D=""XN+K]=HS%S"p$MK_V"<.V$9>7J40X=_C&j;%OQT)hM?/5#APm4)VMhY1W4Q8@ON+K,BKc`OD/V:XeQ=Q]fSt6TGSeURIB63`'>t_T9N*bR6P/#-tu7!n:MAje;n5UqPmD_CVboMMNDsRZC><5jb5@%8?VfQ58_!?pJfqR9ne-]XOSC=ITo4H"\,P`FW""0JN/X.\LS>e(1TimDf;C@i7`QoaHfeeLiEH%p0S>!A<*TWds"=R$mJ&aB&#C'$:iJeI\980.k(lI]R*XaP6.J:gW?'g;Ls^oi1361E"tMhdV`#XNeA15HVpnB6.)mF(Vb'bP(!bp3A_(EBtY7(7BT>\YCP-YEj$U.-jD!C;!_J#XJJ$I'H4@gc)iTZn?3:\\H4H#a@V>-nF[4*TLak;qeWL**T2?56g7Gn\,)XoCYF]g+!IYuF!T6Q$`-EDjGIrCFeuKbZn!5e:fCVlBP`3>7pQQPT\EhD=-"K20K=4[qLlrIc$BVK7[RMB;es+&agiKkF%6rS25&^%Ad,U?]"=KfhG&2]7VabJqB9`iLB_<*m8UWIbs]N"5h.Z[!_&Rf]lq"RV8u0HJ?8._Rb[NI26%UKOH(f,KQLiE0aRF]&ts4#6%0oaR%f*L:O$SAR"bj)8?PHe6p6_]:h8uqc''W*";mTDBF+lCU']EBU(e+/WKTtkDf\3g'm#"'D\E15]*UV&RbCoB7b+C;jD+j,fOdAoL(:h.:'leO(sLU=;.Q_so;kcgCV'=gLO"orK9)@t?2;o[4i*?`I#C>=6;Sj0@@I=^+,p6'9]PPcA?lFTN>])=q'>3fS]I[tZl]Vc\f%4IspGjmHOi':plRJ#nR#r&%$1DL3bAuFt/_S0*bI#j=4DH(rc_rsHW7mP;08lq",A9s:iTQ<-VkM`.7NqUP=d':\\7"]KN-6Q8pD>HNZ2#:A#`Z4c#lm?ZI#L$P!&WN3HbGSEiX"'b,"Xi*0\Q3Ct(EI,$*$'eo4J,Dj)ib-KK@!RLmu]([8NTWc(\+Jn)5faiU7/au8Y0rTsmW=OX9eHbO(Ba%>ZsY)BY$aU2XVm]tC+p=nRJ!hDts2:%69Ip7cXC3!Q$pLD2g;YX`TL_#:G-5i_@H.JaJpclf^*r]KOKNoI*U-aW=M!_q9GApQo4Q.=`Y,c].)anBJ?t5Ii8u^>`WgC:RMQ3^(fddVR%lACg]E#U6Z1V60Sm;2'j@_VqWpcQSrD$'/C8V:c(!@USrC]0ETE[?S0ZIj8jN71:JF2/Ve?#1/;\Kh~> +endstream +endobj +93 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 92 0 R +/Annots 94 0 R +>> +endobj +94 0 obj +[ +95 0 R +97 0 R +99 0 R +101 0 R +103 0 R +105 0 R +107 0 R +109 0 R +111 0 R +113 0 R +115 0 R +117 0 R +119 0 R +121 0 R +123 0 R +125 0 R +127 0 R +129 0 R +131 0 R +133 0 R +135 0 R +137 0 R +139 0 R +141 0 R +143 0 R +145 0 R +147 0 R +149 0 R +151 0 R +153 0 R +155 0 R +157 0 R +159 0 R +161 0 R +163 0 R +165 0 R +167 0 R +169 0 R +171 0 R +173 0 R +175 0 R +177 0 R +] +endobj +95 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 719.0 188.66 709.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 96 0 R +/H /I +>> +endobj +97 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 708.0 278.92 698.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 98 0 R +/H /I +>> +endobj +99 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 692.0 186.42 682.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 100 0 R +/H /I +>> +endobj +101 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 676.0 214.2 666.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 102 0 R +/H /I +>> +endobj +103 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 665.0 312.25 655.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 104 0 R +/H /I +>> +endobj +105 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 649.0 330.85 639.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 106 0 R +/H /I +>> +endobj +107 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 628.0 154.79 618.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +109 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 607.19 241.16 597.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 110 0 R +/H /I +>> +endobj +111 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 591.38 174.22 581.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 112 0 R +/H /I +>> +endobj +113 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 580.38 170.32 570.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 114 0 R +/H /I +>> +endobj +115 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 564.38 139.22 554.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 116 0 R +/H /I +>> +endobj +117 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 553.38 195.6 543.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 118 0 R +/H /I +>> +endobj +119 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 537.38 126.44 527.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 120 0 R +/H /I +>> +endobj +121 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 521.38 123.67 511.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 122 0 R +/H /I +>> +endobj +123 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 505.38 124.22 495.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 124 0 R +/H /I +>> +endobj +125 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 484.38 188.37 474.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 126 0 R +/H /I +>> +endobj +127 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 97.0 468.57 117.0 458.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 128 0 R +/H /I +>> +endobj +129 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 457.57 187.0 447.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +131 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 446.57 217.55 436.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 132 0 R +/H /I +>> +endobj +133 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 435.57 353.91 425.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 134 0 R +/H /I +>> +endobj +135 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 424.57 364.19 414.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +137 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 413.57 455.02 403.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 138 0 R +/H /I +>> +endobj +139 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 392.57 185.03 382.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +141 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 376.76 168.95 366.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 142 0 R +/H /I +>> +endobj +143 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 360.76 237.8 350.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 144 0 R +/H /I +>> +endobj +145 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 349.76 291.13 339.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 146 0 R +/H /I +>> +endobj +147 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 333.76 225.6 323.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 148 0 R +/H /I +>> +endobj +149 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 322.76 278.93 312.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 150 0 R +/H /I +>> +endobj +151 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 306.76 263.91 296.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 152 0 R +/H /I +>> +endobj +153 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 109.5 295.76 147.83 285.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 154 0 R +/H /I +>> +endobj +155 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 284.76 360.84 274.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 156 0 R +/H /I +>> +endobj +157 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 268.76 278.35 258.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 158 0 R +/H /I +>> +endobj +159 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 257.76 331.68 247.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 160 0 R +/H /I +>> +endobj +161 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 236.76 160.88 226.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 162 0 R +/H /I +>> +endobj +163 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 215.95 242.01 205.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 164 0 R +/H /I +>> +endobj +165 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 195.14 192.0 185.14 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 166 0 R +/H /I +>> +endobj +167 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 179.33 256.42 169.33 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 168 0 R +/H /I +>> +endobj +169 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 163.33 396.93 153.33 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 170 0 R +/H /I +>> +endobj +171 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 147.33 238.65 137.33 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 172 0 R +/H /I +>> +endobj +173 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 89.5 126.33 153.39 116.33 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 174 0 R +/H /I +>> +endobj +175 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 105.52 182.0 95.52 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 176 0 R +/H /I +>> +endobj +177 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 89.5 84.71 172.27 74.71 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 178 0 R +/H /I +>> +endobj +179 0 obj +<< /Length 747 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$E>AqtE'Z],&.JuXcJS.'$099C3,nNKY7"DZ3+\UTC,+$Gl-i34Md\sF_TG4a@c9pY;+.mV'rqg^RGo'*X0S0QuCCbuULB0.P#)jB<6NiPP6j:)l?j`gi(.97XAcRE+_L_i^2W3f:"UlED06/Y0//)/.?2Fo[kE#nnGkhQ5LbhEe&r_jL>lZc2^]6)b0p$f(JNe$TZ/XRGaKkqj5k60fN2:7%=E+jGi636V,B_Q\jS<\Li_S/!\ucQFO;$1U"66Q#5rkSb?((=uMjg#U)Sf%;=jFKXC=h<"JNXnQ^G1uh7YbnAPp,-IAE^Y3,_G!=MUSCV-0\#9!+U-'dl4MJM7b0$P"N8=0[/\RKV@ED3r?@aSQF`#pnF~> +endstream +endobj +180 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 179 0 R +/Annots 181 0 R +>> +endobj +181 0 obj +[ +182 0 R +184 0 R +186 0 R +188 0 R +190 0 R +192 0 R +194 0 R +196 0 R +] +endobj +182 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 89.5 709.0 136.14 699.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 183 0 R +/H /I +>> +endobj +184 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 693.19 191.69 683.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 185 0 R +/H /I +>> +endobj +186 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 677.19 196.13 667.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 187 0 R +/H /I +>> +endobj +188 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 656.19 320.33 646.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 189 0 R +/H /I +>> +endobj +190 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 635.38 285.58 625.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 191 0 R +/H /I +>> +endobj +192 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 614.57 155.61 604.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 193 0 R +/H /I +>> +endobj +194 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 593.76 275.87 583.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 195 0 R +/H /I +>> +endobj +196 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 572.95 96.45 562.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 197 0 R +/H /I +>> +endobj +198 0 obj +<< /Length 3722 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=/gN)%.&q/)-W;NqrPq@n.Fius[F4nf=2G#3dSZRe4!?r[!'9_:(U_W(Y(Y"GB_1nb]r".$cEB2j'qK)i8a8N?V\$J@3%7c'Je[*Y2r@AeElq]=&L%]Gkd:*rOVh=konF\gZ4s/QT7O.g)lZYd7FOJ+8d`(go++sH?gOU](@'gm:0Z]t/A3-=k7u'Cu;e\`.-ehML>%HG:^$:'MnS>n_jm/95m66FfQ;uI`NchrP(eK1[APq-W_3Yl6?L.%Rdca2d^hf`qQ\nrb>tQ?-)13`sE@M;qjkZP0g2nG'_^&1"HB/J'@hs3#,cZ`]CdActkpJA[Ao"$]U;e3!\COh/U)*57;2DW5Y:Q=D=BEp58lH1K_&CPSVrM[`0`o#kFB)XZ1:K?^2L5da,V.sk3KS=5AS`3,u`i'eQL@80t8bm"A.UXn$;?6C!&+BK&p/*eOoi0Hs^Yq]?a=O)^(u-HHLKgnjNigK(>^-@=HmBGUoRL_TrAD"X;k.PG8#'Z06/2_ZZJ,`t"9E`EYaD?jVNM1Q5!D1'nkg;u1ie*NE,`&bS8.;AbkVR-A-JSW5mg4ffafm)"T&SIL\)L+04eiVMZ:pCGS)oICq4)TJ#Y:4).sfY/ApY!a\V%JM#/gihHaK@soJ1u:m>p9WjhB<,sNLZdT!RoEk`9+e)3qDYXo;T>hSnB5I7M0SD641/Q\>/"<907sk.P1CrmddPlq-WM#o[)ld3N`IQU"U,4'qRAi8^o?BTMa=+QQFX"-4F$N9;eW536!FW@X8fcGs$X?_dg&p(UcLe;a5%(8iPR.WD06TMBlP@#Y!mN/t+^=7lh'4W)5+VWl44R`$Va,Z@k`n3k?\#pAL;?]uX?E(G-D]("(Dc3as]T14qnrm#WF/iVf"+$6N't:jA,$Wm&C[+``_b>)'qHFjIRN=]i7mjri$ZY'"NU88,N\fpq]NjX<$67+7P_H^n+`rqcYV3BYf_2d&i_e^hpZ5RL]t6<2lAD]uQN?nDWjlB'$mgbqFW3ePE[\,qH_?AVt*_i;*hjGfH2GZoC*Woj61*d1g"qEekUt\j#3@!&ekWjD_Kl@`Vfk=AJLC"C1X`]hfJX:#?u=@j6/&C*b_&(4[G$NF-1GJb@#c]-WsPYG;A@d`oAj']DiG,'1::dYUJ)4Hkg>0BfU_Y!#MR_,QDMC6#tW:ktt,4-ZpO1,U-,r#^@:5QA.,2*(,eg/HDnAZ1c-cKAVEJM6W$])KK\gK@p&!MGSHMZdtqHEn$afn-'(AUDqmp$m%&GKFu;12Kp3$IS8hjB?uA!rHR^CUZ#_t/>e&N=V$N+KY_Ia):L%KaI%9J1^oH*FJgGD2!ho5_L,p:I];-?^]/hh/F*'7qBu,[qjIFa&R2LlT(-:>D$4@*.PoeNqXjUDVml+Y]_5@bI@H%/,G$CE#`\A?Pa!ohdTK!oSq>Jhh:FpHp#M5rWV`MdOVjL2\=cT>;d!u)l%bF"l8%HE%+Qdd>cKa\1.7Ge,CdtMX-NM#e9D%(&Z7d!K&^$X'&pq^\N.?j;AsFP:LeZ'>NhLmI)]+/qF#`%.@eJ;ba)@6h_6Ij8R)qI=3@3!3Fd%de9K-qMQ&turW&PcV+0u*"M5^+8D'YKcQG.>(mM\AHH^99@0`A*f"fh%;c[/aHlr1LDAV)!!N%%F][I;LW-P)rHYI7]un_lp8q?YAQ*s$+7-lb3TRA5RO<-SeiGGSi$g4)/nJfaS_sjZ_Gu;nVfW1`5#%&&R$4;R`/)Z9`8CL?7`;^`EqGt+.oN>\8Oi8VOa.GjEe41s';mgqGIlL3_m6f`f#4L<,E\2R%GPS-(Er@;dEUB85Gp7EmP$rsckM1M:YWrB&1^RL6:-CV-u2[Q]&(&r7&nBkPCmN.9fRpVcDe5KJmUMM>lH$*[#;Z($8X@5FZ&N`al^&WYBH&A\pH$g7GOT8BaOG!`DWkQElY8+['NQqjO"&"4L@^G'ci5EQ$(A5o;28$SJBKpW"UEV7u?aX,L;m+d]@e9,L[8XDA?%:9gQhH&6FMS0PHGLn_h/hW;fLrc'3_>4sAN<9^R&ddL[0?9HhH>X)[aD$'^3T03"[>$gSMhNcCKfRhDO]-oBg#59o'HW?dc0!("Wg/$5m1Xqj-X10?A6jM)b[/BL'aM8aBX`l>GF;9hnhAd`P.-h7RI:bh0Ek&%)?[YRWO9"3.-c3bs4J=rC4"F<]q:gg51_W.[4U?LInbVQ3F.:#+tR`-h=Vt(f+XlB?+]hf/I;I;6r=D*u@ib"ShW76=;Mr*cGXk"+Pje^:bmY)4:A+#&T[\1p1^t.N8b-u`8e%&Ve4g=I4kIk\FCE[q+A+*?UBCFWZ&((dZ$a&=0^#=3%d5h-T@?6gpDf]_6%ek%4=ZD$:C:ADhDHd.HdkO3^':e\qIg?i2Q^WYdD-sACqX^+#9@si8+@niEC\kA]Yoc+()eHs#L9-X+^d:VC0/Y4g!5V)%l`uY-q-d`Y]*^D6)6+O2p(YdNOe!MJF^%;W6pA[/NWAC57@ej(nC]J#8._@PQX)^p7&9g3=B%Xf]JpYU["%iND]X\]4[!0t/062B>NsfDe/((lh&t\#.OCD6O9lEWmHs5G=\K!re.[E7ons5d.p!=S#FiA9G'FEW6%BWP3;D*aRj48d@AY./BZ.>VT53nF5jVDRZNr1=dNTL$87KJmC=c_;=FWk"lhKhKc_>nW@2LaCm!(2*H4t+,^cWcLL]/__%C(&$UbQb];[H!7;bJdR_Z_/W5/MkGfF1O_j(6_2mQS9@^Eu?=Qt5@^S8k&0,f>j#Pj"pF7iWuo51uuG2TSM[&e:`%>)*`CWDBink#XA/[O+qB$>?(5ib+8d?u2@]$N9;;)2M*I?"A%A#nHX%9&9;!,#p,=&"CQh1FAZ]hApKjkO1OSFHYY6'OUNZH/nc4$@2!t^B"gS='&_J,Mobi2%iK3#65[-FC9r+GCN1/MH+aXCt,po4E&ZM"\U%rUoGO<+\P3;jkb&%7k7BA1%X^8e%GquV;!-c!YaRkEkb.N1gSX+JUjhqk5#WAg(k9FbGDR_r5]X3hs0<[bD<.jO6#r&H#*ROUoFC3ar-QduD?pgj%P8Fo%cP=Q.,?3/AcB?XKOF'8Ani8oXH9^cK=AAo$DO.lcZ7JY;b(#+6eAK0l2,L^h)Q=.WVel>CbCS@B?k8_mGq_\;;cfE7m>^>bgGn+%LJ[eZr`ot!O1(0\G/WVAg?*&lR3\lU$$3NUMfiia05>*/nigb1Oh54nm.Ukq@Hn*s8?H)GOQnXqYjB&tR*XD9s9=D4)nVLJ@DWb2e>VTq,nMjT85Hi4ZE@9:t\->UWD4&[E?&+FRO6AjGW`?(iG?POhjBB'*iA).nV#7I;`'*E@h"/SY^F<\S&dXJGJ2u)"(UiX!%&cZR)k^p"[;aa&N"ohN%r\qRfKqh9f[#5s9d0]^''np6Z=c2MZNPRZCJ4armruBF^CFP&b`,HMkP&!4C`Zo:=Qk^"O`>ZMZ=\210o.uf[jXs6mK@A.F#*0P4E(eoJeoSrHbg(gCS;oAntJqd*-sh@8cd1`R\]?IW2)#JEr+&k2ias3He^T\_W4R,7uutja;("/b+YDuEf.*W+V:)0)m9p+_cF'0^6.0mN0-L$1pRF^rZT*5]jTfE\@@=n@&k_l)n\LHH9+QQ*T4$V=;01,T=m1jkU;1I?pW3+rdO_s]3ADLkrA%6sNRKCnin[%sMQ>o^812aZPnobt(K_/!`j57>GJm;nJ-"KQSMilsYnn?5ee9mZ>VTZ&BXRBSVu+_/[=duPEI91bhN6pbO"JGV*%"Z#L"#Xg^M(m?]:Y!bM5alNa`PQ2@W,hHf"nkCNpfe$`D%6NK;MRAGoCQCHeXZ;&J6k.h^pa]$HjlI6<7WFHY/L!3?\J92>$ZdJM1Xrr@GPF:/~> +endstream +endobj +207 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 206 0 R +/Annots 208 0 R +>> +endobj +208 0 obj +[ +209 0 R +210 0 R +211 0 R +212 0 R +213 0 R +214 0 R +215 0 R +216 0 R +217 0 R +218 0 R +219 0 R +220 0 R +221 0 R +222 0 R +223 0 R +224 0 R +226 0 R +] +endobj +209 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 262.81 634.0 307.81 624.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 13 0 R +/H /I +>> +endobj +210 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 334.75 623.0 372.25 613.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 17 0 R +/H /I +>> +endobj +211 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 442.79 623.0 480.29 613.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +212 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 242.52 612.0 280.02 602.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +213 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 140.88 601.0 178.38 591.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 55 0 R +/H /I +>> +endobj +214 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 388.89 601.0 426.39 591.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +215 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 293.93 590.0 338.93 580.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 116 0 R +/H /I +>> +endobj +216 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 401.38 579.0 438.88 569.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 110 0 R +/H /I +>> +endobj +217 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 239.22 568.0 276.72 558.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 126 0 R +/H /I +>> +endobj +218 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 205.86 557.0 243.36 547.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +219 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 370.57 557.0 413.07 547.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 162 0 R +/H /I +>> +endobj +220 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 155.6 546.0 198.1 536.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 164 0 R +/H /I +>> +endobj +221 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 304.19 546.0 346.69 536.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 166 0 R +/H /I +>> +endobj +222 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 434.45 546.0 476.95 536.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 174 0 R +/H /I +>> +endobj +223 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 205.31 535.0 254.47 525.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 189 0 R +/H /I +>> +endobj +224 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 283.65 472.028 329.21 462.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 225 0 R +/H /I +>> +endobj +226 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 391.69 472.028 437.25 462.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +228 0 obj +<< /Length 996 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%">B?eu&:X@T0L"/-b97-ZcC7fqm?st;Q(H1O5"(?q.=rp40E;#&kc\c0gqSD_K48d.heP)T4B_,?QiTA(;]V"[?H-@Y9bG^?JK>=<"T4i;X.UjlVD>DZi;?O;_=D0\dVpEpZ_=HJS"Y&M\,V3-D@oLt4Q1=WXCGMa:se>?O0bSp4.*ZZfsEdN7*!,.k!Lr#KhI^a07:k&o6U8pbM>Wt9YTuOBCm2q/]%dls;IZ&s7=`=!g!+g"p5!qmDMh?ITr..TqBdDJLCAF.UZr5OB@mt#bukg9$Aj\8'"B?!C^qpjdGbGcm,MgGFH3+*laLl0.42/0#hs1T=+,33W^MSbT6VFF)c;Rr>#O6%NuX_fS;IK%CQ<;16*ji/aQ7Z@mbSA%hHo%I#E!JHdC[!d.&f_)?;,7?WYhR')jj')?m#@3:I_,\OjpGT_C-?T8c)n-CM-]2\=(mP+fe$(FU!85$:3pVTL0m5<7OLVrq?Lb^9B(Y?d+:oFKQqAU!eMST_Ib6lG&"SH+%-)L.&!TI)uOf_E(F&eQH):);HG4H<+p^0SRMpan;+1lmr=phMLVZ969WdT=sJdi-/5^FSPM09!:eoNl9ch46nu_L\\&YQM*muZHt]f*7QsbR\j3LhZ.+>;Wb6L+o@E8sXUnG'`7Y(X]4`$ +endstream +endobj +229 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 228 0 R +/Annots 230 0 R +>> +endobj +230 0 obj +[ +231 0 R +232 0 R +233 0 R +235 0 R +] +endobj +231 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 651.028 137.56 641.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 225 0 R +/H /I +>> +endobj +232 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 640.028 137.56 630.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 225 0 R +/H /I +>> +endobj +233 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 597.028 137.56 587.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 234 0 R +/H /I +>> +endobj +235 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 327.51 565.028 379.17 555.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 236 0 R +/H /I +>> +endobj +237 0 obj +<< /Length 1222 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%#>B?8n(k'`6i6dra.+hp8qW&um9m4t9P;qsr[!f_4)pJFn-/i\>rQ7*VNef+/j<VEOU[1+0t@_ml9Mc&k(Qnh2SC=,7!(]XAI4+p#j+gHR(\ic`@D+;iQlrJXmAWYADR3Ma<4,-5r&p%F$rN^t"5;Q!TUQ'V+54_iYsr\3cWUg&.@rT;TcNm?q9D(,RNGl[]4n^c17`7n^-V7f#NAH.jree-P-]ig0DM89Yo'Gu"5oU>`0s>Z)c]780tVLlBe?)SFD#cHj1jSf>pht&1c8cZXhDNmj!d?A/H7`5X<,67UUUeAr9?YcATKnT0[5Rjk.Q)*e!'j04D!VEUC$_CCfN-s^]LeC38)lZh8WIteE":@a!KVg-@Q86>;E-!?dnJrNOe@.ecr!UJ[Da%T#)FchQ``TGP?mOSJeZX7RheVi%9p4^ZlgF%)9:\M&)@Fdgku;f)^mTQtlc[7+BQCf;E5?;2)>LMu9X:bS;*n6A#Z\fOQ(3/,@'aRI`Yea/n-db[.tA9`G[#nmeX+JA)Vf3o0gCc\DA&3r'lPrKkS~> +endstream +endobj +238 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 237 0 R +/Annots 239 0 R +>> +endobj +239 0 obj +[ +240 0 R +] +endobj +240 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 450.83 647.866 488.33 637.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +241 0 obj +<< /Length 3186 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>>BAQ/'n4K4;"IRt-=TQ'e>;PS`m*5nFHY:?..-B>qZ)b?KNX1?!Re:NlO3!tmskE"9fdQU8WeekI-:2D!.S@gqU=hR&B-pp=KGh_KCu9Y&=S*r?og(;VVsXn`fn5J48AA"_c^U`,_]Re^#="4-@=t'#eD_tK6DSq5^-HHn#-lKO4-RsYLOSpl-)U&Li126je4[G\06D2/rGkM:H92huV9$sS!.00@IMk%MoiP##&=dWm,(S\U6&p*CVBMO.4@,U?08`o4G@G^V*m;Ih,o?:3+<$GdB+ItS,F#:atK5Gc!q,t-^3Cs"S>m5>q_hN#K@lB+oGPXm;dF<,Tpo+j"1f@GC$jNaMd2g+q[ckTSF[*u%jeC$=8_%iRf@!+!c+p4HR)5t+lqGajPGZ35*(+1H?c5,;!b*H+Fdor/R9QgY's(t"u%_mB-%Rq545mi([eJ=AA2-[7!?^hIIIJbrJc?ft(-TJF]Bjl19_q89Gs&n99r)#Z^!cY3&rsY?6&[#W5?GOl`I+\\iWp/ZGM2]$KG-c5P:o!1HYdJC+*;+jLj8/rsBsAEIK)2l;+pDG`:j`3s6;^`\r;q/'oKB!f"0+L_([bHg)mDjMYhTA@FH.CUF8S^JP;BK(jA]O"L.G8GI.B.!)M5fR$G,a7MjAkRFg16$m"m/A$D[p*T]R;UjX&t^$ol%=Q3g;o]#OsoT"9o@34+ddYd[\\)ZEW8"I#!#F&*ZUEfphY5G=7\>OLs(+c'dP&5fQ5o2V?.,%)oWSeGB^[u!8!g'L)nC!q8IcC[_.0m"K@,CtJn**+>6"Ht9R86Q:XFOSW/-oSu#Y@D&O-H#L+2]UOh'Z/DrrRK5Jpjif`DOK$E\F#ge/Ng,51_ZY3s5g=N\=6J'*^Yp6@pS\h.Fr"Dif'+#HV2sa[if!+l!#^4ii_0eE'ffi6#8dDiO(,c,@--G(DE5GlKN(`*E$QG57^ncs)9rfeO.)M0&V7'#2XT[3H5,0j@>2%3&a_fQsl30G)>0FLgN#$+c^B6NPF7"[j2E;dC6pMAJ';@@`]T?*SC-st02ue8u=P^+rQ!-8`EIeZ4:/1M-K:rCL&Ep8rCK?8kimrZ:q45,:pYGI+et"Bq%9JM+mNS"mE1Dfen+=%pQA)h(:ZKUF.qF="eLAh2JY-G39O:=.BB7GQJ"g@3r?DW@q1=D*17ick$iWFJh'()Mj-m!d2m46P!6\=9jjg[r1T"$lk=2?0(qIn\2oJnYFq3LMCb.b15H-Y@FQF`V0N"Xm[QhS/i;AXtOckL7C`e3u5!FEB%)8JI=AmFeih%3uTJQ&eWoaU-W9!>"%56I'alB8s=Sf;D71DXDF^ZAugW'!4fENP\OHRaJUUH^YHE7WJ1A*c1Zc`"T@cGP'1iV.`CrT`ru5Nk87Q+h5e>1Kj*b1c+\[1Tj^ZpPquYloJT4#KGNgCG)uLMirP9=d,^'n;h\TAT\LOmUu?]!TZ*NBpjknA385U8UEp*ZM4=77A,^n^Y#4[m_NruY5Yljc][E88bDHlp0ArsmDqlO;sBXEb+.U_Q[hErnHmsPX[5K]-:#V[>aLCiYC]$5.meRTL+YX;@E*#0$q0jf5u`om!?2i]%oEkkko./qdQVL>S#V&P8Z*L0@!hkFo7p7\ma&p2>/J+arRn\X1_5`p5+R[ki,i4J&qCk!s0u;j2k4au4%MRNkSST*:uI@fIE9TWUO*1kbbG6tPo*RS]O,'&j"_]#_Bk?)G:k"Ct'JpN2Go-D7\!7[(@$VU&Gd5XLM8jarG8A9R'1VJ;;;7jm574Wf0o?GeR[NehK6&-tJ0@?3PSY2a$tCrmN%]+i@i[O4#Y;OJ'0MoCI[Rf7a5]DKSE]2)f#e8-Z'gf#.^dpNe0Xnq?*DEcj']_U')FV:F3a/b\jpPWDXG,^AFa2)jS3Yh1I^a4s(_-'qKDkhJWU4 +endstream +endobj +242 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 241 0 R +/Annots 243 0 R +>> +endobj +243 0 obj +[ +244 0 R +245 0 R +246 0 R +] +endobj +244 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 446.38 647.866 498.88 637.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 75 0 R +/H /I +>> +endobj +245 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 436.91 331.866 482.47 321.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +246 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 102.28 309.866 139.78 299.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 55 0 R +/H /I +>> +endobj +247 0 obj +<< /Length 2073 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#]>Ar7S'RnB3d,#D.;+9:u$j/7D_(KG:Us=^gklYGC2Gm'<8D.n[r@$?]%OG"MdPNBC@aW?W3T%Ap+6hZp7rL$^$TYY*bn`g0[6-dA,QEjj\8j0A+r`u%/@Re`cW9,F3d$)?h;]NR-$W[9\tp9IJu(IX9$T3>Y2lB`YnSaR.pTMY)4Q+iT5a[i'>ZSS%W!p2XKn'Cm\/$g';R&6Zn!TA.J0+3#qm_>DtMV!O(i%.NJ2cVCsDrY0aj2L_2KtQ&^=IEOa0N$EI.cWamTKO/%Pr!/.B-TJ-,WaS,!D-#CrE&beP"u!+:-O@6-Q@9n'S+[4;G[X!UE&(k,V?:Zo4pV-ToaMb>$H\DU&b?E%H<34>Yc/Ibl7@9Nd7DLC5lfe*E9Vd`c?;`WmjJV0-4ZM;j_r,\XSNfi>[VsB7t)kctIo=o-XGck\+On/A9#ttJXN@Yj1F'E_Si;&lK!AjW1L-H\a1VUJqO(8Z3\C#Oq^mD=TKG)k3K+W:fkhS8f"cV8h]mML`3e3B_8,:H>TX(i%9M>hF-'.'.*;@,=Fa^g0PlhoR%lkP$rnmQ^fU<1Is:V\cZNiNo>Fthi/tkW"["o:>db/9Nn(cQ5cWR;*Ldq3E.YSSW"JTC/[7&G5QUS@9gJJ]nJ$YP*@E^`q5AiP@U=fu!J`D4n91ISiLAj3Dsc-1Mko7;fkO6a#.]]8<[-loCr;Ca2n#q_qa?m%E1TI%!(tl/5mu>Ius/_#[c/jfm6_ZCHXP8^g($u%@EHR2ui6E&1dNJh.9h@p$Y)`4fDDF$kGu4s48enO)-t^uoqWmeN;h69'3Pq58FoI@HM`GlQi!%5/O$%OKpdU_!+Q`Cp=Y\,4IMCOV'G$C\=q=Eg$fC^G7&oAO?hdg=3%1V9OG3/7;O3)jAC7a_h3k"H,;XEIq+=*/dEBjF$6PrW#H8_maN<%djb8r"ml51R"*oBM$"g9Q=.3'9E4e/E_>-sJ=4?>)V%-cQ.!c^UEjm4m0YC^b4:Q0oA2cq0hh?"*>lUL6TKuJ9ap,sJ-aGdA2*_RorJ$&UUe3@\Z=*/a$oO18Jto6#@+!fX@^qtskuMON&*6D02TO-68]2!!g[W0O8>PXn-S^F4&fZQ("+p^s#2'^Up"sHJE0JWY,Mta,?*g:c6c!gg+R5hf_9LNlLWNn/gXr0D-rd25!Be$X1=>WM99DdVa?UMTjT)!p;3T8lrc@DU"jX+ag=-O1Rs6/0'kR"6KE`sYBsEi=0!/HHiYWBZ9GrX#*`^;tDGBf=ok7rRb$]K2%+nWMf-#dH>4g[596i55134oqgS/ISK9MLL(>lqYRH[(dn-8:785'uDkTX!H03srQH32TNqD+auP.3r]l3##@GO#>qpH`>E*K_ei9R;TZTH-MF9S>_op0ETip1Q?2S^8:=#Q&Pamo=8M*s!FW@INJF<)WBs5,j9kJun06W>c"R,'UfuLbNAeZS8OrSq@\\=H +endstream +endobj +248 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 247 0 R +/Annots 249 0 R +>> +endobj +249 0 obj +[ +250 0 R +] +endobj +250 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 277.81 692.0 323.37 682.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +251 0 obj +<< /Length 1760 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;d=`<=Y&:Vs/:u;m.Ap#2RU-P/lJ[DtL-sICTI@t0*AuihB,\Q`;4*"kQ.$K[Tm7S&,#S52Uc,UuW994Q$**J:pCb)%sg"-)BZ#'2VXJhTr'9@fdCo)jYp"1K$3O)&)D8XNd`S$SRK$ig2CP?/-1MU\P7SCR!4D/^nW`]Fnh-dqlDDQ]MIC%AZ6"s^ep8cW$_ITc&EeoqO$I]0B,2T/7.J7PTZDq_/Q.L&R_;[bL*)n_)qYfg;kKB>&oN`dC(T\4=Xk5o(g.Bi"d$B]7^)Z9)[:H"Wd9EL#03DMf,%F$Age4I6o:1G>'F-+L**tP)S?f-N>pW+fK%Km9`?27-h9nE8_2>*PUQ)!OnVmo:I(Vl%)HJ7hUlE3W.9gu0P\@`[hp.H*b@#lo"g;]JQ*id^F>jB$Eb\Vu-eNWH/9AHd61LJtN@.P\hpP!P$G"ZM>8a31@o"M!UufPfD'CNG`=Zh#]44"82r^e93$!LgWPdY'1^ACskS()2=+!U9e*6^Jgc#d+4;=4@-sdh^NXB$E#.2>@;7WCImA>FRRL#$hR'@k&=gmPFr4qjNE''\\T#u^+WX?Qq@P&[qN)OEiFRE@S1U=6Bi4>T@pKPhEP$G)R=oRUD2&f8VY`,R-/`(-'1"<^kLYE!`k-kGQrq!j_pS>aL6XkoF4LP73qGMID>\:^)C@>Dc.,Zg=m2+DdiL%j6O>LY@"d(A!-KF3ppW1"!?+U:TNroCI9jG"pEI\#^@.j>q^lKD2VG-J6&+gjMZ-\3.W<"`E/i`2]Iuu3hHd<=8H/2Lt0T7p>ZM*]#IgMSWs,le+H'1:"[r]K:[-_VtmT]e,eS%*FkgNU\V&D4+U0/GPcakHXrSkqRA&PV`akWo\#1i9ZL?0>?Gi,ptbRn%ns&Y@IUF?$V8`M]r;m?$"g[cC+uE9I&Si+QYX*Z%_'iB_Ku!Wq9YIEms=/u9`j'@6H^!_C[8_WqZQ&G#XImgT?0#]5Db1\fK"7NI$_Srfb&.'-4JAXs:F*mGr2O2$[N9ClodVA(*kFm[Hh+*Xo`s:+-gt\Vi?hrt1fL\&&Vq,.ODQ_HaSaUo8cK\?!fl<`irVBB(%_rnf`$`J(:U\/2WHHl`@7$-Q-dqnJ#jq*'`Wf6%_c>K;6!#q1r%B7h"lo@NjE0&Wb/ZLnV3M*_h?\FGK1CYQSK-5WoMKUWcW +endstream +endobj +252 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 251 0 R +/Annots 253 0 R +>> +endobj +253 0 obj +[ +254 0 R +255 0 R +] +endobj +254 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 484.96 138.868 497.46 128.868 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +255 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 500.79 138.868 518.29 128.868 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +256 0 obj +<< /Length 458 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#X5u5?O'Ya$;oJdIbgX^%ZsDV7i&>q&fhX5m67aKYT^e!4`UB5odlVLR81S"QT9gpO)dFZcXOc0014!Bq6!r4cNtkh*6.80(MVGGmit2SF'h;W*',#)gQC24(&-NmCkt8fUg#.;6R)ZH'PI(A!<2&hQqp(S"qkU%+aA$.5h*eGt,^K$/;93A+oB9r*9"/jja5?EjVIR?,CJu6%!7%Q*F>$b&<5UW-(Vn/8PFIT?f_MI@B8Q,@BU7n5+:TWq+gLZ?c$I0EHm[l@'`Fg\`\%e4#`9/0G,dMj_/>m4)GX%+~> +endstream +endobj +257 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 256 0 R +>> +endobj +258 0 obj +<< /Length 2187 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`UgMZ%0&:Ml+W;@8FYq#$R/W'_[`/ANl,];W6K(V=S&oA<4`MMXkq>4'uAI^"aZ56d\$Nq*/iXF/WS2mBpRU,j\0ee])>/kB(@2Z*RA6AaO1'3r+"]f:\a#@V0nbLTj#KG,s/3\MCAMf>/"t+f>p'sIcL9Qi(@UOR8]q\*?Xe[OZi^Aq!lAtd-/[CA!'fpjdU]Ic^t_5$uct4!O)3%LTGmX&1)5.,?Fb=YR&Rk_KH!%KOcaIYcpWXklgSTq#I9l*7N%VS0W#S8lU,CNKHr\8l.N`,%Cs\:]Te6_R2Vr-K7seOH^Q>)G/&l$Vn,uL/I8q*8kF0"(Z$5bEagPk:qf+bci)AA^8T;_*nD1>.d'T>c;'jjI'=nR/c2jj2q(6Hs]=][p!3%%#s_QZTg\H;5Y46-E7lRX_ArP<9@&L^l;FK?>/h7k"1`NdVK("l^2TBn0qU4!`,)@+Au!_#[!oG9G%I0=hoReZQWI;?V%rRK!6'.<&*ur:d>1'"sYFh6mCS<+pHV+P\2:p8CEIO:Nn70V1DPl@Yr(8/"gQg\ch`UcO\?c>Od1B0N4adpU+R88+0>`jGsF5e&R7.a%uHF'^)%0,A?**/T8Ui;IR$tP?r*i$P.>[\(NR=jE;?W3dZUHk3j/\:h"s3;R"1D]l@CHG@56#KV9[mJeG5Gnnhk'0[8dg+s%:IW2b1f^L`"s4]8BU!RH9:mK2Mc/0(Q-I8Z>7WKjgb*+,*tImJ&-bT3kK[QnT@g([u\S93oY5DWhEid[W0=2'5(/0X-jPYX_glgl5d4PUG\G]ikUE!&:W1XB--CIqAFk6K48H3o\[4pQ=n-4_2\lEI"f1@B!F\>Sc'\f+,s;Cs^^$;^VcD#MG!JAlTZ.L%!jtsO8p?X_SChr&XX$A=VY`!BkSX!I,f/rpGs]l%UE.F9.1]m;>;nJ'BXuN%D`9%mKWb%>S`qO`/id_euE4UUS-HATfS`MH$,(YZCM#K[`]V98O?qD\2#%!HNM6VuZ(bAdcR$lqgSaI&Cq=>C!+sNRei*FE3I6$'N7cUKjrKDlU0e]KcI>O:1?6H'80RBt,rEg-iDsq*5HVjZD(!MR@ZX/GN6GW07-"f,$D4e?n$]8W>KB+e3NKn2BQ'rBMmhdg+k'%lh"4@>mdc7I)HPCTC^lm\di:AEFu#'*X;A(kiBiDs-bPT^W!t1@F):s3IQdLe;8Y]Rq`K#,/'Ij\Mb4gRr?^Q%nkm[jXPU^ENr)&Sn_9@J=sGVa./&.6gIM/p$:ctG\/frpXQcUC:QQNa61+5EoVZ#qfJup0'BF:1UUB5G'eQgK2c9>+rUrOchWH2!`a&%>I4H_2CcaS;1D6)6l8nR%qM0Lm>;/hXYRrM!b+tRal:T1W.L0l+kh(GOV]#G7q-]R%SJ$JZEKS&8mX?j&0gl2J&JHe';jutErf'g^k3p%F<'Z4d!T!buKgL6#F\ed96_R<1P/.q@I+.-#!]F?=ZJ5ZNH\H("*]dQthBX@@5uk`J[Fauj%G\`-\J-Lg\J;FfpbZJ?WicMf?eHNTI+IJ[FB+!aJ['R#Y;+&'MgkbGSrb5~> +endstream +endobj +259 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 258 0 R +/Annots 260 0 R +>> +endobj +260 0 obj +[ +261 0 R +262 0 R +263 0 R +264 0 R +266 0 R +] +endobj +261 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 361.13 680.866 406.69 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +262 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 237.81 669.866 283.37 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +263 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 337.81 575.006 383.37 565.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +264 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 320.01 490.034 365.57 480.034 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 265 0 R +/H /I +>> +endobj +266 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 224.21 479.034 269.77 469.034 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 267 0 R +/H /I +>> +endobj +268 0 obj +<< /Length 250 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gaqco4V*,u&;GCX`Jlb&g=AUXLa[n4$.23I_GHp]a@?;;r:^s`WM[kpHVa+%SI3q^#aA]ul4FT+!t)t0$A,f^6kW-A5dc]KF.+Xf`PMpN)o9)N-t?4c%d7NmqtTI>EES`MoR5/3G3BkeA4(;4E_bC&&`F;J8\[C'#]UXf:re&57W:nnO&FDn[U&_o`qijtGhUCG(K^aM=PZu'W.(8dTZcN:7+;+:FB"jrg3A31K=NDE-3k9u!>M+nrV~> +endstream +endobj +269 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 268 0 R +>> +endobj +270 0 obj +<< /Length 2372 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0E>Beg]%Xua-d/#1OVkumgX_a/;r'p+j(*W;J?fl@1?H*RZR7CA"bOMphufqU1cq,<#qbESgR9]iChn/[/K=\f'frQ7g(C#r@*MU8bGe:_XKc]uY^ZEuNd4X@Zf=!jYOehJo0b0X/G"I[[GW2"eXHF=cLEK$n':1CLu]Gie0fE``b[5geuIZ5=GiKr,1"d0JhKR4OW7:aZVTPDgm.bN?$mL3aX!H5)Cg[3s;huLQaq<4KHC&Ik^qKatbb0JZGHCGko-JCjhlCK8#KKHSH-N(m-^:L6Su2#d>S:39"Fth/F?tqbr6pgh#gRT8CqTZ(Bk&+]+;#:Z;QnM2=rSRmqs?Pq/m?POBoe^>-DImJ(f"om*U2,A+eKiYZGA26Bkg767<9$V>%`g8T#@!f/laP(U$Bl"De+CFqrdL%V#@5*"Eie7ub`?@SpIBJ_nO#(^9fb\tY3cj`e)W6Ekf@JFr?oD5N/A671r;+[U@1B4n\&g6UhESheT8#+m]5!/k*)@&`%FKR6IL1_`*]EMnt@h#Cf#PAS%IEp$&2+I[RZi2AT3WD6?MHRdX+q]1hO0$D6$2,?B2U*AFEMq'H$==Vo3N&G?u%.!O8#RFF5Fp#%sr*lNCE4U+L9r/'L]C9jjSBZZ([[j\H['''6r3@es325tctk8%4MP*V61?-cPtY-)^up-NnAdI+c;Sc!eCPK9LUKF+gYp%OYa-l'T^2"4bno3i-<`Y?G.a==_X5PaKa:U>4#Cbl@7*%<2#IE;K?C$607,c(nQl,Y.7"-M9WM)NCuP,@7Eg!Z]/EW$Y-FA$H'Yq$^"+tk)Q._g9Hi.l*;(U_EBk#Hi4p@U2^[,$Q_'"I=PH2iCKO6`]q,;h?.X=''CnlN45Y?;2+oZPXZ?B1p.UZQ8Y_Q]s6*R@dZ)X2]uHo,^N,8*-e-SMhi[Wr+jlTfk%[@\%=e%-q,ZQt=2_V@n7lWC540UB@cXluVMIp>_igM2sFaea!P0>]H.qs!RlBk&PQ9@eX84d$7RSo:XX\R)DB`s^Ljk[noXL@YN:([SOP1#>jmu7\(pPEPF]Nrm=8<5YFes>Nu(Q_H**Mg@8$kf*`I#.!tOG]V]kQd`cdQ82c74Jj71oeFeXLjbRBM/jUI=<"HJ/CXVuVc0C:.)Q&Nbr2@X^ZTj)=-sR_/55uYH;A"HSs"1T,be0n"R]gU/$e(0$>3HV)`ihTUU6,gUAb2'$AE]]u:d`a0r[Q1Q>,E4[*^In@XLl,Xm=s`ttdE%W0]'4ZPct6?ZoJ<\BJ.Z]`;bUeDoWT10srjWe!'lD=KJebo3BWq^cA:!9.~> +endstream +endobj +271 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 270 0 R +/Annots 272 0 R +>> +endobj +272 0 obj +[ +273 0 R +274 0 R +] +endobj +273 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 658.866 137.56 648.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +274 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 257.8 583.866 303.36 573.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +275 0 obj +<< /Length 2080 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D968iG&AJ$CE--,4,)CIUUh_K'ijbC7RCJ:2*g.LV$F\UXTo`77o[e<'LB(5FXE<0K4TtEE@UE7(m`WSklc$u4'C7ishbPW65cu>NJ@NM,&)U0JAqt[7len;>BG.U360hg2U6rBQ$f&9dJjm7BG_O`c[S]/nWIU4-.jZ@qW,%$[:=RCZr'%@$:=jR9>!LXSV'*0q.&85P&4bsu7`fIF.[;'t9#fP_@Z:XuTV+*jCeTRQ/I)6cL0F7bOOQd&)aIP@;pO8O(,n%.n2"^Wr9?>@`-(b4r@a%BMmYVIQrD]T_!2)i.C=tJU!\QFf6LE>+ZcRhlh\[Yb`H_*.dkJ8]+uMZ'6"to:paJXE%Yp^3PpRpd#'bc]l#rR#!7Li4-uB*>u`Xc=*gXap%J]*r,?fSHfLTd1jg,=4\[p1OP^q*Z'(QpI@__[2U3B9IBTn+'\]jMSJQM?E[`g9JED$D%%"Y801H!fN!q.7n6,*IW+i`@ldEVli=9OZ.lg#a=^s1kS;OOaX&TiRF_]j5=V.Y-]A#EZ$Bi_o1gV(MdJ_'^"LJ_kLCi8MA#/\&T+J@In@Sp+^&e4K$0(6oOk.mqV[\F%pj?HtCUL>Pb3n6iiZRJ@2tl[_cVF11Rg8?&bo-2`=k"pTt#ZDU?n9J_r5jQat.FCB6K[;h],HBFl"EDiJ?iW@b'[b#[up$I%K2(8)d`\\6,.l:fY9hEO*4M_K"i?U8Rb@UWGg>>'TC,p02@A"*=g\"XU*T<'XlS*GIf:TU(cMhi3hIOha[C1Zm`I!i:6',Ro+Ycd&u8)mc%^t-0)lK+mHj[K)kSIQ+OX8&BFb''oPm4:rm`T5?)SNOC4UCYG\q,91Ct\#-M-VMHlPS]a_c?)lY!l@-h++^D0^gL^aA+B3Mqr!RJ6VM.X:"I!(aTHKiiD4/4M!guIRZhrj<[Hsb3<6Mr3.]Lj/ZX/BO1q];)=a>150i;lX`*Vn2NY)_ZY!_V9#'CkNiMGZ.M>jUE@jm&<0/V,6*7]HO,t3m?FX%%#&LOT9IJ%n"UE=/-=o.D1WOPbB%3+E=B4B=Z"4g9'(_H`4P6IUI6E^)jZO$i6'&sH?:&Oa^&e#dD2:Bq*kb,J1fRk"ONk>-X%@]%V)K1_1`huOuGt<`W3pVsZCjpV,K;jWX@n&=M8(K;9rRc[X9uER(`+ZnYBtWqI9rkb75/#eJUjGJZWEK]3FXT&pkqNCA@ffPdLBi*LI.d^M;K0f6L[oRNb7^DH!o'[l1aPV\,8c%J-:Ei0S^Tga*@P]^ZD'mU7RNj>6]i`c)Z^_uUO!04#aKm8hhB(7K=ENQBN*(W,ojs3Rro@3VFFp-Nl](*2<:,2aSu;5E##`o@:@G<(.B)=V(eK%hhUtfrHJu>(lo5_0!G9LX<43e!$gBfM?~> +endstream +endobj +276 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 275 0 R +/Annots 277 0 R +>> +endobj +277 0 obj +[ +278 0 R +279 0 R +280 0 R +] +endobj +278 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 241.43 530.649 286.99 520.649 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +279 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 102.83 519.649 148.39 509.649 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +280 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 411.42 519.649 456.98 509.649 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 281 0 R +/H /I +>> +endobj +282 0 obj +<< /Length 2177 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=-lVlP-&HC$_TnY51K8&.m-I[nU:=B[a)P"5Ri58\]gGbC>MB4\RpKbnH[3qLrg<>54Nn(l<\%B]$fu\>Zo&E,>C6d3^aTjpM6CX-rcAMG-Q@]hY;%MlEE\+1ojHa:6rXlIA\@bFM(_!iaHE-E3VdQtk.pK`!!<*loS#=8bo7dpHB6d+$qu\uacn%^j;F8P%;4/hZ7.gTT/Z2s(S2U&fjR2nQ,+?6FC4!B&N8:lR7hbLfgV^+<]DI1dk(p+[E8e*o%PbM_p/X+I!6Gf!m=U""90d:'9TnRM@mZnd=4id!qPkJLdk_p:*HBb?G+,k73a.#X&b#R,H@%l'6[VU62>2DF(etV;3!f'?+sQP:V.jjkJ%fi33WYBjVt[4Ib/"Xk.rb'B%jq+5_/K:$I^j@cUl`!ao0X9ou1h1kHhdXQ=G_hl>K[_5VMnW4K.X#&D9K9[u;S`l6$KLSslJf9=luY^d-aoZTJihs_>4169EoQ2u=@F!>:kEr(^>@7%lAG;CRM,';s9`*ubJI*OX[6:*"maBMu(8>On;Kj6TZRcD(f^h-a;(bg+Sgp(64j=&r-rc6kZ\hFifD3&S]*cMH2":j"QM_f]B5(^).S-t9OiG4L6@fI&6b(6fG:FR$b6/jKFBtOU]/rL8sjf=V6jdm'7HX>gV7g(Cj>8i7VIq$-OO6cMh*LGrFIldaPYN6<6oU=kQ7ZoA8o4hjYSjqA-V8a/>@@K6D0D2#@,O+`OSI8(=HBI#RA6!h=3pr?nC98E`FC1P0R.6K(Suc,u1/"14>E)P_?E3\8nt,an[2+O6^(o!1XN,(r75W'YA,I>sR(:SpN[3&)AP#F!"[*>/H&K5O':RU!U4W_$hLKaV])h6#%6Hn_!:+;%T"^<7F-d?ZY7u:5h2&'hO#I,^@V=LiSFt6':**((I]\CUTf/[`r+F]kD"4Aa>>WnQq,T\Y[JN=hL\EIH7kY;_A@4=BCpnElQN^^)m)I\#SgTj@(Ye=XXY!PH.P;>8P-U"[6>^$"oM/ute#]-4=Mt7l'Z$'Q?INZUQ/d"&:ji!cXR^Q\U00ea,TJ\qO@0&(J`I^)U#LC$03jTe<.)u/trHu).j]BHQ7SCDphP!eLR7U<<\ASCSa*/;caFL-#'&]nWj2BTc[]&osrr7"`QG4V\t!n=I0)KAMJ:E.SrD9?]cY"i',jT#V2TT3XVaK?pr96Kuqpk,lm,\X``D7U-J.U7'9?hJhk-g9loX!Z,hCK^/()i!d]Q=LVFX]nl=Ka_H7cZr?X!#)j>'?f&\6$[$TRaj1Mh*J_hla>WRTVFq(I>'8b]_!92Q +endstream +endobj +283 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 282 0 R +>> +endobj +284 0 obj +<< /Length 1811 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DgN)%,&:N/3E9%g\-A%$]!Q)783cWu_PI.KfBch.UckJ%9Th'ok&uAsm"YV&F;)[6g4l[^%h6Xo5m_QbYVLotfK?jW9q&FjRq<$q^JM3,J#i[&[@@i"aA)B3eSQn/cKAQlOVM!!UF@fYNNaTQ#<)WH>"1kB:rhFEHl.\SKP]\!IoGT=eprEtup=i@&?d%cZ:rS[+Z$tY*o.i;(N31Y=o'fN7#Aua$CR:,bTbP%!fJrlP4f5VjY\MUl,%e4O6B"HG/jc1srOeHXIT]!9k0pjR,iq,SN?#XcbXQ5;e!=PeE]'GLgF8\c[pD#Yo1BbK*h8hQrj;_uL7rSLS#9-a!Dj?G.0BnKN7lVjU?#I`k]-iI,)-j0[;@>">#NA#&Dn'kqUM?D]dF`Ht1hEhFMd%I[b[BbRFP5p(>f95$rjG^fZ2U%<$V%2)Sue:KGO85:#]_mPcA!kJ3;/,uaOEY1mi]AHa]pJMm.G\\kmW??Cm+&rC>AN&4$>8eDF<^.8h\UTqQ$](bO[KXZc-1c:QRkLtk4%!tG7)Sc`jbZ"J*huVdR]Tl88TAcr2Dh>QE#5>4_8F@g[o)iMYrNPed6H&_(98uZF00'/S]g(Z*(gc[P4CmVIfPVnjVh6Ka6F9,?PmrCU/])U4)jAMKLcTVus^_\i,Ie%se/7`_?C?oqKfdM-XERX'V#W`r$]Mat]+CuQ*-4mF1Bs&QTdT4>?p75?VbZ#2:;-#W@D'kdkJOqM[lmIB5GCmWtF)uktrT!%I;0gt&GK%T(EShC&CB=`QljC7d]St]fMHE1qa:HX5Y-cOF>G8j/)ZMn9I'ZUN_!S*Elis2nhSBtU%t?p,h`V_B,MJKraobP[HQdU`Cmbf+rWIH-gA;k>'b^2%n+OlLarDm4Z5:p%:$0FLp\h&,'"KogrU\0sM#7oaV*q\SdiE4%Y/HZG-+&s2aoB0sTMnD"fiEI*'=4`(&Ot:RQUo9qSpthSf",lg,6r.S&>5+FUHd61S/CYfM_J+aFZD;A@@kL+^ic761b;,s7$rF5oHCHATKiM3=\^t)B_+C2lVlAVi7SOO(!g9d'[))&lmS^BCUllbL6n@fP(0+7@nGlYeQ3\#'~> +endstream +endobj +285 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 284 0 R +/Annots 286 0 R +>> +endobj +286 0 obj +[ +287 0 R +] +endobj +287 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 489.16 620.134 539.16 610.134 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 43 0 R +/H /I +>> +endobj +288 0 obj +<< /Length 1807 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU4gN)%,&:N/3Y^)Bn(/06$6$Q;R30)HlP.HLu9IJ*f6pVG!?;qQEj1k'R@KDQ==csln+i(d]4hFfIokHV!*Y85h?tTt!^CESb>_caJI#%PA4HCo`:e7-D3g7D;K3+VV$h>7B/j+^?CgAZDIDUAbK1:Mlg0%[IoVi@5p(D54GERC2ce`UG]"U[<-i>\f8a5qhfN2]8TD,]8D>HpF]Z*[5bEE=GqjreJ+A)i5tgMS.LQj2cWYH0s#K"-.=bH'_TNq`XfrH$b_).Su/(^W2a\WV)pfn\^6p/S92e6QK&bV1BS2+dWlu:6ai$*o"E?=50K$M"FiRE'Kq#"oZ;AX?096_W']IG+8Vo.,Ia280)deP?.L`Fn>.E7/be+ou$Oafd^pGbXYQ3LB$8KN<;X7^f_7cErC^W/%f*H.PgmsQYNo,eNspZu;t?_1%)nIHi_<'ZQYj;lmuBH;Wd&`iCEP9Uo62O$d#$m@#Q$E@t\>9kphTg"=0&umE7IN+>dcCftNST%!hiibQc7'5ge=kjST'#]2;5V9_omFLV:mNL1)$$*N!ZZ:[9?8G2?%hXG]Zq=;6@l\nR+M;k%IFMhf/dsic%9mBc;$8WLD+q,ds#KPf_,3?!g,,uAqlA],#"8T\<'.unR2Q[PE>b,>=m/B69#7;IVq+m-BKkk6rC=e/SB^_u>"H>g5=B&WZ5i$`dr^Ai!-:49V*&'Mp)gd_UY2J.bUG+[4nXuZ^ski`UmA7RWYW&)kQR,Z<5==,l90iik+^b$5+9DqTRC7LS4gOk_h5/7C"K)8RcAj%OIR=;nEBrH?ug@6=(N1(l'W,=_1&L;a\S2Cf2^IB4f92bh)TI3*P[O=KBfS-shd!DbY;Z2R;,"?AQ5d9<4m;,Y!L0,)q/I4>Vk.r'ZRIIZp#,Q./s%Cl*9a7Je2AGX+]&XpWM,iVE&4!%h]hH5L^LH1dt,@MR&X$o`nP&F"ig/W714_TI=`ImEZ00btB*c4]@C"9$EEDEpeYq./"^WD6FB"sIb9W_:4fp6mWSOGHK`Oenfa+f4\IH/ToXH[T8!+Yh^J1T#9O!"6\;`QB!F9h^qPOBiA["bLsq,kGkX0.qG5J4PTC*',asTr=1XY?uQ./fb.AI;kBD>??GTr$EJS>PRefR%7W;VM@q)\Fo,><,45B3-IY8reln_eo#Q*`@~> +endstream +endobj +289 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 288 0 R +>> +endobj +290 0 obj +<< /Length 2162 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0E968iG&AII3Ye!g%=:i;-?!F:Db=u.fEG`*H`&YUI,]Q/5$;"AqIsmY:@mP[34OZu94Ml-Df]S#qjO\*aFQI8%&m_!PHb-ZL;,)gq<-&MA\a[RY/8_uH4q2=;&PA(jTrmYMD(p6EX*)?VJ7@.Wjd,(DaH=r@V\rH/8;3T$S*4lCUeh)`C1F&@D(jUO]Q-$_E:OnoaumSS_-!r*DK0Cg@I,UL:R3!e+e&u69>d[`J-LBR]KY5`(T;Vjp+AMM?jbA\VAAK)0i\Jd.^Mm,`+"\B![Qdn.4:UVqSUbH*bdB4^0'2d[)de865UuYsh&9eh#AIU,*UUID/'3QAZ[-7S=Q[Om!ONBq=5FT0)]gEJg;#3JeO<:bkIdF:Qhkfo/OuZVV*'%89k80+I6m"8ON[\f6q!LtDoTpECkK6W]2((UMG&.7Q5:S"(^'Xp9a!"KI4p'T0gbEnP/Db<>4<35>/Dk!cAL&^32lU9fP.8d&VN'FjbP<`Gp7XBu%a$_F#ubP9$.Ws7@!iFd3gZEagi5@G7'%1gi@/K:4JX9:Ynl29LY7KZ['6oLF)u"*'nA_hV6ig@O$?=YWJmR_p?X@?V.H0qnetJ4di@@L&Lq":PAh6,XD3~> +endstream +endobj +291 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 290 0 R +/Annots 292 0 R +>> +endobj +292 0 obj +[ +293 0 R +294 0 R +] +endobj +293 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.55 699.109 265.05 689.109 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +294 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 428.28 655.109 480.78 645.109 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +295 0 obj +<< /Length 1630 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#\9lo&I&A@sBYW$8c5mVoFA1*S$lc>W?@`RLWrKnuNoCbE'AArH75,TCGN703#BVM-1&i;2m?2K5N4([tMS?N_qA*]%1s%Pro5"2,h57K:jGjkU#+(jtS=0q5'1c2W7<[60Z/E@>Qe\$?Is2O;UN-6qI^e(r7O*<%D&!#VAp5EAnpj:?HFNm3YNEqM#5(uBUN7F"\4ja]Nn\"fo+8H18'OLV4f)CI\m>OoP&-eE#JWCe[EV32&d%S&IG&ru)6adh0/(9@W=-qX>'LKoMR5<5SD.jN`@6,ng2#&nGGacFj^PDW[7`-pd?2+o5_2ot$doH:T(ia!^8:gYSZ\5B"$!)hMC*IDs]V#F$VbdVa"(.D,Z(CKXDVkQ#H!8QS2,AmM*KW`o+!'p#$)Pb0mdbo39:cYqQL,f]Qn(h/Q.a^4r3&DPH=Vn"nkq"rYf]X@A\qjQSr]"pO2S6RKG0JeV?l4K6D>hk_cW_IphjJMTMs_V9])=P6/rS,P6^[g=nT(pe7"cb4G?+@KIid!U$:B1/5c<;&kWe`TN1p*^rh7W/QlCR9Rc9UfiJ/tlErUU=;hdLKuCsXLqq)C=Vr!\j9=26(EMUpo>qSG3oF`so3F5k7fH9dOLpGB^Ng5\,A0HZ(i/fnFe*]8s&3.IU+L:j@E<&lU#"N!.,.t_7@1b"jL]O?#+*S_j:gBs!'XYQaAGI'_)JAM!icO1S"Vaj`+PPJ7Sf9s;OFhgGQoPq+2&B4do1KsII*`kKH\^IJ*A:Q(]_Or>^`mMN[1'ZlV07%Dc7uh^GYOi!:_Wo'4A'\Ku(oOJj@koPI0>f6@N'pe,_!d(co[-e.c[P+[j;FcHGH]!h;;ba^Bb\aNfbb4VMuL>j'_VI>K1Q2E-#_QE<4"^%?cI7I)e)*S9Y&5i]5.a*nCt8,9MaEWiW0'ET)KLs*TI'^Y5j0=-N/;dV4Q6.SM[!n:UkHS(!+\-HdMP5p<>\J?))5-%&kZ@DHV2F$aA^f@u<^k=0e3N`DFk7`sX=%'6Y?8,0q2+_3P]P5k-733QU*3lrO;p38Y!Jn>Ld\NK>U[XjIlb)TT;"%_p(k*KZ>[H^??qZ8RfX0\Es._]J=R$E_^r6:Obi*UmbqB_5(XAm2;Y8AuHp^7d'm&I4B!qTkMJPM,m#)ji8CRf~> +endstream +endobj +296 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 295 0 R +>> +endobj +297 0 obj +<< /Length 2445 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0E=``W5&UsJX@$HH!PnD"4E5o&*a-u@jAgYf^2Q>2P+(,G=:uSf-1pZl-Dc8dF+9BS_Q'>TG&D_#IrUg3TLXjB']l2PhBc3trKk5'RCr:jsD8ZpCKiVm@Ts;b!T.cNVfj,J:a)<;3hV\:tH\lKaZ"uH/d.m9?pMm<,j$r@K?"$lg>erm"X_Ffu[@dNN4#pRAX6EcLt8ZJ@R`+)7%@UCEPo$^BA=%Flfr"b2*I??mg^Gt;C1:`Nq@!A$EedsDG5)s7oi8PWiNQ5u.!\2J5"ULc8?G1>jbu!YZJS2O&C";$66^)\WJ;!87IB*K7\:_TfgZ(td)%qPRG[$.LalSB)L-*fRjO!k3A%\$b#5n6H:KHtj1,dY`tOg\^eUmf@pcBq7(Vp95`]]IDPTJW7*W_H+I`g7F:=GqE$_1*61&KgD7u^H!H"$q5gFO1FXj/6=(no1-uf7+E@6Cd+(@]`>!&^,WE2/Q8_SjEL8VJo@(CEBm(:,R\f#L]-ha9*gPfP;L,2+oK>_IhgQ/jn@(MIFq@OhB,Kj__PpsB^2LhF5@e?-`9`2FIcC](D9+kF'pNOF?W^)OAaD-1,_f=MZ[,^SQfP"[g?JhU.eo"b>qUuuZqL5,@`\MtcNW[1t[`5PWj#+N&]QSA;Ym3R7p#1S4l&FhgI!p/EE31(=Qgg54\CPci)]cI9&Ce>O:b/*&UeGOn.f/J&f)5.`k]flWNH!JUKQ5?aQaGa*3^9-+K(<&4C<(LfQe./7iG^K_NLEG$/P6*1eI,>UWHQ=Q+(!njGJqk2'/sN=`e]-f!+L;0jI#"HOAimY>"3n*\TH3i@BN^S"NkpH!aL_WB[L^?l`Hnh&2-0C-sbZ%4Td2ALla7[P!R['M!AW2&^@5-))Kf=diet882O"bR#4JOGt,&K-Pedm2-DZ*&m[$d_09oC5-8K2OqV7eU/RUtRR>=Ap3M(D40<"T('AL4G4Ni,`NL'n1bQ3W6;Ha:rcSm-0[Gbhds"-ro-8LLgCEiiN_r[6l/JK*a3/hKuYW#pnuY&T':U9Bt\ELEVXLLC3=C"CkJPDG)/;/q8^DM&"i;ZA)-ea@cYS[l="*3GuRf[L8Go5b'kmVIP'VM,ulB8KVi[_Y-m3iIjlgE_s&sa4D3nc`)$N5:nYnSCsP'3BVs;GLJT$]RQo5JbBHMOoCK:dfF,s.-e1@3,oK?)!Bk)NK6k"^ZWbmb9H_H([VZo8]f2b58OdV@/8L&7>0Yo_PhVMUBEo/R4j"=rBa.d[P/goA^5Zs43A2MGhTS[Be7l97e*2G@=6sPMq]2_gQEu6SJC)eq-qXQgeIJY5.f*LiX?q/?*47XiBtXnaWJ8<+pf,]&)4ne4; +endstream +endobj +298 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 297 0 R +/Annots 299 0 R +>> +endobj +299 0 obj +[ +300 0 R +301 0 R +302 0 R +] +endobj +300 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 248.94 344.027 268.94 334.027 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +301 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 288.38 344.027 308.38 334.027 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 71 0 R +/H /I +>> +endobj +302 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 148.65 279.027 201.15 269.027 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +303 0 obj +<< /Length 1757 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<9:),+'].0>TX1)W'4g`%i(bUHa\:,Qd\a!>]1B58Y\_ct+<]"jhtK`N9,P`pdDa<1n_Mf@YO7\RH?",73;[k7TU9.=]_17Jh,u""i!CAJmP,d!ZVP$ULf[7#%@%diEDOp?ZY_i;f/[.tbk`+?U'J9`#e87'O/QHCliCB@laT/uu$chH+Xcb5iIoD'9[Nnih#X(!Aabp6HfEoE>CfX:r5JZqn2d?$qf0<>LS$/!lTn/e]0^^33Wb6\<6?5+b`5BJc-4*r()=THlpFb:X5cNT5jT"_he^^o-bM-O?GZED+YSMoE^S7MnZ&"%*D%KRN[#b>m^)W=lUS7[(al'[g%4S$Gkr;#]IZP&mZjaTj\Mf]GEPV0%pNNDqRVGA?50kG*oWq2)iFpTdo%NMH@/iF,\$@he8[kBakm0f3/A#LRBp2"U#LkLZp:R\(Z!5ue\4`sgj"#(2cb[ZS[4b,K!>[;32WogY@rV\mB&';9&,tcAJX@asF2`"ah"u`V;JgqN@ICf8t$j/32M<@_++msmd5c3T(+\'=HPGJ!WNaidB%W3+@f2Qeqs/BKf/(1LkL0?r=.&R2VdGnm[7gPhp3)_)Sla(_>m]'m-9sR+%+-8P*C$R>tLX+8TPUUf\GX)hL,cp=-)"MSbAl*]G8&JVubI,!7QhrgBEd$!dldeSnZ&#&D=SKfERf"j*;;cQDYlqJMO@j-'bH0gh=WT5_+OBuCPp/oVYC$PX#S-O+"7KS[0u[;BJVA">]c0>IOeQ$1g-%RW6D@GH'7[)JDE8/XdW6blY[1`_r]L15o@VZkiF/Kh_*F:q%Yr9$r,$?Hn*Ll&Sql!=gHqI!OX=fO5ri2P0*&O9d[WEX2E`f>NS=NOOaAT%m$Z[/u/9*p@`kt%e5F+R4Bl^VnTb!*RdoA9;km&I(eHa[f6?)=]6NNDSZrS73Rg?7P"bT&3KT##5bKF95q\SR_i;88DhA]8sH\M*jT]s++u^81$sND5C!`@-H>NWB@P`->mEGR:`Kn/ffrgY/42,kN0D!ePT'7pkfQm(6>Vu)^g6UFeL3(LRnB$@Md>2eo"@]=TN*.N*/hXZTjp*GCTTVVP,GAdn>MADlNC'_oceZ#cc,*>XdWAPd-Rs-/(n8Q\LJD=rG1Pn]IbSGA>l*mXem=Q+\$cnis6_gK\oI&F':WHPDd[tah+Cb6CeWB&4b)ICK>S7<-%#ZA\9WSqiT@V'SW1N)ZN'LW_H,dAkB?f#1O?;r(^W&"_hJu%ZT-%sPRrJJ)t&B6/AM,RX>_^#aKWT"VD9KA=Q6)qBu>42:rXWl5Ig@KUs.r/f.4.8cIF6Z'\8s!1:\r=,O<>3O3-*!MU6"-H2)-(dJ+7Bo4j`~> +endstream +endobj +304 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 303 0 R +>> +endobj +305 0 obj +<< /Length 1718 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm;;0/Kj&:XAWTRKLNcE[QS6(7S;9:WW[,Zcp9g?IDK"2V:f'SB3>f]i%KGJP[?Neime)o$pUrNs2olI5TXfGe-#(;U8\iN.g:H&l&EI`)S0=C[^A7]^QCI5s@eBm`(X)1N-WXau6XO/'7AZ>u%>l%<.Yhb:o%8&s1>/,U-tR751,>QV5=fOHQ)0\mni,I.nH\3#Mio06$VI#qn5>a2I/]Ckaj`E9fYjB5?;.*9WPXk^rZ=ugi1LFD8!2:`qm^9:fj%,hY#2UQTCNjW\H[TRGQ?j[-H;lK*+(rTKZo,,*\&\c*mLo*q=da4,^$e/XDK2sB6uAB.IY&dda3OT'&^>8g">a=^egn0&n0ON?s=4/2'Qr6pN7\TDs8JW\S-nPr"JSc)4-Mc#G,+bI1,jH&gZr/95CRNC'.Q321?5QL"q,p,[3]U?`d@]u4P3MK_fs1>aSmP_EUl[?dW674NPiW`qm;VA@5fSU?9t2^9YkJ`m=W-IP&?3]BYOYE-L#.a"N*@)RA_aAXX>]QY683p\r7d1JY^s-43O<21k!Pi8NmAW%IIhe1isI6fbM#LSg^SMN$T,#eNK]N2qfZGXq(V.b-+(7DeLt<*!-kEB4*ji+$"=E'5:X--("fms2N^5IJdUl[!fiYMqBadR+R6d:%JGV/,HUT68eS'ADmsUcB]AY)A]kL;DJ4SP<,`J9VS;WpLB#M%r9K0PC6=CB\Mckpf0T?0B=+3b40Y4"@\d812/)Y;YU72ZDa@pUph'#URjGo>da>4m5^Yfn#&Pi.:Cmn2>f_7=cbpFk?N(pHED`_9HAHO]tu6p+;t6/`M#2JRrC[^cF/P\eP#c>03f];b8ajm<8QV%dCWsI?+L2,g[(f#eg&QALBjrd>:Y`F',Sg_@uAqQO.J`;&kFS?bCr5Ka(jUu5JPRf:I_3B=O%RHq?<_Ms,&d9r7qWi/[nG>[cf0PG1LO@@QH+D;+\(e%4R6$/HVRkihQ4Gm"3"Gj\BKXT!7-,Wg[m!WWkMs[JpLh<2(+o>b^CYB18hTsYNV[GF%Uu`dJk,&<;?uCE(TkE:YF;Aq(gmqB?&/WDN@b!2,\MPI]++q3Z3!$`8Ff-^MVrUDQ;-H`!C]BlnFRa*VgMps0;'q1I8K^Q>38tX:I,$rWUe4oj*a7AMB#dhk"^MJC\kW3NA-5gdl4pHaP-Y=&OOCm`4Q'REl>`+\kr#\N)FmF[S6fkXeWZ+*?5Q+\;Xp(BX`4$KugZkH6Vo8O6[M\@26UK2j$"ig)/;Ph!18Ci/,P1@%:P<@hN_YA$/9965conh'6;NRmpke`>"1Xrhm'lWr;krXM.B&\^oh"6r6*q;pG;+BeisL~> +endstream +endobj +306 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 305 0 R +>> +endobj +307 0 obj +<< /Length 2473 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHM=``?+(4Q"]@/X!8'1:Ok:Qg`R/)?@8:"7q^`-m.a8A^T8)3D5cJ):?#+q?"dG1o$+Y_RlAcLRM@i-2Mkl0RK[MUu\T8@pP5=O*M2#*nMc5\HVeVWbH*uSllG[Jh/h^,pbI9.NSNi[u:Ep/Y[;75Lk&Soc/T4hHG)b2^Em*V9k/HT0ec.6ta)!jsP^JXI`505ZYbnSGL)ghPYp>>COsC/">-R,3FkbK6PKEe%4^juIVCi_-X3IH;2&JB"51u'dq*bV3]RaEAW`n+q5jV/=it22[)Y!i:..*%IP^!NRRMQcJ<\&FO!q=INVPqNKA-<6pQ(V]8;3f,<`'WoYi$8T;:E:6fED[*Mk.IS1_7$k/gJ(8@/>bjEi?)V[8bU_1Sg9sf5(\;[1UZ[Kl[*HC,U73"FrA-P;T9UEPhk)UHhkQqp28k%:W8po]'ADEc][OQeee]jP``c1'ekA/+1+n4O\c/#*4:3l6idu5KEt284oOGA!p4?@H+u<&pUUt,h/p7h+pe:>U9.-FVg,aaK"'YQ$$4#(Qe4O&4/`e;47b)!%J,`Cueam_J?_\Y/RNaf,?tINJ84M-];V6p?n:kb<>m=\&tZ)!ZGOCD9i,btY%]Jr?Tq#B2-Wk^>q6hG\Ia7it.s2Lg99rj:6eKLPT*2gN0dR=%%rV"1ecFH&&>5>YrVg)WCo2ud^`["4-!E'K1t/s8/1>e5JDVim71LZkHJ*[q"uN_hFPb01S-;hCK:!LVX8HA2GEf#+W)i;&"FC*#`6QpDF7&6s64!^$O1:<#S($2O!%_o'oFZXJe+m9P?a$\3V)gfrAOCI/SYG9C7/2P51,BL$=BBarRfgPN^U7f?W[ZI+2e,lX:N*JZP8kBhNYKU6m1["`2JZQJUp0_Ke6N:9S8M2?@'rK<1@V3]FD3]]khHJ]<7UR'OQqX$D'1XJ"Rh9W,>GoBsm,a(*.2[EBp^FL3E3Rl.TO%4C7J.H(&W>]f61Q.]6Ifa!_n"OQb]B'\14SN=G'G;,fB`f`k+Jlq8r/nAF#eb"k#0%\8m%./GjakZf$R-3T:E9A2OdflrDcnm-tWn\(kqrF$VGgPJLZ]oVVq;-d&a^Agjg'FaT3dVLeO;8gZT:N_F78urOfAM3[-gQY:5tX3SmYQ==@6qhHDJ?ZkV6[__\V(Nqnf/TPWYImc(N%fb$FiU'/r!6Ku"L:gUTAVsuT.YIYM&Lb%*URc>`c;sVqWGeiP/-t6cVheZ6#&&`-s&h.%mZ8:M>jWimp,+u1Xe)Xo\'9Z"T["eYB:k<&%5CN;"rQJrI]Ya>s3HW*']Gu-Xj.iFuYGQG+#NKPg6GnoLV87E>m5[dilZ(q<8,b0[_3>^XEMm)'TELQPDt^FSU\1Ln/Sun9%V@ABA1dQ\k8f`!/W*nN=ZG[u(n>?DT5P0AcV)W%6ar(7TG4,ont$ZO:5Ki% +endstream +endobj +308 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 307 0 R +/Annots 309 0 R +>> +endobj +309 0 obj +[ +310 0 R +311 0 R +] +endobj +310 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 207.54 330.716 253.1 320.716 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +311 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 138.1 170.856 183.1 160.856 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 152 0 R +/H /I +>> +endobj +312 0 obj +<< /Length 1983 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D=``=U&:W67i93b(YonW$Gdt32,`\A&8`RDI0pCZ!VBC^o?K#>GrUjIc&3pPdj\.FU+i2tcrk5#--)UfAK*'4YcSTeHch4=S0Za;8mamSQ1JH2C@NXNN@\S'\RIWp'sohSf")Zq(I02*8fHG2/k5b7'im)B9W$kZ?Xe@Tsr1n.P#,/$7oo%Q>`7_AXSEZR)nHkQ[bR.F2$.qgSuA+++QJs_^#LD(Mmr>j@/$CsG7@LHmkYXKa'VucA$t5iEI&VDtD*g*N]hBo#`kWh\3&e;gDQ\Kgm:st=R[L/O[EM!ecZ]rJ!'op'!$W3p@?F"F0k'Ug\k&.i=NbYZKH3-f^V5&MPBr],3r^2/^8E'hol('*TY^/O\Hd!DptFCPA3/fY1<5>7/TOO2+/@74"VO1Y#m.K6TKLLUEAPpY\&0sbDU@?/uRQ.CY1M,A+pGIj2n<+/#FC0*jiM=M;4+>Gh,eJ[0i!pIZ*A^\.-kKr3%_27<*Zi[-s_a"SH#Y+'G0^X7>V+MG59g'9#W=2$tml_.c1J5:XGUe]quo-3;ae#RqZY+cn;8J*]5XfOTI%^1T;81K6^fa.Uh3bf"NH8)d%.q%W(GNdfE<>-(kY1c\]g6[>lXuJ@!5m`>\\I:0lgI!c`bUs?SBFt+:oV>QhHC)$"?C1H5LqA,1tbn;p"#taMccI2D1N09u?LiI\5=Ci^g6?"GKpJYhDbD'l=1Y>/G%V35g'ODFdut,F47X/^*rBZH466WNq:mcE_iY3XpN5?>ED6F@V2=S%"R,i];Z?ZJ0bh4Otg:A\.HoI2/%+oT,0#Q1dZn-:NU]3G&1AaNF3*UAic,Q0hK1IFj@_*N0*DENqd.BL-r>dFf4G"'K^da63oW-BQlcn3l/_H$IaLF]44_m6REW]3"O>LHT'=Um>j`$G2)XJI'#-YjLEtl![AUf!q.Yo&rq'b*ctjbM&`qj-P68Ck#5k.;([]k/Tb+Yu^;\l]iM*eDYNncYO6j]CUb@(n-\5),&/6>8iS26jaUs-Lbh'p!WuhqTjSRfln5Z@=XSjZ0k=?SmkBE?K$h98lYB`kgYUm49ZLdXY%kde%`uDHbGf4VtN;DM>/@YW[i_kO=~> +endstream +endobj +313 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 312 0 R +>> +endobj +314 0 obj +<< /Length 1532 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0C9lo&I&A@sBE-+uiU*]=u!35-[MTjY)dY06.PL/WFfr#0=K0D-IGG=g^2^kHZX`DNU`4R)DFtN)f!uSRj9HP;p&NFrul(ppR>l&#,;)pc\r.X!!bt[]@K$GSl:WQZlN%&JtaUo6kmI:9u3o=N5DtVLunapj+^*T)LSg*;ZVETK:8tjl?]'Hil2?/,-hiA!f:X`<$=u6h>Gh-<[&)UGQ2(?Uee(7_(!D)']0Hmu>5-QDqA2R>gmHpB:8WPou$GJ`Y"A'Yno?GIroBo#uae+ko7LoFC6EY^KHW%=JYj2S@K%&S>T\mcp7H/!lU1YHeg=fib_!0LgW[\oC+A!IS'0>2\RsKBnT*6r60bO57Cp#?bbA$%,<%g&t:OX:+-Vg$I9YJ8G$jO4t`uf28dmrD<^:3q#`T_1:gVq4Kh&eBI8.<=S=Ee@'==:]+Ue_T*eJV.;Zj__K.r:ttMY.UEgM%G0A%[aWgF^uYNp!E;:oD7,X;.Bc>.Q@Zq[&>^?E'K(4PHo`--(.C9-h9BO6%Ae2A8"C2)apE=$pNG9Dh5JgNJ?8<(B'S@?7*)f!RiX-.h/8YK&D7[Q6c]iVRl[C1!%#sl+#P26pNT9W]PY5ouLH]:5a&bhMJ%YSe^Z<45]+i(hMV7)L#j>^l]Dj8iE=9_(!`cDVsVG(5*\X?_R.Vg#AF#4Ns+j\SSYC4DTO6J]TFX3gbjAh,6b!Ibbb_Xn3*(m(2e4!!&o#;,*+^T+O%g^'2SfK$,drD>PqIQ6k2b70e#G6*:^/!k).4:+SjqX+j41b>m7R*Mce/2G:;=6:GQug262Pum`lpPWNl()8>RU96TR]AuM9N9U\_TTLFQhj0Jn)Wp!K,m&GIkK9d&B76BFbZO<]tP`Fn'`siH#A,$qeqcgG2Y>PMBj`SZkI=(MrD_7aT?Rql+ngl4^gJs+M[>)F8pN$D,as$K*[ESb'!MPB;k;!nN=nTNBPOn[/Onr^54N8eS"[Q2HVej/$-dIo&YcMZMk$3b@\*~> +endstream +endobj +315 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 314 0 R +>> +endobj +316 0 obj +<< /Length 1303 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU3gMZ%0&:O:S#lEZ_c'VR0%,(^7UhS3aRVn,Q_dX]%RrR5K-'aYZc/3^nBg^R>CcOtH5Z5nOS2iANL[V.9+/bl`7l4?+mn]]L!=9Ij+9Nc3I(g"8!?G8U4HBC\]l2g3iHnCQ:PaAO`J+(MN.=rZ:t"(R/kcV/5*"['YSF5ilJu7VPi1`h;h[\Rpd5.`6H>B??o47RJktFqn#kfMR+Z%W0!k0O'.(qAr,n90-#pNkiLNQt?!d`,KBY&G:#nWq=ojttRlch$dWkAnjXPWTa3;\3(DW,Fdh'*!1>/D-(GccN5UcuL2^m7&T%VQ906=c'DjcuHkqNS]..s&#&A+C)<1pp**fVi(`:M/sQ;"G%Q",gWC*q7CSR]n``BI[WDd5K8oY7s8Xd]KPJFc\BV\'4-(;WQF+['+MW?Y6F>O[VXQ=dA\&d=3cfnrG!JM`O_X/q3^Id82/ml1jH;MjTg?cK%#*^o3Gr4#T]'HjarY&i61J$EB5;9W>$e?>@um#/UiG^Z<Out8>aG39A?sm)^jM,CH"(0IXX5Zo@U][#:L`WFXh`a4nE>d6Re2fBi@'BQ&hIjhmYb)ojV0+mHg,;nMur>0T5)+Xk(Q\EB/WSW4eX!&@WeSUo`3SX_UbaQC4[?@;2>E5$Wf;=$uQh%18)ORARVof?;+Dn9:7CB2[`-qCj<:*_19#hkk(^,/S?3Pk`n1I@hdE$02>17_c0+MNM??q4/&=O^Oq(X'9QCDc:?[m2+so8tI:2:L??M3Jd!X3?@gigrJBBVsg/E$AAVkXPK=<>rN"XJ:OSDc_56i8ET16[.^i*K`MEY;kc.mPbH8mpX'XN0AR)Brl^t*(LC'PkIRI_"pm43oX[]E2]BbbD27<)JCD5UApV=_hL%e6g9&H']cdH8u"7.#a+eN,.GgI*C_o(<([buE5j)Gg+Ipr!%^k/1_p%p)*U)eBF*3Y@<8R`E%0^]"1NAi!@"rr"`J9:I7,_h64VCfYsM-d(83a1oYJ*eV$tgh(es(%cZW_e?D+s2dirkljT`j6:K(32ZSBbV+DfM6"GkT*b65Ml'0kU@J+^UNOLZfq\*^2MVn4*CAPs5gd~> +endstream +endobj +317 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 316 0 R +>> +endobj +318 0 obj +<< /Length 1543 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DgQ(#H&:O:S#h^"I&BhXXoNSWG(+k[n:#-2]]+8/7[Y;ec9$utemlQbMBdf+PP99laT]jdBc5Fpd:iD\aGK\+C'A(sRhp7K3[N-lAM&fbcR)!t/3q,@Hf(7tsakNSu"4e\i#=`+1*nj%:+$8OsiOZ/'Tii4cdbdL*G1jl/\*X#6?Bl*Uf8[=VJVE2h`.PUV\@*>\bFS;[?#J>t2=j.K4+3gu*^,hTIOq_Z:N-59P_Xk\I]DC=`OP]kcX_/_VZ!aEMG6;([H.fKp!8Efr<(6rNYS:\8BX?HX8jo.i;WZHrX&oGc\aQ[BRX_[=j(.W"=6f5OP!I<3hWX\7gtQe]%lnAfij^cq0pWTI8uP;Ygj0!J&u`>@C)DZDodOgbckciL;Hs&)"R>Zh:G7/?9L,jdi]0a%f($ba6JPZB^OFM0J?@n(LZh+$:S,B0Itc_fc%\:1u2mJICri*6YQqJ:*TH)DcgQ]Zg(I9aH+m"#dNX"!H'&pfVr]naTDGJ+.G\f"gJG_jc#YHr9uZ/[J1oMGCsRuD#V$0cs#56;-aI%;*_gNiHJcVb32Y.c2`f3KWD6E9`uL\f>Hb]&t'5FgeIk^L"('lAdaq;!O;/[CVe0O/?Tq)ZMEEg&:QK&auu2Ktu:HP@XI5&meNibTKg`>M3e#Gqs$\@LSBGDuY78Q04&,h2dA3t.#.-QgY%L:h^UPV.GipSd2RK@AMXTCjcKg@a!"g-hgp@+-geprarW\)-EoFMK(JHaO<"/17=!IZQQR^8`ce&U]*dkjWPnhG*7`S#YDZ^l70H8o^e?"Xdo804V_WAr]XpKIh.8?$V]D[gW.ST$M3.TD1;Phmu7U2mlDNgZoE"-)-_)5kW4P1NaPoiZk3>q9H]rQ;2%hjR\"H&3M8n/F5WP`E[s#enPg.sk,'oV3^PW+W/Vqu\>4qfRJ+Y0up'"64$'D1\=R[:VomE`b(8X,K;8LYa/&BT!ZM@B5aWlSH:OWp)>ieZ6?*%r8'mdL#(r&=u?an7dLo7Le`B-8"qQDU`QbM7YLn.I&4,;2KR2Da8:Q64#A+"1#FcZ'"l9V,aLDUaD_#IV02]\mD*/NbtJ+VVe_rZP[];nbRQ/`CmL=kZ8arIf^O_ah:(e9^3,C'IK4f7X)(R0;2&~> +endstream +endobj +319 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 318 0 R +>> +endobj +320 0 obj +<< /Length 2028 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`UgMYb8&:N/3&:(5a<9q6g&f:fc-SA<(7[gh]JKta_[?;aBY\2n?GT![\3_mcG0$/1)p@n'#-b'uH".#L1=0#!J7jddBZl:EhuZG3!q>2,;k&H:F\Y*U!P&mZ;bNF9ma>p3EVPNVo#4jpmFC#Sh@a%B9pQ6Vu'rH4m9)M<&.qNs8aCdQNZZV!qh_nR?a+nP$LegB-2r$q)-gRe_lnNH#PMV91H;@HA^7la]r4mOF@<n5I`CUCk&BM_3BsK%BTJY<8j./P;NS95M^)]:@9\fgkE=fmtEuC,'boHODgK7B*NZ@X5,[$1)mk4m>^V$*$ipk0"CObp^W*!\*a6;!/gJlmU3Zk4QtNos_1KK4^-C-s:eDr\:-EL=pkW#dGu',8#!*gETZ_(SZX.G`CE^R;)c/Y[m_a/[^k$7gJAJRNrX!/brT`.LXdcT&0[SLr=-<%GnlVB:+LM'FhabdV(9*sZK0k0b83Qq+!*6Fp@`)S)"slS^PXPRDaGVG`>R4gWm]c^l]>.U20Q\qeij0FRlmIIfDcH/5jUUAY,(*"%u>J4Do[,-+=_alnre#D.ui?tY"Q!#Z/dH)-WD)[kMRG<2;cZd+9^70M@:_JQQ#Boe=eLqSL-RqruI/p<31m_T"huFK")-@p7:]?`oCee/*!"Jth:o&>&@]I#eJMp/9O;nX02-#hKJKZ+"LQN1q%GQl]9=^G?=?D,>FRkg*oct''8i[LXn4,T"EKZQ5:)+-8>-W4p#')$rO2T\(Llanh&oU+jhDci*!_udJX3h4c;@oh[Qm*,/&f!k8]gt19>`jgL^QtuZ[2tZ;(.,Tq;7.`a6kZ=cUmrGQdm3V7Zt`qM\=$DLueU=M:t]nd-)^Tk2dV1N\$NN"cf6B["3RNL^3\;3X357T%]c@!m?$fPh/@#P^A8(@[=N'P#T_1BF13ibZ-+u,$PaAMLRB'lg8OX.2RfRAX#n?XQB%A11DM0,&c9@,PM4gqdGCC\\@'jeg:q96VJbk0]h&=-AB`l$O!P3nP;XoLJHi9c2N/LO3t$dk!`X[qr58!q/V"Hr@d8WX6FkSFhL7A)5V.ZX`J:md9+ClRldIC>V>bRZPWLJ*(=:/Ob\nGl"-iD1fE9+gpXq_&$"@4Kag;g\+7nc$51LqStU<.uLiG,?uu5cZ+6`_jL2I&TNiV<~> +endstream +endobj +321 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 320 0 R +/Annots 322 0 R +>> +endobj +322 0 obj +[ +323 0 R +] +endobj +323 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 454.45 691.866 500.01 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 324 0 R +/H /I +>> +endobj +325 0 obj +<< /Length 731 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gasammr+[L&H0m]iK$B1(oAR3WN)2[l*P0,\rHZMg3ta<8C7iX`_"qbIRfQd,:2&-,e>eXn*[lqI@`FA`;/e>[_gpKB,5bVMg4S_=?)^W4Q\j,)doZ:ff@rbI71h:qA:A.5K$994n@VrK(C#=+`%cd>/#LGG%J.eTsASiEGOPOX!>YDY7F`]D/X5PK'bDK)UJ95!\^=uTM(\^LrS],t"LA-XjTR5;(URtKP;n9ZU08QLE@Rb&)ec<%Ub-]=S@tU2UfqEcc1`dbGi79!J+;mtigin<5=.92%IP]\U!>]j4',rqY6SgmI'KJ65SeX[@H,?944/h09Q+=h&ZrWrB%Xg=Cqf[4C8Z@[G9]F`#0iOT4Yn@BV64imVY5C.T1Mn$0GiQa#.2MKZ?dNDs$m>WM6^1O8dq,lBR5"E!j76^Kj6,oO'Kk0ii*a]=W'$tYMNV2)CS%.*2J]E?ha.m(WDa73L1Qchh,ocsl3b]j]WM\n5D#G`1-mpq_^6:2_rkfOfBaBpN2g=Dn8Lq:83.q6j3+0f[%^[D;tjZhnBIMD~> +endstream +endobj +326 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 325 0 R +>> +endobj +327 0 obj +<< /Length 2076 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%$968iI%)2U?G^W2-C0B%jfebL!ij@N5g,*f]LUIFkU16skE$[N*J%qX-R@m6mmc7h4+:*@FJ.I#d)bdF4+`Rp33*Qg8iPU0S:&#r#hh"#K$k6Zm_c]%uIcMHg_m+!GY^^a97BA%KD7T?9,1!85:ZM`)@_#mm]W?AO+Ep^a/b9enr#F#]"WQ5?0fM"tpP7aW05qB,]mmKOkf/cS(!e\H&[MYe(m:h`9J1S$U/<5gq#]7'E@0s3ZWLl5AMiAr$dt.Xi1dLL&TG0X+)?fuf/6gbBe^qOjUBu=@5babC<*oB::K=&[Mqcn=SoHRncJ'2OHX^H9,2[9JJh[1bbqIDXan>JK2)[EtdYsNlga^tF2(s8C[P$eHdmm)m7CsVC$3i5D\VOBK6@gb/\s-(.k7!pAfdl;U%'Mrd6,O-pM$>m.mcqZt>T4t$#5e>pqK6^EhJ@n0A<3tM=OK]m)+>%^GQS4?atQX?s(RY#=R.PdO'?NGegD>FnT\('8"QpiL5l2Bd$sQOkSBC13(RiG9U4X7Q0ZD@8"=-*G,I4Y9i"MM[]LZ"d*=b7?hAOj#=jegCD$(WM9&\'b]t27%-8I-^Ts?4pG95*:n2^OSYlPJqYV5ZQ`q0o7LhU6H*l(6EO@F)S_O"T[o6!dca5l_[e+jK4hJ2c)?-]L3,)]@m=BfLlQTfM#aF!'Qq(+-^E"h.4nq[,qsm>=Z>3$u[:uKp]Vd'XH;I@MoLgoX<1k(DV+A/q:ekLPR3o)-ZUY*!(6(J'JbHe-K-^.+lfd*$)qs'an&0p\P3RK-2C/U.>Ztg;jp2._$Jib;#=R>]mJh$_&-af>:J`LI,Z!r.ij&b=A@&aS4E"&bdio-3nl%N6lRVSjdugF"G3kHTQZLl_8/Ia%A.QO+$Y7dhcJcm$NmfcBY[!2dX!i8*3IgCdATo@ms3DKoSfOTWfCWo;[95J7bP1r4BUki'gpSlCfc)Fb+%CFc=f6:JH`j`%*SS_d]:)a"7HafIrkVWl9`YJ^>m"d=;HJ85kA0imn*DbaK%G'GK=YXa%shVrWc=f^((~> +endstream +endobj +328 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 327 0 R +>> +endobj +329 0 obj +<< /Length 1648 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=,gN)"=&:N^loSBQ"XLlh[.TM/SERkq.>=iQ[cGLb60XYAE8A`;pT@O1q,;arF`MJ^/H[,XLK[a.4_?*LIc7"EsCFSHnUH5L:FO1i\I8@,'%N>_mE\A[ur#fX:J8FeG-,]FO,$]r?R:AnllsUhoh;ASU0rqpD$0__74/V[IA")23M=E\<$ZbZ@C-l6Zq;Q_nY,BKrooD`O+"pq#inZ#2sqs4+Vr[p,F&-ED8Uogl/F^#9r+:jJ**cRI0FGEacO-&Q3^SsS'sr_.>Hcd^c3b(gMcBXsu",L+ekB=)sUrd>ff"O\iPW@@RH9+S,q31"HBP&=J"coI$V1G#T?/&P;]b5ah!c`TjnPI\T_15og"\aAJ"2K[i[!dko76faX':a#C(U53F7+9GG>O`\UMT]g,*NfleH;:Jd7.,O-f&?Vg:V:FU:HMusa;sVtU'TR+Y]@$EAP`jf%\>XKF_Ah=l99qH9>1E$1s,kBkP,_lb#/Mh*kZ-OpE%=60:VL,*<.k^1mFqO9Os3a7M.*Y\,E,0J:\]O&2c#?D\6B9"]Fk7F?ihiS?kNXYJV4SQW5>icX:Eo/qJ/I)>U'P0TT<<9:s)26Nl8,ZU(+$`+=M/@U2h%,I!n(&44d'LuVl$oq@SRIs7%3e9U2-r`;DqJ"Q6KlJa95:^WbJL92oBNP.u(9lIF7b*p_IjmIF1K3nSU+r4k1?c(C#PcFrs:cc@=Yl*M'/XcShbKAemL+%?@P"[iF:?&XHZ^'>.c\?7@fYWAHZtR/Q&(\R^Z%og@#@piP7Xpfs7klXM;$VbTcp4OsThrF69HGXC_BYo07L09&rEm3:h$c*KWt;selEYM[(hnSprapYm*]IuFml7O,/^"QE8*bZ&1[+N\SQ85u\%ggtJ>%eA2\;j`_G2l9[C57G?ma-8+dDmk(fPn-*KT7$V'~> +endstream +endobj +330 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 329 0 R +>> +endobj +331 0 obj +<< /Length 2768 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!Sn968iG&AI=/kgj:A;Fubq,<+5ZVsZBuVcNY]\-IhC,YUd]"Y%RmpB<"&r0@I23]Zge\V)d?m!ej.G+n@@YJ.W>%U=ME$l5mFc=$Un*BIQSV0*=a4t9W""N&GW.*>Wh$BVgE#lN9?EF^Ei033d`J-=l=,Tib8L4a,b=J];#Or1,b?DK2D;AZ`(X:Fhlhs;+)u-#$c,1/P8?_V&Nbl:5Aot:aEXXl#[&-3Iqt2-/;u?DT0YrL&lI_,PXAIW$$[-sGJ>:Z@"9_*+@[j>0-]4k,mk]Ecn(1Q/Cc]q4Od$Kk`!1ZSZB+F$6n)+nrU1/IHCeu/Ms7tUJm6+R,a#r<0M9c%Wk<,K.=*g3:4/9T2kEE_IXTp]pr^=Mm;CETn/VogI;,H`1,gO133_O=@>A7]R5ah"7CDHkbbL$Sg)*O#\>+;E.(IElUoW[PH`"q/aIAmN<_G=l>I#lpd'b"A%K)*%[j5i#DJbroBmBQ@!@6KeQ2N&Ca%q>l0m)0PtEQ@(@d8tqQXF\FOl^)\id8tYEJW=)7:KfIb)tRi!R5U5b@7CKCAMVTN1?KtjeqGn_ALH1^V.ErBh$eRW*KmC:2BV[PNJOQ)8h%?X5AAT,9+j2kpdX<-nic]mUoi/Bn%Ki/Kd%L1iVm16'8>)k7YR/S.)47k_/Wm71erj#qesaN_[dhRrU^S6b$SAHb^o=8?Y[]Ok?*Ig)I[W:j[r`.G>n:"YG<@L'M@j8J`^_tLk28Qi_L-">OXJTKPa^p["KZ?[*$-KX5mc.%ic9NkZ/30.fka;e:9!kAI1KoJdECrq>a^6qY'%5p25[b:T2(\V.BIFSskrOY5cR6moaIoE!=bR'Q%TV;3WL%(reHVlI42"WOX<:"Mfhq,g7\[u8<>&RCT97-HDknSGmVG3ZQ1?FC%k@?Uk->m'9_sdh:`Seh_#R>']"XSJn+.+E3)IM@WI>0>:]BZd*,X&fJ'o[[DEr,LN:,hO2f#;)9R*AEckP`>fUG%NZ%ioZu.u67dbBRSZ`;l255L&:UMoD>>ADE30no#)(eut..,02V0H;jUfR349o"6_KQbM*[>%[.dP@Ej%62'X`gh*#eP\dI:WXFSI+WZ*q#.To=0W#=r*2%<9T.&1kUt1/AB[#$J"`tdZAY^Fgp%&;-K>:=K/>RhZSocl`0_"Xe>nZKe.Gt3+YLrCb*>;,47Wc;!thZIBHN!f#$lY\d<2^VgGQ);\4NhJA=IdrZW3GO:Gr$,Z'XHGR?fG6TuiK,frMB6TVpK+Xq>"IX3khAh_0GVO;`We]+$I$/2#dB^4Cg/q#eofJ+YnWV">m03i]iHM)jR@W'jYl/0,O'5fG2h<;]uWJ&.F_+7Yj*MX.8`U6a@qB%Crmafr)1"bQJg6cbT7+_cE>JLIomt`EjXU(p2cAB$DK[Me,aVdk@X'QjZS'ItQ!6B99!.kNf]FZ1g]Tl_h)0JSDMd;H<+Y.7D*[dSR'eipi7;I@e@>*s?pRfU:f`G%(\"S3;JMh+Bi@^lrTEVg$Rmcf$T1heZoe497q/pNBgcVCltrl*#PPl(UnPk3D!d7&g9[\KM'U,QMg+G5Qr9,S9gHa-%'F4`Y\4[Gg]oD57,HQoc?#:j"'k!8F$)hReXbk&0(dpZO_W!FF87L5jX7K=,lL8N(>>.oj&aGdt-;/%*I8?6GFsG[",iiYg?)d__j:!8-%s[<&"9#=38f%VkX#)un7,+beJ72eMtbeXVh]Pnm'u7u2/bUkrqtSNlXb*6bL8jo`(kgDHiu&MM9KrSV4'^YokVHIpL;(*RAe`Z$XL>_ZtARcCb.mR5:@4Vc^+&YJ<>!8%d24Ch0'YMGC^1r=/#24h#9a/cTgX$4*')UeJoXu=r:l%S1FQ/23@EH7O&_qNB*c#d-]aJJDp:db)j^kD>pFL`hpsp$hpfKF\2HK3S$f?1S`":`bgJa$d%ll,9KsA&mrE=nWUG&#Fib1b@QQd*4s0R^Sn2dUAME"48d<&QcJoLHI2'BgZrInGj>2&an7c?54Z=0ckh0:Ha!;W)-1\J-KVF:[:]1Q&^PHEP>%Q($J`a;jMR/a*!(-XU%[b.XV:ATnG8iKSM5cl`='Z-Mt3Q'.F4X2oiIdSfc%(7CC.$@r,It^B(>/)pT(_&~> +endstream +endobj +332 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 331 0 R +/Annots 333 0 R +>> +endobj +333 0 obj +[ +334 0 R +] +endobj +334 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 460.57 627.894 505.57 617.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 73 0 R +/H /I +>> +endobj +335 0 obj +<< /Length 2150 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`TlYkN9&HD156LZ?-W$tRVouN;?l!u)r)GJ*h/PQ#8Re$"U_%:j.Qc-4PC1TVJ`LGdtB4kFWbG-t>EI9bPY/mnO%\h/N@>%VJrIrfXVU+K*VGSdD$jbjp.Y=cMYpU<^nrmi]s:L^J;\9_dnWUD&Vn0eTr[sndq,*]VhH^+$kEr08IRgp'p*()OqqHP)_\]bqrWokndN&a$TA:";nb_\X;j5QmOYl*e[_+H;[QgN$hYEr7"OnB``hsL&W[Kas2Jgj0+KLDXTeYPYHO9>E[gbO>7Oo75.3FbW?S=QME53Jm2hRpar3iJ@_-/!c=4mmlG@j?PR&s/J'/*'ckXuW=/JUEF;Yh6i%3Qu]L:=!O@*9QENe)$a&&>?6jZ_"s!ntW!=8[DhRTh8udAeQ6hiHR?-'?W%iq`cCPGg%-FLu);6q%2C)Feu`NroA`lshb@`Y/+sp?mMc@\FJ!@N(&==Xs<#0/d:HdNIFM0&,`e6e&ZCKXF*c+DiZ?9'17E8.j%.H-b8E\glU0D`qDoO5=&@:u#Zm\riSEgtK)%q?[][AH3a8;onP$i2?uln"fjS'9od"";a_>uF0!"G>TbN5#HpVRB('#qI)_#@f=BXsuY,-6T$^*WLc]l^*B`[bBf.aiOJo#^)%JF`qbIKli9VlVT28LuBV/i`JI6IaTFpLtUffg0(JpnsNAI'?FIc+JeXP)PrI=a'1&0U\Eo_SGY<;``E.B(T2L)6.&U)N88#Za3TKXVE!;Z5pR)"Sr3U=e$)!WC9NpSW:3G8st?5]56;1a'ZB_h%i;RlSmGo17!ifN*u_2q,il6HN^OuTg6P3@$uq0TLM\H+JKQ*0+$^>5@,U#?rf[&#P:(rXD&h_>gJSNfj,0nVTmMUfTJbF+/pjYW[^6`ou#a=>3&MI,s&rFe0-0jE?KdUZ\T/]cUJ$JJ8Vp\2^`(I"_)#>_W"B[Q.gJ!8]E^,BDEC%IEEX0;6;3.XdHE;+H2@1/>"H#PF.*);V&g`H*)Wg!J=g\?r4:4bJTkDidqPf2saUYB.a/6CA)pb+Q(18DRTMqcSLWA0G`.[AS$#,q$)\QA@FjA^L_m$G=-7$=8+]i`)QB3ddN@o#JLr@Jtf'kq0ZOdQJPjqfeSY)%4&]cl*f:PZL?_DP1qgTf*.F=hur2cB\UI,]4NoD%2*t>eQka+L2dKpZ`_dQ!-02W^_0\%IMC_?CN_3gc(K]\cOiKR&e4>+kD"BsS^.Pt'W"+hk?1u/J_`VX#EU^c[L*`_;U+V`4aRZ&iOk9,-,=Rc#Lhg_S#0W6OPm;,:7bf@YAc3?*Um^.VbP:t]TpELH$Xa"BB.3`aPS+K3Z,D0'L3`O@YYO.N'+r`qc<<=ggoY\g)0WL-f5Y\F"sMdSEVW1=pt%>:CjFR9T=e`'Ik?+;:jG4NNn=V//d*id0j'\b,\mV(Ui(5.8Ym.)3Ih+Ku_-]H=QHIisU%1'sCo!@1Mot9N42l.&$M6mK@04#A.j9g"D`X)jZH.E`uH:biZLWFuHhal8rhj=oNNp@`q*VRcIXg>0(]E\,I +endstream +endobj +336 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 335 0 R +/Annots 337 0 R +>> +endobj +337 0 obj +[ +338 0 R +339 0 R +340 0 R +341 0 R +] +endobj +338 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 138.1 692.0 190.6 682.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 87 0 R +/H /I +>> +endobj +339 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 459.43 660.0 511.93 650.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 89 0 R +/H /I +>> +endobj +340 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 628.0 137.0 618.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +341 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 564.0 137.0 554.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 73 0 R +/H /I +>> +endobj +342 0 obj +<< /Length 2022 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#\968iG&AJ$CE7Be$'7n)S&%K's`/^%il-rGWp5n?#;inq^%g@V;Vn]r=MA/;n[,koJW=L$Og0O&oporDiMs8X4l-\Z=%rm>DkU^maYE#=gGts/Unf?j_n3-;%U5fr5p!I\J002hS\Kg@T(WakqjT:e[^e36M"Q%_b^6m5oYjnr&Zg:`:[]Fht*ULW7FP_g`#:(;U?l[\aH6FKbRHDfln]Ac?6H4^?HfBEpVE\cd;HjTTLoA(P>flUbc,;r=_*Em&k+8-Rk4HBMB"7o3A\9n[%^"p!=u6Xb5db9qgp+sm/Jj'g$r+:6PB/DU?0H\WL/HIcS:UVD@Z:Z`>(Zpa3'_MroN(H)B(Y$_PIr3\lfn&a`c(=pM'-plEn>AFhfLa#[i\*>Vbj*[,K@h&5)o/CuW$ahNuH9@8;Cb:Y6RB;I".eWO.Q:`DpbY625ocP]AH'TfB+f>#87i8^orNS$l>8f$J?c+bhX2`aq^2/A[):&LN/1SH9,GYD\H=Ad>')qE[+L/XMM5>#JWoR("0*`1+f>nO?G8bP,%dC`Uc=Z1tH1.8kL_#"belCMlU_61bXf%8OG_b\aup?@O@b4u.h(lIn@e=.44A[7Xll?$i%FY/c$?.k8KAif:M;q:_*:Y!@>g/r!OWo[XFZ*Ci`cIR`HS$^E!5/$gSW1T>TBE!@H8K.q+fBi`T4cJF)t0&o3e#,^"k<_18R8@TprD=X7Aj+Z*^A8lFM__ciq8UV5J?Xi\!sh\kd0-s&,CSF*Zm'ab2+FI8X^K0@Vn2rHm8F#no)Fekp(Od%^9HE4VaSlo8.)H"F6i(kuZ41j7L4=dGlMpkOOj,E=f%LjlXIFGtsha@R(!m]'YCO75XHSt(e'N$HrP[])baDFj7:lV^8$Ut^=NV75ANNG<#Z9Gcq(Q8G+'8D=hmG2>p>p%R&;Rg#?P:X\&G!k!Qm@K\.CEdb7s(+$>(4--R1j80U1c6upP&(Zn2U&:8@DY_iiPq%Jj2f5=h^)I<.62k;+DWmmWP&CUO$l^b3O-R)f"1S)C"U!MptF8Ie!S=ZW5\247V(fT_dC8NADoWCg)ojA0DB"$B2l(P`oAn21]6ll"Vp(mQh+`2\03SJj(@\mfJkBaj6r8(L[9^]0P0;R[9aD/RSBEJ-d1q0DYlk"QInmJ+5XoA1B&jTQ#RAKsg&];K<"8DqR*A6rio1*c''q=`Xa!>h#aBQTZh[#I]#$b&i\Q_[7SprE@bacbZOj8kmYp&&\&mJd8KgtBO~> +endstream +endobj +343 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 342 0 R +/Annots 344 0 R +>> +endobj +344 0 obj +[ +345 0 R +] +endobj +345 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 542.949 144.5 532.949 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 79 0 R +/H /I +>> +endobj +346 0 obj +<< /Length 2341 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0E=``=U&:XAWd&8Vk9EYs?l.YQf`(`=-S#0rtOS47&+:0V1"b\G=^V7l)$OW-[Bp-\N88<)00#$.h]/Z;;H4t$*CU.'5=d/LrSQPBJ:*LXk"]dm%0NhqM&*HmbpO;u%`PlA"HPQMT7`t,[I(nM,$%%K7D9>CIAFlq]pZ6cU\7Dp*9bi)H0@e7HDgoG8/7-W]"HcZr4Cr;/'(teX>Wp7>0@a:K`'"be/gc'B(-h#KqiTc?r`hgqMARe'f5&4RZ"+&keYf4aA^#4eYB=c!75'[SUU;@=M:&$4lQ,gchlqtT$2EZEC\@7PXe+,gc=NtXS0r\'Vn-n#!-5se:WhV&fhg#^%:*@-JplFZ;PFFfag*KV:)UNR!5>D>+Vi/i?*EEqrR8,.R\s46DIlTCGV4%S+H%b65\ie3F1,H<3tola/eII86@R)jMTnfgSH[*WOmdWCcF"@msfjT\.@-(2SDa6qd5;i`,^58Dg!\V]F(LJ*s&b$&j5#du;f:57P;V6L^q2pKlYDF*fM-YC&hAqT/D#F[ru`3P6++\FXm;9B8c=e$rPHa$/*GFS@K"V/\`fOO*PYi]ad&f0ir*Q(@gYndM+O]@L,kdS29)6Tc=QQ[Dhd<+ndpMmb`\12YN)sIQ"-EEZ^OfeKidj'15n!dGFmuQ07`&Lc(K\VsK5*g:4o/+0SRG!-nj)/%]0kn9Sl@X2"UU].kgZR*ebPlg-p@6?NO\:7Dh*6=3('_0/Mg,oe$fQA#K7Naj7'Oehfe7!q/m;T>3hWUHJ8R6?o.(0k-;I[,cld-N%OjQm"#20cf#h2hPP4fhe:SGu=FNeWg@ZWQa>;_RK/8k5G0]\*HQX8qVr0E#e8d(M1aGp;H%6-6"s-_[gXdiG'l,010'rcE&jjS@=qS$ni9pf`Sdpbl$PTVs(3un?F[$Sp(ojD-Cl3rT`T=J#]_^mo-O)Efl3b+o;IWANB9jpa&n$ART^J/MDN#KcLq%<7Y?kKmn?tER/BuA]=4bpdb+9*Xhd0*>,1YpS?P>BMO,-So6?YUarJJmY8Ds/_I1nZ7G&"!o7)Gn#h?^R$Gm_l&c!*N@IM[D!69d\H5XXt<])iD>@I+Fce4=5$;b:Z:R=esQ`1]ek+Y(`N6!&%h9\5bd#:#5K$!lP9V?Yj%j]I;8olS`-AI%.@fJU9D(fi;"?!3pces>_"hhhK`]FegpasRbORN&hNF_&O:Lt+Qk4rgCEVchGMli[kp>DER[%9jA?uaXc].eg#a.8G"]6538g8fNe.4^@\U7!nheh"46A\c;Nkm<6mZuS&0[(gWeaa:V^na$:1dsp5ASK8l3'SMd4bXFUcS,,YAA@(0RdZ;U?NsN)Km52,h%hF\=dp8f!YDPrbMbA@M$%O4`X:-H?QuP3m&*UYo)H<:)Hd3j-rdo%or'J-=c(#qXG)`KE3L,s?efe]h@t<[Y8&r;/5$Dc!BI;b"OW'mP.\MX9tqhs*D!99q7QN>5W"%%%G8%"&f+K>hMH"X:uV0>MF%i.Y(Z3Za5A"mUA*h5=@):cBNHME:YB1QOE-N=_=*+"*fh2NA_>EG;C`025&_i/6#0',C*-oKe'(#F0#)$b#Lc4m]8,HKc"KM`oVu5rgRT77f@m.DI(kmAq/.Q&EO.mCa^V>2rb01/s(Z)*-dtkMto0:kJZtn/f#&*5^&.Q;dKe/D`$O@'=i]7#n7]jnC/9g$""4a(LW8F1%[(?`(0("-nWpSjU8f*@DR[jC-iI#*c0A_D=PCG&#^X=2j;P&A_(ZS`O#qdRt_dBFCr!,mYpqQ~> +endstream +endobj +347 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 346 0 R +>> +endobj +348 0 obj +<< /Length 967 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%"D/Ymt&H88.Jat7T)M>lG&1+=P\nTlqY!^s!WZPJb&i='1i&&]nha%N-/T!:4ML=OEH>*4g[n_%R-!f^[Yd1-;aq^/Io?SjZn\8l#4"3W2u1A?:qnc6rRuSdbe^ak=&M"(TFHC(FAj*ZH`IT.EH5PQ',.Y>P=rq(,!:M$'!GkML3QM'kVr6kk`aMhu,d!@RTmEK6q`%tc'14`oB[+>Hq_OfkgO@_18CN+J+?@_OZReD(om,/G4f9U$?F/c'Va9>B+Bh>O-FW9?@&U&91:4R`i+8Kg;.cW9%(@(j_Bc%s^O%u)I!=H[jN^UtU@E+S0ZOnSnOlNj@3]`sL39l8)1DABY@c,OED@UN>LYUTq$L"0VWs66N`Ce6=[$pNpk,N#.=2S,.'^?:kmrsX.,3HVM;bZDe3s@D6JdUKt2;oKIjTsqmoa^cE=ue-plQWP#T$L8UCVsUrVjKG=.pVS>(%)2UT0dbY&p=kdqrUdJ/YjG:'lpGnUXn;tkh\j5V4-MXd`+H7"kTl@cHIX?/Dit-="LBHCZ/+rfh+j4PU=7lS4]mlhMi\K?X=PHXf/$5RXqLDg]544*FVqXam$&JD:CU>bQOP+f7ac*Ro1TL@oZEh-@BX*[:e5m(2\H-._"`G_A#,;8kL17FB7hUZBSeHCHFei^s:uX;mYE2f8c_3Y(D3abA$\IbfI@=>#SR$DVi.6R=["V#3Utr7#[O4AUp'U7B8R8rR-R%miP;ht"`SH*[~> +endstream +endobj +349 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 348 0 R +>> +endobj +350 0 obj +<< /Length 2483 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=>>O9='Ro4HFQWt+Bo$hE)KrM$_\@sX^p'B8(CftD;O:LL9/gi=r;/WKPPR8D.Ein[Pdn0J3T%Apa3KIZ_=;e),+`A,(97OaFFQPF2Z/\7i4rH&LDFkWUI@6?H5KgUUTnal0Re6b7BA'!h=7!/ZTJmiT#cIC>N^]j);doA5INnKXUmq:,a_]Jmr\[;&i$"aJ"A=SNgEGXq/Ws<59dPI@G#D[K.W:1_aVe27_ScKG/*JDp2J,S4C`&\BCbj+MZhARJ87m?9>5-UX&ZDVh^7;m1b.o`47/PP?qhYA^T[8#1Y;0QDnEt$J(JfpGH/s'4d$H07K1)h%/GDtjk->9=ip+FL(0/PRdi.u6`[jb2V\V]1S3F3g8MN-Y1/):YV;n'*qV(g"F?>o"Y92ELmTKDDR+qF8%K#nWU'BClgp%8O*tBKQNN_E7kp;PWZir&q`W<%@]HVTcPc]_JW]'uqB_mc>A@Sd*=3+`ABbIt=o$pIahAUNYRc$QO=(R_Uf3MUl\%DN&Q'o%37'ERjJ@_AI*7>+\ZC'UU=/h7#l^h'"toAmBofY0t,"LN4/uRe11s^%Ibq$IhaJ28DbJS'ok>o*;Kl@MPo2R'E^2@#>\XA3ojIlGK(f:S%M@ZWa6[k9rg\O2>1O\3mpgGFOff4ZV9QpK=iZnHS?c2)ge62/CWS=LS;Yc29jQE%&S()WAtMY9Td/&[;NNIX4?+omc3P=npN1a>o1&m,"SF6>#$(P:,^Xm7NB&P5t-kX/Tq3)Cg@?bB$[GcV^+j&M"9:.NVaiH3<%6/0?:Hm0t&b)q6"X-io!G(@a7V)Q^q)6`A9bI4mKrc'<=`c'e+Hr1\'P1sof[&d:-SW)AnH:b)6tj2ef!3lF@dTh=<.j_"Ju)2Q\U$`Rn+d(16$->Is`jHY"RUesUa=k'cmi/&Uj?Kb#.qE-h^O1Tf8KR!=0jQW+V#HXtWBa9Fq24D*_1KknGLFaTn3c%eQG"s&lkDs/->5krjnd=jXW!`j.#)#*3T(&5tG(/\BhVR,%e,g0m#sdB-gec7.J)4GSB-4PBjOJ=_>s@;_'?WI;A/p"tBaeQ(ml=Z[6/usMikDa,FK)^&=[UE=d&cdfHS/B=.PH(X:?YVgi-*EU,q<3Sj8+>/B,%g^)Ws>[(q/r+lk9_`Kn:b&"uiW``"4LtSs?%KJ4f,@'3\:A"U&p01h''uTemj2^+gLMH8a5=k"I(3MM\NUC#+Lm%'1[DcOQbH9;j0l0UOsFl:@0s<:3(eoX1d#Xc'&SEMkI<0<%8;>bYEC&s`T=8UJ@KhJPL7#-LDMkHS0(mT"(("Zl?n6\YS2d'-on.Q8M]>^uS%M%,GuZIjjB!HVA85>2,N-h[XM5LljrRQ^4?n5rAj-5;:*$/WV#@W.>dVlkdJR#S/,s8,7Ucm6u=g5:KqZT1k@JZ@Qit+Qs*a+-gAO&UtU^0=Vq\=]+*<#f>1i$8Ke3j[.oE.e__Zq#j#V(&dK]!Wk(beG6&,d9Y=sT1_J(\Kl5plaY!+3+fI$ku(Ui$%WfKrA34Eq:_j>3=oj<]P>#%0h)X"9SfE/)K$iqD4CrLkY>'1d$TJ!NN-NdX+4+4K=5tGa99tt+f#I$h0!@O,jkkdm!Kq%`s(Q71Oi.E6k_V3HrN+Q[aj1rY(o7"3h/2]?&PWZ9^T?@24CtmG'jM6n/Zjo0M,+PBD_/>o;m7H:@?=8IPl[i_A=tGe2Mh4FUlT(/4r\T:2?`to=0c,Y\H0A!DZc0c\s18P9~> +endstream +endobj +351 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 350 0 R +/Annots 352 0 R +>> +endobj +352 0 obj +[ +353 0 R +354 0 R +355 0 R +] +endobj +353 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 287.82 660.894 333.38 650.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 281 0 R +/H /I +>> +endobj +354 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 196.43 573.894 241.99 563.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 281 0 R +/H /I +>> +endobj +355 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 251.41 313.982 296.97 303.982 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 281 0 R +/H /I +>> +endobj +356 0 obj +<< /Length 1883 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D968iI%)2U?Ge=i*-Dj4hH=bQIdW`=j1n2(gLSb;FVJXT#P"JSsr;-XD=X7iFFo"W],5hNf4oukBB=#GHPrHVkEe^p>^_na0Gj)q-*_$:p![g5m(s^^M0B;Ze09:i$i^rM3%t()Uk?4Yd,u=+X/`Wp1F'%BZ6;9hq1g8Xs>3(9V*"TV:In&$[236`dn\fHi39nu#&rZrGMa(9<$gHW_A3JC2sE5JepnGBs.Yh`coj6M^io`YM"/'t7nk<)XePC8+lH\!e>B\JNoXUIXi`3/cf;DF2FY`t\@5[uEGsf?@l[EX]\9^C9q9bfYZMmLNCC[+oi4k18.Ij&Wm04#b]000S4C/i(3eB<`^F-h4[sOm<"l@k4IOK_m`>76Tet%h(9YZB*XnYohcl/e$_lM/ZSR=G+n\1sSmW0TQOGkn?4[#V4I^K!+LQie?[u?sj*Z]V)#7SB_'J;&DBh=0JXV4ac/Kbl[efVreDHrH=TBeY-0t-``Q-.>n0>UMpmJ#TSDBm(=D)C>c"qPXL"NUb:OsLhEnRNp*EjVq\4&i55QuUqgV,X`&`4cBN&Y_Q%e2#a"O(L%3Ick+:;nM>G.N5"r;a@rt)c=9s1t<-RKq9mmX0Tes[P.Pu;d"8e:GEP1Rn*UnhZf9%j:1HC0ol?dgH[)l?jF+`IgE>_])t5Hii,:AfQiMbk)H\Y-W#F+FXGPY-P;9JZ5o>P\]Ac?6c?R0.UX;qHF'Koj$RW!*i0cWfEpr7%F^.>+hUMG:mWk4P'bWf!Sb!pfEaA:?Ws`;-ITe:$mi:TE0d0FH5FdMOY'4r^#j.1=SfDXTan?19d5Inqf!V@O2;Q0A5[4h^i':'\`'Y4\i!BOlUZYcCiD\KqF9eK+nb^&YGfYpUnHBnX.!0sL0At2E;/iHehuHr,r-_pcEXVSh6o;s3P)HpK!\hqom6Mu_@MD-4fEfBK%d!6!F8D>1h59L?,V/8"ljCL_DcZ4Tarn'5I0V41Q2>^#pX5egl*[qe3nE2-enfcZb[rmo3X8Au/upnDERe.HY&%a)oCJ(n"*Po.SB?2!(1VTGYC;.^=fr=DNGdNeLW:^0%-Q@@]jikl!q4qi2Vgdgfc3n^!\^5$pspXRb9fhlkAa'`;[&PH(qqi1UKQj>>,/O'+nK)?%0J7oiqg6!Mr;l$RZefOQ#tAFq)P`rI3TcXK&NV*dOH]HGKtd,g?`G$"R=*rX`jW_+d13HQl9mA%sNc1U^V*Vd_dUAhq"Q%;:89=l;R+`dFrVBu]2o5DeplqB=Ykj"D5=;WF=bA^l*T +endstream +endobj +357 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 356 0 R +>> +endobj +358 0 obj +<< /Length 2466 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0EgN)%,&:N/3W-Ya^@YirBFDRjH/3!"8WFIHl9Ul?VJg=);JcVk'me#POi!iC:i;E8Oa8@)mipQ4G?i#tYS%uPJ>6C(F-k6o+m0$=kPcmN3m>p624?CP@OH#bKZ:2]"+Mu)r(M&(^(.d""]%fOK9-eH-8J@X4L3>Y?pta3ij/:(inMr&S*+!m_(2CGIg9o+*JOgT-GSJJblZLLm3_K!tTZA]$:7B#o\n3Csl^gla**8bM-i+j=_B_'\c.Sk^SEf5)=76aUQ>?_T.R3l+HS,'/KhmmJp7D19IQtObIuoDp7_-q#g<(4Un?6/mrE-f6>CB6!E`Q_B=t!0Qh&W)p\5jMY)T6Z>0PFDJlOSJea9:C;^Q8bce;A1\Em9Wp38\f=pC]gcTZTQFXVs)1:aN:/ZaCr9)%]7W&YZlPe@--NGg\g\9\h''q$6K%\@\e?-_$l,9C4C]3?8^gq*e7'!0TYB1LMg<'<%,iKjD.rnWn[D34qGOLs9puB*M!m3uUXnA3h>^Tq7B]W\#gKLT=5^$X+*],uQnU6b!YYP>[Oj6Wb4Mp-osn7KfsLq"!.,#V;D\4dNN$qKu>I_X5Yp`MER\eQ6dLgo"?f^s(p]&*J\mR[^#^Y9\e@[h2JOe\X=H*9&$Y?2KW_D@G>tmQW2kaPR`aVJ8Hi4H2?@pllkhET"TU-/870./Z[jC[_(-.`6nVH'=XFK.)fR#D'(W/#QA2p+%RBuT\(S4P4e:sB3%Y`TMDPk'T0QrqSX06.#eEcu,!\r:L[T+MP2n!9^0%i0__fqntgXH/cq0@%%PLQ7jG]X3:E8A9CdEFca*e1%hZ4KB>Y7I#==<:#U/Q?F>/ogsq<:/1VSU9#@NJY::uZg_@9!uPZ&ZD9SLs:=e[kZK^0ud,b9U[ga-5I78d\'_PU`0gE_k#QX/E[)Cjg2;2[.Ie8FtTA1*ONT#j"FGN82^+fU09&*P*;sh'0hSc71+)Zf*MmF5>rr]lKh0&>s!#b'!C=L#K#IQ+ZoP\h3AY3cWJ--dVH/>Ej@&`I1:6ZfTm6U&5eF"<_P@`NX\3VgAA(^R.i-\RPe5?1t\a)%Z +endstream +endobj +359 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 358 0 R +/Annots 360 0 R +>> +endobj +360 0 obj +[ +361 0 R +] +endobj +361 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 251.41 541.48 296.97 531.48 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 281 0 R +/H /I +>> +endobj +362 0 obj +<< /Length 2827 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>D,]IS')p1[nF:4k,EA>iQ(+,/))?Qo_8&CI];f:(.#sIh`jWOG!p&6GgTfe),6c`Uh:gT$DRK>STC22[H8M$2rI3g29DItBUFHS'hVi.##[=H,7Xa!eOm=b2S[AKAbj5!e.9:F&?FApPm'5J.=eZ!bU50Mo+QN!Y+PcRD)W;LVIQA\%NHt*%d:&h%F%cYhVq^UW!RP3b0Z$qFW["lQG%`[8@mm"YEh2t32$HDZJa0+eLGE6I0n2\[YSOi.;:EQu'"JHijbmiiKffGBl80ZXe8XH9a3'7.ZI/`q+B?,EYY2`N0r6UJ[*M8LPK'.aR&\C=t>Qp5(,@6XWnSojdEAee#4[%KjR:J`Ym=],i3DYA;cL$n@deCY6*Ws3!"Q8!+h203b02.mJ\D7:noi/;#p\;A5=(NA2CtEUnRBC4Irf,O:hgoOYb=m"R8GBB=(aou%RNaZWc%scrM@5;5C69?%,RIEd_K)b;>dr1h.U"*['OF&ro_13XddE]3kNFdO#$qp`>%=>s"_Pp\k$.*aPAFC/[M^:0au(r?j6b4I'&P#:;+WFF`Lo=bOE-q5QEfT@i.UtWCP7dr[*P"d7A.8C5)@h4JSV[Jf#b@56)ZH^s8&O_?t!BIO,rp6OL7cfLP#p(4plO'tfa<"5#gDaBk8T1ee2i&BAAAIohsba[hFuXgYlD#ER6/%i0jl,9%USrO;s+ZJ_3H9H_3)2g$/"cmX!ePJJTFd--/[#q5Gea)=IJA([0DqS3Q*RM1f32eN+rRd@mh]aR4m[+&9It*ZP9O-OL8hjE+$eS*A9FrQqbr9+N]#gC=XAr8qi2U#Q;oQ>STNn,UD'WdrODlVnb)a+ir-;Ih/2`&Z"HR:o`,='>*#Cm_n:VQ;Mbgt51-@\9HttV6!)oUU4;h1lOI()O00BK*$[2&7^-.(3Dh7DTYk"J^ksK_ZP[0BRRksX'Y0c$,[GGXh,g"h\VH+Fs"E[]$ElO_)6pChCXbWl3f)i'2n"$D?\"'Tjn\'Y*VM>"9u&j3L_gHAa@?D&"%6Nd,h;HS)6GX9#.0el`]'e>.G7B$#"gRA+;3tTCb66_LNhf,1C[M@GN3F=e?t6`oB?\ATj6*8[9X\45fQ%ZG84>"t:34=gE)8Xu3WI=&;dRPpVeCDG<$@t`"NN39s6WloF!F!ZV)9!Gt*>U$]K(:8Lg(=nl1NR<(9MNhQRr<4f<<;Euf]9-_+cco^K[-)Lp0j"*n;nZb`S$`AuN!`/1)o8#'Z3hd^j]a[^OCF^=8MA4#%$$n'Mi&(4acm+\%S&Ia.`43!OPesuHU[_Um(4"goXM^H9Ng9PJn""G]8aO*PQN_-DQ/Y[7Q6>XX16&G0E*8?VsCj=i-I*La,R7js#.EX>YVq6dG"N@Fp!!9Xb*oPr4Tt`g^S7:?fpk6B!plK@tOq5,s!?%\;e-6oRVn02le/r$m==:?5`$>iGZu%"_5q<%R34Ti?N11%+pV[o"ul^V/ha5IT^2rQ[G?]kukhY0cp75Od-a=u=JVkn,Fm"q\4e-Y*bPF^pC7TV1a)TRsI6D1/g^Rr#RK5<2OS":TEtmWb!ZMUqH][:'X$ESUKQIJ`DIG@2D:*di%&cc7)B^DVS:kQBEuu-o]Z;Z6/Vn~> +endstream +endobj +363 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 362 0 R +/Annots 364 0 R +>> +endobj +364 0 obj +[ +365 0 R +] +endobj +365 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 338.18 310.488 394.83 300.488 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 366 0 R +/H /I +>> +endobj +367 0 obj +<< /Length 2423 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=gMYb8&:O:SYj.,U=;:3<>pk0_9Aq":>[(nLfQgRt.u>iH.cea4;<6UrbM1=*91_m2!.h]9h79(:1IJjFm#TuiTjMji+a:=BGG_We-flAXY`CakZlWnruA:4PUNeDVpP_H5=]^p`c@:tR]S?fW#EKii&(^[*L%J\kr]:ZP&PGiVWIr(Cc'a<((i;UUT64=.cY_e'#07#.@im6cG&HF7@oY4jG^7Gc3m7A'R>-nOH_+*YL1aK1&)3W!7thb]or(L";/7GkpXS+`5BLl44ZlRP\aOk5.$35CC1VtE//bEg`2uBj^@_a6<\e*5ZbOc]-[G0MW@h!sI5X]t!!U3$I3g1-)P6Hu#Z@I*cM/kJE,3[?lglW6F3Xf_ocdjhbWrS/^\GC;+;3O8'9FN)s18'R+W,mBbJ"An#rW#d25=oYfOj.WMa^$cD*qj7L7(:Geo%;Vt2:C]/`Ep")>o/h*WY%K/Io\Q<[MCK.m-;$Z$2^-P.k=J]K[G[q_qo-QH[bSVG&jf;;QFD#dR!sWa*/F0\K\`Fkcp*[XCG`E`bq4qMLBtOaJ%S:B.lDEm'L^^)FcN]UG?u;UF0=&\0"\iHa[I4D\U]Sj>_U0r,U?u-uLUjp+),B2FW$Nd"sJI=^RJpRdnu8gS#kWP+g#M[b&$clq_d?hbQsu/Gr8G#)-$<4g(U;A/*rdNAr7E>AVE%3sZaYYLA/B'4q'D&=)DEbZSVt'^^Gul^6']uoFelXjtqNV(.@`tP7cKWs1o(O=miT$4LT_K;M35p)l@!Uu9\:S"-'N&ON$Y;PP_&.p[OhZTQ6nPUr'[Oi4&9LP9.`958l-;:I5o('uRhKD[toBTP_(ir^9GKH=j7OpAL95r''.WHh:4(,&mg04K#P[fI+q5En=[k./KdU%5osoTB^7`XV[-"lHkhj3KIf(U/b30(*eW>d8tbU3k2G;A3nQ3*DKCKs$V.#7&@Om4^e(s[=hZ#`jH4PmK7S&KQ>!am.+VfBMf6cU3e=Ur"7GKkgBIuWPYr^aGU+D0WX+#)ZPONapf;+eXa$Bd-3eF=R$!R#e)5[`j#FUkJ;Di]MMUh843gS1k%H3+`QorLe3iA[=]H(o\F-:/G]&3)RR-\<$A$FP<'rBc+"ZhSQ('pD;lsF$]Fm]"D5S>2PG46Wq0GNT8M0e;>08ZOYM649BS)j,rbR>H$VMT=]o2TOQGT?PV8;$EJkjgl'.a%ugOhaqggK@D6L`o1qH=bn_E3#[\NYIY_UAFBsg6%Q46&5#'2:M$"ZOgG[Hkjts(6GY&oDIbr;U*/n_gd-A`OhZ_G798aD0ic.<-S?]:'i0)B&bsDisB,-D8.j@+-5]2lAM?&34;YPDm_XX=Nr_ZGP64qHijXJh/i(lKekX;X(&O,4m2p!qUa$Pb?P3$gNOW^1+`kP"@q5H9HTs+MYAL;-Sk3#)8i`obmcU7b0mLrJP1BN)jjZ&b#jDWZn1aj)QLErSsFX-*l'(Y,/PKXDpqD#GG*d?p?rhp"NdN3V[8I'5CAO4jc:RTqtE#P:M>c:TG;s(&NTJ*/rNdh~> +endstream +endobj +368 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 367 0 R +/Annots 369 0 R +>> +endobj +369 0 obj +[ +370 0 R +371 0 R +] +endobj +370 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 251.41 576.06 296.97 566.06 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 281 0 R +/H /I +>> +endobj +371 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 342.06 286.809 438.16 276.809 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 372 0 R +/H /I +>> +endobj +373 0 obj +<< /Length 2017 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<>>s9G'Ro4HB`(81@'0;u'ZbqmRV%^3-#Pfn,S,gG[PZA`dMg#Fq!U?rg!X#KAY9]]Yu^&aSa5QMFte@-)4!ifA%"?NNTG_&R.,#3TabYaUr9"I/2P![rg(;SNbqRT*f;qVqko8#r2>+hR#'`I?hPk7\E)27W%pR*Ns[/#ZRA;%q)l2\jsu`AD+-(?jf2E+Tf)kTid&J)bQ[!^=iDT$.kFd@_*ilt-\>tgl(=fmhi-4n'b`h(4%r&$<:YC%Ppnb%\&B?lL1,^;KcR9aV'*3!<[gB!Q=Ks$P#W>`;;fMGr7\Y!RAh5q"5c&db2D_T&Ftrr'#9kijqUuNSB(M%PD?F\]gH^;A4K[h-rq<_n#49S_oNZ&ZrhlU$$`"i\MueeLdIip[hbDV7.@4C(f99_t\.,B0o).2]GlC7r@14P;o/*_pWTsZU6m2XljXti]DGrB?Pj!0#bNHL%)R)r6K?JNc*P0b)//;G?aZ\o&drnrA-j=)'?K&<.\S@'&"P)'QMKiD["H1MVFj2T8?7(0+@Kek22C9Aa::[0]H%lqfIKe5j\!+8B)lU!'?7u)q1GeL[/2VH3b\=/@ec+puK84:TJqoh\'&]gb4Fij,pR`%OW-e%P?_AIOP:\*_-YM=qoXbC39=Z_P,(tE2[j5R&u@]f89SCh:VBHj.+K5hXbm)V.&,Ok'd<@*\=E/G)[G4M>_&]C">+);"CIiapIK&8nPC2nd#W5IS8#2+guAesk&ARNtQ.`YV$.4'4dGDe4F#9JD%\93N"kk5qCV^Hgg&9VUe#jQ$b7D)JZjl?*)3""4.eUN8g9P/c%Dmk>QFt'/:AmDpPH>4$b#JVLCYVq[$(UZ6eq$-YuAZ'Kioeu/T@E(SK*R>d!Nl23P0;eJHqFc;`mtrt=B.L5H%^^8)9'gL>pES"N,"@aaiD7Ya;;uJ7lB7J@PH!Y_.%si^:1&X]4i8;5$&O-iJTZ+`+2^g_QpX$.9c>\3S(Hh,'fI?6(95j,]c67f^F[XqPKU:h*.*mC<6KB[=K%iG`*sHi4%tqnREU1`Nln^rC!21:_gpCHVaTQfWg@Q-!HC1LV^'&_;Ap)W6..:nkHHU3[e<#QZgA"9%gOTX3Se_$W0][a.GG;&86)NqJ1f.@9oKZh*U#R*8f&l"_Dm:K*>7+R*hl\+S8a9tZY;UW.5XB;Gq#Uk(_Zl3_UJ9g"YfGJQ_^0@6P=].T`$>uaoF>S4[ZQQNR>TaAii.m]>BO>_mj`#:Y`)p]>Q:O0!tPSWhnr%Q=CP7c%;&^Z=P,kdn80?e'?b-;L;i,:,cjL`qu.,42k/`DTlCu>l9c&I1"G$gM.g-9sgV'7r!/=9OR0?_igU%;20Ub87`2\YIDmY@:8V)Rl"p>L(klbX;d):04*j7$u9$aZXYs(8Lu8HZ!p>n!m:653: +endstream +endobj +374 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 373 0 R +>> +endobj +375 0 obj +<< /Length 2023 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DD0)1+&H;*)_;nNM;p,G<#Vo+8c.cNNf5$hbf9pFO6,BkO'p'qbdbM%@"_/)\@]nQda@6&[iJEqod(@aL4)%&[NnWi:E=FMB(6rd^gC2)kE8b2TY@.g@,F0k&!*pcA>7-VhGDqXL-WGA<_Y56)'));R4LCjN=s9%EHqC+ce4:Yd!4NH-eH>%^r7>p$qNV-(.H4naA59ABX!XC[BcchVgEg3O1hQe(7<*(L0mgU\t&/[lCBfm\NZGQ(/h%A09=A1'uG]9GSu\lYJcYN,N$mG'`'d\YSk:E?)0W/5.j^X]$IU3(hWZMj&dOQ[^%u[*R&n%[L8+3hB$j_Ss(6.hnq,P,+:o[R1H7Jk).Pc7=rZVCRq-E^ptY\M3'ba-H-lbP>l:mG[")a_rA07C)Em:'$L!h_QRN:J/`HP8#Lf.[)Bb<^:5PQQ^4JT[imNAqtUB?G&OYtc)0lYusTF=4IV\_B+Hm6^PM>h"%o4X8-^)QQdIs45TPe'hIZkKkrq"u(aFon1cMfs@dJlV!V%mbrLn$m4H)BXYY,8BaQEZ:KX_N/GShA2jrfEg!K9h[&cZLK!?hq^A1/B.a$D?8cN_>q6El))UF2!@A1KmTifN86_qODHr7U%J901p!))rBtC95']@9Hat0F>U]LrQD.3`qF6Y[IYViAmMJLD?OBQe-.oVNif8D0([r*QF1:8mg&DXS'?Geb2!+m'CXPb-X@UQ7=bnQ\JZf_Cumda84c?:u-dKBD'A-L#p[g,J-S-:g0dNbg&a0\)!\0Vo+pmLPb7X9M/s./)XbRqlXMpKiNCGYUqZdoRABD$oZqq$s^B/SU0&9-`*sAkm2F/k7H+Tc?JO,r1!Gi7N1N;[8K&$61tC1De:8hW.#"O'Q$`C48P`VkIs:"e5VjmMeHtGY3&1RI4<^_1pbltfSM6cP-)C22"M+WRAOSr8Z#M;0Uo&W4NXdK_nZ=]0_O$pUUar3SUc6@(mJ_$+c^&!SqJ(nVhq_POHiNBnncG5DhP+.XE',I"m!GT+=8?a#0/93C!Iq%A*^@Kn:B%E6R^R*6Cg\#H+db,2HjItG>`*lL6?o/e/[jZ`dHWl9X(D)^*tMHn'Q0RSBY*N'ibrW:+&)k0;N^"f=-kGIoPuVK.,9MJ2(N"[pukPt7Lf7Q($.RE^sJ[9>5LSM3OU[#u69]DR:sq)2qo"XP-HWSrXZfLj";mV0j$>U1NiH0+=c#STLsXgN''Dm:be+S75PM%.I!pOFYQOTF%6JE\^P-eL*,KOR\O1]G*D>$^Z\HOYr/;gnGm_ZrQIDrh/3j7'>Vm)AFZ/]6E:9DDEndZEAUJ4pMcZsm-\ZQ_j0Z*F2@irE>3s"2ZBidtcHoOKi(G]_k5ILcUcr,&_Gs6*G%;=05*qC::.P +endstream +endobj +376 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 375 0 R +/Annots 377 0 R +>> +endobj +377 0 obj +[ +378 0 R +] +endobj +378 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 307.51 251.768 352.51 241.768 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 152 0 R +/H /I +>> +endobj +379 0 obj +<< /Length 2197 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DgN)%,&:N/3Yj+UCNWg"?6&L]iN3>fV4)o)5BdV+-76q\&?5/Rn-"#Qk7Dse0UtCl+AhjPnlseSm"@AJ>.RO%.+eh^F5GUoEW!"C_*E^((tQu$PF[,GA"enN>\9Y,pHHXILj>J0IRD!H^_&Dun6qq0?2l6s)N1e/EY\aZ0\mrP)^@UbR\+3!I'_t=T>L,O@_pmXd[*,R_sr+C5>#-f8o]Wd`B0D26V=dmQZRQnY+H2I?q*17+Y`8c5r67W+LUBlG[LG2A!A/#!/`$%1oQqr=$O2]'9PfH[*:_8#?:W7:q]eN#;RsN0!SJTT!eumkXJW9_Be(.^eo[u@FVah!_`1N?uMo!ZhOZ-=9A?c?8X"(M9gBcap`H;I#B1\V5,,X3D3QHr#H0qO!U(aD&f@I>$_J<5`46]&d$l:NWebbpM.j$kotJCue7lJV:N8SQVW7f9]LOhKePW-f&`ANrX%a0GF6s^Y+]Jiu_UR-#jh%Q:@cS[r7XBgtpYt\+(kb0h?o$fr]'@'gq/q]N!)sK^HmC"$BbO/Tnj!buMTm)oHmo:$cS2Qc(]mKBG_1ts#2_E.kVs!@Y^+59o5Fj:;0rW/S>RG#S3+q#hrbk<6W)'AJuncVJF9E9RCdk4R`,E9))5fY`SPO2kGK,oD[R?7H4@hsWla1fG%$Ie@^=/7c0G7WQ9E"VEj/C\0Z,2SD=pjPbV(,Mh$XdequIq\Bh[M4Z72j:E:e(84qb!ZUaT:P5'_].=\=gY_-5>OI#DmaH5.QJ@_]G/OMElI-A[3@<-Z`U-L`0pt0R92\ZV37`4N%.4!MnCnt(Ase[4kc-Bb;L7MT%\6Ejk?HI2Cm71Z!EK^N,p]Rq'-*:Ns7#C6K-E-E+hj&Vj?&T0GUQef(Dpa-%qBnRR^]>90j*1Y\tf1]e$hU+u,\%4I8J4Ej:.2YN/]\Q9(>WRY\/jJjJO03>9?k_"VBub$.`;42d%qkMGS5l'6/iGFhC37b[>CKtE%eKQ`rb_.]E5:FFZ&Y$\s@E.4NL^iZ[_!MF'@8?,\\qg(677$7R'YZ]gE?OCo>[F^_]nq3liR,W(\VLWZE5k>19>X[G>(PHg7/YLEJF3TAEju=mgWgoK'o(U>fPJc@QWb'&"R`]N<&n2F?e@$hc/n'DKb)993:ER4T,2.I25d#FU)IAa@HBVP%)QLF"S!E@!eOapbAA?P;\84I]7.A^N93a:a'<94hr\g\28#/7+L7uB/uP'%C<^6AQ;f#I[I$4._;$,cIX'on)WMdo:N0F<"$Z!Kn;_#I-i(qjXTcZ]3aqtk_<-_C*)e-A^;B)QnZX@tWRQo[2l.('Ar[o`#_*Ho/@FQ-Jg3q,7=r:R'GE!h6+8PHW_/N/,VO6Z6$e5EH/3q#.#_g4+T;_6K+.ooF!.rCZO$p#<./Cr2-l^2E5ZP])*WMQHg`CcDBG9:h#hEbND"%c)8YV_o%+I1UKf`W2S9qJp-`fd]H,73[N&Ql,,MDkgJBZW>1McUVlCV82H6bZhZ"1bbKC!a$P9#O!ku@3ND48PN$sXkh37eTd0RA_Fc*qS"0Y$ic273e?R@rg^W=r9VhRPY6$DB'F!]H"[Hk>?42e8sC[76S2:I%a@Hk@0pEZ2+b)!GR5/H5JS!RJ^lbU3JQ'hamb1Qa1:R;[*:&LW[Vo(p;s()qH.^N#!3OVRcGO\hHI,*4OANH+\,7;-(-'l"4OcQ/WAL33uV&Zs/V-KlhT(qCU(ibGMft+tki?1Qi$N[/?.tgd37Lu(P&\<1k:@Q+ed8A)lTR9"BkFfQ&FH"L%9KiD[Z,SEo%j30&T._MDCt[QHq8X;fU&P+ab>M%~> +endstream +endobj +380 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 379 0 R +/Annots 381 0 R +>> +endobj +381 0 obj +[ +382 0 R +] +endobj +382 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 251.41 652.5 296.97 642.5 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 281 0 R +/H /I +>> +endobj +383 0 obj +<< /Length 671 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=h>Ar4L'Z],&.4h&U)NLB`&kA]pJMF.PUi)+!LT1k3Ubl7mF#%J.p,b0`:`(,:",\bHoCoe7c91V\,i2=XU+Q:,HBH,K8ZFI7N(:+D;?e4t9SV-TB5cYu"Sad=^^@TL;bk+gQNSht&B[tUGY3"r609ZH4l2BPHei$X6jXh\:QD=Y#Xjd^XOBSsJ>/i-&**E-\1r8ok)D!jD5L&(4DddJA>X^)I5aGj6*/H.G?Im^]UXtI\)62mblg2(jR+-a#m3^HM*q8\jYL[%X>NDbe<+n0S:Q6NC&r?2@ReT9k".',k;f:[pR8nufK)%2*)8YGh_-&XN?EWc;+DLF_Q+ +endstream +endobj +384 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 383 0 R +>> +endobj +385 0 obj +<< /Length 647 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gasam9okbt&A@7.pm=U3CP.W*R&3:*WejacrK*a:\6p_"^rRj&qsWjdAj'[gWoCmSBAPa85Pn_EHb0lG1/(MFc3UF^'G:(:,Qa;G,f)F@!]ti6gan=^/Ac!^37DK*14kOqIorlAZ>9CG)Ueh13s\jWhl5Of8\>GnkoBLH(Z=IV:`#&BkGT,(rXD?8-%`E#;>I%\GZFJCF;XV:cq:'cb99Zl\Y7/cJ8?SSXPXA.HRU\SdpdK`jKN90;2SF)ao9>qb$$j_hJ#pUA4"k.7:$(VV=J%t2DlpBke1Jg3H^FjFf1iVi&"&),W^LTWJV.e=#dU"&5^NjEs%mu6\!l2P#5p0jZ;]"R8eGuGOqG[YFW`^s+]AT),Y'*nUEVnX7-HFTA5T7'8:NK9UeY)d"=184n!M&``toaJ0pYWq,d6;,4[3J59`9tlaA_j&9,rhYO`pt5X^:9]D3(1'bJ6"E=/3Xl*CA*Vgeg(YCJFinbg7$-CDT*]>5;cHq!`[)NE&6j2=q<*b>M&_;YV:IP,5-Ze8\oe3\N~> +endstream +endobj +386 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 385 0 R +/Annots 387 0 R +>> +endobj +387 0 obj +[ +388 0 R +389 0 R +] +endobj +388 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 130.33 680.866 175.89 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +389 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 680.866 445.56 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 390 0 R +/H /I +>> +endobj +391 0 obj +<< /Length 2026 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=9lo&I&A@sBE,oYT'/:cO%l$#pZ);K'8L@<(cIb-B'I7)gIM;c6]>)'Qaqf1M5]Wcn>RUnV/frl`h*ldaT=qq`]J0$]Dn\7?]_,Fk"n42;Z3]Tu+SWcflHMujp2q5KkM@Hj!r1aI9kCi7Qk=J[V"hS%"+H5mrn#?)dUL6l[:i0sM;C+20SMT)@@]0-A^ZkrFg.'?cg>YMFEk5_Kee],n)rZi=&%#*[M=GNG8[C0P-6j"dW'5aXV$0Mj/@JGR+<^;V4*:pE2pp/GR0BJBo919TRmB10^I!LnQWsiS*&O/-UJ8KR)j.C@1DJ8(#g^VptY3Q`J:9I/A?lnXuURQM=S?Qdlr4LBZLJ0XscO"C/$pA60bTQsJ$I/LA,6U53d["kU=d3';1sM+X"Lq3#-\O_/>JBl*%J)//*/O"Ri9'@'@1WWWS;ssDMN0k5h$jo&9.0u3JX)ld%Pc[KD%!/U%'^-eF"BF"Ln'%5JRc&-.SA"=0q+t&@!KeM"?Jc]g_BsUq.#%h2o$H:E4frPl:P$'<#Zglolp\i+U+UTocT^WDEWhS5KN'bOZ/%L-[CH(#;2k/I<'^U]<@f_oa6JN;.@okNm)E#U=eCs>BAF2TagO?%oqPM.tV+O"Wl)W<3'2fUIggk"s7bI?B(c*!lZ]2TQ!F4iqRZNq-V^1^DXB2>(^:Hq5]4VJerHMO0K7%^iX>/7VNiX'hJ%T*4unh0%&9VT!b.P6R=V#lA&9Ol]Qt`DMP2Qn/i+Snq+Fbq-c[dl9s?",@MknZ6&<,OnOf>CZ)Ud^8jhR?2H-)>_NfnF3",%j,bjnobf3!]\6!6rPk]/]7o4:QeU86/b^\sEZ&+@:4EN`XZ3_KcE+A:=nt4CX;s:e(c3cMt@9Uql@ZL',-r8EqkdXE:YrJd4"HFFFk1rZmK[?i6Q)Q@t*DcSTKZ^F/h\5IPWI:KCj%NK*so(F;>]d5$C;7U^3G'$XX2Y9AubYhoI5?7Qf#aZh.uG^IC+>8"SgSE^YPILCfc8qeSa!@U.J]3IEF03:YA:E>gNNZVClW=Gc?sLJMdrOa#ZVa)2lLbO&5,MAlr\[7CAk6u-ca2F7fPb_IR`."?_C,b45 +endstream +endobj +392 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 391 0 R +/Annots 393 0 R +>> +endobj +393 0 obj +[ +394 0 R +395 0 R +397 0 R +399 0 R +400 0 R +] +endobj +394 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 223.63 647.866 275.29 637.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 236 0 R +/H /I +>> +endobj +395 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 196.43 625.866 241.99 615.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 396 0 R +/H /I +>> +endobj +397 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 480.51 614.866 526.07 604.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 398 0 R +/H /I +>> +endobj +399 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 262.54 581.866 314.2 571.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 236 0 R +/H /I +>> +endobj +400 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 225.04 419.866 270.6 409.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +401 0 obj +<< /Length 3116 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>gQ(#X&q0LUk\\@jLZ)7"I@D6i&23%]YdI%&3tEpK2/8LhCO)%)q>.5EKfheI9/Fl1J2*k@B%?.G;l>NNfCIH&&$nh=Hi@QdIcX?H?Nh+8rWSKg8EXUa_@*+&A6DSihZTYIH?,MM55fHl8Z$@$RO:-0-AODO\u\mY/(#dAlh0>ban">rH85g)j6Gfm(M;dn4Rr@@b'2TWY0up$k64;Bo`o=c/,2iBqnJV[_Rn5qeo7/EZDhs&T@d2Ud4(Zd2nI8m=Xsg%m6r>gc`Q;h]Z#"_R%t.S2^_E]&@8kO,*S!_\R`/0HLLOImn0f9bEQ-HT[3rPfU]!&Di\I\QkMS)C%YBRhP]3N%_qlN)3X6QQ[X0t+hT^OdaRoD4OB5PGEOAYqkF\:!#QqE*L?PGu$YE@ZuTOWR%N^EVPh#f$@:-D*ZT'o_-MI#Cn`:URdT5SccJTXX=44'kF:7(a\@`WPI#K>:-C\0Kf0B_Pf#<`7H2_ad^V[@;e:7(0"Ys^/UP?PFqMV5p%gM^'&EU8VRVa&mk$e95I.l*%7KiGk(MV071.ruub=V"#r1Wk"*Bq]b+F"f82\Ko%F')0"Q>b3CNiE;Z@WqUuS""t1ReCn#T)CSs6L(&0RHf6!NnDjmgHY=Dp*lYlYdjOE_RP>Ma=r?TnAD?0u.k8AR0:m#jeC`>I9r:'HK`8("iJba#^=q9SfT;IUkUb'LA5[7D1do?S@rfUp63A[jk6d+>-.9\mDJ+=)b@mpVr:+h0%sO0RY;bUq/%A1\ku"U[1j@5Xu4O\C\mF#e*MI_8s'^Obpi38YXJ((Q<^Tg`YGo*GPE^ZA$]tBP+$Isa^JC.jBD0f[E;+IfK3u2W>&r1a,FIX.S.8(_p)*lj#N)343VAO4[`u4#l6B_[gC+82DGsW60SoMnY1b>3>r7O3^LA=Y:i\L!"A4OJ1TU[P(s8s;2%42d>,07$&>_SKH=ukc.83eOGb0!$8i's2U.#K5eLF,N@<"qGRrh)R&^51_ih8K@l&4N%LQsrCn<((*QQ[:GF/TImA=9GL2>,VoTM-Wt.2gUN`7$n(Lo)9pYK5&-cd0eU0e0gH]]PY%9f>!FQ!CeD5+IE,-*<+G,p;CE)bc;d;G'._RMo53q]T+/$3R-jS8l;3dU>!uTq]]#cB2Fa[@9d!;Qm?:[e<):"N!u^Z'UDleNj7&&.dMu@[a]48qd_XLk\JK](1UVh5SV%Jt`g@S1HUNC'A)bUV6jQjeS=tW_+$MQ:A`[LNnC'jmJtgq;d`U>]FC6L.QX4(6-pYLTJ'bNhQMQS"ot&=a4APdFCSiPdcF$.[YI"O2Ju@^3K::d^@=hNO?u;^@F6BY__B*EAK;qJs?j5DU=@?[jA"b]R3J0T33k1j\Age1afJ%)8b(NRg.]1+bClT0*')rFdl`2^!C9PRV]V]--6kBTgcRU.L;6V$c;GpSOq-b7;HDjWF;VDn%"j+jmj>oTi7sohN@A13QN8<3@.dQa>DG1;^V>p3^GMjao3l]7'C[J_[6%7asKlLt(]Z]bi!&Kgj8kNJJMJL,Zt]_T8J$qh[^))Y_/01;!MJ;/!H;2/7`SL$/E)&DE@[&r:!/Gou8RkPrZN=$&>/;V3![PDc3$7[hbi9=JSN);EXFnk[j.Ph]8p*EVm;Dn[r1&\6U.40)d.7W,=#QL%#0t9D!q!78h1KXh)VflO?_._fmTL/>_i;rcr/Le4/Z5\RXb3k0U,!\#N7BK7t,&^N(:=A+#-0<5@[6[)d(K-'Ohm-q6S]O)6e/^6\u#YGLZ.=VNj8`rJ>_LOsadcb.6Xk#4W5-`RSk>#r[HDD3roT.fE.cQ?7NOSH",LHj(VQMn=@ofr'#_u9~> +endstream +endobj +402 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 401 0 R +/Annots 403 0 R +>> +endobj +403 0 obj +[ +404 0 R +405 0 R +406 0 R +] +endobj +404 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 188.38 669.866 233.94 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 225 0 R +/H /I +>> +endobj +405 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 477.8 669.866 523.36 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +406 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 256.97 658.866 302.53 648.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 398 0 R +/H /I +>> +endobj +407 0 obj +<< /Length 605 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU0_2b!=&A@6WHq^?=CP0Ih@lE(A>A^48U$X#WK9qtD.U["\RM_2`'5(Mt[`%;2,B,WWb]/"[s\!'_*H=E?[)2F&Xu0^SE;bTpq"&&6Q)^IohgrK;s$o%e62I2M0eb#l\LeMWePICa5)pT_P4Pn(6G_Y]^5DY,hRlkX!:^[%!srQ_e`jeVU8OcD(/pBtZBHXP6q=9kim:nONa`3ba4HI4[%`#gduISM"QNhWpqW*X)M`H9J]^O@F1YW5oRV8+`[EALSC:Wd"L9@?]c:5J7fP).Q!@`i&Y-We5R81Hm[%XdUBm6A2=M**R8%,B01TDHZY5f2an6.K-oi'E;I\Wl\3]5j6D#>lcS2_Wc@N[rd&M)=+!Vk-qWtmDtH(LJqc&!KX^qFTW;Q0#;9nTXK+#>oJ/ur*3VQq"Y;kK8/EW!@BA=:&[P:plSMcQOYX^4.Y"ep]-1^r227d*'Tl[NCCG5LtEKrr~> +endstream +endobj +408 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 407 0 R +/Annots 409 0 R +>> +endobj +409 0 obj +[ +410 0 R +] +endobj +410 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 453.91 680.866 499.47 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 205 0 R +/H /I +>> +endobj +411 0 obj +<< /Length 606 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GasbW9oIa[&;KZL'k0rK[&TqsZaVdY_No(B[dFBR4XdV!LcUiaS3m4'a3J9S2X++ed>Z($Y9(CR:sRqRAq\3hO`ENnA2iP%_8f+o+IB?bnjQ0n"'J,j2(*;U`aC%&>CKjE,a'jd^@jL*u\-[*=oaG6K[Jhg@+_r"H>NWhb&T#Q/7s!=!=h/4fdi11R^"(UkH5uH''@m-kp/glc7bF89k.+noFf;&34=t\:/4!)#jcB-p+o_IcGD.6hd5q6HfV>WG+m8LCpeEnEf/3N;RaD8Ap&Mp)>IbE!ZE3@'=QkHhNkMm(^%I41VcIbbZ"cDH"rZNMqs$gG0-3eTlOhP-rcCUhnhN@I!d?S8"8o:B<'>0s"bc>u9`pihk>0go7P?I5'-5P55^J>aup4dlQC\8rGA`.PCe&b,"CpsZ9`hiqB+uk@!sdm>h-i(P$%OrG01b`;P>*?/Kf#&iWo&5!g<\(QW*2F,*Fb@k3>iU!]D5FOrW372,fp~> +endstream +endobj +412 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 411 0 R +/Annots 413 0 R +>> +endobj +413 0 obj +[ +414 0 R +415 0 R +] +endobj +414 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 280.02 691.866 325.58 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +415 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 147.0 669.866 192.56 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +416 0 obj +<< /Length 1148 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gasaod;mr%&:Ml+pp$6\^,B@=qF1`BPEIAYHJ5pYl,-s`=G&.ZZ5h$E=-2M.R#B]-05)Z6c^m9idVX1#cc7kuLLCm-l`2Gd>fB"iIW:JC>qCuT@6>A1=V^jZ(FS0an32[Q2t:dKDjB0P/iHt>ZLrA01W\ui[Np[(kA7>i`lCcJRj"%*P,3.;Fk8rC]Vtr_M75Mn6N1V9l/^We@a]'FSZ75\ZG8Sc!N%k[Eh9T&j0/>?76"WCUIc7g+$3\R(t@VPQRpT9\&naNc#3d-(^LnrfR5>1d&VHQ^@8g[+YQ9)E)OV(^LRL@-t*:1%5%U+b6bd:6KM[34.XQ"gF]Yoe8"RF+rTq'P>QC=KiU'q]/0R4n$R^u;%i%>4ln]U+Gj>RCNLNWko,.2-,F!.%-_%`Nk;=WkkLWmQ@pX@Jgi?+CEpF\FSsteF1lMFAkB:CBsrGY,K*)%gtgL(XM&KsCm8CeWmf&[J'W]VIB7Rd$9GU1nrY>>"Z3`ggB@PKU1Bd?MX2GRT2*ejZ*a/*LW,mb#C2eOg%(38Bm*V7^(bjL++F+I8()qt(L7J4eSp)%Ruq>OW$d`;XX*Uj\QHCC6,_ueK:?p3Ng=`&M%=E]DKatVOQYfeBH3&QkqeFAO&Nf+O:I%`'_hLMn&#P%Z88sbceZ2)]X] +endstream +endobj +417 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 416 0 R +>> +endobj +418 0 obj +<< /Length 4878 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb"/+=`5ND&q9SY&FqXYU.ufsCTppVBBHFhM64m9OQ<)eENFm@oq^+VO8%7EZ-Oo*aef;GC_k(r=H)IDYJ+n:Fck1qiop8tE#(J%D"7&:S_YB51Dg&^Ak<&6ET24_3a6E5^V93OSc(`V]W("V7_Y'\"!PjX0SgY#6b.Q3n*bEQh8&ct,`IpQ4.^_VWlMhMG>lQJoY&?AFuBk:(]Np`L4A81h_"XFA,%8c;&hT)aEe7K3fh/sjLQbK=d=L!0]\!Mgi2DdA%5t4\5A1]lco7ir*NNl^B7Au"QEQjSMd^&QdoT=f6.(hik'Vf\)A7C?du(8-L.,!"aiZQqfI)'bT!>Cj1BMmWC0OJekd[fjIGV+S#Mo7SXk+OR3_og#$,OI/iS<*ES+L9Vi4TQr3s@r,rj#SfQEX&TRI`=2Rs''kZ+)GY7N,^gKb0dl_tGnkL"e]Nj^2;Ij4YG=$p2'nF@-5TDh9"k??Y;*A:Jas)b(4g#N4KVre'Pf@Lg'hOZAg:>c@k'>KIGCe*L!-=5T&5:h](HtO%nrA8U=2Z%C\b*(IcZi`5&\q<8/"&GZaSHL#C_S8Ei5m;e]!s$7W5Lfl.%]3/m4C@P@2X+JOe+'9u_YjML_41QljT-J1S1qD0gF4>\\%L/ZUo;-=jPgA".KGUgc5:q-$+&RC.=*s#h=>-^dfh$()JjtF@'HI-:jE$HRH#/s;4gs6MOn*Ql>\sS=;m)\Si\@9g/_]b%T@;l.icJ']_h/"p&?&57OCNjU/L-SU/rt.m,W_YP:qA<%rcfeqTD9$jf94/UEMT\22ki`?_-)3+<%bm2cWtp.Q/4OUnfJ(doOH..*>k.:YX)?bQ\Y=U_q2L]m#0]YTL*+ZTSM=i9P4&pReaboq8m"@Rh7F*8b.+"X*AqYBC"25jp`q779)dKin^]=Fk^/YbD11GlV6GnHBF8TiFK;L\-;=JdsM[?7Z@lIj,Zp`4%`#@%P!%LE6>_Ce"SYOSLsL4>ROnnj*'LJNp2K)4M][8hMXrJ`?F1a_7EYGpO$Gs,<;?3,o+$):0'f+fmQ^eLs[GcV&sVNu@7%V5kX,fXHl8\\fU=J2JlCg3+=7B!3;\WcpK47.DjYdPg$>J[X6EYW1@Q,61=#UnJi;BY_$5M!&8FY&p@(_A2GshdH^VC+W$VpeGY0U&kmO!!ph*]VjF3,+>acFn8O`'m:5Mg/S.d\eG6K+])WD'C?JGdAkQjH3CTn:tX6mnrV#(Y%+`nHTf=9LL?&(cK(Y3'#)e.-Zao_WSEf%bUl4jog9E(>tp1%67#E"1SQ?[HH%]+68eo_f`Z6W6o!9,-SbU]#qOrhX'n^KpSofA\]#`bfI'VY^saM6YPqI,prI*K!_DVsq-bQ!Rr-BI[=:U@?i6M6>H]K/FAeJl6WF.;`XL#?\hM>-$/X(?OkCr<@e!4*c,jL[Z,6ts<<+6#m#qXf*ak@DXWlV)Bf8J>S^)Mho*PP$)r'1p'=/XFfGk?+[$@AHbS$kY;K#ZPXf)4f*WSOQ#gd2qSKd'=QLCk0^H5D"f8#U;KfSa/;$#__:0Ckt:\*le&`;V9[4]ULGHYl6'5V).G'nos'nFA``$qYJn0Ha`&[@nQ[HSP0D:14PYVMN90`h?55ObOap54[gL\$_9j.FBc)`pkAN;YU+]!0hG%9cVqO#gU5P$K,J]TF"=a*Ws?bmM1,C`5CU+*^dKm<-H5Y)<+o7ear]nnDGo4lB'O4UA@>GLS?SikJjkCq7RK:UE<4+[:;52-AiJc[N`(W7Ub79?qD>Zqr"p/5H)AmQ)od#HZ(T"p/jjp%4)../KZ20dJ8D"dBqLN`A=n"pk.J8Kp:tY(20LB"KIth>SYNQ9c3gfW#J,=Hf$27A=D4dn%1!HYQ@\Y?pt5AntOqY3hBaa%D.%J,+5TqI=1KDhH6)s_I@MupB$\6IDfR7D*1n<'[IL-cNWq360jqFAXcE&SY'F'`5B6_LXBECqheJI5h[04\tSJq5$UVKa"t&sWgSfkU_*3:0+TaJG9jpcf"/@aPa:0,r"2h1S$kQBEcD(k]<8M#'nZ^lgAW*^5\GiF^lL.<$m"55;?\36U*tshG=DroSG6e5&Q+\>5Y3><:e4>gCTEC+8'[8Y!WG.H[\EtQBYLT2$-qLOJIMD8JI0Le0PO,]At*^9Q1&+<$<_[!=d?k7aP4b/I/.3gin>WJ/(!ISrW>dI.5s0c%am@$@S7LA),.cL"^B?$f,U-FHe"lQZ-D0a0d-MGYbn'_i]][[pC(=CqeHfqZhe@g&kDuf,I+k9#F(+"d20Z[pT/QP:g[c.s*CCQ]^F!T0:9_Nn39Noc+fPndlU/33*d-;(':s'ODOI*ZHV3p&=i;Q!._sg739XWaTDA&o@V,H+U0)j!Vk"2l4Gp,j%bU/G2\:ef26S'Znj-oPg(4q%nKp;llL$BhDn;.L\**gH8_GR;Xr"YBQB)1'>S9U;G/c.kU_lA(1RJ?l:(Nu;I=h$4X)64ZjD,/*c[!M8MR\s3&\G\'gR(=;0#"7A\kZP6nG1;*6HDc=O"s")@O?9j36^cR;YVI#f^A*I#9;-Hub*7]iB;cGD`h+H#V((\?]LXbS/[KSOY<#-Kh`H:T55!:#_R$SZhH=Ep1U2FC8?*U%,KLMis%2MqD,2ZX&o=\k\]coU`"DkLE@0.u2_5&be;?;T$HVK,q;!er/e`*@T<>*Zu.>J*XA%jk>!6g.2AMFL;7=5=$p.&uWO.\D57dh5P]HlD.r@E3eMMS/8s,&Y);O5j[9(6dZ[9]:3+!OF%)rT0d,"p_JM-Z`.pStp3/e5jSM;R2m(C0<])sdGKVm!_=0%RG"qIgtd%mB9t5@j)>/kLLl4EA)'YgA]P\%"ScD`0McKL&H3E*l:_@+_QX[/Xsm*pOlp6XJpLlfsUjs#2L5Ph6_mN7RE!PDCB)k1;1]XZl.b1!9/?0DVn'9&9#9$!OEl]'3qR@VkW!X;:A2gNDYQ/hrgXj?3);sb82eVO`0$!Pu]:Z^5mV=NAD."9FX0R8#NSK]9TgPaCokl%n1'FB7FmY1Qu)J!N*)&>?;YPD)e:tfDl^.;ERh[s>TU&#"RHapriDqhp.gSB[&Ysn0Oi`@CoAL`#'X<4[YgtH/o#?e/$7t-;GD6nM#YW04&8,ucM,bZ90lk2(roCD6M^Iq+*Rk-3HfGR?9!Wiq$!J[,FOi'TEhe;oWO^]Xl^5oP:[.HX_o/D7sdj>^R$_/0!_aLL#p@Xn4YQhg^3Jg2R0;nQrY>9X14(n6T`rn]\[(B.nJ%s0n`'3nhXIO:F2h-@,/(O#s^dC$l"G,pB8B^UVq#)cu8_d``Zf%#it!8e6h'bSdkm:j-Y"Bh+lb9))6<*i8BqDA+,f1lot]rf(QB)3SMU!UYW=N>#hZ;eR&C-^l;9t!tgA@722.&NpI4Acr!Z]sP*eql?1Z<'FFIl93[,\1>`Xpj^.;L,PE(>Eir'G4Ets&9H`HYX5H\5PFpXi(_#qubhppld$R#Qg7(*UXGI)]okCJ!%p1#C/SF,c=n>?HNAmmDP8PTNn8hYM=*p4D@gs1eLgRGpeOBaG4+&_;^km)%A?3fF"CZkBXagE`qV$;Ulf^TCd<<+3rXr*T.qW,(`6psOoBQ*NUrVS'\=@Z-)\mO2JoFcPm?t.7/BTa#20I/n.F[uL4"+9[t\O7InmrS14XfX3Q\l4jtC/b^$SH'8:nL+#fV;KrC9tX^Yo\,(bcO=dR&[RIaGohm[oqo&Z/0doJ10ZpJR7Pt+;NUbJ?2aA3SO<_ll@8^qba8>0kd\>G~> +endstream +endobj +419 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 418 0 R +/Annots 420 0 R +>> +endobj +420 0 obj +[ +421 0 R +422 0 R +423 0 R +424 0 R +425 0 R +426 0 R +427 0 R +428 0 R +429 0 R +430 0 R +431 0 R +432 0 R +433 0 R +434 0 R +435 0 R +436 0 R +437 0 R +438 0 R +439 0 R +] +endobj +421 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 482.78 665.894 531.58 655.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2004/REC-xml-20040204) +/S /URI >> +/H /I +>> +endobj +422 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 654.894 375.3 644.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2004/REC-xml-20040204) +/S /URI >> +/H /I +>> +endobj +423 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 289.22 627.894 454.12 617.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/2004/REC-xml-infoset-20040204/) +/S /URI >> +/H /I +>> +endobj +424 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 354.19 589.894 445.75 579.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.w3.org/TR/1999/REC-xml-names-19990114) +/S /URI >> +/H /I +>> +endobj +425 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 239.77 551.894 483.82 541.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2119.txt) +/S /URI >> +/H /I +>> +endobj +426 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 453.35 524.894 531.32 514.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +427 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 513.894 350.31 503.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +428 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 243.92 486.894 415.19 476.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2616.txt) +/S /URI >> +/H /I +>> +endobj +429 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 311.15 459.894 508.54 449.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2617.txt) +/S /URI >> +/H /I +>> +endobj +430 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 448.894 251.44 438.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2617.txt) +/S /URI >> +/H /I +>> +endobj +431 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 358.38 432.894 442.17 422.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3023.txt) +/S /URI >> +/H /I +>> +endobj +432 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 450.87 405.894 501.89 395.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3253.txt) +/S /URI >> +/H /I +>> +endobj +433 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 394.894 289.21 384.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3253.txt) +/S /URI >> +/H /I +>> +endobj +434 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 237.82 367.894 432.73 357.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3530.txt) +/S /URI >> +/H /I +>> +endobj +435 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 241.43 351.894 432.15 341.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3629.txt) +/S /URI >> +/H /I +>> +endobj +436 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 331.43 293.922 514.09 283.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2251.txt) +/S /URI >> +/H /I +>> +endobj +437 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 293.11 266.922 397.74 256.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2255.txt) +/S /URI >> +/H /I +>> +endobj +438 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 296.16 250.922 447.71 240.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.unicode.org/versions/Unicode4.0.0/) +/S /URI >> +/H /I +>> +endobj +439 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 192.0 228.922 267.28 218.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (urn:isbn:0321185781) +/S /URI >> +/H /I +>> +endobj +440 0 obj +<< /Length 1522 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU4>Ap!%'RnB3@+?(*$F=g4\lpu^`ei2F;FN%jMrHo3R+Q(,+M`gXouHM%f4KPA^p5.QjtLj/hgFV(r8CrZH>@<75ZmVa0!Mdu+<#u!U=g5k2Kf[@QKE%lVqD-r2;Q1fjKlVso5a@;4B&S8roa9`H!$r[DTe6LOa-=4(Z+bM.#H(p9cD\/%!`KR[WCiJk5>TmR2WngY7;VaPt.-M,(0#2NX&hI0s<\f)G+$Kj^BBi"RG.r.aIX82-YCMe[uJVo/KIqJr*G`-Ya<79`-Iq1-a\f0]$uu>Q2R>NJhX1\.kES(Pe_Jo4p$a?L`eGQa:)LPIEV?_ah5FR=&//k.07Y,,k.>!#3hW/[-tO6Zpb>^&kSO=''QIn(6B/o)r,7,]bTe)7u4RN.(G#l_CW`059D_[XR&e@L6;SjSb=Y2D=Uplhn-_5@HS0k?<9c;]:EX-VaIStfg/pIp2_7sQ#:2)0hj`G,,;:gepXa2F"*@%D%p"B"=%U.F58oV1kjkD9]`qtBfqc.UW`POk-%:!%0$2BbBj7?1]+ik)AA+e=@?/-T2-H+M2k/`/`ZVOc]sF-*.^<7G,gU.h8)OHg#aPmWY=M]gr35eh(_Mg9V,7k)DrZu*]DNg__`RaC1Y)s&&V7EFEe5_LgQrKb/D4R^8]ISrS\<>]Kjl:6M`/1/:&D*9WBQq`[CW5OGk>L^0O5Rh=-@YsOT=0q@#i%QEOgQ2Ot.L",aD*8UnC*B[W0o#a)#HeEW*<`OtAQbIg4R@4HF.flp,&PL9,\?Qk,A)/_M*OrD,H@]A_Z:jQPq3fQMH-HG;/IVZ\n[a-NkG$3!TY(nJ+YSF0aspdPPr8m'2__NPX:q*fL_a@Y9p.8j>QsO#s/.\Y8dW?A>NHM@RK6TFRL[M\%p09c(#WR3&#GohG<0E!(n6?CINiQ[!+=$]CSu]WJd.lBLJ;ZHiZ5N@+o.s!F!R]Hq;V\Y)rM9?'o&RLW!kql3J<#CbETpOPJmDn-;EIK/]_\-*Y6J_K*/f0tjF*D.A[\;$irdXpFKJ=[;8+P>^uHhnBfqW_fHm(GK.Pm:)Hm[^,[]rSj6a6nh-h7aX?(oA"UEYo7[u6=Q>'/g5`#rVK/U@9KcGW"`9:T,qa?-ME?4Z<\%%[U6UhSoMEd./4~> +endstream +endobj +441 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 440 0 R +/Annots 442 0 R +>> +endobj +442 0 obj +[ +443 0 R +444 0 R +445 0 R +446 0 R +447 0 R +448 0 R +449 0 R +450 0 R +451 0 R +] +endobj +443 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 327.8 680.866 373.36 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +444 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 165.12 659.866 202.62 649.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +445 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 195.12 515.406 232.62 505.406 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +446 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 220.39 420.246 257.89 410.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 55 0 R +/H /I +>> +endobj +447 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 202.33 399.246 247.33 389.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +448 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 200.67 353.386 245.67 343.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +449 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 270.1 307.526 315.1 297.526 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +450 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 278.97 202.506 323.97 192.506 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 69 0 R +/H /I +>> +endobj +451 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 189.0 156.646 234.0 146.646 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 73 0 R +/H /I +>> +endobj +452 0 obj +<< /Length 1346 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<;/`4!&:Vs/TPmRDk1F6RfrAeOVU!*DEfG*?X6P'C"KrhF$ngIR;r*N'/9Y."G9'MT86/i9^>54Qk*rW*St3R,'b9cLF(WTOcWQeNa2:.$L^;VBa+4n:[WXi1^7@?'kc(,:9j):C=[25;cHAglbPCs0kKXlVp0PX6Hl#6lK\4->ptbS[oPXMFVCggZdgsp>qq3'''HR;Fm3p`c8`k/a`3ihY_@%T]gkrSca!1d#0OdI5%c?qL$P84=#cp9@e:[0V,Db[u9Co?3SaQI)4@qJOAd7i0&A*"a1jVd)7LJ^b3]oA5V%A-8o1!XN!I/LWG"LR.OQDO(1]782dPkM`c@#]%$%kiU44Z(AfOZlGj#0\h>#A6MF>0Mc.+q=;Rm"Ir3Y40&3mUXt8@=*5-iuGa`CXTbIkPKQ]tqh@7=/(iXhXlRf[r-,dQ_-PFEc"b9GCCEPBd1dsofG:@j`W\Wc\bZ)C,\4QI(%hOTZnDW7nh@DMb`,9orD=7+0\[W3a+]@!A)Ha"k4gG4o_o*`6J'MRVZY]kj=TItIrT.YI)3#m"(a04PB77+J]?ES31k_F;E3;pK=AQ%TIoO,BMOA6L6eiWOQRlK^90rYfJ6hF'm-C!NQamL#o''goPAiNTql$&@g+Ej/?`-,T0O6rWggC(a[oiaf)@2&QLG%t[S\q/,8lUP*h`DR^Q%0DP.-=jLFJF*2D]K9[!pRQTB^BEOk_L/3J#K!Ue"q-?2D*&OC6j#g9W8[F!k4G3\RG2*srW95naSL?s]KY&bjA9,Ate2H-6/'^Cgj#8/.jHE@s:()'p*=$1Y/:XZ_TfPCN\Eia7gp$]f]#$+hbs,S/2jqt:rW?/1D(k~> +endstream +endobj +453 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 452 0 R +/Annots 454 0 R +>> +endobj +454 0 obj +[ +455 0 R +456 0 R +457 0 R +458 0 R +459 0 R +460 0 R +] +endobj +455 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 236.77 531.38 281.77 521.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 85 0 R +/H /I +>> +endobj +456 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 242.32 396.78 287.32 386.78 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 100 0 R +/H /I +>> +endobj +457 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 270.1 350.92 315.1 340.92 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 102 0 R +/H /I +>> +endobj +458 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 267.62 305.06 305.12 295.06 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 110 0 R +/H /I +>> +endobj +459 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 225.4 249.34 277.9 239.34 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +460 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 158.19 95.02 195.69 85.02 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +461 0 obj +<< /Length 795 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%!gPun$(r#/^C:kDu\oVc$0cFRGdE%'DUW+j3;+oT13%&@M.Z"[SB)l//".6X`r29#\:[Qo8G;U\^Z(VW$[pD4eI$i,)[o"4U6bDlm-qf>,Q/jJFBlbcH@EjMSO)D_HmXk`U^#'>>&d33Y#qKkW_bZ^bnS8S!*GbZ7Mgrj%qNZk=!u/=^/Ii=#WF,i8fq`XEqL%*5iC4>*AIZpTb:0%\)NFT:>$^E,tc(4J\%(Deg^IuiT'hP)29/Hb`G;I\1EmHA9).+F`e#_/4_J#BQ,$*cV-@'H=lsbda%:d)'EQ)!,l7$1jaq]Gho;B3Ek00c41lN\#$q^2\2^>@%-F7u.*i4AukQ_&ASan,%ea_M_.:8O06OCt)0S(9*6)51.;/(":>+^R;"9r0[gqhh9Hp57^7AFoY!s8!=S%d!10<*;33fK&ffKOjsgS`=^0@$K_9AQQsG=4Vlmj5/?Q'BNgT83/Wk]U$5$["c&YGT.N(NtRcg=J3H[G(#fh/k2Rh4So`ORL@Wa)rC(I:.K^hrNgloHVA#?*""1>iMbSQ!do5U0t)k#!jC@J2O*;A`2:NIuns!agGR8KH!NnH#mKujjSC&H_tLuUH1U0jnf:aO+QLe=tMU,;+914`[5NkZ$[h!q`0)cas'*!clO7:9I +endstream +endobj +462 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 461 0 R +>> +endobj +463 0 obj +<< /Length 3412 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauI;D/\/g'$&nm;s7coCilBs;UXQDJEsd`/e@Y&DAL37?RRgr#jYSq=\eD!rR!N;%;AEU`l=]#?o^SprkmViI$"#8I.E=OM+;#>nA^M:JbO7Q]KlB>?4r.aOs'?f`4YVupW)MGs3*R&5p3m+rLH0#mbNfeOKPdjH2UuCS*NpO]G37%`1*sMq83VGr]IkCX-^IOhY>F'1[e+Xr,:O%ogl=W7JW(Jr*UuH"6ISu)-$30c=Z0b0n3rPWqD+ha6'_mp<21W"nX8L=&]*#bW1bbbaZqf?iϘiXc\7C8DbBsfbLdE#^'W^QA/?arOJqJ7(T8Ug4F8;hf\WCgZA!=i*D"7o]YP@"uU4WA/Q5/n(1?"-[b9)!>4kR]pDn`_1CX>oVSCd7fgO3/\c&+s7V]\1B,E2H2d/J:grGE%shmUT-(R#>V'X,[:p'5qGt?%2dV'tT*1hY2Q:unEUh1!U)PIFe][9;^[QM#+>6"qf=`7Q@Ls:DGrdNX7IqDD^Kf:MM^?+2(ei(t?'R3]+D;%jl%gSaO6A5K_dqJO-p?a3RRKCB5/RfYi$da:@Zo?):`D(:&mii+)L@RUGE/B]]mrj@!'d'THsX!:Y_4GP9piMrTB29Or\'^A>lFD9d6!2n.-;16,&f6>l=.%5"-k`1;:%_0%JlD2\?)S^n6l'2D?2eJHE-b(*jasUb,Z&+GYadl@0?\MW-\q"=VHDC@CP6WR^6R`j-+%SM9s(jMfMt:??tI4T"S1db#(;W1Q?o_1f:q&0][8UC5C@bgr@khR%8[9e1paMKaE'5%f)h'[KMFu8'?/2;Sjq&$2:SZ>QM+UHMUYC_(MlX\G_4@Fs!fs4H:PEZ0;K:7DH.33n['j5m6j^2(dWcLAPj,c)A_Go;VNZ_3D#l=F'-3UC\?;:bkUqOfCgUB"+krQF9#soia,0/@YCgg&@46+*/h=_6JRJB9R=%:ps8;e1`Ui?/i2g%<>SRkd"XITL=$:h'4d>e"dHe@''E&s`q.*]*Zd=NIMS^pU>nK'gQq7]r9d]9O#%9"9Q^1o#Q'AMsc]jr:'u5i#QiKSY4]ERuAc'mg!FSf%m&:$>WNI'Xa[l3Y.Z5fB7gn@'MYeh)u:t^"''T;um?I]3d3&)6T,-I&7VhMt.`aG>''Q(OkG/uJM#u^E:L+nn[91oCHU7uKsp%ha,`*`^.\>b9MM.]p^MAW:+92%O;\IDVsk\.X^$Rr'7'U"n2>=%LMAUkC@FIQQ5g^+>-FORt07"?InQJ)?1jl3.[@>@_Ai2`XW(BK9@V.bFs%[jK;H5rjKYUI/frB,M!/\un5FAK3DmDV[TE=1@7m]=d=M"g=B^mdgUcg[lcU8+aSp(-P6E*j%`,#G+M+53:8%V!`i\9^9-UWpNGoKW/cP`m/@0Q8E)NIlR:E^rd"\g-LWhH;oP7JkV=BBD7s[^RnEMk\K6KJ&k0jC?I@p1lb0%*4c!tR-C%\uQDJ;PE)m&qbXfJMIFA$*0m0R8PgJ@O8o5E&/9#80p9K1!+6`G7UU/DC7+bnN!dF3dC^`k"Q.,R"o4mX^'5ELhDR/!Z;,WZa*SYE6N_?YFprDce.W,l1t?C!Z^C9l&Mm%t]@<44G3&M,^l!W>-j)N4o#&VHJ'd8=^XltDSO)Z#:uo#YYZE'KrfH9P++L&qOIZ^_H:D5AWE@6:l3:<,mVTc`j,X=&@Ej>et_+\Um82``5M79WeXg%rV_Pffm6TjJ*9fXM3S."un\H(ekL3.^2bG)-3"(.h%C$fW:V@jZ"9UX,UDe-4=#,3ZAHMMRM@J4%,L8";F!%+\ZJE!VVa6$Gc50s)$,t*.DgQar.%u_,gdJ.KU$Z9>L[`*U,![X\Q@'.(#_NfQ>1jYR.WA42PB!2si-=rRcc@4@`UaF)fYu%7ZS7X-+B+!#Sb&UdigU,\j\Ih5J_Y[SbL"c_MlsZ%EM*<44G3&_*V`8V5^5BR&8ie=\WYGcIE&l"=*u$5t>YJ27tC*k*p@Xf24aKsi!1Iil3"Tc0P!oH18RG*OXPSGf!q_Nk>g+UVX-gR"33fTNbdSbp7jh8SmG]Ka%HhmQ:d;;rR#.kb/>l.bglR_rHi01V`7lmFScU9p?n.7hmg.q$\cG0`tDr@nTCoctuEI/(t>(LGfg5)#^L$N2^VXhZ'oC#n2NcVjG./D_LQK]m_Ii(h!pB.VqpI?s%+M)bMYKbot`dm^(:nCae:Ve9,K_'ak\]YZ\*p3AnaBb4\3hH7b)kdtaQAGCurSS&)hn)Fg9Mc\aSZ,T'RS@P,`_^2DEbFM+_E>u,h$NqgK<[dZC)=i0T,B32BlLfJ(hkEt`Pr2LE=.bc?7<@:Ar(u+A;0CCL55ME?^k]6/n@?69H_^\U7&DP(n&8Fc1Wj@U%OrRndh0.WNh,::\nn*_`q8&CH77f%83@A6+1-aWDCK&Q(a%>09HcN'7lZ0<17T?[B?,>b_HiZeU"L64L:kXn./W:QsGl`sI^b_U1[X'e:M%_TfADZQV/Q+g:i/=DNe`M!.KWWs(1L]ss.#775U$7uSrpXP&li-t^E(Ve~> +endstream +endobj +464 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 463 0 R +/Annots 465 0 R +>> +endobj +465 0 obj +[ +466 0 R +467 0 R +468 0 R +469 0 R +470 0 R +471 0 R +472 0 R +473 0 R +474 0 R +475 0 R +476 0 R +477 0 R +478 0 R +479 0 R +480 0 R +481 0 R +482 0 R +483 0 R +484 0 R +485 0 R +486 0 R +487 0 R +488 0 R +489 0 R +490 0 R +491 0 R +492 0 R +493 0 R +494 0 R +495 0 R +496 0 R +497 0 R +] +endobj +466 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 587.556 400.57 577.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +467 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 576.056 400.57 566.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +468 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 564.556 400.57 554.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +469 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 391.68 553.056 436.68 543.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +470 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 356.14 541.556 401.14 531.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +471 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.23 530.056 447.23 520.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +472 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 373.9 518.556 418.9 508.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +473 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 507.056 400.57 497.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +474 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 488.21 507.056 533.21 497.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 31 0 R +/H /I +>> +endobj +475 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 314.58 485.056 359.58 475.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +476 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 473.556 400.57 463.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +477 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 489.33 473.556 534.33 463.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +478 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 473.33 462.556 518.33 452.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +479 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 440.056 400.57 430.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +480 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 453.79 440.056 498.79 430.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +481 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 366.14 417.556 416.14 407.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +482 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 356.14 406.556 401.14 396.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +483 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 427.8 395.056 477.8 385.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +484 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 366.14 372.556 416.14 362.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +485 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 391.68 361.056 436.68 351.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +486 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 356.14 349.556 401.14 339.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +487 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 356.14 338.056 401.14 328.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +488 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 365.58 326.556 410.58 316.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 29 0 R +/H /I +>> +endobj +489 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.23 315.056 447.23 305.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +490 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.23 303.556 447.23 293.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +491 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 355.57 292.056 400.57 282.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +492 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.23 280.556 447.23 270.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +493 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 391.68 269.056 436.68 259.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +494 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 391.68 257.556 436.68 247.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +495 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.23 246.056 447.23 236.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +496 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 391.68 235.056 436.68 225.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +497 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 391.68 223.556 436.68 213.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +498 0 obj +<< /Length 1613 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%#;0/I&&:X@Tn91;$*6TH,E_etiAQR7]]Nf?\%gWgp&]bE#g$GoW6.BVmR#RB2PRQATGCSF3h=Q?FkE-o29ie(JTqb0E%[%P"LEo5uB80mEbI;&%O3=I/[kJ3AOAGaDRbRQ]6FrEro)bV3IBX6#q$@&J18ueo5.R"ni,t0_R-G;s!kKcmf39\euF>S9]!(PReP3&Y)p80>#Asr5X$Yp>b*?TS@>]_i9Y._2\(3q!'S7/-<\@qn]F*bBjg'hTC`YCXfJ\`p(M(0ioPp3FR5QM)iAA_4q+/qR(\F%gPDHR*PB\%p?Y(+UT_[_,A)*[OfPF:A.htJ(s\+]9k"&:[/h+EgFZ+?o.+B_QB9@q,UpjXG7)9i@Cq7;Rg3$rd(P;KIgS*,PF[XI5t7NtS/"?s(5$rl`"h\=8j:?a@iF[gFKe?P4Gi[.C:ld$^pcb?H6a)I=m"X$FE&**]JHuf`9%=F?"!s*(j<`[XBk-r%Z!M5X19K#5VN3@[6Id_#GZKk#+POH=AOL70I"Tt6VbgaO>%,b_H<=m,]L)(734A1*Xo02!=6IJ@]B_tV+mI:[bcBkBs%/7mNia/1>S"TGBe"W"Gh"%I&?^h8IWQ])VfW+fGd_P&JIaW3j.P-BF4[%2p(3JJ*UGq%"BKsFmf)M$2aLF0Q8qFr\:O%XFoY4]#EBh[6.k.aGi("*mauMrgN2.)d'dra1hRuBf1ja@>6(&*8Jd3"qr=kopr&a.hGl*3J#pWnu1Xd=@+SmE!FLb"^WjJhI>p-Vmg"(*F8P[)Z.d$1uMPC6`]YfUoMM#2&6F"%B[Bqd3rI2fPjl(--a9V]:aG;8%TE=@d#SM^NRF%XXC'g-WOU0l$f>YI=Qt@L#XH6epABGb9WSL]?MIV6Q-'!!k+@XeM;Gf^Lo=$Di8QDDVFsACA6aMhj"*Fcoo%d~> +endstream +endobj +499 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 498 0 R +/Annots 500 0 R +>> +endobj +500 0 obj +[ +501 0 R +502 0 R +503 0 R +504 0 R +] +endobj +501 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 642.866 224.25 632.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:geoffrey.clemm@us.ibm.com) +/S /URI >> +/H /I +>> +endobj +502 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 566.866 225.07 556.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:julian.reschke@greenbytes.de) +/S /URI >> +/H /I +>> +endobj +503 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 501.866 199.51 491.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:eric.sedlar@oracle.com) +/S /URI >> +/H /I +>> +endobj +504 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 436.866 178.41 426.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ejw@cse.ucsc.edu) +/S /URI >> +/H /I +>> +endobj +505 0 obj +<< /Length 1982 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%%d>j\U'Re;/n2MZ#'ESF$Y#[WTmB!259!^>$mR,_6#Fr^^!G\.KcJPfgM=J83VP'&@5^^j?55)DSjhlE7mOn$1Ic.?;Mnn(!68+FMTpK?]=do.pRX=@Qp\?;Op:bk=:Ts/lF*l_$O&Xld:SQCMm=DK62"):sgUF9G^T.aWiOb[*othPeUeAZ=jF4%!mV>+hMAG2BWb_(j1'qKLWs@LgP`$?N-4DFcMJAD&I\<[kHYODc5HaJ!SKQg'B5[%T>dmGJ"D_cDK[Ut#-*)4i&#i@#@O4t4oTP$!l`Q>t_,"9V9ko4S=i^3!8%.01DE(ZAFb*bGC&7*4p.:9KmuuB;@6b&$1:J3q]r22uck=t2I?QDmX+u0mI^)7Ed0$KS_*5c.*'8pgLe5s&?T1uk"9+Ol.lc9KR7Pl"!V=I23%?4V`.23q#l4]Md/O6c_d5Rr=gk>M<5=Fg@D,6jcMp*Ssb*0B\1Cs`_m&CM*kd44kO`M52P,p3.!sGK%.]U]kug5)s7';qd=35`k2jT$Z`bj0[BFN,EqhI\g%Fu7odpX3cP]3cOGZfNj"fHrk"t(Y\"e#gpP?!$+<2nZ:a1QI_;ecj*]#XK!'Qss=,_$`(SQTJAL%elM9Q7'0taJJmO\dmiC(,eE@EA$?4'kK#*S>jm<,@m^hk%2WQ,gVF-rU*"_A!`F3\7k"##=\q09*b`d!PoHAaaYV(pDjo[TRdQ\:2;V>Z%h$iDi?]k?eJnb;%\>%@BRlQ,CRJB*![g@pVpV%:@TFL^3CbP'8.)m1k0XHgOfG*RUfsi\:,G><"],n*_$EI_[>+g_cVCI,q61Wf<0)L!.L^P]'^lJaS!CD!`6$rqD_BAL"0ns#07KSjh6`!MW_i.\P9X-u"nmE;.E77('<9BF&=`p6JN`6E`'UbT6,8M9V;El%o0N'cM8m5B!3_h_+.L&G$j`6m.8C^NZ[;edG=EU-ML/\PR-k\bf7OWS^4VUHK:8sU\a3!L8`;iIA"lg:dWeLSS4-rp)ob(nt;VYRj7/grDk\D<&l$BbA3-C<#gQW.D'IuCC-L=Y83b@un=EU,o)jLSg'_:%95n$pZu'qG#^/EqeL([%6#8]P):,"ka6:69@l!]kqiTF/U2,NGqIDL[Ug*f^E'9>X=u4F="Tl0+cb0rR$pZBTD1FVe)6LFR6VlT`>\MR/7?Ijt-O34e0K'LmQ,hbQ6;pCs8JmS=jU?WSHIrZG;(3:i>bcR\\oNCY*%Eu`NpRl`g1,k>LrO>"\YX`')8Fn8kcEd&DBnS]e[>ZPm)]fXg?MhepYK1_:K93%hhY[^tsTnIJX-Gt\*E!gJh-4TTAH!iMq"IJ`@"Bmr/gD3eDDe/JRqDRs,lMfr1k0(0Gb+1acX`?YPe$B:XA+K70b<,O*''=5s>l@;SJ)'r(!u5KEh_qn#%K;M8Rc=!s,Y;'dia)KdF)r3%nW5)CpeF.];jB@g!:(/L92q`c0oT$k;0-Hj,sQsX2(-@%eRkZ;B=VjOq17m_J#hM]HbK2h''XnGW(E?~> +endstream +endobj +506 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 505 0 R +/Annots 507 0 R +>> +endobj +507 0 obj +[ +508 0 R +509 0 R +510 0 R +] +endobj +508 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 488.89 604.866 535.374 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.ietf.org/ipr) +/S /URI >> +/H /I +>> +endobj +509 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 593.866 144.783 583.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.ietf.org/ipr) +/S /URI >> +/H /I +>> +endobj +510 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 247.26 550.866 313.4 540.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ietf-ipr@ietf.org) +/S /URI >> +/H /I +>> +endobj +511 0 obj +<< /Length 3465 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm@>BAQ/'n4K4d+^T;+\LO@I7Wo&oZNmh0kr-`o^5E.r[qJ=^8$T#rm?F3niZNkO8shkAe.C#7;i@XmTmP;N1>d!,0+oU1psm?h.X-$C3e!0ono\8.Yu60eV@cK5/]e8+V6qe];4N;W2!%"+;%eiWDZ&VHFUVXb>9f8\+@+5.2unCk"4.\m"NVFB52]b`l[8G+4i@oin1g*H70iq:Lt_=oj("WEB"Rt3@oF1n'W_-dO'r+%/@7^;pUV1]E+q@)8LREt1B040n>r-MH,a^^mPK0P-X+]\r/11_1P0.@2$Gr8iiWa*6],,?YiL;r5j'It4:9;Ip_pjnX@etbDGllC^ql6#Tar!qgL'TOe@7=[l5,k9(b3R(FhhiIbS-[4j=r9IKO$`rqC"74p<#O%0el[gE:VO9G%G2Fj"DAFV7Xc_qBV=OloZ74ll9gZ2DXTH*7'dOF9dL[lt6a;NJ*%f'RThVi;4hjqYmaOa0#cL6"N&5Zf>&G6.YWfNWLM.'e&/]XU'[ZG3tI59"/]$@!kH/:k)4)gUk0$)!TJm^\ZF:bE@Nqg#Fmqa5ti$B4p$L.ib<$0rbWOhOlYbTuK^hqE4*\Mb=j9taHiV)=$4AU=YQ3bNV`G1J*_,Q-UW%IDLQOkF6QSIfX`,mci@5%c0uFJK2q\)$ln+`#c"4kSRL^*s)DYoD0GnX\SF+T=BZskG0TAA@B)8l(ldE*sVONk[!X^_Dd'`7?sAF!Zac\&QT!5nH-/<1L^%1(R73/ah\%0_ZP-bBpt."b)G?&3[/dN1cm<"#Fk+Y4*0eW8//mW/H.K!\3U/=He1't;ieO+\aV0WXBjmhtIu2Gp:N<4\MQ$l5(P:e9",-p9PJX&\#@]l-^Q\B:.H[%IPT;N&H.O^DV`1YMtk++oZ3+@Ja.K;KCh!8P;-aoRc=[A+P'UM7lUE"N`!.9*/.31Z@hcRQ)[!o&uD$m7mF%B:7+cKLUuT4b%,Xttn7JdgJ2fW3VL2S8r(NeoE6_BEs^?>sQb3RRg]G>BOL2<jNqA(Y[AN?HOk\JH)1BXu1D3kL$9a0[>T](S""@j)[g"o[Vdk%A;M;+4ZS%Q2T<,LL4k9FSO8C7LA6P"-R':Y+O('*(oD`l7>W(+n2$T5r(f[f68HFk/[!Ns#J9AUpF&ClJ6Z1:L;>5&$]7+!n/4`LY+1M3Q9"VOUSGXSp0],+&L)Hbe:RT.$(^@/F?CDZ53m'UW%^r\$;$l$h!YMGMZ$Y!dd&Mf"il3A/)V1)V:^b:Dl1EP!9HIeNdR7'Hbq>Q/o>hF*C0cI.p#hb+:X,3aKpl]4%GYdn6cG.Sa,ch4>b%6X_ZT[=@AY.+[K$9q@6_,SD9ZQJO^[ik;BL-5YXe,SS#'J)WeIG2csI\<="?XkK,>WZpK/^@4a!cfgggA\1O6$5i]!LBdaaF\e&G;PHO'JH9b?ffB]Ku?T4UtKSH6b5Ki2(me/1\eT3EPP2(iMI%.Lg!PQDe"r+?U"Jfg\"jj#)SR'PqE\n\!t@6TrkO%LP7GQe\M5Ts57Qo[0k[T2QU8aJo:DX?0OS%X;2.:aCfNrL^:@CsW-M.>b*V\aWr.FaX7@W0s/?],LWaH4n:7g_>('N(n_epkOH<;YTPbL&Mgsm`kkd3'^$j6RotNS<$^k>/Cd%kC=qSH(p%_,ol@I_fBWt(h[/diW3R.XZ@s'_fYth=>uj."on1 +endstream +endobj +512 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 511 0 R +/Annots 513 0 R +>> +endobj +513 0 obj +[ +] +endobj +514 0 obj +<< /Length 1438 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=l991#N%*.i6.H\,@&lfJ-F\F_#ciS':`b>rL%^+hEMXIVJa-V8PE]c"sif"r2Mg%knr$R8o8H$'1qr*1l4QG8cmlTAhhMI9QTBMf\B3%Rc[H_q]h3\#r5$f7R6`Y6:tj.%:@G9CNsPl]Vk6k[,@3@nUa\ed&CTJ%mEu>'ZJ\,7DD]QIZeV\MgfG6ofM!(hfD-!VYK",ka,NdqC0,m/a4\fOe..sIjp4JbOZGroTOYkA6PQ8OK+ISHo-t"H+p_d.W?*Z&=JjI>EPoeW]isDA6PSn+B>8;qB.q`Bj(F3JMBal%$q):GKGhIQL'1e!jY'!?&9?P-)uN*j6!;*/YkpZ:[,B0MHtjE0`5B`n`b;.r,[10e\.*Vs70L@KRs;#C'OGe,*F=6oZZ22(7[@Gi^JufY2=eG/o"GKUr(R\9['$Q]hK5B0t""_HN>InFLWKK+a'r+Rl^'"K2iqd9#U?G6Y)Sj/;eaZ"NCN._?"gV&oq(-"d<+*ZjU>6^5--\I%Zj0pCss4lE+Qun$=[#dV`OGQ!!2)6rif$Pq6rSoi6CW:#FrB#Sj@-L/cC!b.oY\j,J&g^@i*GY!bhLns5BKbs',lK]]<3on5.AQLgcA;,"2Wb@d%FZSeLGL;br@[*!:1.$aRI7`j\A\)>*Vlm,&H_ZCYR1XG`Ir/US*f'[&%J_)Sa<>?Cs2_E(s'r\a2h4IO$SmsEWcAt6`6H)>'XnW@:)@sW6HatO4k,*t'ts:;&Cg6jXW@"k!f'ucLOm?bD$aI'+ii.oE1pigi:?dpE-;(-E-_Fb'.`lg9oN+%E]u:f7K7Vc'D'>aFeV>)GhH$2pX#Xa]^3c3,ptIMi7^YtWdq*O;A,C`kDtrm0qW3"V'7[si*+28qT3Lr+>U(Wg.%p=b0FgRXZ:r4[;FB4))>9:\O\<1G.G:lV.>FL$9#m,m###ETa4gYIVK0.@tJG'G0$JgoG$Z=Z96cb4sjQE=\r"EWf#T[_RLB[=Aff4jHf4,C4gtJ#lR1p+CJ\K_hkQC$UU`]gF@r*a@@iMrVQ#eXIjL>FX0X"FEBa!/`>%iJLc3u4q**4(l>R*4]\2,"<4XSA;T,*%70$r_4(rq31Ehql&%RP:CUrMGB15^#8"3K,Ksb0*VGU/a6R./6CSXQcqgm/2Hf]jh'LDf@0=Sj@T#kOGa"DuWqRIsX-p^OKViIlP,HcDRT%_8l03E]RHt+9^`,]DGt/rrr!=kEEnVP=-dr=7bp'I"$OXX>sUQchf*@Zbc>OLY_p,bPpVXrqn@pZod^es7S@Js*a(XlH@6Hj)M=Xp_L"S^5bhBn*^/[Pdq@L~> +endstream +endobj +515 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 514 0 R +/Annots 516 0 R +>> +endobj +516 0 obj +[ +] +endobj +519 0 obj +<< + /Title (\376\377\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\157\0\146\0\40\0\164\0\150\0\151\0\163\0\40\0\115\0\145\0\155\0\157) + /Parent 517 0 R + /Next 521 0 R + /A 518 0 R +>> endobj +521 0 obj +<< + /Title (\376\377\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\116\0\157\0\164\0\151\0\143\0\145) + /Parent 517 0 R + /Prev 519 0 R + /Next 523 0 R + /A 520 0 R +>> endobj +523 0 obj +<< + /Title (\376\377\0\101\0\142\0\163\0\164\0\162\0\141\0\143\0\164) + /Parent 517 0 R + /Prev 521 0 R + /Next 525 0 R + /A 522 0 R +>> endobj +525 0 obj +<< + /Title (\376\377\0\124\0\141\0\142\0\154\0\145\0\40\0\157\0\146\0\40\0\103\0\157\0\156\0\164\0\145\0\156\0\164\0\163) + /Parent 517 0 R + /Prev 523 0 R + /Next 526 0 R + /A 524 0 R +>> endobj +526 0 obj +<< + /Title (\376\377\0\61\0\56\0\40\0\111\0\156\0\164\0\162\0\157\0\144\0\165\0\143\0\164\0\151\0\157\0\156) + /Parent 517 0 R + /First 528 0 R + /Last 529 0 R + /Prev 525 0 R + /Next 531 0 R + /Count -2 + /A 11 0 R +>> endobj +528 0 obj +<< + /Title (\376\377\0\61\0\56\0\61\0\56\0\40\0\124\0\145\0\162\0\155\0\163) + /Parent 526 0 R + /Next 529 0 R + /A 527 0 R +>> endobj +529 0 obj +<< + /Title (\376\377\0\61\0\56\0\62\0\56\0\40\0\116\0\157\0\164\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\157\0\156\0\166\0\145\0\156\0\164\0\151\0\157\0\156\0\163) + /Parent 526 0 R + /Prev 528 0 R + /A 15 0 R +>> endobj +531 0 obj +<< + /Title (\376\377\0\62\0\56\0\40\0\120\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\163) + /Parent 517 0 R + /Prev 526 0 R + /Next 533 0 R + /A 530 0 R +>> endobj +533 0 obj +<< + /Title (\376\377\0\63\0\56\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\163) + /Parent 517 0 R + /First 535 0 R + /Last 557 0 R + /Prev 531 0 R + /Next 559 0 R + /Count -12 + /A 532 0 R +>> endobj +535 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\56\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\141\0\144\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Next 537 0 R + /A 534 0 R +>> endobj +537 0 obj +<< + /Title (\376\377\0\63\0\56\0\62\0\56\0\40\0\104\0\101\0\126\0\72\0\167\0\162\0\151\0\164\0\145\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 535 0 R + /Next 539 0 R + /A 536 0 R +>> endobj +539 0 obj +<< + /Title (\376\377\0\63\0\56\0\63\0\56\0\40\0\104\0\101\0\126\0\72\0\167\0\162\0\151\0\164\0\145\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 537 0 R + /Next 541 0 R + /A 538 0 R +>> endobj +541 0 obj +<< + /Title (\376\377\0\63\0\56\0\64\0\56\0\40\0\104\0\101\0\126\0\72\0\167\0\162\0\151\0\164\0\145\0\55\0\143\0\157\0\156\0\164\0\145\0\156\0\164\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 539 0 R + /Next 543 0 R + /A 540 0 R +>> endobj +543 0 obj +<< + /Title (\376\377\0\63\0\56\0\65\0\56\0\40\0\104\0\101\0\126\0\72\0\165\0\156\0\154\0\157\0\143\0\153\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 541 0 R + /Next 545 0 R + /A 542 0 R +>> endobj +545 0 obj +<< + /Title (\376\377\0\63\0\56\0\66\0\56\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\141\0\144\0\55\0\141\0\143\0\154\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 543 0 R + /Next 547 0 R + /A 544 0 R +>> endobj +547 0 obj +<< + /Title (\376\377\0\63\0\56\0\67\0\56\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\141\0\144\0\55\0\143\0\165\0\162\0\162\0\145\0\156\0\164\0\55\0\165\0\163\0\145\0\162\0\55\0\160\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\55\0\163\0\145\0\164\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 545 0 R + /Next 549 0 R + /A 546 0 R +>> endobj +549 0 obj +<< + /Title (\376\377\0\63\0\56\0\70\0\56\0\40\0\104\0\101\0\126\0\72\0\167\0\162\0\151\0\164\0\145\0\55\0\141\0\143\0\154\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 547 0 R + /Next 551 0 R + /A 548 0 R +>> endobj +551 0 obj +<< + /Title (\376\377\0\63\0\56\0\71\0\56\0\40\0\104\0\101\0\126\0\72\0\142\0\151\0\156\0\144\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 549 0 R + /Next 553 0 R + /A 550 0 R +>> endobj +553 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\60\0\56\0\40\0\104\0\101\0\126\0\72\0\165\0\156\0\142\0\151\0\156\0\144\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 551 0 R + /Next 555 0 R + /A 552 0 R +>> endobj +555 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\61\0\56\0\40\0\104\0\101\0\126\0\72\0\141\0\154\0\154\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145) + /Parent 533 0 R + /Prev 553 0 R + /Next 557 0 R + /A 554 0 R +>> endobj +557 0 obj +<< + /Title (\376\377\0\63\0\56\0\61\0\62\0\56\0\40\0\101\0\147\0\147\0\162\0\145\0\147\0\141\0\164\0\151\0\157\0\156\0\40\0\157\0\146\0\40\0\120\0\162\0\145\0\144\0\145\0\146\0\151\0\156\0\145\0\144\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\163) + /Parent 533 0 R + /Prev 555 0 R + /A 556 0 R +>> endobj +559 0 obj +<< + /Title (\376\377\0\64\0\56\0\40\0\120\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 517 0 R + /First 561 0 R + /Last 565 0 R + /Prev 533 0 R + /Next 567 0 R + /Count -4 + /A 558 0 R +>> endobj +561 0 obj +<< + /Title (\376\377\0\64\0\56\0\61\0\56\0\40\0\104\0\101\0\126\0\72\0\141\0\154\0\164\0\145\0\162\0\156\0\141\0\164\0\145\0\55\0\125\0\122\0\111\0\55\0\163\0\145\0\164) + /Parent 559 0 R + /Next 563 0 R + /A 560 0 R +>> endobj +563 0 obj +<< + /Title (\376\377\0\64\0\56\0\62\0\56\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\125\0\122\0\114) + /Parent 559 0 R + /Prev 561 0 R + /Next 564 0 R + /A 562 0 R +>> endobj +564 0 obj +<< + /Title (\376\377\0\64\0\56\0\63\0\56\0\40\0\104\0\101\0\126\0\72\0\147\0\162\0\157\0\165\0\160\0\55\0\155\0\145\0\155\0\142\0\145\0\162\0\55\0\163\0\145\0\164) + /Parent 559 0 R + /Prev 563 0 R + /Next 565 0 R + /A 51 0 R +>> endobj +565 0 obj +<< + /Title (\376\377\0\64\0\56\0\64\0\56\0\40\0\104\0\101\0\126\0\72\0\147\0\162\0\157\0\165\0\160\0\55\0\155\0\145\0\155\0\142\0\145\0\162\0\163\0\150\0\151\0\160) + /Parent 559 0 R + /Prev 564 0 R + /A 53 0 R +>> endobj +567 0 obj +<< + /Title (\376\377\0\65\0\56\0\40\0\101\0\143\0\143\0\145\0\163\0\163\0\40\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 517 0 R + /First 569 0 R + /Last 605 0 R + /Prev 559 0 R + /Next 607 0 R + /Count -24 + /A 566 0 R +>> endobj +569 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\56\0\40\0\104\0\101\0\126\0\72\0\157\0\167\0\156\0\145\0\162) + /Parent 567 0 R + /First 570 0 R + /Last 571 0 R + /Next 573 0 R + /Count -2 + /A 568 0 R +>> endobj +570 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\104\0\101\0\126\0\72\0\157\0\167\0\156\0\145\0\162) + /Parent 569 0 R + /Next 571 0 R + /A 59 0 R +>> endobj +571 0 obj +<< + /Title (\376\377\0\65\0\56\0\61\0\56\0\62\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\101\0\156\0\40\0\101\0\164\0\164\0\145\0\155\0\160\0\164\0\40\0\164\0\157\0\40\0\123\0\145\0\164\0\40\0\104\0\101\0\126\0\72\0\157\0\167\0\156\0\145\0\162) + /Parent 569 0 R + /Prev 570 0 R + /A 61 0 R +>> endobj +573 0 obj +<< + /Title (\376\377\0\65\0\56\0\62\0\56\0\40\0\104\0\101\0\126\0\72\0\147\0\162\0\157\0\165\0\160) + /Parent 567 0 R + /Prev 569 0 R + /Next 575 0 R + /A 572 0 R +>> endobj +575 0 obj +<< + /Title (\376\377\0\65\0\56\0\63\0\56\0\40\0\104\0\101\0\126\0\72\0\163\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144\0\55\0\160\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\55\0\163\0\145\0\164) + /Parent 567 0 R + /First 577 0 R + /Last 577 0 R + /Prev 573 0 R + /Next 579 0 R + /Count -1 + /A 574 0 R +>> endobj +577 0 obj +<< + /Title (\376\377\0\65\0\56\0\63\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\141\0\40\0\114\0\151\0\163\0\164\0\40\0\157\0\146\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\163\0\40\0\123\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144\0\40\0\157\0\156\0\40\0\141\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145) + /Parent 575 0 R + /A 576 0 R +>> endobj +579 0 obj +<< + /Title (\376\377\0\65\0\56\0\64\0\56\0\40\0\104\0\101\0\126\0\72\0\143\0\165\0\162\0\162\0\145\0\156\0\164\0\55\0\165\0\163\0\145\0\162\0\55\0\160\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\55\0\163\0\145\0\164) + /Parent 567 0 R + /First 581 0 R + /Last 581 0 R + /Prev 575 0 R + /Next 583 0 R + /Count -1 + /A 578 0 R +>> endobj +581 0 obj +<< + /Title (\376\377\0\65\0\56\0\64\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\164\0\150\0\145\0\40\0\125\0\163\0\145\0\162\0\47\0\163\0\40\0\103\0\165\0\162\0\162\0\145\0\156\0\164\0\40\0\123\0\145\0\164\0\40\0\157\0\146\0\40\0\101\0\163\0\163\0\151\0\147\0\156\0\145\0\144\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\163) + /Parent 579 0 R + /A 580 0 R +>> endobj +583 0 obj +<< + /Title (\376\377\0\65\0\56\0\65\0\56\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\154) + /Parent 567 0 R + /First 585 0 R + /Last 590 0 R + /Prev 579 0 R + /Next 592 0 R + /Count -5 + /A 582 0 R +>> endobj +585 0 obj +<< + /Title (\376\377\0\65\0\56\0\65\0\56\0\61\0\56\0\40\0\101\0\103\0\105\0\40\0\120\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154) + /Parent 583 0 R + /Next 586 0 R + /A 584 0 R +>> endobj +586 0 obj +<< + /Title (\376\377\0\65\0\56\0\65\0\56\0\62\0\56\0\40\0\101\0\103\0\105\0\40\0\107\0\162\0\141\0\156\0\164\0\40\0\141\0\156\0\144\0\40\0\104\0\145\0\156\0\171) + /Parent 583 0 R + /Prev 585 0 R + /Next 588 0 R + /A 77 0 R +>> endobj +588 0 obj +<< + /Title (\376\377\0\65\0\56\0\65\0\56\0\63\0\56\0\40\0\101\0\103\0\105\0\40\0\120\0\162\0\157\0\164\0\145\0\143\0\164\0\151\0\157\0\156) + /Parent 583 0 R + /Prev 586 0 R + /Next 589 0 R + /A 587 0 R +>> endobj +589 0 obj +<< + /Title (\376\377\0\65\0\56\0\65\0\56\0\64\0\56\0\40\0\101\0\103\0\105\0\40\0\111\0\156\0\150\0\145\0\162\0\151\0\164\0\141\0\156\0\143\0\145) + /Parent 583 0 R + /Prev 588 0 R + /Next 590 0 R + /A 81 0 R +>> endobj +590 0 obj +<< + /Title (\376\377\0\65\0\56\0\65\0\56\0\65\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\141\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\47\0\163\0\40\0\101\0\143\0\143\0\145\0\163\0\163\0\40\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\40\0\114\0\151\0\163\0\164) + /Parent 583 0 R + /Prev 589 0 R + /A 83 0 R +>> endobj +592 0 obj +<< + /Title (\376\377\0\65\0\56\0\66\0\56\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\154\0\55\0\162\0\145\0\163\0\164\0\162\0\151\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 567 0 R + /First 594 0 R + /Last 599 0 R + /Prev 583 0 R + /Next 601 0 R + /Count -5 + /A 591 0 R +>> endobj +594 0 obj +<< + /Title (\376\377\0\65\0\56\0\66\0\56\0\61\0\56\0\40\0\104\0\101\0\126\0\72\0\147\0\162\0\141\0\156\0\164\0\55\0\157\0\156\0\154\0\171) + /Parent 592 0 R + /Next 596 0 R + /A 593 0 R +>> endobj +596 0 obj +<< + /Title (\376\377\0\65\0\56\0\66\0\56\0\62\0\56\0\40\0\104\0\101\0\126\0\72\0\156\0\157\0\55\0\151\0\156\0\166\0\145\0\162\0\164\0\40\0\101\0\103\0\105\0\40\0\103\0\157\0\156\0\163\0\164\0\162\0\141\0\151\0\156\0\164) + /Parent 592 0 R + /Prev 594 0 R + /Next 597 0 R + /A 595 0 R +>> endobj +597 0 obj +<< + /Title (\376\377\0\65\0\56\0\66\0\56\0\63\0\56\0\40\0\104\0\101\0\126\0\72\0\144\0\145\0\156\0\171\0\55\0\142\0\145\0\146\0\157\0\162\0\145\0\55\0\147\0\162\0\141\0\156\0\164) + /Parent 592 0 R + /Prev 596 0 R + /Next 598 0 R + /A 91 0 R +>> endobj +598 0 obj +<< + /Title (\376\377\0\65\0\56\0\66\0\56\0\64\0\56\0\40\0\122\0\145\0\161\0\165\0\151\0\162\0\145\0\144\0\40\0\120\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\163) + /Parent 592 0 R + /Prev 597 0 R + /Next 599 0 R + /A 96 0 R +>> endobj +599 0 obj +<< + /Title (\376\377\0\65\0\56\0\66\0\56\0\65\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\154\0\55\0\162\0\145\0\163\0\164\0\162\0\151\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 592 0 R + /Prev 598 0 R + /A 98 0 R +>> endobj +601 0 obj +<< + /Title (\376\377\0\65\0\56\0\67\0\56\0\40\0\104\0\101\0\126\0\72\0\151\0\156\0\150\0\145\0\162\0\151\0\164\0\145\0\144\0\55\0\141\0\143\0\154\0\55\0\163\0\145\0\164) + /Parent 567 0 R + /Prev 592 0 R + /Next 603 0 R + /A 600 0 R +>> endobj +603 0 obj +<< + /Title (\376\377\0\65\0\56\0\70\0\56\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\55\0\163\0\145\0\164) + /Parent 567 0 R + /First 604 0 R + /Last 604 0 R + /Prev 601 0 R + /Next 605 0 R + /Count -1 + /A 602 0 R +>> endobj +604 0 obj +<< + /Title (\376\377\0\65\0\56\0\70\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\143\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\55\0\163\0\145\0\164) + /Parent 603 0 R + /A 104 0 R +>> endobj +605 0 obj +<< + /Title (\376\377\0\65\0\56\0\71\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\120\0\122\0\117\0\120\0\106\0\111\0\116\0\104\0\40\0\164\0\157\0\40\0\162\0\145\0\164\0\162\0\151\0\145\0\166\0\145\0\40\0\141\0\143\0\143\0\145\0\163\0\163\0\40\0\143\0\157\0\156\0\164\0\162\0\157\0\154\0\40\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 567 0 R + /Prev 603 0 R + /A 106 0 R +>> endobj +607 0 obj +<< + /Title (\376\377\0\66\0\56\0\40\0\101\0\103\0\114\0\40\0\105\0\166\0\141\0\154\0\165\0\141\0\164\0\151\0\157\0\156) + /Parent 517 0 R + /Prev 567 0 R + /Next 609 0 R + /A 606 0 R +>> endobj +609 0 obj +<< + /Title (\376\377\0\67\0\56\0\40\0\101\0\143\0\143\0\145\0\163\0\163\0\40\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\40\0\141\0\156\0\144\0\40\0\145\0\170\0\151\0\163\0\164\0\151\0\156\0\147\0\40\0\155\0\145\0\164\0\150\0\157\0\144\0\163) + /Parent 517 0 R + /First 610 0 R + /Last 617 0 R + /Prev 607 0 R + /Next 619 0 R + /Count -7 + /A 608 0 R +>> endobj +610 0 obj +<< + /Title (\376\377\0\67\0\56\0\61\0\56\0\40\0\101\0\156\0\171\0\40\0\110\0\124\0\124\0\120\0\40\0\155\0\145\0\164\0\150\0\157\0\144) + /Parent 609 0 R + /First 611 0 R + /Last 611 0 R + /Next 613 0 R + /Count -1 + /A 112 0 R +>> endobj +611 0 obj +<< + /Title (\376\377\0\67\0\56\0\61\0\56\0\61\0\56\0\40\0\105\0\162\0\162\0\157\0\162\0\40\0\110\0\141\0\156\0\144\0\154\0\151\0\156\0\147) + /Parent 610 0 R + /A 114 0 R +>> endobj +613 0 obj +<< + /Title (\376\377\0\67\0\56\0\62\0\56\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123) + /Parent 609 0 R + /First 614 0 R + /Last 614 0 R + /Prev 610 0 R + /Next 615 0 R + /Count -1 + /A 612 0 R +>> endobj +614 0 obj +<< + /Title (\376\377\0\67\0\56\0\62\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\40\0\55\0\40\0\117\0\120\0\124\0\111\0\117\0\116\0\123) + /Parent 613 0 R + /A 118 0 R +>> endobj +615 0 obj +<< + /Title (\376\377\0\67\0\56\0\63\0\56\0\40\0\115\0\117\0\126\0\105) + /Parent 609 0 R + /Prev 613 0 R + /Next 616 0 R + /A 120 0 R +>> endobj +616 0 obj +<< + /Title (\376\377\0\67\0\56\0\64\0\56\0\40\0\103\0\117\0\120\0\131) + /Parent 609 0 R + /Prev 615 0 R + /Next 617 0 R + /A 122 0 R +>> endobj +617 0 obj +<< + /Title (\376\377\0\67\0\56\0\65\0\56\0\40\0\114\0\117\0\103\0\113) + /Parent 609 0 R + /Prev 616 0 R + /A 124 0 R +>> endobj +619 0 obj +<< + /Title (\376\377\0\70\0\56\0\40\0\101\0\143\0\143\0\145\0\163\0\163\0\40\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\163) + /Parent 517 0 R + /First 621 0 R + /Last 621 0 R + /Prev 609 0 R + /Next 629 0 R + /Count -6 + /A 618 0 R +>> endobj +621 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\40\0\101\0\103\0\114) + /Parent 619 0 R + /First 623 0 R + /Last 627 0 R + /Count -5 + /A 620 0 R +>> endobj +623 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\61\0\56\0\40\0\101\0\103\0\114\0\40\0\120\0\162\0\145\0\143\0\157\0\156\0\144\0\151\0\164\0\151\0\157\0\156\0\163) + /Parent 621 0 R + /Next 624 0 R + /A 622 0 R +>> endobj +624 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\62\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\164\0\150\0\145\0\40\0\101\0\103\0\114\0\40\0\155\0\145\0\164\0\150\0\157\0\144) + /Parent 621 0 R + /Prev 623 0 R + /Next 625 0 R + /A 132 0 R +>> endobj +625 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\63\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\101\0\103\0\114\0\40\0\155\0\145\0\164\0\150\0\157\0\144\0\40\0\146\0\141\0\151\0\154\0\165\0\162\0\145\0\40\0\144\0\165\0\145\0\40\0\164\0\157\0\40\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\40\0\101\0\103\0\105\0\40\0\143\0\157\0\156\0\146\0\154\0\151\0\143\0\164) + /Parent 621 0 R + /Prev 624 0 R + /Next 626 0 R + /A 134 0 R +>> endobj +626 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\64\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\101\0\103\0\114\0\40\0\155\0\145\0\164\0\150\0\157\0\144\0\40\0\146\0\141\0\151\0\154\0\165\0\162\0\145\0\40\0\144\0\165\0\145\0\40\0\164\0\157\0\40\0\141\0\156\0\40\0\151\0\156\0\150\0\145\0\162\0\151\0\164\0\145\0\144\0\40\0\101\0\103\0\105\0\40\0\143\0\157\0\156\0\146\0\154\0\151\0\143\0\164) + /Parent 621 0 R + /Prev 625 0 R + /Next 627 0 R + /A 136 0 R +>> endobj +627 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\65\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\101\0\103\0\114\0\40\0\155\0\145\0\164\0\150\0\157\0\144\0\40\0\146\0\141\0\151\0\154\0\165\0\162\0\145\0\40\0\144\0\165\0\145\0\40\0\164\0\157\0\40\0\141\0\156\0\40\0\141\0\164\0\164\0\145\0\155\0\160\0\164\0\40\0\164\0\157\0\40\0\163\0\145\0\164\0\40\0\147\0\162\0\141\0\156\0\164\0\40\0\141\0\156\0\144\0\40\0\144\0\145\0\156\0\171\0\40\0\151\0\156\0\40\0\141\0\40\0\163\0\151\0\156\0\147\0\154\0\145\0\40\0\101\0\103\0\105) + /Parent 621 0 R + /Prev 626 0 R + /A 138 0 R +>> endobj +629 0 obj +<< + /Title (\376\377\0\71\0\56\0\40\0\101\0\143\0\143\0\145\0\163\0\163\0\40\0\103\0\157\0\156\0\164\0\162\0\157\0\154\0\40\0\122\0\145\0\160\0\157\0\162\0\164\0\163) + /Parent 517 0 R + /First 630 0 R + /Last 642 0 R + /Prev 619 0 R + /Next 645 0 R + /Count -10 + /A 628 0 R +>> endobj +630 0 obj +<< + /Title (\376\377\0\71\0\56\0\61\0\56\0\40\0\122\0\105\0\120\0\117\0\122\0\124\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 629 0 R + /Next 632 0 R + /A 142 0 R +>> endobj +632 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\56\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\154\0\55\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\160\0\162\0\157\0\160\0\55\0\163\0\145\0\164\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 629 0 R + /First 633 0 R + /Last 633 0 R + /Prev 630 0 R + /Next 635 0 R + /Count -1 + /A 631 0 R +>> endobj +633 0 obj +<< + /Title (\376\377\0\71\0\56\0\62\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\104\0\101\0\126\0\72\0\141\0\143\0\154\0\55\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\160\0\162\0\157\0\160\0\55\0\163\0\145\0\164\0\40\0\122\0\145\0\160\0\157\0\162\0\164) + /Parent 632 0 R + /A 146 0 R +>> endobj +635 0 obj +<< + /Title (\376\377\0\71\0\56\0\63\0\56\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\155\0\141\0\164\0\143\0\150\0\40\0\122\0\105\0\120\0\117\0\122\0\124) + /Parent 629 0 R + /First 636 0 R + /Last 636 0 R + /Prev 632 0 R + /Next 638 0 R + /Count -1 + /A 634 0 R +>> endobj +636 0 obj +<< + /Title (\376\377\0\71\0\56\0\63\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\155\0\141\0\164\0\143\0\150\0\40\0\122\0\105\0\120\0\117\0\122\0\124) + /Parent 635 0 R + /A 150 0 R +>> endobj +638 0 obj +<< + /Title (\376\377\0\71\0\56\0\64\0\56\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\55\0\163\0\145\0\141\0\162\0\143\0\150\0\40\0\122\0\105\0\120\0\117\0\122\0\124) + /Parent 629 0 R + /First 639 0 R + /Last 640 0 R + /Prev 635 0 R + /Next 642 0 R + /Count -2 + /A 637 0 R +>> endobj +639 0 obj +<< + /Title (\376\377\0\71\0\56\0\64\0\56\0\61\0\56\0\40\0\115\0\141\0\164\0\143\0\150\0\151\0\156\0\147) + /Parent 638 0 R + /Next 640 0 R + /A 154 0 R +>> endobj +640 0 obj +<< + /Title (\376\377\0\71\0\56\0\64\0\56\0\62\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\163\0\165\0\143\0\143\0\145\0\163\0\163\0\146\0\165\0\154\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\55\0\163\0\145\0\141\0\162\0\143\0\150\0\40\0\122\0\105\0\120\0\117\0\122\0\124) + /Parent 638 0 R + /Prev 639 0 R + /A 156 0 R +>> endobj +642 0 obj +<< + /Title (\376\377\0\71\0\56\0\65\0\56\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\163\0\145\0\141\0\162\0\143\0\150\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\55\0\163\0\145\0\164\0\40\0\122\0\105\0\120\0\117\0\122\0\124) + /Parent 629 0 R + /First 643 0 R + /Last 643 0 R + /Prev 638 0 R + /Count -1 + /A 641 0 R +>> endobj +643 0 obj +<< + /Title (\376\377\0\71\0\56\0\65\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\104\0\101\0\126\0\72\0\160\0\162\0\151\0\156\0\143\0\151\0\160\0\141\0\154\0\55\0\163\0\145\0\141\0\162\0\143\0\150\0\55\0\160\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\55\0\163\0\145\0\164\0\40\0\122\0\105\0\120\0\117\0\122\0\124) + /Parent 642 0 R + /A 160 0 R +>> endobj +645 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\40\0\130\0\115\0\114\0\40\0\120\0\162\0\157\0\143\0\145\0\163\0\163\0\151\0\156\0\147) + /Parent 517 0 R + /Prev 629 0 R + /Next 647 0 R + /A 644 0 R +>> endobj +647 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\40\0\111\0\156\0\164\0\145\0\162\0\156\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\151\0\172\0\141\0\164\0\151\0\157\0\156\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 517 0 R + /Prev 645 0 R + /Next 649 0 R + /A 646 0 R +>> endobj +649 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\40\0\123\0\145\0\143\0\165\0\162\0\151\0\164\0\171\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 517 0 R + /First 650 0 R + /Last 652 0 R + /Prev 647 0 R + /Next 654 0 R + /Count -3 + /A 648 0 R +>> endobj +650 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\56\0\40\0\111\0\156\0\143\0\162\0\145\0\141\0\163\0\145\0\144\0\40\0\122\0\151\0\163\0\153\0\40\0\157\0\146\0\40\0\103\0\157\0\155\0\160\0\162\0\157\0\155\0\151\0\163\0\145\0\144\0\40\0\125\0\163\0\145\0\162\0\163) + /Parent 649 0 R + /Next 651 0 R + /A 168 0 R +>> endobj +651 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\62\0\56\0\40\0\122\0\151\0\163\0\153\0\163\0\40\0\157\0\146\0\40\0\164\0\150\0\145\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\141\0\144\0\55\0\141\0\143\0\154\0\40\0\141\0\156\0\144\0\40\0\104\0\101\0\126\0\72\0\143\0\165\0\162\0\162\0\145\0\156\0\164\0\55\0\165\0\163\0\145\0\162\0\55\0\160\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\55\0\163\0\145\0\164\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\163) + /Parent 649 0 R + /Prev 650 0 R + /Next 652 0 R + /A 170 0 R +>> endobj +652 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\63\0\56\0\40\0\116\0\157\0\40\0\106\0\157\0\162\0\145\0\153\0\156\0\157\0\167\0\154\0\145\0\144\0\147\0\145\0\40\0\157\0\146\0\40\0\111\0\156\0\151\0\164\0\151\0\141\0\154\0\40\0\101\0\103\0\114) + /Parent 649 0 R + /Prev 651 0 R + /A 172 0 R +>> endobj +654 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\40\0\101\0\165\0\164\0\150\0\145\0\156\0\164\0\151\0\143\0\141\0\164\0\151\0\157\0\156) + /Parent 517 0 R + /Prev 649 0 R + /Next 655 0 R + /A 653 0 R +>> endobj +655 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\40\0\111\0\101\0\116\0\101\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 517 0 R + /Prev 654 0 R + /Next 656 0 R + /A 176 0 R +>> endobj +656 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\40\0\101\0\143\0\153\0\156\0\157\0\167\0\154\0\145\0\144\0\147\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 517 0 R + /Prev 655 0 R + /Next 657 0 R + /A 178 0 R +>> endobj +657 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 517 0 R + /First 658 0 R + /Last 659 0 R + /Prev 656 0 R + /Next 661 0 R + /Count -2 + /A 183 0 R +>> endobj +658 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\61\0\56\0\40\0\116\0\157\0\162\0\155\0\141\0\164\0\151\0\166\0\145\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 657 0 R + /Next 659 0 R + /A 185 0 R +>> endobj +659 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\62\0\56\0\40\0\111\0\156\0\146\0\157\0\162\0\155\0\141\0\164\0\151\0\166\0\145\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 657 0 R + /Prev 658 0 R + /A 187 0 R +>> endobj +661 0 obj +<< + /Title (\376\377\0\101\0\56\0\40\0\127\0\145\0\142\0\104\0\101\0\126\0\40\0\130\0\115\0\114\0\40\0\104\0\157\0\143\0\165\0\155\0\145\0\156\0\164\0\40\0\124\0\171\0\160\0\145\0\40\0\104\0\145\0\146\0\151\0\156\0\151\0\164\0\151\0\157\0\156\0\40\0\101\0\144\0\144\0\145\0\156\0\144\0\165\0\155) + /Parent 517 0 R + /Prev 657 0 R + /Next 662 0 R + /A 660 0 R +>> endobj +662 0 obj +<< + /Title (\376\377\0\102\0\56\0\40\0\127\0\145\0\142\0\104\0\101\0\126\0\40\0\115\0\145\0\164\0\150\0\157\0\144\0\40\0\120\0\162\0\151\0\166\0\151\0\154\0\145\0\147\0\145\0\40\0\124\0\141\0\142\0\154\0\145\0\40\0\50\0\116\0\157\0\162\0\155\0\141\0\164\0\151\0\166\0\145\0\51) + /Parent 517 0 R + /Prev 661 0 R + /Next 663 0 R + /A 191 0 R +>> endobj +663 0 obj +<< + /Title (\376\377\0\101\0\165\0\164\0\150\0\157\0\162\0\163\0\47\0\40\0\101\0\144\0\144\0\162\0\145\0\163\0\163\0\145\0\163) + /Parent 517 0 R + /Prev 662 0 R + /Next 664 0 R + /A 193 0 R +>> endobj +664 0 obj +<< + /Title (\376\377\0\111\0\156\0\164\0\145\0\154\0\154\0\145\0\143\0\164\0\165\0\141\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\141\0\156\0\144\0\40\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\123\0\164\0\141\0\164\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 517 0 R + /Prev 663 0 R + /Next 665 0 R + /A 195 0 R +>> endobj +665 0 obj +<< + /Title (\376\377\0\111\0\156\0\144\0\145\0\170) + /Parent 517 0 R + /Prev 664 0 R + /A 197 0 R +>> endobj +666 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F5 +/BaseFont /Times-Roman +/Encoding /WinAnsiEncoding >> +endobj +667 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F6 +/BaseFont /Times-Italic +/Encoding /WinAnsiEncoding >> +endobj +668 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F9 +/BaseFont /Courier +/Encoding /WinAnsiEncoding >> +endobj +669 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F7 +/BaseFont /Times-Bold +/Encoding /WinAnsiEncoding >> +endobj +1 0 obj +<< /Type /Pages +/Count 62 +/Kids [6 0 R 8 0 R 93 0 R 180 0 R 199 0 R 207 0 R 229 0 R 238 0 R 242 0 R 248 0 R 252 0 R 257 0 R 259 0 R 269 0 R 271 0 R 276 0 R 283 0 R 285 0 R 289 0 R 291 0 R 296 0 R 298 0 R 304 0 R 306 0 R 308 0 R 313 0 R 315 0 R 317 0 R 319 0 R 321 0 R 326 0 R 328 0 R 330 0 R 332 0 R 336 0 R 343 0 R 347 0 R 349 0 R 351 0 R 357 0 R 359 0 R 363 0 R 368 0 R 374 0 R 376 0 R 380 0 R 384 0 R 386 0 R 392 0 R 402 0 R 408 0 R 412 0 R 417 0 R 419 0 R 441 0 R 453 0 R 462 0 R 464 0 R 499 0 R 506 0 R 512 0 R 515 0 R ] >> +endobj +2 0 obj +<< /Type /Catalog +/Pages 1 0 R + /Outlines 517 0 R + /PageMode /UseOutlines + /Names << /Dests << /Names [ (rfc.status) [ 6 0 R /XYZ 67.0 465.084 null ] (rfc.copyrightnotice) [ 6 0 R /XYZ 67.0 374.95 null ] (rfc.abstract) [ 6 0 R /XYZ 67.0 317.816 null ] (rfc.toc) [ 8 0 R /XYZ 67.0 725.0 null ] (rfc.section.1) [ 199 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2617.1) [ 199 0 R /XYZ 67.0 163.866 null ] (rfc.section.1.1) [ 207 0 R /XYZ 67.0 502.0 null ] (terms) [ 207 0 R /XYZ 67.0 502.0 null ] (rfc.xref.RFC2616.1) [ 207 0 R /XYZ 67.0 478.028 null ] (rfc.xref.RFC2518.1) [ 207 0 R /XYZ 67.0 478.028 null ] (rfc.iref.1) [ 207 0 R /XYZ 67.0 446.028 null ] (rfc.iref.2) [ 207 0 R /XYZ 67.0 398.028 null ] (rfc.iref.3) [ 207 0 R /XYZ 67.0 361.028 null ] (rfc.iref.4) [ 207 0 R /XYZ 67.0 324.028 null ] (rfc.iref.5) [ 207 0 R /XYZ 67.0 287.028 null ] (rfc.iref.6) [ 207 0 R /XYZ 67.0 239.028 null ] (rfc.iref.7) [ 207 0 R /XYZ 67.0 239.028 null ] (rfc.iref.8) [ 207 0 R /XYZ 67.0 202.028 null ] (rfc.iref.9) [ 207 0 R /XYZ 67.0 202.028 null ] (rfc.iref.10) [ 207 0 R /XYZ 67.0 165.028 null ] (rfc.iref.11) [ 207 0 R /XYZ 67.0 117.028 null ] (rfc.section.1.2) [ 229 0 R /XYZ 67.0 692.0 null ] (rfc.xref.RFC2616.2) [ 229 0 R /XYZ 67.0 668.028 null ] (rfc.xref.RFC2616.3) [ 229 0 R /XYZ 67.0 657.028 null ] (rfc.xref.RFC2119.1) [ 229 0 R /XYZ 67.0 614.028 null ] (rfc.xref.REC-XML.1) [ 229 0 R /XYZ 67.0 571.028 null ] (rfc.section.2) [ 238 0 R /XYZ 67.0 725.0 null ] (principals) [ 238 0 R /XYZ 67.0 725.0 null ] (rfc.section.3) [ 242 0 R /XYZ 67.0 725.0 null ] (privileges) [ 242 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.2) [ 242 0 R /XYZ 67.0 337.866 null ] (rfc.section.3.1) [ 242 0 R /XYZ 67.0 233.866 null ] (PRIVILEGE_read) [ 242 0 R /XYZ 67.0 233.866 null ] (rfc.figure.u.1) [ 242 0 R /XYZ 67.0 144.894 null ] (rfc.section.3.2) [ 242 0 R /XYZ 67.0 108.034 null ] (PRIVILEGE_write) [ 242 0 R /XYZ 67.0 108.034 null ] (rfc.xref.RFC2518.3) [ 248 0 R /XYZ 67.0 698.0 null ] (rfc.figure.u.2) [ 248 0 R /XYZ 67.0 633.0 null ] (rfc.section.3.3) [ 248 0 R /XYZ 67.0 596.14 null ] (PRIVILEGE_write-properties) [ 248 0 R /XYZ 67.0 596.14 null ] (rfc.figure.u.3) [ 248 0 R /XYZ 67.0 496.168 null ] (rfc.section.3.4) [ 248 0 R /XYZ 67.0 459.308 null ] (PRIVILEGE_write-content) [ 248 0 R /XYZ 67.0 459.308 null ] (rfc.figure.u.4) [ 248 0 R /XYZ 67.0 370.336 null ] (rfc.section.3.5) [ 248 0 R /XYZ 67.0 333.476 null ] (PRIVILEGE_unlock) [ 248 0 R /XYZ 67.0 333.476 null ] (rfc.figure.u.5) [ 248 0 R /XYZ 67.0 190.504 null ] (rfc.section.3.6) [ 248 0 R /XYZ 67.0 153.644 null ] (PRIVILEGE_read-acl) [ 248 0 R /XYZ 67.0 153.644 null ] (rfc.figure.u.6) [ 248 0 R /XYZ 67.0 108.672 null ] (rfc.section.3.7) [ 252 0 R /XYZ 67.0 708.0 null ] (PRIVILEGE_read-current-user-privilege-set) [ 252 0 R /XYZ 67.0 708.0 null ] (rfc.figure.u.7) [ 252 0 R /XYZ 67.0 555.028 null ] (rfc.section.3.8) [ 252 0 R /XYZ 67.0 518.168 null ] (PRIVILEGE_write-acl) [ 252 0 R /XYZ 67.0 518.168 null ] (rfc.figure.u.8) [ 252 0 R /XYZ 67.0 473.196 null ] (rfc.section.3.9) [ 252 0 R /XYZ 67.0 436.336 null ] (PRIVILEGE_bind) [ 252 0 R /XYZ 67.0 436.336 null ] (rfc.figure.u.9) [ 252 0 R /XYZ 67.0 380.364 null ] (rfc.section.3.10) [ 252 0 R /XYZ 67.0 343.504 null ] (PRIVILEGE_unbind) [ 252 0 R /XYZ 67.0 343.504 null ] (rfc.figure.u.10) [ 252 0 R /XYZ 67.0 287.532 null ] (rfc.section.3.11) [ 252 0 R /XYZ 67.0 250.672 null ] (PRIVILEGE_all) [ 252 0 R /XYZ 67.0 250.672 null ] (rfc.figure.u.11) [ 252 0 R /XYZ 67.0 205.7 null ] (rfc.section.3.12) [ 252 0 R /XYZ 67.0 168.84 null ] (aggregation.of.predefined.privileges) [ 252 0 R /XYZ 67.0 168.84 null ] (rfc.section.4) [ 259 0 R /XYZ 67.0 725.0 null ] (principal.properties) [ 259 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.4) [ 259 0 R /XYZ 67.0 686.866 null ] (rfc.xref.RFC2518.5) [ 259 0 R /XYZ 67.0 675.866 null ] (rfc.figure.u.12) [ 259 0 R /XYZ 67.0 632.866 null ] (rfc.xref.RFC2518.6) [ 259 0 R /XYZ 67.0 581.006 null ] (rfc.section.4.1) [ 259 0 R /XYZ 67.0 553.006 null ] (PROPERTY_alternate-URI-set) [ 259 0 R /XYZ 67.0 553.006 null ] (rfc.xref.RFC2255.1) [ 259 0 R /XYZ 67.0 496.034 null ] (rfc.xref.RFC2251.1) [ 259 0 R /XYZ 67.0 485.034 null ] (rfc.figure.u.13) [ 259 0 R /XYZ 67.0 442.034 null ] (rfc.section.4.2) [ 259 0 R /XYZ 67.0 405.174 null ] (PROPERTY_principal-URL) [ 259 0 R /XYZ 67.0 405.174 null ] (rfc.figure.u.14) [ 259 0 R /XYZ 67.0 338.202 null ] (rfc.section.4.3) [ 259 0 R /XYZ 67.0 301.342 null ] (rfc.figure.u.15) [ 259 0 R /XYZ 67.0 223.37 null ] (rfc.section.4.4) [ 259 0 R /XYZ 67.0 186.51 null ] (rfc.figure.u.16) [ 259 0 R /XYZ 67.0 108.538 null ] (rfc.section.5) [ 271 0 R /XYZ 67.0 725.0 null ] (access.control.properties) [ 271 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.7) [ 271 0 R /XYZ 67.0 675.866 null ] (rfc.xref.RFC2518.8) [ 271 0 R /XYZ 67.0 589.866 null ] (rfc.section.5.1) [ 271 0 R /XYZ 67.0 561.866 null ] (PROPERTY_owner) [ 271 0 R /XYZ 67.0 561.866 null ] (rfc.figure.u.17) [ 271 0 R /XYZ 67.0 462.894 null ] (rfc.section.5.1.1) [ 271 0 R /XYZ 67.0 427.034 null ] (rfc.figure.u.18) [ 271 0 R /XYZ 67.0 353.143 null ] (rfc.figure.u.19) [ 271 0 R /XYZ 67.0 168.243 null ] (rfc.section.5.1.2) [ 276 0 R /XYZ 67.0 589.54 null ] (rfc.xref.RFC2518.9) [ 276 0 R /XYZ 67.0 536.649 null ] (rfc.xref.RFC2518.10) [ 276 0 R /XYZ 67.0 525.649 null ] (rfc.xref.RFC3253.1) [ 276 0 R /XYZ 67.0 525.649 null ] (rfc.xref.RFC3253.2) [ 276 0 R /XYZ 67.0 525.649 null ] (rfc.xref.RFC3253.3) [ 276 0 R /XYZ 67.0 525.649 null ] (rfc.figure.u.20) [ 276 0 R /XYZ 67.0 493.649 null ] (rfc.figure.u.21) [ 276 0 R /XYZ 67.0 269.309 null ] (rfc.section.5.2) [ 283 0 R /XYZ 67.0 687.14 null ] (PROPERTY_group) [ 283 0 R /XYZ 67.0 687.14 null ] (rfc.figure.u.22) [ 283 0 R /XYZ 67.0 599.168 null ] (rfc.section.5.3) [ 283 0 R /XYZ 67.0 562.308 null ] (PROPERTY_supported-privilege-set) [ 283 0 R /XYZ 67.0 562.308 null ] (rfc.figure.u.23) [ 283 0 R /XYZ 67.0 517.336 null ] (rfc.figure.u.24) [ 283 0 R /XYZ 67.0 455.476 null ] (rfc.figure.u.25) [ 283 0 R /XYZ 67.0 373.896 null ] (rfc.figure.u.26) [ 283 0 R /XYZ 67.0 301.036 null ] (rfc.section.5.3.1) [ 283 0 R /XYZ 67.0 189.176 null ] (example.retrieving.a.list.of.privileges.supported.on.a.resource) [ 283 0 R /XYZ 67.0 189.176 null ] (rfc.figure.u.27) [ 283 0 R /XYZ 67.0 126.285 null ] (rfc.figure.u.28) [ 285 0 R /XYZ 67.0 594.134 null ] (rfc.figure.u.29) [ 285 0 R /XYZ 67.0 409.234 null ] (rfc.section.5.4) [ 289 0 R /XYZ 67.0 263.16 null ] (PROPERTY_current-user-privilege-set) [ 289 0 R /XYZ 67.0 263.16 null ] (rfc.figure.u.30) [ 289 0 R /XYZ 67.0 163.188 null ] (rfc.section.5.4.1) [ 289 0 R /XYZ 67.0 74.468 null ] (example.retrieving.the.users.current.set.of.assigned.privileges) [ 291 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.31) [ 291 0 R /XYZ 67.0 585.109 null ] (rfc.figure.u.32) [ 291 0 R /XYZ 67.0 400.209 null ] (rfc.section.5.5) [ 291 0 R /XYZ 67.0 178.729 null ] (PROPERTY_acl) [ 291 0 R /XYZ 67.0 178.729 null ] (rfc.figure.u.33) [ 291 0 R /XYZ 67.0 122.757 null ] (rfc.figure.u.34) [ 296 0 R /XYZ 67.0 699.0 null ] (rfc.section.5.5.1) [ 296 0 R /XYZ 67.0 653.28 null ] (ace.principal) [ 296 0 R /XYZ 67.0 653.28 null ] (rfc.figure.u.35) [ 296 0 R /XYZ 67.0 612.389 null ] (rfc.figure.u.36) [ 296 0 R /XYZ 67.0 519.669 null ] (rfc.figure.u.37) [ 296 0 R /XYZ 67.0 468.809 null ] (rfc.figure.u.38) [ 296 0 R /XYZ 67.0 417.949 null ] (rfc.figure.u.39) [ 296 0 R /XYZ 67.0 269.089 null ] (rfc.figure.u.40) [ 296 0 R /XYZ 67.0 196.229 null ] (rfc.figure.u.41) [ 296 0 R /XYZ 67.0 134.369 null ] (rfc.section.5.5.2) [ 296 0 R /XYZ 67.0 98.509 null ] (rfc.figure.u.42) [ 298 0 R /XYZ 67.0 677.0 null ] (rfc.section.5.5.3) [ 298 0 R /XYZ 67.0 621.42 null ] (ace.protection) [ 298 0 R /XYZ 67.0 621.42 null ] (rfc.figure.u.43) [ 298 0 R /XYZ 67.0 558.529 null ] (rfc.section.5.5.4) [ 298 0 R /XYZ 67.0 522.669 null ] (rfc.figure.u.44) [ 298 0 R /XYZ 67.0 405.778 null ] (rfc.section.5.5.5) [ 298 0 R /XYZ 67.0 369.918 null ] (rfc.figure.u.45) [ 298 0 R /XYZ 67.0 221.027 null ] (rfc.figure.u.46) [ 304 0 R /XYZ 67.0 684.28 null ] (rfc.section.5.6) [ 304 0 R /XYZ 67.0 314.9 null ] (PROPERTY_acl-restrictions) [ 304 0 R /XYZ 67.0 314.9 null ] (rfc.figure.u.47) [ 304 0 R /XYZ 67.0 183.928 null ] (rfc.section.5.6.1) [ 304 0 R /XYZ 67.0 128.348 null ] (RESTRICTIONS_grant-only) [ 304 0 R /XYZ 67.0 128.348 null ] (rfc.figure.u.48) [ 304 0 R /XYZ 67.0 87.457 null ] (rfc.section.5.6.2) [ 306 0 R /XYZ 67.0 689.14 null ] (CONSTRAINT_no-invert) [ 306 0 R /XYZ 67.0 689.14 null ] (rfc.figure.u.49) [ 306 0 R /XYZ 67.0 648.249 null ] (rfc.section.5.6.3) [ 306 0 R /XYZ 67.0 612.389 null ] (rfc.figure.u.50) [ 306 0 R /XYZ 67.0 571.498 null ] (rfc.section.5.6.4) [ 306 0 R /XYZ 67.0 535.638 null ] (rfc.figure.u.51) [ 306 0 R /XYZ 67.0 494.747 null ] (rfc.figure.u.52) [ 306 0 R /XYZ 67.0 424.167 null ] (rfc.section.5.6.5) [ 306 0 R /XYZ 67.0 368.587 null ] (rfc.figure.u.53) [ 306 0 R /XYZ 67.0 316.696 null ] (rfc.figure.u.54) [ 306 0 R /XYZ 67.0 131.796 null ] (rfc.section.5.7) [ 308 0 R /XYZ 67.0 519.52 null ] (PROPERTY_inherited-acl-set) [ 308 0 R /XYZ 67.0 519.52 null ] (rfc.figure.u.55) [ 308 0 R /XYZ 67.0 430.548 null ] (rfc.section.5.8) [ 308 0 R /XYZ 67.0 393.688 null ] (PROPERTY_principal-collection-set) [ 308 0 R /XYZ 67.0 393.688 null ] (rfc.xref.RFC2518.11) [ 308 0 R /XYZ 67.0 336.716 null ] (rfc.figure.u.56) [ 308 0 R /XYZ 67.0 304.716 null ] (rfc.section.5.8.1) [ 308 0 R /XYZ 67.0 116.856 null ] (rfc.figure.u.57) [ 313 0 R /XYZ 67.0 634.0 null ] (rfc.figure.u.58) [ 313 0 R /XYZ 67.0 449.1 null ] (rfc.section.5.9) [ 313 0 R /XYZ 67.0 217.76 null ] (rfc.figure.u.59) [ 313 0 R /XYZ 67.0 150.788 null ] (rfc.figure.u.60) [ 315 0 R /XYZ 67.0 575.82 null ] (rfc.figure.u.61) [ 319 0 R /XYZ 67.0 640.0 null ] (rfc.section.6) [ 321 0 R /XYZ 67.0 725.0 null ] (acl.evaluation) [ 321 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC3530.1) [ 321 0 R /XYZ 67.0 697.866 null ] (rfc.figure.u.62) [ 321 0 R /XYZ 67.0 545.866 null ] (rfc.figure.u.63) [ 321 0 R /XYZ 67.0 142.032 null ] (rfc.section.7) [ 328 0 R /XYZ 67.0 725.0 null ] (access.control.and.existing.methods) [ 328 0 R /XYZ 67.0 725.0 null ] (rfc.section.7.1) [ 328 0 R /XYZ 67.0 669.866 null ] (rfc.section.7.1.1) [ 328 0 R /XYZ 67.0 639.894 null ] (rfc.xref.RFC3253.4) [ 328 0 R /XYZ 67.0 620.003 null ] (rfc.figure.u.64) [ 328 0 R /XYZ 67.0 544.003 null ] (rfc.figure.u.65) [ 328 0 R /XYZ 67.0 461.283 null ] (rfc.figure.u.66) [ 328 0 R /XYZ 67.0 394.703 null ] (rfc.section.7.2) [ 328 0 R /XYZ 67.0 192.943 null ] (METHOD_options) [ 328 0 R /XYZ 67.0 192.943 null ] (rfc.section.7.2.1) [ 328 0 R /XYZ 67.0 108.971 null ] (rfc.figure.u.67) [ 328 0 R /XYZ 67.0 89.08 null ] (rfc.figure.u.68) [ 330 0 R /XYZ 67.0 674.42 null ] (rfc.section.7.3) [ 330 0 R /XYZ 67.0 568.84 null ] (rfc.section.7.4) [ 330 0 R /XYZ 67.0 483.868 null ] (rfc.section.7.5) [ 330 0 R /XYZ 67.0 387.896 null ] (rfc.section.8) [ 332 0 R /XYZ 67.0 725.0 null ] (access.control.methods) [ 332 0 R /XYZ 67.0 725.0 null ] (rfc.section.8.1) [ 332 0 R /XYZ 67.0 690.866 null ] (METHOD_ACL) [ 332 0 R /XYZ 67.0 690.866 null ] (rfc.section.8.1.1) [ 332 0 R /XYZ 67.0 417.894 null ] (acl.preconditions) [ 332 0 R /XYZ 67.0 417.894 null ] (rfc.iref.65) [ 332 0 R /XYZ 67.0 301.003 null ] (rfc.iref.66) [ 332 0 R /XYZ 67.0 301.003 null ] (rfc.iref.67) [ 332 0 R /XYZ 67.0 269.003 null ] (rfc.iref.68) [ 332 0 R /XYZ 67.0 269.003 null ] (rfc.iref.69) [ 332 0 R /XYZ 67.0 215.003 null ] (rfc.iref.70) [ 332 0 R /XYZ 67.0 215.003 null ] (rfc.iref.71) [ 332 0 R /XYZ 67.0 139.003 null ] (rfc.iref.72) [ 332 0 R /XYZ 67.0 139.003 null ] (rfc.iref.73) [ 332 0 R /XYZ 67.0 96.003 null ] (rfc.iref.74) [ 332 0 R /XYZ 67.0 96.003 null ] (rfc.iref.75) [ 336 0 R /XYZ 67.0 720.0 null ] (rfc.iref.76) [ 336 0 R /XYZ 67.0 720.0 null ] (rfc.iref.77) [ 336 0 R /XYZ 67.0 677.0 null ] (rfc.iref.78) [ 336 0 R /XYZ 67.0 677.0 null ] (rfc.iref.79) [ 336 0 R /XYZ 67.0 645.0 null ] (rfc.iref.80) [ 336 0 R /XYZ 67.0 645.0 null ] (rfc.iref.81) [ 336 0 R /XYZ 67.0 613.0 null ] (rfc.iref.82) [ 336 0 R /XYZ 67.0 613.0 null ] (rfc.iref.83) [ 336 0 R /XYZ 67.0 592.0 null ] (rfc.iref.84) [ 336 0 R /XYZ 67.0 592.0 null ] (rfc.iref.85) [ 336 0 R /XYZ 67.0 549.0 null ] (rfc.iref.86) [ 336 0 R /XYZ 67.0 549.0 null ] (rfc.iref.87) [ 336 0 R /XYZ 67.0 528.0 null ] (rfc.iref.88) [ 336 0 R /XYZ 67.0 528.0 null ] (rfc.section.8.1.2) [ 336 0 R /XYZ 67.0 468.0 null ] (rfc.figure.u.69) [ 336 0 R /XYZ 67.0 394.109 null ] (rfc.figure.u.70) [ 343 0 R /XYZ 67.0 654.7 null ] (rfc.section.8.1.3) [ 343 0 R /XYZ 67.0 601.84 null ] (rfc.figure.u.71) [ 343 0 R /XYZ 67.0 505.949 null ] (rfc.figure.u.72) [ 343 0 R /XYZ 67.0 281.609 null ] (rfc.section.8.1.4) [ 343 0 R /XYZ 67.0 159.729 null ] (rfc.figure.u.73) [ 347 0 R /XYZ 67.0 602.0 null ] (rfc.figure.u.74) [ 347 0 R /XYZ 67.0 397.38 null ] (rfc.section.8.1.5) [ 347 0 R /XYZ 67.0 275.5 null ] (rfc.figure.u.75) [ 347 0 R /XYZ 67.0 168.609 null ] (rfc.figure.u.76) [ 349 0 R /XYZ 67.0 556.1 null ] (rfc.section.9) [ 351 0 R /XYZ 67.0 725.0 null ] (access.control.reports) [ 351 0 R /XYZ 67.0 725.0 null ] (rfc.section.9.1) [ 351 0 R /XYZ 67.0 690.866 null ] (rfc.xref.RFC3253.5) [ 351 0 R /XYZ 67.0 666.894 null ] (rfc.xref.RFC3253.6) [ 351 0 R /XYZ 67.0 579.894 null ] (rfc.section.9.2) [ 351 0 R /XYZ 67.0 551.894 null ] (REPORT_acl-principal-prop-set) [ 351 0 R /XYZ 67.0 551.894 null ] (rfc.figure.u.77) [ 351 0 R /XYZ 67.0 390.422 null ] (rfc.xref.RFC3253.7) [ 351 0 R /XYZ 67.0 319.982 null ] (rfc.figure.u.78) [ 351 0 R /XYZ 67.0 254.982 null ] (rfc.iref.91) [ 351 0 R /XYZ 67.0 166.122 null ] (rfc.iref.92) [ 351 0 R /XYZ 67.0 166.122 null ] (rfc.section.9.2.1) [ 351 0 R /XYZ 67.0 114.622 null ] (rfc.figure.u.79) [ 357 0 R /XYZ 67.0 592.0 null ] (rfc.figure.u.80) [ 357 0 R /XYZ 67.0 436.68 null ] (rfc.section.9.3) [ 357 0 R /XYZ 67.0 146.18 null ] (REPORT_principal-match) [ 357 0 R /XYZ 67.0 146.18 null ] (rfc.figure.u.81) [ 359 0 R /XYZ 67.0 647.5 null ] (rfc.xref.RFC3253.8) [ 359 0 R /XYZ 67.0 547.48 null ] (rfc.figure.u.82) [ 359 0 R /XYZ 67.0 498.48 null ] (rfc.section.9.3.1) [ 359 0 R /XYZ 67.0 357.12 null ] (rfc.figure.u.83) [ 359 0 R /XYZ 67.0 294.229 null ] (rfc.figure.u.84) [ 359 0 R /XYZ 67.0 109.329 null ] (rfc.section.9.4) [ 363 0 R /XYZ 67.0 558.96 null ] (REPORT_principal-property-search) [ 363 0 R /XYZ 67.0 558.96 null ] (rfc.xref.UNICODE4.1) [ 363 0 R /XYZ 67.0 316.488 null ] (rfc.figure.u.85) [ 363 0 R /XYZ 67.0 159.488 null ] (rfc.figure.u.86) [ 368 0 R /XYZ 67.0 679.5 null ] (rfc.xref.RFC3253.9) [ 368 0 R /XYZ 67.0 582.06 null ] (rfc.figure.u.87) [ 368 0 R /XYZ 67.0 528.06 null ] (rfc.iref.97) [ 368 0 R /XYZ 67.0 375.2 null ] (rfc.iref.98) [ 368 0 R /XYZ 67.0 375.2 null ] (rfc.section.9.4.1) [ 368 0 R /XYZ 67.0 323.7 null ] (rfc.xref.REC-XML-INFOSET.1) [ 368 0 R /XYZ 67.0 292.809 null ] (rfc.figure.u.88) [ 368 0 R /XYZ 67.0 249.809 null ] (rfc.figure.u.89) [ 368 0 R /XYZ 67.0 143.949 null ] (rfc.figure.u.90) [ 374 0 R /XYZ 67.0 688.0 null ] (rfc.section.9.4.2) [ 374 0 R /XYZ 67.0 589.56 null ] (rfc.figure.u.91) [ 374 0 R /XYZ 67.0 430.669 null ] (rfc.figure.u.92) [ 374 0 R /XYZ 67.0 117.589 null ] (rfc.section.9.5) [ 376 0 R /XYZ 67.0 292.74 null ] (REPORT_principal-search-property-set) [ 376 0 R /XYZ 67.0 292.74 null ] (rfc.xref.RFC3253.10) [ 380 0 R /XYZ 67.0 658.5 null ] (rfc.figure.u.93) [ 380 0 R /XYZ 67.0 582.5 null ] (rfc.figure.u.94) [ 380 0 R /XYZ 67.0 515.78 null ] (rfc.figure.u.95) [ 380 0 R /XYZ 67.0 458.92 null ] (rfc.figure.u.96) [ 380 0 R /XYZ 67.0 391.06 null ] (rfc.section.9.5.1) [ 380 0 R /XYZ 67.0 347.7 null ] (rfc.figure.u.97) [ 380 0 R /XYZ 67.0 284.809 null ] (rfc.figure.u.98) [ 380 0 R /XYZ 67.0 149.209 null ] (rfc.section.10) [ 386 0 R /XYZ 67.0 725.0 null ] (xml.processing) [ 386 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.12) [ 386 0 R /XYZ 67.0 686.866 null ] (rfc.xref.REC-XML-NAMES.1) [ 386 0 R /XYZ 67.0 686.866 null ] (rfc.section.11) [ 392 0 R /XYZ 67.0 725.0 null ] (internationalization.considerations) [ 392 0 R /XYZ 67.0 725.0 null ] (rfc.xref.REC-XML.2) [ 392 0 R /XYZ 67.0 653.866 null ] (rfc.xref.RFC3629.1) [ 392 0 R /XYZ 67.0 631.866 null ] (rfc.xref.RFC3023.1) [ 392 0 R /XYZ 67.0 620.866 null ] (rfc.xref.REC-XML.3) [ 392 0 R /XYZ 67.0 587.866 null ] (rfc.xref.RFC2518.13) [ 392 0 R /XYZ 67.0 425.866 null ] (rfc.section.12) [ 402 0 R /XYZ 67.0 725.0 null ] (security.considerations) [ 402 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2616.4) [ 402 0 R /XYZ 67.0 675.866 null ] (rfc.xref.RFC2518.14) [ 402 0 R /XYZ 67.0 675.866 null ] (rfc.xref.RFC3023.2) [ 402 0 R /XYZ 67.0 664.866 null ] (rfc.section.12.1) [ 402 0 R /XYZ 67.0 625.866 null ] (rfc.section.12.2) [ 402 0 R /XYZ 67.0 518.894 null ] (rfc.section.12.3) [ 402 0 R /XYZ 67.0 291.922 null ] (rfc.section.13) [ 408 0 R /XYZ 67.0 725.0 null ] (authentication) [ 408 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2617.2) [ 408 0 R /XYZ 67.0 686.866 null ] (rfc.section.14) [ 412 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.15) [ 412 0 R /XYZ 67.0 697.866 null ] (rfc.xref.RFC2518.16) [ 412 0 R /XYZ 67.0 675.866 null ] (rfc.section.15) [ 417 0 R /XYZ 67.0 725.0 null ] (rfc.references) [ 419 0 R /XYZ 67.0 725.0 null ] (rfc.references.1) [ 419 0 R /XYZ 67.0 690.866 null ] (REC-XML) [ 419 0 R /XYZ 67.0 671.894 null ] (REC-XML-INFOSET) [ 419 0 R /XYZ 67.0 633.894 null ] (REC-XML-NAMES) [ 419 0 R /XYZ 67.0 595.894 null ] (RFC2119) [ 419 0 R /XYZ 67.0 557.894 null ] (RFC2518) [ 419 0 R /XYZ 67.0 530.894 null ] (RFC2616) [ 419 0 R /XYZ 67.0 503.894 null ] (RFC2617) [ 419 0 R /XYZ 67.0 476.894 null ] (RFC3023) [ 419 0 R /XYZ 67.0 438.894 null ] (RFC3253) [ 419 0 R /XYZ 67.0 411.894 null ] (RFC3530) [ 419 0 R /XYZ 67.0 384.894 null ] (RFC3629) [ 419 0 R /XYZ 67.0 357.894 null ] (rfc.references.2) [ 419 0 R /XYZ 67.0 318.894 null ] (RFC2251) [ 419 0 R /XYZ 67.0 299.922 null ] (RFC2255) [ 419 0 R /XYZ 67.0 272.922 null ] (UNICODE4) [ 419 0 R /XYZ 67.0 256.922 null ] (rfc.section.A) [ 441 0 R /XYZ 67.0 725.0 null ] (webdav.xml.document.type.definition.addendum) [ 441 0 R /XYZ 67.0 725.0 null ] (rfc.xref.RFC2518.17) [ 441 0 R /XYZ 67.0 686.866 null ] (rfc.figure.u.99) [ 441 0 R /XYZ 67.0 665.866 null ] (rfc.figure.u.100) [ 441 0 R /XYZ 67.0 521.406 null ] (rfc.figure.u.101) [ 441 0 R /XYZ 67.0 405.246 null ] (rfc.figure.u.102) [ 441 0 R /XYZ 67.0 359.386 null ] (rfc.figure.u.103) [ 441 0 R /XYZ 67.0 313.526 null ] (rfc.figure.u.104) [ 441 0 R /XYZ 67.0 208.506 null ] (rfc.figure.u.105) [ 441 0 R /XYZ 67.0 162.646 null ] (rfc.figure.u.106) [ 453 0 R /XYZ 67.0 537.38 null ] (rfc.figure.u.107) [ 453 0 R /XYZ 67.0 402.78 null ] (rfc.figure.u.108) [ 453 0 R /XYZ 67.0 356.92 null ] (rfc.figure.u.109) [ 453 0 R /XYZ 67.0 311.06 null ] (rfc.figure.u.110) [ 453 0 R /XYZ 67.0 255.34 null ] (rfc.figure.u.111) [ 453 0 R /XYZ 67.0 101.02 null ] (rfc.section.B) [ 464 0 R /XYZ 67.0 725.0 null ] (rfc.table.u.1) [ 464 0 R /XYZ 67.0 610.866 null ] (rfc.authors) [ 499 0 R /XYZ 67.0 725.0 null ] (rfc.copyright) [ 499 0 R /XYZ 67.0 417.866 null ] (rfc.ipr) [ 506 0 R /XYZ 67.0 725.0 null ] (rfc.index) [ 512 0 R /XYZ 67.0 725.0 null ] ] >> >> + >> +endobj +3 0 obj +<< +/Font << /F5 666 0 R /F6 667 0 R /F9 668 0 R /F7 669 0 R >> +/ProcSet [ /PDF /ImageC /Text ] >> +endobj +11 0 obj +<< +/S /GoTo +/D [199 0 R /XYZ 67.0 725.0 null] +>> +endobj +13 0 obj +<< +/S /GoTo +/D [207 0 R /XYZ 67.0 502.0 null] +>> +endobj +15 0 obj +<< +/S /GoTo +/D [229 0 R /XYZ 67.0 692.0 null] +>> +endobj +17 0 obj +<< +/S /GoTo +/D [238 0 R /XYZ 67.0 725.0 null] +>> +endobj +19 0 obj +<< +/S /GoTo +/D [242 0 R /XYZ 67.0 725.0 null] +>> +endobj +21 0 obj +<< +/S /GoTo +/D [242 0 R /XYZ 67.0 233.866 null] +>> +endobj +23 0 obj +<< +/S /GoTo +/D [242 0 R /XYZ 67.0 108.034 null] +>> +endobj +25 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 596.14 null] +>> +endobj +27 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 459.308 null] +>> +endobj +29 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 333.476 null] +>> +endobj +31 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 153.644 null] +>> +endobj +33 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 708.0 null] +>> +endobj +35 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 518.168 null] +>> +endobj +37 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 436.336 null] +>> +endobj +39 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 343.504 null] +>> +endobj +41 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 250.672 null] +>> +endobj +43 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 168.84 null] +>> +endobj +45 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 725.0 null] +>> +endobj +47 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 553.006 null] +>> +endobj +49 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 405.174 null] +>> +endobj +51 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 301.342 null] +>> +endobj +53 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 186.51 null] +>> +endobj +55 0 obj +<< +/S /GoTo +/D [271 0 R /XYZ 67.0 725.0 null] +>> +endobj +57 0 obj +<< +/S /GoTo +/D [271 0 R /XYZ 67.0 561.866 null] +>> +endobj +59 0 obj +<< +/S /GoTo +/D [271 0 R /XYZ 67.0 427.034 null] +>> +endobj +61 0 obj +<< +/S /GoTo +/D [276 0 R /XYZ 67.0 589.54 null] +>> +endobj +63 0 obj +<< +/S /GoTo +/D [283 0 R /XYZ 67.0 687.14 null] +>> +endobj +65 0 obj +<< +/S /GoTo +/D [283 0 R /XYZ 67.0 562.308 null] +>> +endobj +67 0 obj +<< +/S /GoTo +/D [283 0 R /XYZ 67.0 189.176 null] +>> +endobj +69 0 obj +<< +/S /GoTo +/D [289 0 R /XYZ 67.0 263.16 null] +>> +endobj +71 0 obj +<< +/S /GoTo +/D [291 0 R /XYZ 67.0 725.0 null] +>> +endobj +73 0 obj +<< +/S /GoTo +/D [291 0 R /XYZ 67.0 178.729 null] +>> +endobj +75 0 obj +<< +/S /GoTo +/D [296 0 R /XYZ 67.0 653.28 null] +>> +endobj +77 0 obj +<< +/S /GoTo +/D [296 0 R /XYZ 67.0 98.509 null] +>> +endobj +79 0 obj +<< +/S /GoTo +/D [298 0 R /XYZ 67.0 621.42 null] +>> +endobj +81 0 obj +<< +/S /GoTo +/D [298 0 R /XYZ 67.0 522.669 null] +>> +endobj +83 0 obj +<< +/S /GoTo +/D [298 0 R /XYZ 67.0 369.918 null] +>> +endobj +85 0 obj +<< +/S /GoTo +/D [304 0 R /XYZ 67.0 314.9 null] +>> +endobj +87 0 obj +<< +/S /GoTo +/D [304 0 R /XYZ 67.0 128.348 null] +>> +endobj +89 0 obj +<< +/S /GoTo +/D [306 0 R /XYZ 67.0 689.14 null] +>> +endobj +91 0 obj +<< +/S /GoTo +/D [306 0 R /XYZ 67.0 612.389 null] +>> +endobj +96 0 obj +<< +/S /GoTo +/D [306 0 R /XYZ 67.0 535.638 null] +>> +endobj +98 0 obj +<< +/S /GoTo +/D [306 0 R /XYZ 67.0 368.587 null] +>> +endobj +100 0 obj +<< +/S /GoTo +/D [308 0 R /XYZ 67.0 519.52 null] +>> +endobj +102 0 obj +<< +/S /GoTo +/D [308 0 R /XYZ 67.0 393.688 null] +>> +endobj +104 0 obj +<< +/S /GoTo +/D [308 0 R /XYZ 67.0 116.856 null] +>> +endobj +106 0 obj +<< +/S /GoTo +/D [313 0 R /XYZ 67.0 217.76 null] +>> +endobj +108 0 obj +<< +/S /GoTo +/D [321 0 R /XYZ 67.0 725.0 null] +>> +endobj +110 0 obj +<< +/S /GoTo +/D [328 0 R /XYZ 67.0 725.0 null] +>> +endobj +112 0 obj +<< +/S /GoTo +/D [328 0 R /XYZ 67.0 669.866 null] +>> +endobj +114 0 obj +<< +/S /GoTo +/D [328 0 R /XYZ 67.0 639.894 null] +>> +endobj +116 0 obj +<< +/S /GoTo +/D [328 0 R /XYZ 67.0 192.943 null] +>> +endobj +118 0 obj +<< +/S /GoTo +/D [328 0 R /XYZ 67.0 108.971 null] +>> +endobj +120 0 obj +<< +/S /GoTo +/D [330 0 R /XYZ 67.0 568.84 null] +>> +endobj +122 0 obj +<< +/S /GoTo +/D [330 0 R /XYZ 67.0 483.868 null] +>> +endobj +124 0 obj +<< +/S /GoTo +/D [330 0 R /XYZ 67.0 387.896 null] +>> +endobj +126 0 obj +<< +/S /GoTo +/D [332 0 R /XYZ 67.0 725.0 null] +>> +endobj +128 0 obj +<< +/S /GoTo +/D [332 0 R /XYZ 67.0 690.866 null] +>> +endobj +130 0 obj +<< +/S /GoTo +/D [332 0 R /XYZ 67.0 417.894 null] +>> +endobj +132 0 obj +<< +/S /GoTo +/D [336 0 R /XYZ 67.0 468.0 null] +>> +endobj +134 0 obj +<< +/S /GoTo +/D [343 0 R /XYZ 67.0 601.84 null] +>> +endobj +136 0 obj +<< +/S /GoTo +/D [343 0 R /XYZ 67.0 159.729 null] +>> +endobj +138 0 obj +<< +/S /GoTo +/D [347 0 R /XYZ 67.0 275.5 null] +>> +endobj +140 0 obj +<< +/S /GoTo +/D [351 0 R /XYZ 67.0 725.0 null] +>> +endobj +142 0 obj +<< +/S /GoTo +/D [351 0 R /XYZ 67.0 690.866 null] +>> +endobj +144 0 obj +<< +/S /GoTo +/D [351 0 R /XYZ 67.0 551.894 null] +>> +endobj +146 0 obj +<< +/S /GoTo +/D [351 0 R /XYZ 67.0 114.622 null] +>> +endobj +148 0 obj +<< +/S /GoTo +/D [357 0 R /XYZ 67.0 146.18 null] +>> +endobj +150 0 obj +<< +/S /GoTo +/D [359 0 R /XYZ 67.0 357.12 null] +>> +endobj +152 0 obj +<< +/S /GoTo +/D [363 0 R /XYZ 67.0 558.96 null] +>> +endobj +154 0 obj +<< +/S /GoTo +/D [368 0 R /XYZ 67.0 323.7 null] +>> +endobj +156 0 obj +<< +/S /GoTo +/D [374 0 R /XYZ 67.0 589.56 null] +>> +endobj +158 0 obj +<< +/S /GoTo +/D [376 0 R /XYZ 67.0 292.74 null] +>> +endobj +160 0 obj +<< +/S /GoTo +/D [380 0 R /XYZ 67.0 347.7 null] +>> +endobj +162 0 obj +<< +/S /GoTo +/D [386 0 R /XYZ 67.0 725.0 null] +>> +endobj +164 0 obj +<< +/S /GoTo +/D [392 0 R /XYZ 67.0 725.0 null] +>> +endobj +166 0 obj +<< +/S /GoTo +/D [402 0 R /XYZ 67.0 725.0 null] +>> +endobj +168 0 obj +<< +/S /GoTo +/D [402 0 R /XYZ 67.0 625.866 null] +>> +endobj +170 0 obj +<< +/S /GoTo +/D [402 0 R /XYZ 67.0 518.894 null] +>> +endobj +172 0 obj +<< +/S /GoTo +/D [402 0 R /XYZ 67.0 291.922 null] +>> +endobj +174 0 obj +<< +/S /GoTo +/D [408 0 R /XYZ 67.0 725.0 null] +>> +endobj +176 0 obj +<< +/S /GoTo +/D [412 0 R /XYZ 67.0 725.0 null] +>> +endobj +178 0 obj +<< +/S /GoTo +/D [417 0 R /XYZ 67.0 725.0 null] +>> +endobj +183 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 725.0 null] +>> +endobj +185 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 690.866 null] +>> +endobj +187 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 318.894 null] +>> +endobj +189 0 obj +<< +/S /GoTo +/D [441 0 R /XYZ 67.0 725.0 null] +>> +endobj +191 0 obj +<< +/S /GoTo +/D [464 0 R /XYZ 67.0 725.0 null] +>> +endobj +193 0 obj +<< +/S /GoTo +/D [499 0 R /XYZ 67.0 725.0 null] +>> +endobj +195 0 obj +<< +/S /GoTo +/D [506 0 R /XYZ 67.0 725.0 null] +>> +endobj +197 0 obj +<< +/S /GoTo +/D [512 0 R /XYZ 67.0 725.0 null] +>> +endobj +205 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 476.894 null] +>> +endobj +225 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 503.894 null] +>> +endobj +227 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 530.894 null] +>> +endobj +234 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 557.894 null] +>> +endobj +236 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 671.894 null] +>> +endobj +265 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 272.922 null] +>> +endobj +267 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 299.922 null] +>> +endobj +281 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 411.894 null] +>> +endobj +324 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 384.894 null] +>> +endobj +366 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 256.922 null] +>> +endobj +372 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 633.894 null] +>> +endobj +390 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 595.894 null] +>> +endobj +396 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 357.894 null] +>> +endobj +398 0 obj +<< +/S /GoTo +/D [419 0 R /XYZ 67.0 438.894 null] +>> +endobj +517 0 obj +<< + /First 519 0 R + /Last 665 0 R +>> endobj +518 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 465.084 null] +>> +endobj +520 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 374.95 null] +>> +endobj +522 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 317.816 null] +>> +endobj +524 0 obj +<< +/S /GoTo +/D [8 0 R /XYZ 67.0 725.0 null] +>> +endobj +527 0 obj +<< +/S /GoTo +/D [207 0 R /XYZ 67.0 502.0 null] +>> +endobj +530 0 obj +<< +/S /GoTo +/D [238 0 R /XYZ 67.0 725.0 null] +>> +endobj +532 0 obj +<< +/S /GoTo +/D [242 0 R /XYZ 67.0 725.0 null] +>> +endobj +534 0 obj +<< +/S /GoTo +/D [242 0 R /XYZ 67.0 233.866 null] +>> +endobj +536 0 obj +<< +/S /GoTo +/D [242 0 R /XYZ 67.0 108.034 null] +>> +endobj +538 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 596.14 null] +>> +endobj +540 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 459.308 null] +>> +endobj +542 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 333.476 null] +>> +endobj +544 0 obj +<< +/S /GoTo +/D [248 0 R /XYZ 67.0 153.644 null] +>> +endobj +546 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 708.0 null] +>> +endobj +548 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 518.168 null] +>> +endobj +550 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 436.336 null] +>> +endobj +552 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 343.504 null] +>> +endobj +554 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 250.672 null] +>> +endobj +556 0 obj +<< +/S /GoTo +/D [252 0 R /XYZ 67.0 168.84 null] +>> +endobj +558 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 725.0 null] +>> +endobj +560 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 553.006 null] +>> +endobj +562 0 obj +<< +/S /GoTo +/D [259 0 R /XYZ 67.0 405.174 null] +>> +endobj +566 0 obj +<< +/S /GoTo +/D [271 0 R /XYZ 67.0 725.0 null] +>> +endobj +568 0 obj +<< +/S /GoTo +/D [271 0 R /XYZ 67.0 561.866 null] +>> +endobj +572 0 obj +<< +/S /GoTo +/D [283 0 R /XYZ 67.0 687.14 null] +>> +endobj +574 0 obj +<< +/S /GoTo +/D [283 0 R /XYZ 67.0 562.308 null] +>> +endobj +576 0 obj +<< +/S /GoTo +/D [283 0 R /XYZ 67.0 189.176 null] +>> +endobj +578 0 obj +<< +/S /GoTo +/D [289 0 R /XYZ 67.0 263.16 null] +>> +endobj +580 0 obj +<< +/S /GoTo +/D [289 0 R /XYZ 67.0 74.468 null] +>> +endobj +582 0 obj +<< +/S /GoTo +/D [291 0 R /XYZ 67.0 178.729 null] +>> +endobj +584 0 obj +<< +/S /GoTo +/D [296 0 R /XYZ 67.0 653.28 null] +>> +endobj +587 0 obj +<< +/S /GoTo +/D [298 0 R /XYZ 67.0 621.42 null] +>> +endobj +591 0 obj +<< +/S /GoTo +/D [304 0 R /XYZ 67.0 314.9 null] +>> +endobj +593 0 obj +<< +/S /GoTo +/D [304 0 R /XYZ 67.0 128.348 null] +>> +endobj +595 0 obj +<< +/S /GoTo +/D [306 0 R /XYZ 67.0 689.14 null] +>> +endobj +600 0 obj +<< +/S /GoTo +/D [308 0 R /XYZ 67.0 519.52 null] +>> +endobj +602 0 obj +<< +/S /GoTo +/D [308 0 R /XYZ 67.0 393.688 null] +>> +endobj +606 0 obj +<< +/S /GoTo +/D [321 0 R /XYZ 67.0 725.0 null] +>> +endobj +608 0 obj +<< +/S /GoTo +/D [328 0 R /XYZ 67.0 725.0 null] +>> +endobj +612 0 obj +<< +/S /GoTo +/D [328 0 R /XYZ 67.0 192.943 null] +>> +endobj +618 0 obj +<< +/S /GoTo +/D [332 0 R /XYZ 67.0 725.0 null] +>> +endobj +620 0 obj +<< +/S /GoTo +/D [332 0 R /XYZ 67.0 690.866 null] +>> +endobj +622 0 obj +<< +/S /GoTo +/D [332 0 R /XYZ 67.0 417.894 null] +>> +endobj +628 0 obj +<< +/S /GoTo +/D [351 0 R /XYZ 67.0 725.0 null] +>> +endobj +631 0 obj +<< +/S /GoTo +/D [351 0 R /XYZ 67.0 551.894 null] +>> +endobj +634 0 obj +<< +/S /GoTo +/D [357 0 R /XYZ 67.0 146.18 null] +>> +endobj +637 0 obj +<< +/S /GoTo +/D [363 0 R /XYZ 67.0 558.96 null] +>> +endobj +641 0 obj +<< +/S /GoTo +/D [376 0 R /XYZ 67.0 292.74 null] +>> +endobj +644 0 obj +<< +/S /GoTo +/D [386 0 R /XYZ 67.0 725.0 null] +>> +endobj +646 0 obj +<< +/S /GoTo +/D [392 0 R /XYZ 67.0 725.0 null] +>> +endobj +648 0 obj +<< +/S /GoTo +/D [402 0 R /XYZ 67.0 725.0 null] +>> +endobj +653 0 obj +<< +/S /GoTo +/D [408 0 R /XYZ 67.0 725.0 null] +>> +endobj +660 0 obj +<< +/S /GoTo +/D [441 0 R /XYZ 67.0 725.0 null] +>> +endobj +xref +0 670 +0000000000 65535 f +0000195902 00000 n +0000196446 00000 n +0000215528 00000 n +0000000015 00000 n +0000000071 00000 n +0000001629 00000 n +0000001735 00000 n +0000004020 00000 n +0000004140 00000 n +0000004446 00000 n +0000215644 00000 n +0000004581 00000 n +0000215709 00000 n +0000004716 00000 n +0000215774 00000 n +0000004851 00000 n +0000215839 00000 n +0000004986 00000 n +0000215904 00000 n +0000005121 00000 n +0000215969 00000 n +0000005256 00000 n +0000216036 00000 n +0000005390 00000 n +0000216103 00000 n +0000005525 00000 n +0000216169 00000 n +0000005660 00000 n +0000216236 00000 n +0000005795 00000 n +0000216303 00000 n +0000005930 00000 n +0000216370 00000 n +0000006065 00000 n +0000216435 00000 n +0000006200 00000 n +0000216502 00000 n +0000006335 00000 n +0000216569 00000 n +0000006470 00000 n +0000216636 00000 n +0000006605 00000 n +0000216703 00000 n +0000006740 00000 n +0000216769 00000 n +0000006875 00000 n +0000216834 00000 n +0000007010 00000 n +0000216901 00000 n +0000007145 00000 n +0000216968 00000 n +0000007280 00000 n +0000217035 00000 n +0000007415 00000 n +0000217101 00000 n +0000007550 00000 n +0000217166 00000 n +0000007685 00000 n +0000217233 00000 n +0000007820 00000 n +0000217300 00000 n +0000007955 00000 n +0000217366 00000 n +0000008090 00000 n +0000217432 00000 n +0000008224 00000 n +0000217499 00000 n +0000008359 00000 n +0000217566 00000 n +0000008494 00000 n +0000217632 00000 n +0000008629 00000 n +0000217697 00000 n +0000008763 00000 n +0000217764 00000 n +0000008898 00000 n +0000217830 00000 n +0000009033 00000 n +0000217896 00000 n +0000009168 00000 n +0000217962 00000 n +0000009303 00000 n +0000218029 00000 n +0000009438 00000 n +0000218096 00000 n +0000009573 00000 n +0000218161 00000 n +0000009707 00000 n +0000218228 00000 n +0000009840 00000 n +0000218294 00000 n +0000009974 00000 n +0000012490 00000 n +0000012613 00000 n +0000012966 00000 n +0000218361 00000 n +0000013097 00000 n +0000218428 00000 n +0000013228 00000 n +0000218495 00000 n +0000013360 00000 n +0000218562 00000 n +0000013492 00000 n +0000218630 00000 n +0000013625 00000 n +0000218698 00000 n +0000013758 00000 n +0000218765 00000 n +0000013891 00000 n +0000218831 00000 n +0000014026 00000 n +0000218897 00000 n +0000014161 00000 n +0000218965 00000 n +0000014296 00000 n +0000219033 00000 n +0000014431 00000 n +0000219101 00000 n +0000014565 00000 n +0000219169 00000 n +0000014700 00000 n +0000219236 00000 n +0000014835 00000 n +0000219304 00000 n +0000014970 00000 n +0000219372 00000 n +0000015105 00000 n +0000219438 00000 n +0000015239 00000 n +0000219506 00000 n +0000015373 00000 n +0000219574 00000 n +0000015508 00000 n +0000219640 00000 n +0000015643 00000 n +0000219707 00000 n +0000015778 00000 n +0000219775 00000 n +0000015913 00000 n +0000219841 00000 n +0000016048 00000 n +0000219907 00000 n +0000016183 00000 n +0000219975 00000 n +0000016317 00000 n +0000220043 00000 n +0000016452 00000 n +0000220111 00000 n +0000016586 00000 n +0000220178 00000 n +0000016721 00000 n +0000220245 00000 n +0000016856 00000 n +0000220312 00000 n +0000016992 00000 n +0000220378 00000 n +0000017127 00000 n +0000220445 00000 n +0000017262 00000 n +0000220512 00000 n +0000017397 00000 n +0000220578 00000 n +0000017532 00000 n +0000220644 00000 n +0000017667 00000 n +0000220710 00000 n +0000017801 00000 n +0000220776 00000 n +0000017936 00000 n +0000220844 00000 n +0000018071 00000 n +0000220912 00000 n +0000018206 00000 n +0000220980 00000 n +0000018341 00000 n +0000221046 00000 n +0000018474 00000 n +0000221112 00000 n +0000018607 00000 n +0000019447 00000 n +0000019573 00000 n +0000019658 00000 n +0000221178 00000 n +0000019791 00000 n +0000221244 00000 n +0000019926 00000 n +0000221312 00000 n +0000020061 00000 n +0000221380 00000 n +0000020196 00000 n +0000221446 00000 n +0000020331 00000 n +0000221512 00000 n +0000020466 00000 n +0000221578 00000 n +0000020601 00000 n +0000221644 00000 n +0000020735 00000 n +0000024551 00000 n +0000024677 00000 n +0000024730 00000 n +0000024868 00000 n +0000025005 00000 n +0000025144 00000 n +0000221710 00000 n +0000025283 00000 n +0000028093 00000 n +0000028219 00000 n +0000028376 00000 n +0000028510 00000 n +0000028644 00000 n +0000028778 00000 n +0000028912 00000 n +0000029046 00000 n +0000029181 00000 n +0000029316 00000 n +0000029451 00000 n +0000029586 00000 n +0000029721 00000 n +0000029856 00000 n +0000029989 00000 n +0000030124 00000 n +0000030259 00000 n +0000030394 00000 n +0000221778 00000 n +0000030533 00000 n +0000221846 00000 n +0000030672 00000 n +0000031761 00000 n +0000031887 00000 n +0000031940 00000 n +0000032077 00000 n +0000032214 00000 n +0000221914 00000 n +0000032351 00000 n +0000221982 00000 n +0000032490 00000 n +0000033806 00000 n +0000033932 00000 n +0000033961 00000 n +0000034099 00000 n +0000037379 00000 n +0000037505 00000 n +0000037550 00000 n +0000037688 00000 n +0000037827 00000 n +0000037965 00000 n +0000040132 00000 n +0000040258 00000 n +0000040287 00000 n +0000040422 00000 n +0000042276 00000 n +0000042402 00000 n +0000042439 00000 n +0000042577 00000 n +0000042715 00000 n +0000043266 00000 n +0000043376 00000 n +0000045657 00000 n +0000045783 00000 n +0000045844 00000 n +0000045983 00000 n +0000046122 00000 n +0000046261 00000 n +0000222050 00000 n +0000046400 00000 n +0000222118 00000 n +0000046539 00000 n +0000046882 00000 n +0000046992 00000 n +0000049458 00000 n +0000049584 00000 n +0000049621 00000 n +0000049758 00000 n +0000049896 00000 n +0000052070 00000 n +0000052196 00000 n +0000052241 00000 n +0000052380 00000 n +0000052519 00000 n +0000222186 00000 n +0000052658 00000 n +0000054929 00000 n +0000055039 00000 n +0000056944 00000 n +0000057070 00000 n +0000057099 00000 n +0000057237 00000 n +0000059138 00000 n +0000059248 00000 n +0000061504 00000 n +0000061630 00000 n +0000061667 00000 n +0000061805 00000 n +0000061943 00000 n +0000063667 00000 n +0000063777 00000 n +0000066316 00000 n +0000066442 00000 n +0000066487 00000 n +0000066625 00000 n +0000066763 00000 n +0000066901 00000 n +0000068752 00000 n +0000068862 00000 n +0000070674 00000 n +0000070784 00000 n +0000073351 00000 n +0000073477 00000 n +0000073514 00000 n +0000073652 00000 n +0000073789 00000 n +0000075866 00000 n +0000075976 00000 n +0000077602 00000 n +0000077712 00000 n +0000079109 00000 n +0000079219 00000 n +0000080856 00000 n +0000080966 00000 n +0000083088 00000 n +0000083214 00000 n +0000083243 00000 n +0000222254 00000 n +0000083382 00000 n +0000084206 00000 n +0000084316 00000 n +0000086486 00000 n +0000086596 00000 n +0000088338 00000 n +0000088448 00000 n +0000091310 00000 n +0000091436 00000 n +0000091465 00000 n +0000091603 00000 n +0000093847 00000 n +0000093973 00000 n +0000094026 00000 n +0000094158 00000 n +0000094292 00000 n +0000094423 00000 n +0000094554 00000 n +0000096670 00000 n +0000096796 00000 n +0000096825 00000 n +0000096960 00000 n +0000099395 00000 n +0000099505 00000 n +0000100565 00000 n +0000100675 00000 n +0000103252 00000 n +0000103378 00000 n +0000103423 00000 n +0000103562 00000 n +0000103701 00000 n +0000103840 00000 n +0000105817 00000 n +0000105927 00000 n +0000108487 00000 n +0000108613 00000 n +0000108642 00000 n +0000108779 00000 n +0000111700 00000 n +0000111826 00000 n +0000111855 00000 n +0000222322 00000 n +0000111994 00000 n +0000114511 00000 n +0000114637 00000 n +0000114674 00000 n +0000114811 00000 n +0000222390 00000 n +0000114950 00000 n +0000117061 00000 n +0000117171 00000 n +0000119288 00000 n +0000119414 00000 n +0000119443 00000 n +0000119582 00000 n +0000121873 00000 n +0000121999 00000 n +0000122028 00000 n +0000122163 00000 n +0000122927 00000 n +0000123037 00000 n +0000123777 00000 n +0000123903 00000 n +0000123940 00000 n +0000124079 00000 n +0000222458 00000 n +0000124218 00000 n +0000126338 00000 n +0000126464 00000 n +0000126525 00000 n +0000126664 00000 n +0000222526 00000 n +0000126803 00000 n +0000222594 00000 n +0000126942 00000 n +0000127080 00000 n +0000127218 00000 n +0000130428 00000 n +0000130554 00000 n +0000130599 00000 n +0000130738 00000 n +0000130876 00000 n +0000131015 00000 n +0000131713 00000 n +0000131839 00000 n +0000131868 00000 n +0000132007 00000 n +0000132706 00000 n +0000132832 00000 n +0000132869 00000 n +0000133008 00000 n +0000133146 00000 n +0000134388 00000 n +0000134498 00000 n +0000139470 00000 n +0000139596 00000 n +0000139769 00000 n +0000139964 00000 n +0000140157 00000 n +0000140361 00000 n +0000140562 00000 n +0000140753 00000 n +0000140944 00000 n +0000141134 00000 n +0000141325 00000 n +0000141516 00000 n +0000141706 00000 n +0000141897 00000 n +0000142088 00000 n +0000142278 00000 n +0000142469 00000 n +0000142660 00000 n +0000142851 00000 n +0000143042 00000 n +0000143240 00000 n +0000143411 00000 n +0000145027 00000 n +0000145153 00000 n +0000145246 00000 n +0000145384 00000 n +0000145522 00000 n +0000145660 00000 n +0000145798 00000 n +0000145936 00000 n +0000146074 00000 n +0000146210 00000 n +0000146348 00000 n +0000146484 00000 n +0000147924 00000 n +0000148050 00000 n +0000148119 00000 n +0000148255 00000 n +0000148392 00000 n +0000148527 00000 n +0000148664 00000 n +0000148799 00000 n +0000148934 00000 n +0000149822 00000 n +0000149932 00000 n +0000153438 00000 n +0000153564 00000 n +0000153841 00000 n +0000153979 00000 n +0000154117 00000 n +0000154255 00000 n +0000154393 00000 n +0000154531 00000 n +0000154669 00000 n +0000154805 00000 n +0000154943 00000 n +0000155081 00000 n +0000155219 00000 n +0000155357 00000 n +0000155495 00000 n +0000155633 00000 n +0000155771 00000 n +0000155909 00000 n +0000156047 00000 n +0000156185 00000 n +0000156321 00000 n +0000156459 00000 n +0000156597 00000 n +0000156735 00000 n +0000156873 00000 n +0000157011 00000 n +0000157149 00000 n +0000157287 00000 n +0000157425 00000 n +0000157563 00000 n +0000157701 00000 n +0000157839 00000 n +0000157977 00000 n +0000158115 00000 n +0000158253 00000 n +0000159960 00000 n +0000160086 00000 n +0000160139 00000 n +0000160324 00000 n +0000160512 00000 n +0000160694 00000 n +0000160870 00000 n +0000162946 00000 n +0000163072 00000 n +0000163117 00000 n +0000163294 00000 n +0000163469 00000 n +0000163645 00000 n +0000167204 00000 n +0000167330 00000 n +0000167351 00000 n +0000168883 00000 n +0000169009 00000 n +0000222662 00000 n +0000222716 00000 n +0000169030 00000 n +0000222782 00000 n +0000169227 00000 n +0000222847 00000 n +0000169423 00000 n +0000222913 00000 n +0000169572 00000 n +0000169773 00000 n +0000222977 00000 n +0000170002 00000 n +0000170143 00000 n +0000223043 00000 n +0000170384 00000 n +0000223109 00000 n +0000170560 00000 n +0000223175 00000 n +0000170779 00000 n +0000223243 00000 n +0000170996 00000 n +0000223311 00000 n +0000171234 00000 n +0000223378 00000 n +0000171537 00000 n +0000223446 00000 n +0000171822 00000 n +0000223514 00000 n +0000172066 00000 n +0000223582 00000 n +0000172321 00000 n +0000223648 00000 n +0000172711 00000 n +0000223716 00000 n +0000172972 00000 n +0000223784 00000 n +0000173204 00000 n +0000223852 00000 n +0000173453 00000 n +0000223920 00000 n +0000173684 00000 n +0000223987 00000 n +0000174013 00000 n +0000224053 00000 n +0000174290 00000 n +0000224121 00000 n +0000174524 00000 n +0000174750 00000 n +0000174992 00000 n +0000224189 00000 n +0000175220 00000 n +0000224255 00000 n +0000175527 00000 n +0000175733 00000 n +0000176023 00000 n +0000224323 00000 n +0000176352 00000 n +0000224390 00000 n +0000176531 00000 n +0000224458 00000 n +0000176858 00000 n +0000224526 00000 n +0000177338 00000 n +0000224593 00000 n +0000177682 00000 n +0000224660 00000 n +0000178168 00000 n +0000224728 00000 n +0000178377 00000 n +0000178575 00000 n +0000224795 00000 n +0000178815 00000 n +0000179034 00000 n +0000179258 00000 n +0000224862 00000 n +0000179682 00000 n +0000224928 00000 n +0000179968 00000 n +0000224996 00000 n +0000180171 00000 n +0000180471 00000 n +0000180729 00000 n +0000180977 00000 n +0000225063 00000 n +0000181332 00000 n +0000225130 00000 n +0000181581 00000 n +0000181914 00000 n +0000182302 00000 n +0000225198 00000 n +0000182736 00000 n +0000225264 00000 n +0000182935 00000 n +0000183299 00000 n +0000183540 00000 n +0000225330 00000 n +0000183729 00000 n +0000183939 00000 n +0000184144 00000 n +0000184294 00000 n +0000184444 00000 n +0000225398 00000 n +0000184579 00000 n +0000225464 00000 n +0000184867 00000 n +0000225532 00000 n +0000185023 00000 n +0000185245 00000 n +0000185515 00000 n +0000185984 00000 n +0000186470 00000 n +0000225600 00000 n +0000187072 00000 n +0000187361 00000 n +0000225666 00000 n +0000187549 00000 n +0000187910 00000 n +0000225734 00000 n +0000188261 00000 n +0000188582 00000 n +0000225801 00000 n +0000188893 00000 n +0000189273 00000 n +0000189442 00000 n +0000225868 00000 n +0000189892 00000 n +0000190280 00000 n +0000225935 00000 n +0000190673 00000 n +0000226001 00000 n +0000190877 00000 n +0000226067 00000 n +0000191207 00000 n +0000191507 00000 n +0000191829 00000 n +0000192374 00000 n +0000226133 00000 n +0000192672 00000 n +0000192877 00000 n +0000193111 00000 n +0000193328 00000 n +0000193551 00000 n +0000193786 00000 n +0000226199 00000 n +0000194033 00000 n +0000194409 00000 n +0000194766 00000 n +0000194973 00000 n +0000195346 00000 n +0000195462 00000 n +0000195573 00000 n +0000195685 00000 n +0000195792 00000 n +trailer +<< +/Size 670 +/Root 2 0 R +/Info 4 0 R +>> +startxref +226265 +%%EOF diff --git a/dav/SabreDAV/docs/rfc4437.pdf b/dav/SabreDAV/docs/rfc4437.pdf new file mode 100644 index 000000000..dbbc0ced0 --- /dev/null +++ b/dav/SabreDAV/docs/rfc4437.pdf @@ -0,0 +1,3127 @@ +%PDF-1.3 +%ª«¬­ +4 0 obj +<< /Type /Info +/Producer (FOP 0.20.5) >> +endobj +5 0 obj +<< /Length 1426 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau1.9lo&I&;KZQ'g0Jl/M2EE]fAiX3\sMo[ZVWi*$B8j@S@Cc!8:J'I@$0d/83IS%NQX%JAP7jm14Np;(SeEKP@G'--l`n2QG2pMtj5htNEkWh!hh"IY#Pa:sO`B49*(>AnQVJMHK>h'se=3!5ojY#cL/&`._pE0Ge&$A!(Q8&BkNjsD]]EI.a8%7C9u=u.'&9M&hcojc)`.Kr='G/hX!'9ikH-@i[)Bt!o)0Lu`C4VQ\F8d53<+/O3XW^@&/LDs6rE#78U+3?`[.FL_V9Zq3X(G>BOs4iklV@]!.-SWM3=.aR*9&:c/?c2/>Jp^H*UjTeD?E^6pO-bd5:$-B0?Jj,*(`-KRZKOnZFH9^'Khu0>ASl-a;%4j&fZ>nf]j&f#*kJ3rG^cNCNopJLHZ>/6=Y,noKc8M<<+J8eqMV%Km"'o9all3%`S-P1K0;Y0FA$;MBdRoJ0*)`o@"IZG=73!r]\_`Rj[d+7]tKl1P9blX`L;O-Te!QPpe/1@I,C6Fo*5lN121`ld&ktn)QQ!\/SX?XQ_UU59dPf/'q-0Vo!W))".l!"790C-VC4lI'WN]"5gLP<0WO@lJsA9]S6"@VAJ/q+lQ.f3/Qg4hZW[7?;!A+F0I2oo>.f8TVNSiT*5``o9_jD$U,TPsiu>u=o#rPG+fnQu@g@0dde,d=N4A"k-#(gmpa2(*JO+mU-j?AaNS+Ab"0jabj(DJDh#$j\o+%+OiDE4=8@14ZZJ.;k=9X+oFDpl9!8g)ps5Sjq`&jP;rU8'bqiC',IfWendaS~> +endstream +endobj +6 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 5 0 R +>> +endobj +7 0 obj +<< /Length 2115 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$I>>s99'Z],,'Rk_'7;L^1X)D8LZg=!D8^m(P[!cm,"[$;SXCK]%VdIPc@N@tY,!]%MVD'>eQ@Jb'MiRAKkihaK&R^@1?o1"RK:=)*K_cc#"OZs3-kW=R0Y.WU*KQAeR5tXL#nQ^$Nq6uVqcQ*5eZ<#G"CHc.?`Pp!eX0aW-k6h!lgZ[/5AiXr+s(22"[R,ACi!R834-5dPsmmI+.$\*gK6Cu`XF*C1-r\EV$Z.C)hP/?)i0DF6tH6;`"SJSo/868K.>!Y=tTDNl+7<>_-!@t_,1\:Blf]/^l`mKm_hP(`8Z*ke.\0c1K)1t3m4l-k(cl?>7W_NOG>FtCUN)_P8+i]'(p#o:KFpK];7!CWVY`e#X^I@$T0R(4&].XfiZq:(AWA5<\>DU84T9&,PR$IY.RZBcuBqs33)/[rQkS7GB6b0[0(=)c(5T?Lli0si1^#LLS[Qha0Chud*Bi##t:HHCn%K?LP,3AiS\N/**bbnJj@:=g7L\';P,-O*CLOD#bV`L*JqDlDkFLKGLlgr`'_'=Y(,:!O7k9\X=_9'UsbmF-ENDN$<%4>MjfiHjBq_4YTYhC(tZ`Q2ZX*A=(eJDR5cRaNN3XZ7'lWQ=Be,b4)d=sO5P0PR-rFbPN/hn\ju,I#l;j7Votou6%IF#Vso7Cl-7:Zj2fiW@DM8#TNf!#:)"Tae;W'PWf&FC*;VfoOTZI_*OpJ!p9Ce@D^eKRg8XBQJliof5'C&k/V)Q@=i(D#,-"PS?'V[/]okPr0iri<[_B7ur*qQLQGB3_O-U@MIjF+!Bo_-k/PuBW*\GE2afo/9#b\4Fd=UX$TP($EbmjF+O[P:O.+9IF*eOTZ+V7;%$OT#%2q2`8R$<[UU94ABXCgh9"H3gp4W)ccYnPI@6>o$h1g>Fp$i"4F0"ISFG"SneTphpSLjg^ZeU@1oZElJ^FQPC;"t^GGKCP0c;4Yi%ps#FcTseUT42X6C1VLJIu5_N3GgaWWHfPrMbs:.\(?),':bXT0<7,a'<5!=f".,>C0;^RA%,VkNgF/cCJbj(L,d";0M1tLH`l>TSJ/T,7"e]di0eV=MNR)@('?:R5.bAh5a!/"3sRT_nl(8;TeV*[_VQYHisYefMiV7,3[%&rocV,LBkG8@1(a7CK.p#"9[o8*hj$PAM?aM#EAe1dO"8.i21Yt4]%-a7A$Cu0ihR8jZNg-;8hY*cRQf&<>MPjifs%!shnNJdk5J$7]cYQgN,ZOZ+_9'$*d!Gr@kaa';@WHMmL[qf@Q@3ce:^((M:f$7FN\\JC!WP(h5"NUBCpr0b^Y=j8S!"eH#)>6&DW5ND6%nS-I@Rn]siWFi..'d4,0c.>(tc46DJYS`)CGq"h[4MXfA5+?&3d9Lb"m=oATOplFr/,(W\/]MdCbVNo.0nEiK#RkW^D0PBk&UL3?2m$VOK1hCPCn&UNWInCQ4gXcn!`*c]ZNXbu>@9/E+`Psp'[`6b6mRnZU/oOX=nAML;.4iYQ\P^Ki:s)DW[ll\9*nm:d\n)K.OiBg0c13aV]ubd?L.=6a4mt#[jmgDf%AZfkTm4g':!Z>B7HPS+uQ3?'aLt9XQoG,\k[.Eg$.m>p%(iTpqc1XInfOj6N~> +endstream +endobj +8 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 7 0 R +/Annots 9 0 R +>> +endobj +9 0 obj +[ +10 0 R +12 0 R +14 0 R +16 0 R +18 0 R +20 0 R +22 0 R +24 0 R +26 0 R +28 0 R +30 0 R +32 0 R +34 0 R +36 0 R +38 0 R +40 0 R +42 0 R +44 0 R +46 0 R +48 0 R +50 0 R +52 0 R +54 0 R +56 0 R +58 0 R +60 0 R +62 0 R +64 0 R +66 0 R +68 0 R +70 0 R +72 0 R +74 0 R +] +endobj +10 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 84.5 686.866 138.95 676.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 11 0 R +/H /I +>> +endobj +12 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 666.056 185.34 656.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 13 0 R +/H /I +>> +endobj +14 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 84.5 645.246 139.5 635.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 15 0 R +/H /I +>> +endobj +16 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 624.436 266.65 614.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 17 0 R +/H /I +>> +endobj +18 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 603.626 275.56 593.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +20 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 582.816 210.33 572.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +22 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 567.006 404.45 557.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 23 0 R +/H /I +>> +endobj +24 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 546.006 234.22 536.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +26 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 530.196 430.56 520.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +28 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 509.196 385.29 499.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 29 0 R +/H /I +>> +endobj +30 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 493.386 393.07 483.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 31 0 R +/H /I +>> +endobj +32 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 477.386 510.27 467.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +34 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 456.386 321.66 446.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +36 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 435.576 249.71 425.576 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +38 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 419.766 379.75 409.766 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +40 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 398.766 235.84 388.766 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 41 0 R +/H /I +>> +endobj +42 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 89.5 377.956 125.05 367.956 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 43 0 R +/H /I +>> +endobj +44 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 362.146 225.85 352.146 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +46 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 346.146 262.51 336.146 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +48 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 325.146 260.26 315.146 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 49 0 R +/H /I +>> +endobj +50 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 309.336 237.79 299.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 51 0 R +/H /I +>> +endobj +52 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 293.336 206.68 283.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 53 0 R +/H /I +>> +endobj +54 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 272.336 154.77 262.336 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 55 0 R +/H /I +>> +endobj +56 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 256.526 204.19 246.526 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +58 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 235.526 408.92 225.526 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 59 0 R +/H /I +>> +endobj +60 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 214.716 179.22 204.716 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 61 0 R +/H /I +>> +endobj +62 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 198.906 366.1 188.906 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +64 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 177.906 192.0 167.906 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +66 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 162.096 172.82 152.096 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +68 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 146.096 163.38 136.096 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 69 0 R +/H /I +>> +endobj +70 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 130.096 311.93 120.096 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 71 0 R +/H /I +>> +endobj +72 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 114.096 215.32 104.096 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 73 0 R +/H /I +>> +endobj +74 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 93.096 242.01 83.096 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 75 0 R +/H /I +>> +endobj +76 0 obj +<< /Length 801 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$E9lJ`>(ru)m$K"=LiVG-T3G)RE2JH1A7I-Z4+XJa/$OI/P/)Pf!P*22dKu;F,iBXX<\,H5LhEF>kbIITi5m3qF#m/9V#0;6I&B(^X6m,n\>(_SZXcRQJ5$PQQkU0QGbpJ\Q/ga8ODFhZ6$rJs;u]e.5NoP2qZ8LdV_#Xa:uOP&Z,'dl)LZFTV1Kb,S&WOLW2#!nM]*W=WnpK3ngPamM],D]>U']hesRB=383,=r;QGl=[B&Bc0H.&7S2Z#78Y"ZJ"c,AaTjq/a[(#HK>`.ceL7F1!\'r:Dc9;5d>>Yu-6^jGY^4S`&ZFfqaJ;`6Hl$Eg%"U7#]W=7'A>(_pp9gB7gn@!]#K5).t, +endstream +endobj +77 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 76 0 R +/Annots 78 0 R +>> +endobj +78 0 obj +[ +79 0 R +81 0 R +83 0 R +85 0 R +87 0 R +89 0 R +91 0 R +93 0 R +95 0 R +97 0 R +] +endobj +79 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 709.0 182.0 699.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 80 0 R +/H /I +>> +endobj +81 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 693.19 160.04 683.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 82 0 R +/H /I +>> +endobj +83 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 114.5 682.19 166.15 672.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 84 0 R +/H /I +>> +endobj +85 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 114.5 671.19 208.92 661.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 86 0 R +/H /I +>> +endobj +87 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 89.5 650.19 145.61 640.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 88 0 R +/H /I +>> +endobj +89 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 89.5 629.38 172.27 619.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 90 0 R +/H /I +>> +endobj +91 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 608.57 184.18 598.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 92 0 R +/H /I +>> +endobj +93 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 587.76 155.61 577.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 94 0 R +/H /I +>> +endobj +95 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 566.95 275.87 556.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 96 0 R +/H /I +>> +endobj +97 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 546.14 96.45 536.14 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 98 0 R +/H /I +>> +endobj +99 0 obj +<< /Length 2872 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>>E@OI&q801n7)Od;0B2kpMG92+Bu3AYhaA(T]lP1d2!*u]&SKHMh2u2]\MYqL:8I2B.?)Mr5MgU`V2\<-!%_"p6"8E/R-b5gS,Ir24@iV.*`6'h"'8-((+#8-&,tF$^?u"CCT2hmpm*F2_u5sgXkSi^C7Yi;*C1\eCtNA1Fqk30/@?6!A#Q_5)qKn]_64rPSj!bm!9#LD:^O1S.dS7*#<&\eJWR7QebDOKPjCKD)@H8pb$@ZWON,)GckDcn[7.@c22hS[_&c`FAamV$GTTdtr@2;"9tU4=U?\Ls-1$oV94>JijU#WY4qRpaOQK/iDJ;Sa&(0D,.,A4CpI1)u6RKu)e-/('C4_/B.0PCGmo6M<9!%Bh'')$hr5eto$;0Zp7(c'S5+d6l#Y?1/AbcmAb\?B*881dC%D9YtQk)i1PoJ1KI-[G2r)I!o=K\bP(BK/^B^aml#N!D]3qi:sW(LimU><1n!X)rGY4JoIHO`[,S9g8p4OE:OO$6-g+l)X_#)kb[n7MW'Et-]q<.D4P66$!bOA?u_T"0aJNKK&sZ`'c3rJ&t7$VU-4><$-+#"M's6FPLd6;.:?o.G.>?YA2:d6N$u1Sk1mpR6NNE&H,l.`C]r+!8%&u7jlH#0Q!L-Y>m]b;e8o@(SU>eQL"@*8A6i@8$;C>"/c=`>HaJdb=:R*]ZY6M.`PoT?(X/8_;>7Y3Fkc2<3#h_OfLd%IBpt.A,'rrPj(g(F@^@b=7?]husrcVH`KIRn88^5&+0k?5qb:oA/-0)E_>f$:ZoZLk9>9VWBX=_I3W-NRB^;"Y'l1>k)Si[,c#M6LClJ0T_>(8#EMF45UdanS!:=&aW<$iQ@Fh0J7;5('9:\IS45U8b)deJjnF^?!N25Mp1b0]j2pA7(cSA\fj201FPQ)1XC#=bI+MXB'm\"Vs8Z83#?h(M:`j&03Q-odL`cqQ^%5U.^[7`.gk\/*01H:`ENmJL_:EWc,O:+*#q$MB)Oh.b,LoJ2hOF/"Z!@I_9[s&9\!iJ#KAn[Xm6Vo/EQEtI@W1E>H&QBcP"r1gMm)@.PT`7WMSd>hb%(V!crK5aY1\%uRp(Y2N)./h5'l_NEl5UpZ&\X"g$_TPNG$3fG,s+6=jM[JTi8_J('-T=fg/Y@,qfU[#^CHG)EcHoh=lEOXf.THj7SJ?M*(R'3S6b+@lRt0/Fb>%.-k)]_RiP1BZA5@q-+YUK:3t9<'X(LN8JZpWX?tUq^$hY`%q)/&^JUZI8-O;NHSCDtNn;"ndFY=C*!e]SF`_(/TH[0p>bB6,A;a7EoOE`*'AK-f,2"YAEc8eKf\e>i!l[;KjMFe14e@7u6JSNR0Kn\S@A'gcTsFIeF?k&_0URXCE\X&_c0Ti;,dt1'I-+eVj^)tn1;(k*(=NMLKfLt-=,kg`4#DXXS\)nCA7K@Qae$^S8Xt'8!5/^KXoT+diZO;e\-W]q!CRo&+!cRlYe8iH/mlE^'7.L\\tZ6OV,8"=aVDMOKO1f&+$V_B0KfV3,#F>8lh)j$nNhT$SP[3u,tjK1cmt`kZW/c&&@04K.d/2%/;UPl'CZbOGff$W;Wsk,)PUOQT]*S!+=HI?6FkFa9Z/Hc4,"U=6loMok^:j:(hl4TAZdiF.rmfJEua%'-oaED2#HB\eN_YATKc/ubL6UPRR?4r]0MKdU)eVTBKbRaIH=URkM"h*ZfF-;ZmX%3%o!#:@?5IhAs#g-BX!`&UjVMCAq0@??HahJ/`8%?fgd%l/Ro2O[)CO6P\>p4tNO&>:Oi`L3j"d)RNs[`m?ir;Q:`^dQ4WA]S`me36NV&+JLX8&ZJ3@OfPE6QJ=5sJaEDjLW/j1==/:ILo1/?OH(+5k[B=;J#B&).%X#)fVKCkYd;]ORMf1[]]bUa"+@.g)Xkh7eYc2TI%KtLs3!RiYB/'F=^E_^`1-S?qsba($uOE\TR<^d\`2$GI*7c4`\RWU3P+B46j+cal:#f\sI5X1Ps7lT.TO,'LOM-OG0TUj.2YiSSg4;F_UZiAtJs"%J+R*P5Edq1)LZ*hGRO_[S1P()m[cGrpc,el(2_u%Y+L@D?%N&RstF8YBEt~> +endstream +endobj +100 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 99 0 R +/Annots 101 0 R +>> +endobj +101 0 obj +[ +102 0 R +103 0 R +104 0 R +105 0 R +106 0 R +107 0 R +108 0 R +109 0 R +110 0 R +111 0 R +112 0 R +113 0 R +114 0 R +] +endobj +102 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 320.58 344.866 358.08 334.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 15 0 R +/H /I +>> +endobj +103 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 162.82 333.866 200.32 323.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 17 0 R +/H /I +>> +endobj +104 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 417.73 333.866 455.23 323.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +105 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 397.2 322.866 434.7 312.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +106 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 220.54 311.866 258.04 301.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +107 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 416.88 311.866 454.38 301.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 29 0 R +/H /I +>> +endobj +108 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 447.47 300.866 452.47 290.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +109 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 488.58 300.866 498.58 290.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 41 0 R +/H /I +>> +endobj +110 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 428.28 289.866 438.28 279.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 43 0 R +/H /I +>> +endobj +111 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 474.39 289.866 484.39 279.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 59 0 R +/H /I +>> +endobj +112 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 497.99 278.866 507.99 268.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 61 0 R +/H /I +>> +endobj +113 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 253.93 267.866 263.93 257.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +114 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 300.04 267.866 310.04 257.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 80 0 R +/H /I +>> +endobj +115 0 obj +<< /Length 887 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%"b>.(O']&L6XKFeiA*9CoTg^[#CYCIUKNOVk[+9o+MA'CsI[p&hlSe!X\nHJ3I=:PR\TY-NcIK%XW.PD6?989A3tr;"UWBnYk*$?TgI'@-H9/D%3P';=;>Ag(.p*t#PU-[Cg`!fHdW_9`W'!Sr./KWX]82VEmTn\Rb3K3`-G-p3OiCY(.98e#d3=Ll2jG+#>fO=!i?3s@8'+&1p8Xb\B>INW+h(f.K?F<+O8Pj3qk_>]+Ub\i=n%+LHY'J)Efht31sN?NMmHquIoS5>/tnto:qf>E&Y-YS$8Z4;Rbch&/e+EHPP&Ot[Y8#1N5+#bRDSf\NY$p*$'9\6%bA,M!>1_e=4X_V([Gj2bVQnnAj-;nV$*Vn]gF?a/1nLhGiL_YRP.rU@-$t9`aC@Pe]`8\jH!D2T>#;WLNi.;6"Va&JUg!Z4UL'-;CeOTF2rcWcF/lR\YXbufT,/hCgo.JnBd.;m(?L)gH'uIL.cakd^7hFs/N#[%417`GUmdP-0JqfaLJ-K-$;S&o>#)#"OhNI&@uR&.T:h8qLbT8\-7/dQ,PNo32K<9rW2S/VpURSOuIMs^sg3C-D%R6!7'AtI_tP_Mn%VEo_qc\t(Fe`M>T6KNHA';6%R:6&i]C.'KW/F4A6LblaV\`utH]W-&.3,iJ7An<%8D*eu$3n&oHnI"nF+^Pg>AHnK=\Nd5b_,m-Y^b3KNSlI@`8aXm-LP*o1cUH@fp#cYSD9&^HV^YgOG4%Ajs*"e[U2X~> +endstream +endobj +116 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 115 0 R +/Annots 117 0 R +>> +endobj +117 0 obj +[ +118 0 R +120 0 R +122 0 R +123 0 R +] +endobj +118 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 486.96 691.866 532.52 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +120 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 281.41 669.866 326.97 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +122 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 266.43 658.866 311.99 648.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +123 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 143.1 615.866 188.66 605.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 124 0 R +/H /I +>> +endobj +125 0 obj +<< /Length 1268 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU3>B?8n'RnB3@.G#X,Uu,O^gh]4Cj4U(N(^SiE??mU7Pcsn$IL4#IYAHn877r_-P%*:3k+R*kJI,gin[o$i=Pfk]".=Dm0332o7t"-0R&YO+AA/hm_-XkJem2PomLn2Q#a8KQ/'LKo>:C`V10FIeAM9DSk_f(l8I]"DB\PX!dUCn$$FI,_HMr@WU"U$`(\!+Sli?G><>Q1FDLMT`NEjt&4/kT+"FOXC-VL)m-SsTT*nt>K/6Rb^4]pHU%f5bL^S)NKhbIYUVWoj4a]-S_=8#YPICqDRS-h_UXhNsd+2o_')eH/H>3GS7aYX?:%-tM3$%04r`Be=nr"fJgm\*KEY&i/54_6?C0K`HJT'^Z!7qKZV0?=(CoClI-1_)*"u0SJK8G-,V,c3_?)$=Yr;WL`T8K%]TYFarH:-We*WY7KMg=Q'La1^ou5]Q5jWO1J$O)?j8W#?pEM@Dkq.mSeht(=:6[S6=PYZQm/"06\KBYqRGc3FV9p!=HkS6bg%o[-h<(mSVHb9Yp]Kpcb0f/MO0CYNbgg$I)58'nE^I>%lWC@?Q`4n^E]\T4il&g-l6:,\6<@j*e?>hANZ?ruEOPV&0.6\/]n5~> +endstream +endobj +126 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 125 0 R +/Annots 127 0 R +>> +endobj +127 0 obj +[ +128 0 R +129 0 R +131 0 R +132 0 R +] +endobj +128 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 145.6 680.866 191.16 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +129 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 256.97 669.866 302.53 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +131 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 334.74 539.366 380.3 529.366 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +132 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 504.866 137.56 494.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 133 0 R +/H /I +>> +endobj +134 0 obj +<< /Length 1805 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=?#uJp'Re<2i2t*p9_l-Q\*UPkAuBK$.D'jrqB1EH2$M)P^a/r/0A$S4+PCOk-P%peP0Q3OpIkK155TRf[G8Q@X>i:_XQMCkEo>9OVB1[jS[n.aOVJ\`j*>$>=Hh*O@/TqG:$%hk#"H80)\H6Qq4=HWd\O`))3pF6'G$J'Q3f,rXC#$+(B=anf@^.L"#4^[7'k6`hEY_Fj4h^>nks:LIbYe#/,I/N#!ue"EUk,`*pMKJRH(imiSJm9CuW*<#)aP*ASO4kRS1?BOb0Oqrg_LQ4HW/5&02n'HI&[q@K3geZQ0cD+adokaANTjDJ_31c$V?I2q6:D\gGSf/%8-5*%j3AtcI-,JBErBNgCG^o[i(V.pH%JnG4m.gq"k1h/1(H$d244qW;)dZ@pHoIm>2n4;Vi57T(*0Y,jX3'`*:8h!Y&)_cTjb+"s44Os=KCC6oQpL397gb3hsEA73\jD$,FkpA#YAWZj_,I;C+/0^O_?m(SBZ@PDG8>7N"8N!"OU^D>n[(0@F@'gA)K>:SfkAG*T=(_G@NHK/`,W"%<9j2:Rn5GA9;\^YoT2r+Bgs9rUU>%*NE`u>J;TJ4NAcDWGfD"ML:r9I,!,GahSkA*@H+hdOYPSSCSsh[6>Ht;86Qa3o;j+_9(-eNCb+o&:,8W^"r5VpiI5Ti@36@Th+:ThQL6eL.J0Lf=?(^%Dj13&=U3@+QQ&\(sU4Ql2`EmK1Um-HtR=XZc@gPM'X.5U3f)M3VoG(IqES!kgZ"Qg;gCNs7c[dBTN1jR/;6Q0c9_Npf.qcijP%Hlu(fB!=DKbSr%N^d`$^nO:[F$TbPM<&H"*Uf?U+TkVR[.$5d_*3*@U>)5f54\da(!Fs'Hb@*bjif6q8mk-p:bJ_DDA;H^h>Ke3Te_lC=+0L[s=M:m8/"Zg3tun\-tK!RGJXY]^$:;Y4b4^o[AgN:`&u/-reg0eQd5(DD6)JV/bmCB_&4I>.q44&_UE^aX#&o`=.;a,7-of/QY=Hc%cSl*QEk3NUV$EXNNLd00kJgc_h_oTSi-7"bAml-eEKQSfLqr_ao:@0+8L1q!1uW:1SqXM!$a[%[mXAKY(gn@EC"Q"uLG+fV;!ob0fnFIeWX!"^#9*X1*)lSbbY%N^)A4I/*m +endstream +endobj +135 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 134 0 R +/Annots 136 0 R +>> +endobj +136 0 obj +[ +137 0 R +138 0 R +139 0 R +140 0 R +141 0 R +142 0 R +] +endobj +137 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 303.88 680.866 353.88 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +138 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 491.34 680.866 536.9 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +139 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 373.14 583.866 423.14 573.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 53 0 R +/H /I +>> +endobj +140 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 429.1 540.866 474.66 530.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +141 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 215.02 529.866 265.02 519.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +142 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 389.69 497.866 439.69 487.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +143 0 obj +<< /Length 1357 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHL9lldp&A@sB:d2iu(<\W\2^`URZF0KV#B()q%O2gdWF/1WJ8FVVh\MC!)`/s8;S2%C`<^cr^[?CGd&Zg*\$rX(#.A$<`L15\cWnYe5M(cU>)SriG5]KS+0<'jTm^?D6H>em(E\uKYsug7VDU$nen!<\^>ZV!4DSo4i/1SRa/)tJ.cV6'X-NZM:dW?J[SZZOOq!@`)VWIX``:\CA34/jE@P*ce1SD4jqII0'PrtuJl`B]budt;J)_t"d_p-\IKTsIUM%Eg-S'OV6"Gud%M[@82Ys.!&q)X\)o8S^*;C,H@h]F.H&N/V+9BZL*TZ?;.on.G`gmII0I@o_&%=?HNAunhAJ[#Jr]D9@PVV#5a`V&q-[=0P_iP8($O(<9#FRF0XDLPu,PfWr1;TM%eS$IZo-)$gAhd2=UOAba8m13dF:AM3"!$V`U)B_h>S.Df=K@0@u_DY:RC0gRb>'hiu)UVT,q:;pUK%qYeIWio;3177H:eEM="lm+cEb%VBiTJd/r.)"1A_D.gd^RlUXQ0qoSb/DXS9C4'uGLOqGN#N':)dpkTWT3bX6ST`%=t)]DZXAWD\5*M5S=5)pnr29%a;MI%t?YB6U8QdNg+]5lh"HWaXY'gS/.>\Gde^L*l`XF.TR%n['GlAbg2]ftqi9U&"5U80/)hei-X&3)k"0:G!ZOd!bp^DKk^rKVsY-V:t19SNoV"b,5E%u7euE&^sKoOj&<9,Vi6hD:E_#+jjC->4G\7 +endstream +endobj +144 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 143 0 R +/Annots 145 0 R +>> +endobj +145 0 obj +[ +146 0 R +147 0 R +] +endobj +146 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 427.03 647.866 472.59 637.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +147 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 337.22 613.366 382.78 603.366 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +148 0 obj +<< /Length 1962 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU5?#SIU'Re<2d,&6)de#,l(cGp("([>=b]25DFWY:RZA?Yt-W*G;m)P0MAb/eD*="qR>1)?k4nn!l6hT'm`K)s?iV8^U/@,`=fN^Cjc2TamjRT_"N\/9ZNTJadEZ&$o_[L@NY@#S)?d(^h/nW_Y`U&=4]>8q6Z^dm+j/]H_NEZl$a51#a=H-OciA@e*K@OOn_1&fFD/].;>iC/N>dIV80PDc30Of^$@YJFRh[%q-8VIf^=#+%>1;Rq/VG3=`X+qug$%2j90D..jcNF#of[kMJa4=.CCKcqGq\df=k4Z9ZkWrQ&I!(M%sC9rH[dE[M@Fi*#_;S0ocaR]ZXPeG2^X(k1U98nEY)#BM-U-XJu+YLqt;hAZ>$Ao<'FkYoP&`4$&BU2b-l=US?sE8__7Fdr#TRa9n/[4A+q.VD%*H+/kP;=+@sAIQOnbDbT]M?h6ALJ.'$O\IpUVOh4r/&-S-W"hhN4r@kjQc"HdQ+5rg?L55HFX-OG:W]$6X)q7Y*#!]Ek\@6f=@2:-+N,8#;X3&\?D7r#KA4;X;S`?]rQ(5$m%GFb(5(RY5)Do+E'eeOKjqSI+G\T5DM#W?\XR25Z8Kcr>)TsaT,S2q6,mi4djTb;QX)[`P>i'4iTeO"Z'3[.Z^g9_0oVe!kq.XgVk,S?>WcD?GGZj!W&*>6U9(>$%.@l99N@UK6KB1!M,u_B-+&l27r@q<(`-IeBq3ENjFF_UGKlK*M9.JPSWq4LbVL""2OqDETSQ#b[@a3:6,ZeBBW3sb].N5MnlD>=&BJ;k;]JpJmGXa/*l9KYmX:F<*%9#2WJoQ+FAs/of@FeDNk$?_m20n#88=[O,C,K'kCES;%)M04npNfCi(,Mt.\IN7XB2*@i(pImKR1r@FPmo`HqjeY?k1KWM%]b0nJ?BRWq5ER,PccQ-$pI7!8s;_^O-DuA=]5#n8%l-d6#:m$C/)(g)7L0.?No,,"QVJC+SP,UBhg]:aDW"V$r@Hel__P'//YN<+1Z&Z$Gp+W%BsK(5r3$\laEgTH8\=^GHR-FR7ZLnVGs1][j4Du4KkeL2mt=$KEBPU^]48t*lnSGJp+]1F:W"HE*K\C8O$Qg&KWR>:`WQoWh1(($`COPC1aWR=Z/^RR_0HXYGACE0;!B>_VYZq%&>Ds`L^=.53pa8kOJMdik+1st6H15JDV[b0Zm./M_Z&0:?$[-L[M$'c*T["+_Z.F>ZARa,Y_N@[>m.]W`LNdrqYLCkqJndVL/ZV1*2(e[@$p_M9D(n!8;Kh@'^s2<2&sdJTc,l]T=Y(rqN_1rHNg7Vl:4T*mjuETY`g?bKt=(We/cHmnAU/m=c822IHCg(5H:Le!dsYKMUD9qr;<&W!Whf!5`)qhS.rQZoU[urlP;CK%:%3$,7r1kcWBf:_)W$8U(Nu,,W(M+#%\LA,au.eZ!iJi8kF<3*bA*\Q+F$kmS`tC(f0.Eno2(O72nBX^K[72[YCCg_)rP!a0tP+1?QNh&MuNbtX=Q(~> +endstream +endobj +149 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 148 0 R +/Annots 150 0 R +>> +endobj +150 0 obj +[ +151 0 R +152 0 R +153 0 R +] +endobj +151 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 272.24 638.866 317.8 628.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +152 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 259.19 440.186 304.75 430.186 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +153 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 177.52 429.186 223.08 419.186 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +154 0 obj +<< /Length 1233 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=*9p;&)(r#lK0YV(A-3so'!Ud*Hc*"P/[[B]LZoMZ;^pYpjD'2/OHS>?b'c8Le7nBVU7[bS*DP'FlI/@'Z*.'`W=ZW5#u;Ws?uEL&8G:9'0lf.$SjV!@XKO88/9IQkB#k-M[`m4*FQm9sKd+TR4umF/&;V$Y,:'ekP85=Z9<(a36U^\dkE'_o'M9EB,8$^3iiCkMk?WPt:5(k!EQN*:,@B0\gLC1K(f8rQ2D*tT>I(kfb,kR^??O`Fc`D%,X$-3KdMn4]W?$s]uqe0)ah4f7!0GFrK!V(>q5X8'N1CC^W"ZUA4c7F;nV!Rq=^YJ\>po336-MC/6AhgWEU0?jGX1[[UVU6N--uh`l?9K3LUXD8MnN)\=-MD)na3N0:W>an83/1g;$(!u6qID'k%c<8^_*^-Z@P-4i2OXjS]mB'^uIi8Ha]$$@$eM;`Nn.KZ5geK>WFCaCk=3OgbiXP4_qs_H&hsjC%._ge!EN(OgEO2N$FuFMnEN4)1\1/g\*d]12U"`I0bGrD;PR_fCh1S^k]s2G$uoMh"@k&2X3BbS!kB'!UrkoH0X1fSm83WR`Sm>RHMJIW=+;QA<^]V1[Bh&C:=eY>$]<`Y8#^J7:P%,3SWj,L'rc$>r_c(_MNuG%P^8lZW)a$i])RiFacTif@F)n/[^"-(0N8s.#HsMp)_qUXlPuj/H<]LJDK,hL<>X)<1Ne\Z\fPFj.a6t3mrF02gSrf#eWB3pe\E8Om7p$FcX<"pI5\LnASA?1rD/rUOF-<:fHg4UNuE\(aS#F(NPp,c%.lMPo`i:BT<-i"&d<7SEm;S(S/i]DmG%QD#::[Bj@3#8UI'J]S:mmCVdGj_..pT6$m7B:I>\JmEn#1]29J#pAE,1%"j*=Y]]Jq/YooVH"PWF+R%3Bdc["`Xm&/C9r?$(9Fs?jp8r+T/^AZSi_<]La:#]T@[;S0Z->^BT*^;X\6cKXhnR*FbrPt\XleZ!>#,(urCulS~> +endstream +endobj +155 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 154 0 R +>> +endobj +156 0 obj +<< /Length 2043 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#]h/f&F&:aF]_<'dIR6V=UVBa`_t`2kB1M_6.s2]DL?Jni@b]htS+/$r[P_Of?Sq5VpMHT_T@+o?G&N[\ngPmE7L0o#bu3YAPLiT]B)LWeJ2$mB-Z;XnGM25W*muf.];2i;te14fXjNlL)37h]#\XR]Tt(;c>"Tk+5GmIRh&:,cb=3q3;XpY_XI;<26=JU!YnJ3o`6ER>sIW'4t2"oV3URVae2$<653PR7Fn7Kq(HDN>%.#[:;.O_MDltZ&G[G[3X9'uCQAGRcE7gN::)IDb\]P@+;EGsAj1I%[om-:\X,L@)#H8cguPjq(mkZ/tCX4jR#^ZR*8335%9[Sa./"B?/%;1;t.NJ^YG$L"hfe%.K"'.B92"-SWf&9[q0?,`TkT"&;4GoY9WVd\i!="-UnjRs9rEd(qm"J?srgX1c)tEL(3'rU2&V4er]DU1+TIHMU\4@.bqVCFCqX/;N2gCGZCBbjhs*oj[E?_es/>!'Q2=%\XjP.qP39UZ[Se*'L"iD]>%#]2ZaJ2fT8l3c4FN1rpo4'li(U!moUt:ilHBDL#/825:lslnRKs;[M_P1L4-Q>I&X+\1I.&^"s,-@Umng81=A^R[`s2f9RE'3\kDUt)>RY3RDmt^Nmg>:u_UH$*<4#D-Lm"f4@ljI0OgH%g5iB+dnEKm[64B/&\>i>P7mX417Zh369Em&!A\gI1Z-tH1;2()IABn;?o#oRY*R3>g#M`5,s-'#:/LI25#]FJa'/C=,+O`&H29G,W]enc]SO)fL0dK6k+W-<4/ggPcD+Y%:%M0+,0MgE5ZE,1)Hj4]pcZHGVddgR>T5pcIR5-\J?kVW^Y1#DU?8MK5%q4QD(9,p&GC1c'?g#n:)_o?/YX>7Nu+[Sel0kJ3(=XrMosX<"=t/)buH"X8\A<+]4qK(A`Mi+>*_20n@PS[#mJGun8pq5e"Il4!Ssn6]Jr#i.PEUZR0J&B[K#5qcqg7B$7=`qqoC%\F9$]O[GA%D7Ur'*E?)kA-[/=62W/e"X4>@CgOq=kH:97pV[5D%`NRE>?gAl`]dK<,*8;i:!19RE45.#:E(9Wi-OZ:s*\_GLUZj\:@]kM=e+@rOpfin;>gRNHV_6H.jsn_s:72C?J_b>Y+khkrM`/6\G[ndhlX0'(rpg/Q]\NG[b0']jHQP-c~> +endstream +endobj +157 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 156 0 R +/Annots 158 0 R +>> +endobj +158 0 obj +[ +159 0 R +160 0 R +161 0 R +162 0 R +] +endobj +159 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 189.47 638.866 235.03 628.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +160 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.94 563.506 166.44 553.506 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +161 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 271.4 305.066 308.9 295.066 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +162 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 220.29 262.066 257.79 252.066 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +163 0 obj +<< /Length 861 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GasambAQ&g&A7lj#[_OiBp'5`g89VO*0M:TMmK/GdoK5sQRp'Sk$?Sfao=jbM1dJqMb7ceri5WH-i=O]j2=ZATqW:)"(Q&-TC_].i.6'ut#nF);<>GHH9,!fU)<8fBYn$3;gdSQ[V>d(pi.Jc!jJeYHBh9QIu0<:IF44;e6ZTK7A*=_2k=\b\G$](e?)Sr^]4Lq`Uo(jWNFO@%Rffi5[r@_q.f1aY(`$2;:X]i=KB41/NZ(+bU!$_PE)fij;#(@r`V:b[NB,L>]d\mGK7OLQ=/I7-D#4WtM;WmU:Xhe(P#Ic&&P$m(-ha)J3.QK'p[RdtK)j<^F?"O4/ie,@Qh`-%c[Fd2Gj5Rpu?KH@N8G$s5c&-m?2#>Fe4aS+m7Gfj*+H_Mq+c;WVpkgm`bCEGPBl-8Z=V]+oW8oI38/^mV,=R`5GQVn'SbW'H0)=)fXE\k0FYo--_i:Au/@$&l=miA4Ab*$NmjJtRQm'E$#GeNh0BkIr6+3W[HFle<#AA2UMia@nNqR03N9+rr,Q#Z`GZAM#_s&DU-1,6>W];IpZl2iWilg3/Z$>[5dF39^*Z$C=)F.r?/aul44@]GU3TCfQgEl:USnrF$r]5uLq;.["X?<3J6SE<<,O1F=[jDHjdj1W,K:>ZH,]mcMS8g^SOmNg6C$RR#*_sS&'hL*^BT''"Z_\`OeoYB[NcdYY5:M*\cFR.pF5>b:73NkT!O\6UiT1mIq)Pc.7fdBKWA07"9g"eEK]H3KgFRrA6XS*9M6at`TDq#FtI`j%\l8T*_qTg8>hm.s0%Qe1I/0=ISq%'0HbHV~> +endstream +endobj +164 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 163 0 R +>> +endobj +165 0 obj +<< /Length 3180 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm?D/\/g')nJ0+o'6g5V.hf?A#\EDNen?f3WK#@i'o<5:AE1CKHh7P+7kgJ!;LQ6^b;N_6lOohg$Cc,LhAg#I!+RR%qih$pg7Ka0#&2`Yt\n>br=5J2j+'iRei&^n%ZEdZ8/E.,-Uc)f5L*m\buB)a>13f)9g](%8HCS8\pcU[S0=X#i8^hP3!&UDcOcZ>VJA/%r;/u]C/tBft/nBNuQ]'aL<2KqT$RL.$nZI+=F1IDMDb^A,?am"3W/9XMZm5$g]iR-i/:9rQ4_(o]%W-1.M&M^9?7_b6-Hn<@"ucLgXb\I]MYuLGH?BHZ=M)"%];p9kXgHX]%CT0P@V;M?GL-II((Hic/uiTIf)JhL/.#(0K-^n0#T<_0Aqq5?\@a^OUJo`*L'aQrULZqrjQEDD%I2WM>T"plaI&?BKa=CmMdqJddS%MrL9KD&^*4'@9rI["b@IRhRF-@j6L_tn=D'Vk2Y!C7`U"?Gu`@X4(,NaQTs>^_ia=u=AVSpRV%Jrp%kP4D3@.')0I>Em8g\nm7"e]6sc'TI6os#uE4n3XB^4Z>F=$5@h4:+grZjG3QHncE,R:MA4d]J+"N)k2ZI%=PG=&nBTI/dtN/^/m#_dtGGB6TgB8OP27,6@B-gDZ.jY?U9^Ec+48A`ARt3&&bB8g]*]l"pWlhT7uW.9Z0D_Yfs.O-@/(T?>"BAsAMD%u^X>Alc8dGPm4n4O<+I[*7sHY$%#PRQ"Y!%P"?DlG$p4KG$2^a!Sh,QtW^W:ulZ\Ym!-Lo1TcFM'QWIOtk#Wn3@PEf2<2;)QIlrC!:ob-T7]XmBd;!@C.YaQ-TLHkqW!on-(KuO^L@g<'2Sr[l:D=$J$V]at!]006`.WPeEa[O*'k,guhcV/qkm"HMI(f@J09\FOLdq.g&f$*a*l9b4:5BOX:2$i0P7Wp?0a*J$/J9,DAUk6$O=.t*Ah1iN*9"Gi.Z+ao"Y$5S6(4Wr@`#iY3I08Z'Of^thcV/!8j$7&^>8^!gdj/U7eeEq(54pn_DEZ@L_:q4BWk%Y0hE*Icj8,FiDEhafUrVO6@<:=M?,`SF!4Nt*u#)\8/6_59D-5V4fNaIN87JgVYg%?,snUAQ)3[;mcO6>TDuV^aVBGCKWe\@iY[K,E"0r),/qVnl3/."lb@ge]Q[::gkke(Qr>Bj\J\JDPP`VVV*>,so5+,OpSRmQW`@HR9??GeB_Dte)>.ROWPq:nLDNQDNZ&=`6['XArb7q?#-88,@&o]uT*h6>9X5\KHq5>f"I,8NK5FOsS&.#GBctgtHc9=E3:is,PgZ#nWr/:"3cj"+!;6`c)?S_VM3](3#Do,``J'gkI\PZ].62:,@7ru7eMnW@:Z%,:=JrjPY^K.m#iZg:Q6gY_4_&cF/(mdtQ\GqJ=PHa\!&'?F>2]H&;V2&MFKDR,'RV3rfO40rT=[]mp`eBkC;1X&ED]8&+@jGCZ<,,&5;O;$VsD>3pN7LjnSPFC8F=dq6mFDeXi+;e8^j+6Lm8M+"0G,"b8AY:HX?`e4iPoh%[nDq?^k@.(lIAgJtmK>ePB+]l)R@_]AKE,D5ngQ[Fjpm8nD[A5!a.rXTQF^O3QrT_@*qG>iU[7S*4$;K>S5_,%[5#$Xnji+YZ[[acPE++sle]$YIalUS#J7k)FFMqmB,DYc!MF2O#r`ige:Y'[%]pR&0G\;M,NGUO7SsV`:3GgMb+t".@Y;t]#_[ma06N?Js`F%unW@4+`Z2*7!2n%t%e(NT%4_]RNIbM,E=SjdOBOY]]<*O"8/O+e,p)`lcRY$o>H.`)Qi:SHXbk#MhY6]+lfCl:)^U&8.V:Qe8VSo/o44C[7KVr&+E<;\H/h\1)n:[7]X>s.1*L_PUPH6c1nY"7s%)iaKC"!(t[oWCbLTJ>l"%U?7#6~> +endstream +endobj +166 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 165 0 R +/Annots 167 0 R +>> +endobj +167 0 obj +[ +168 0 R +169 0 R +170 0 R +171 0 R +172 0 R +173 0 R +174 0 R +] +endobj +168 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 146.99 691.866 192.55 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +169 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 194.25 555.556 239.81 545.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +170 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 194.25 544.056 239.81 534.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +171 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 194.25 532.556 239.81 522.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +172 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 194.25 521.056 239.81 511.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +173 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 194.25 509.556 239.81 499.556 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +174 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 194.25 498.056 239.81 488.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 133 0 R +/H /I +>> +endobj +175 0 obj +<< /Length 1976 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0DgN)%,&:O:SE:d\e6m+bq]P&9r&j'uo2N=c9bsS!+>(S<6'WI1UD>el!]E+6^8a42MSfscG@e]6B>l\f?B5a\\hp8P"!<59S"[6rX+,>YTPQgP>,a9I==uj([c[Is&;p.6F^F/?_(W\1OjZJnX5!cnaM@f(')L!JFm,a`a>&IY>RWc/Y645k/4^FG3R,G'r@[CV$gcT"gaiDf(I&Kf/KB\_u7cHo:]/8OP]=/0_>J0!'!BoKu]F(]&rl9%?q-$D*.6FM$Z[V*aZa@6^q.j)XXAL>Ct+nFT!t2$M=qB3ag!aS2aSQ`nOPRO+"W0B+O23P`]RW9IAnUE8QmY_M3/HF/hf(U_;c%BCa+7b99uf<>2k%]=:q803Yd&B+S/:KB/bknkp7sAK&.Vq'A^2*%qr@B"9+jE%i2V3>f@/mTDL`",f9_5CMgN=DUlpK87/`/8SfjHrd6/6J-toilHi0oir=Csr[>GZ.;p%(7nEFSb[$EQRK6\WWIb+R01eO>n(*\?qF>$K@Bc81A.]XD;l?/1g-1GRsY\'7C7pc?"+*t+Nq77ksRlD\L5d]"qIC-4e8M^[Jsbu;9qQ-a`O9$6,BOBU(DqG+J$qT)EhuV@4kCYBUf&9EEkB2aYrD]-rMZeXs.28bp$k&:UDfF'R.7DXn%Spjhd&,9e;;=@-V.NT4Y<[6]%;SH.cKhKHKZ!>ZXbl\!E5TKRf(W+%`cni41,HW:7*G$Z6q6r8IV?56S`Z;(*6aKc:&fG8)Am.XQ)DZ*D5OG`'X>e7/6p$#J"gJ^TcZ"5,t#r`m"cTX_eVA1U.b:?7Wa.i"X8q-IDUpTF-oANjOTYS,63er>:)C9>-YH:-RLj]`7YNcNlHEu%&cF,CJZa,G$XU^4<)jHDp:2-BU0B&U*5q$FiX!F%sC$h0?:4+D35$f<<7+dbcL*GDlYcN3eZ'_XLF'3Kj8h%bMOg;jmD?(R#=u+!0al2YB"$Vce:t8g>Z6'F:%TbTN:]1ZX.i@NIH[>?1S:j>Vo]`K:]YU[c3FpZDtrGLDSc%.cmli=/QR]`dR4Nu1qOfd`-9NsX*:0K?MQcI,^93m3bau2g6N0POQ%nY?f!nO;#km!H)S"/t^!AQAiBn,6(hi?#l59ed[?XMYK?Zu^jn=\pYnrl>gc0s[W*@0q4\kI:>>+blDO4Z]Ig>'l*U:&C4tek&\a6!^S@\'T'uOmU33?Yl"gg2KAb'pk!JM03lTP=)I+@l)E+8ECaG9=Qn+P2).t!-o*^_Q_64T?n8u7prVb!,Cf7H6,%H9]/XteAI9TAZs>FKh[QeKc')/If6pUdNF\QPiDDAed-_seVB[[!e>Z=teN@A2_UTV61?mAgsr9c??MS;&Qoi#OQq#h,@+LYa1KUb/'omtG]aY-44VRk)H]8r7ps`RkJ>B%HboILeTte?[TJU#%WE%e3.c+/=4\eQ*K:0_RZf'Wul^ZA\sRf-IIbl_hcOO_l,&OOT71c3LU:)b0C\'mftHGM;Yl\']h?*MsWRq=.1Uk2-Tc[_2~> +endstream +endobj +176 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 175 0 R +>> +endobj +177 0 obj +<< /Length 1320 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0C?#SIU'Rf_Z&Fu%co9CrCBrirCe;'O1K]tK6WD@E,S/53\pQ=!]cHeFkp;u?U5Y];:VZUfKHu_ZJObYLpY8gWP_sJpII$jbHa=*&5tB;?4ZV)bfrHWVjJm\-H?^*ONCL[jo*?q!ng_uA@$NBHP?IT]LUkpfH>rWDlL("R#?Q[@0O[/%gBuE3,Y(*6*[ik_LrV:8=ltk'(9=Y)ckhiWVGOM3#M^h!J#("",i:ro4c`A.C"@s7eFQbpI"hgPPHF:uJAl'd%e:GSQ=#q*'"gLusT+_9hS5aM:Hqqg6!EK1[TiJsI,mOF.R1%dr)7M$Q)M(puGW-OhN"N;F*D'd)GhC7-XT+V]m:P0s/6j@&8gUsibaQcL7o!$93e]Fq)](^&#aYeugYmXLm)`!A$+B9*1RKC8rSO0eRJX,"Z1(t(=$(kf)*'!n9uiJ"lh67''5A<%5S>pb;D4K_aEoZM%K^N4"a#UjuTh\B$e88:K6!uQ4'1T:U$IeQDaCaW"P:r%\bdn*Mqk&ar]0!`V:"\(j]V%e/N.4sN8mk6/ej`6H7\kl!LUoRL!?`'8/KI1k17Z?-LX,NcG=J<$.>Z:a,\.P_#-O+6PC:iIDbceeC+UrkiSA'+Rusg59PRS-ID2'F,c`BHr[m_I-R[_*RZ/gPTsZ"!']c@0-`5hebf\!%L?Q7>P_?0[s.Aboh8(X[jX$l>sDV2Hfm)A']K/[/_3s%eM>KLcA3hg@XDYg$\LgWWG@qAOb'^f_9DJ6?D$`+E)Db`V-4\Xp/l^[cq6Y2_\"e1q3!+gW]$KYC/DiCUER#\]IK(4m:]4V!],)B!GP%Y3bQLIu*@OT/>:";f4"E'cW_BO"3>[*8^inX'_oUUO,c672tH.WiTX07N6U?NZjS^,'t5W(-JDmq6\4!eDqorU(Y`V^$IJ,,Hq2-,M1-7U(Nei0RX!O#2Y)?~> +endstream +endobj +178 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 177 0 R +>> +endobj +179 0 obj +<< /Length 420 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GarnSZ#7E4&Dcpm()'6X[@ND@DR>+k;Pbk&?pG-<\.D:V[N)'NIpXQGCt_?bmbU,(h*'Y**o*r2L_\4hu+m[j>#jDpX[hS4:i-lE*ZnjND#B:Q7KR$l@DGDe_2*oZ?A=VX9]o8&TT%p.j$58n+9'-OPBY8nWkJc0*Z`>_hq;X_`EM,+VOtc<2HKD`:*/>+=s48/6GXg_7B:DL1kk;nu!:Ze0NB;hJ%Jt8CAcGgOmDAA(_rH"VGPLlk+2%$?ea8"F7=U^157D#2p]~> +endstream +endobj +180 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 179 0 R +>> +endobj +181 0 obj +<< /Length 367 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=fa\K`-&4#^[$6OmgbrCUp+'I;"&cu&K4]FS3FL"GSJmG.#eh1mW0k1C7@BK];2i95H,'YPa;;[Onmhn70/?M_l=T6k;8F[EM4oFA7!Mm4f\q'IRKri$GL)_`9@(K?4WGj4>LhYi`cEFr:25p"F,KI_Fs\:+=*o4Q9*GKBEPFA]Y+&HKSA('.._30%"p(p$SOYJ$NPV:D_u;_/^SYag3rnMPZ0NS=&qk6^VQqF+DU*O?P%K$@DI/q\(cIB@h~> +endstream +endobj +182 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 181 0 R +>> +endobj +183 0 obj +<< /Length 1896 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm<>Ar7S'Roe[&Fu%c2,-uu/SJFs9s\D8gW]7i$q27KLUIB-Ot+j^oC$n%Mb2'Um1`!?e=t6Jh`9l[U#+'bPMb*Mqk8dakBO_gh#'=IZeH;-e-8%g`T"^i]&f[1$XS]<[IW;$^Zo7DXiAKc5fsQJUt4smJ.iBlAR+aA"1q^O])`%&DFR[EC-idl"m)oH%n^8qQ[6/ASj"LS>/O(e*Q8Bth$It$g1kra^da:0bub0KqnFa-AdV9I-[:!D=_g84pT1Q5!JQo3DNi6t]RR#9hHY@gqiJu(qIYV'6fW-"=-[N3Xo'bc"g\Cm'>T%be.lU/lQ^[ua8"*\8GM_.WChqIgFQ;X-140_4dcCK]%fU/(M[b]o3(ISqN]VaQ9E&eK/"m+]B+G*MZUT*HR%aJ+"iHo'VTi38@-;e9niX?&m"dRR^&K-lMj.T7Q>P-Gh@>Qt-b8,`VTeb3_=8u%F(%]5gKdLN#>e[7#2[ZrhaSGON0"$/h!WcI,FJYSUHW]CjtI<\fGP>,s"UG4c=6G/kj?%5#!Il$7\5iLod,#gGComLJ?b=^/8C1<76Lj%3)d6qTG+^:0Y*/IOFe(:i"i!onHou2b1AJ-&CsB't6A@VV>.EOFfOu2SE#FVLITIc_jJ\9!nbfBER\CdO-Ju;3o&a+NO/o(RJlWCuB@Z\"Ws_MJ(O[NiHYJ1[aRr7X#Em#lP/K^/T["GPoq!"0c5MlfADl/>`U"Ohe$@*/WIY6<\UA2Yl^.g&2]AAV.qt`$>54HmT[eG;1e+'MEFnVGP+@DTH6dq[Ub1`M"[>!),97V#eB:,RuBfKo'*,#S"+@O)Fc.%g1)XW?$0f/1]!%7&W4g#WN4;#3&CWQ>3![ACYCJJ/GAqfXpEQP6F34o\-LG]QA)DfQ_%l>-ML'i0D"SU5DA93Su0g81,nYg]-WZE5O7/Nj^`W?1>r0^S_oH#\LYI3C4e+ii"9^H&%LR>%';$CC#Ap].)f]9%J=^!>KFnppf".1[jBpi?j4 +endstream +endobj +184 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 183 0 R +>> +endobj +185 0 obj +<< /Length 773 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GasanbAQ&g&A7K+=PSn8XgJa;aYC35n:7r=Ti8,V'm_iq^uHrUhn`O_16\h>gCPGl'p259:arM7;M[HEb7$Q.&I:\;%V%2q_C]]&0T*B_YN0GoPam$,:J*r^h&;2#kCgq\>Y%5Ub?ru%"f+kr"$(p\d+7dmG94H&kJp'LSkChLa#G9c'cN&UF=rkkK3Dam'r$]"ML2(&aXeSES=[R4-\e*8V8@kj6q)Md&4*rGH^PFbhW"8#oC)[\n7Y$uHs/\U!=hi<+e=Eg)8eCbP7$Nk&LBBAYiX<7?Apqs+6LptY1Ihr"qsi;).Ij19dPiN?d=I'0)PcH2X%f?0Z_q%VSo_?G-IB2VFTY&1`:(RT2n62'!uHq33ZlTJrCe5$n*Wi@j7Ffm:%Z~> +endstream +endobj +186 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 185 0 R +>> +endobj +187 0 obj +<< /Length 1905 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!;eh/D%+&:aF]+nT=POO0`#cTVbNdroTUDI)8)(m7'Z&oA#?-!-8cACr!=ZYZ%"R8f\eI9;C/(+%hnmruhL?scYuj#;NX?".$%l,^;F,APp$QWeqBCOmK)BA;q8jAMdV-'AI5*,i;A'pU0[;9QAEPeSlo4GOAK=hk8LXiYodm9.rqWp\A+)mt.Y&\D!=Y)kCu:fip)IJN#W7A9a(^KLCP"HYnI*)Umeopf8p">`(j/,>9aBi\b+8*ai;[cG^2)>6RX?LEVu7W(u_lFX%3ICl\F,Lg\[Yj4(O;&_qc?Y29\O7fATg:H[q&7+dS#m?reX1,ilrpWNDa$Qhth;4l&Qsc=tT!*g=nlE5S$dXr?HmfZ23dE?&.OmTjpRoV^G;YDF4>4J$caD=X.]lR0j^Qppar4-k$))nu0V3$]_j`Z)I_>R$R/g.sS_l5^J`jS0'ZZN8BMi7qnO]b"P#o:F.mlX@q@)]SbF16#>:Zu&Gp8?ips(#\^;t[9[m/DH^7]$(q0M_nO67XZi>$hoLhQMcJ_]j4fnehf*3<7j1s;UaOV,:r7jVPZ.Ls]6,IT"YF0SiI1"#:;OQ?;+=GTbOIn87%Kel^[AiO]=TtWNEPmaOh4?"s)bfWHQ"V[$8gSJqhHg$L[JVGu,;`K,SCH0rK.T1]11=WO55RkjMA+X'dmJ(Ss2jlU_*$*b6eT+gD$;atB@$AF>VjE/,F&d5+@GMDn^P3+$FZ.@Uh*r7V=OEQ`-kmi41uQRRO`9C(h%P?2K/RrTsF\Y4n'Y7&&Dr+ECnn%s9XI$d\r2Q-qn;eAPS:Y%Qk].P<\l`fo+YX,;Yr*aE%8,Q29%7D/^`Hji"-QsL"B*U]p&3:SX5Q)'t4G8\_2PJM@2AT3-W!8_*X9I71WEs8r&9,LZ?C82aIh8O@&jPZ#p@K4U#oMF8TlYHMTXHlfYDknOP%VqeTGHt_W$7j89.Zra3RG4gBEcC4,n[NE&MgCoZnjm`8uca14egJ(aAK_;L/?BDU3!GAigBSZ^/k?a.UHY1:,7dGbRPpp#B2@1abpQV9Ol9TPq\2%u-BbXHH%3071EP!k,5-MtFp*9:Bt>=cIq]FNA-7PpuWb+SWNe3/\=@km04%!%\>8k#:M=cA+I*g^Y)@H"7gfWW%>K=Fcr$7er*(s*OdNdJRi1e3De1eYVeM;d05!>A(pWI*1!--N%r31<;Fn_9m[CaLq'm:>d%^kXVs_:BN@;,>Ya5Wr&U!-40aO"^Jp3+>At)qT9+W*NW%XrH6!PeK?#A]-F2-d$iZju\uhj=d>d[WcB?6Sp^r@W?12q3joR:$Z2gIE[6s/RXHP:]L9Y$u+8C=$]Hj8,_VEU4qT(0LhDr%3lV)B8;fjrII@4`-fNe9I:r,j1Dpo)*&SiIqh_*k]D9-NL$hZm=hlL17i\Q-CT3CWCYB&"c(EWHb*<~> +endstream +endobj +188 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 187 0 R +/Annots 189 0 R +>> +endobj +189 0 obj +[ +190 0 R +] +endobj +190 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 151.14 680.866 196.7 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +191 0 obj +<< /Length 972 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=*9lJc?%)(h*#WX1rUIkHHm1)4"?%[8b]-2=K6ed7Zd"sL/c=EhdJc5AcM.=qnSUM[tM3/i$a[]NArP>D26b#Q&nY6+0MZG2!rA\d*ATO5/\LE@788^rQTOTa^),l$D^-aRZR?,HQnUG@@N%IR&5WFAL[T*EK">POS/0QPk1Ph:GYUaM'b0h9:Aa?Aij:T\+T"L%1bG0YhSm'KMlgTg=%e5D9U&O(iF?M_;gOFMu+UO&$#pp:Z]5^9;8\t%t@oX4,l^J7O=RU9\l>h08;4DQ$&0_pd7cUr3L:a!N:JK.Mp[qft.YI285LTP6<0VBgC4k3]h=.B/mW6EG^FtD#'uiBRKFmP2LOub/;(KNO;$=5([DVR%J2PoS]OdqGh*0-WHC_qnrKHMhOHb3[8\T8Sa*dWlPOR:11#%"5KO6YAA!f!D5TkVhU'lo6o1Ofcd5#"YGkA?"Ae@=Y0QJ<660Ol;$c?Z<,O=YDOPnq4eFST(ogZr>I(I+feITt&/eM2lmk6(.-\;-bWX>Yh9%)9:jbStI>CCRN&0Foi"P".i"GdCrPPrBn/iagW`4CH#2)OJb4)kZ/H>Wl>/%nJi)=:8rH5aEDZ_%b/0fIlYRtbdglbG`cUDTp^(p[Z4\C>."`^CV#6bP=$oc1YJ`W_TI5fm\e\rq)/E76U%gU$&XaS8X*qdpggiEG>q@s%Su= +endstream +endobj +192 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 191 0 R +/Annots 193 0 R +>> +endobj +193 0 obj +[ +194 0 R +195 0 R +] +endobj +194 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 158.0 646.034 212.0 636.034 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +195 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 212.0 636.174 266.0 626.174 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +196 0 obj +<< /Length 1038 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0B>Ar4L'Ro4HA8an?;`e&99pN:JBPaMV/6t,u5+'7h9=]/aBns&;56hgK63:@=9h<>hM9k9FhfkS$618XRC=pss(;rA3/UCMdnqT*SO>[o#3]8!18@=3dD!HH9e4)tm3183NE>f0u1$m$q;bjZ\6IRD;34)A(r^=;)FOeta[G[5WDeg2,_q1Tq1:/jAF]=,+QGCNSn57pa)RGAlatT_brh?iS_mK=pGGD-eV(?nhf">!#DcarZb#Z*Z?%=].\!,e7k3@mN8XV1npnOK4;,uMTfX4\t?U*E(?%WQ(D?K)j'HEoBZ8JR,`HRY#VIeL?4glY]Q2M3DYK>Yk_h$(ZGFDm;sN$Us1$tN=g+J&eK!lgefEYEO@I9P%CF7PH[E94pJj!P[k:fI4p\Tku5k6?ag-fqmK")ed"VR$VAMgiC"aL#rn*2ofi0OKMf\1G*#,FSk2gr<*""TlfNe93BF;nJ,dO'p;d/,W>Gjt=op]c4d)<;(g"^aC:OFYej&U)h8YIm%h=B_nEu>b_=p];f"ffJ3p[8uWQWs\;-LCBU1*CVBs^8G$c3^e_6[4!d5$jPR)Zmu&P@Yp0ah-:nh6(_0r/]SP]$V!+pS6\X"]$XP%k:!DE@WPZ+1B<4o,3LS+XWUl"i-K'NU`ekVUVKoeF6Q/792d2!1/>%?sO7?'[6\N$!"H,H1e*W0mo2:+M,$]HtAO)h40_Wk;IfK;h(Oh0BIFBI>KITRb]gEt;81\sAMlJ3F96)rq=s9G!%Y[eO2l3'NhUJWuaA*19MnHBkJ0W7mda&`<:kDFDJ:KmF=n/X?YHY +endstream +endobj +197 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 196 0 R +>> +endobj +198 0 obj +<< /Length 536 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gas1\a\K`-&A@fgbcR/GfQ$77#U-I+"/8dqKh\KNP>7Q;/W#u3_;F9(Xdq?m?T^,nn'1!aE,^9p+mNNg_K\>mC-WBYLEb'l5*,K+14d:OH6;ER,BUp3d=L9BjA?j+YDlp!)u(tB&R(jsTqQQHapt!3"QnLqdnu$"_(o"%PmUtfgo%FoiJ#k"jf07&c12osZQj]PI@gpXkn!g]\t=dXQ(J2+e!H>ApOr5ZE!%0]2/<=tH[\J<4dQ@gZ:/7<)X71N05gVH:DIq\3DX`-cZ[>L%N4e^-5ak/F]f6UXD"pLVrO!`^Zg$bOg*tp1r97;'u49h!W)JUJD[na%]R*n-)#J85+D[<:'OmfQg\;eON6!W0nu17>ZGpTpb\?r/?]qdL"Q].'i4tMAlft*>k-Lomhg.T:l79u!jVME.<;uW[>Zo`?CkV)sU=Bdk_(XB^r2K~> +endstream +endobj +199 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 198 0 R +>> +endobj +200 0 obj +<< /Length 767 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%!?#Q2d'Re<2nD;$J:n-rW9rkjc4A=!VV.cE!qVVneQ3_$@BWJK^59!.0'!eX^MgaU^Fmcf,O4!$'klG=a&4GacJo$#='L)^_94/eL9XJb)Zia*&jHp.=3tR96VVs1'b*l@B/MP@^0j\SWS>aW/gMb]&e9g^h6bfhmeKlcfNg*cLBuSS5eJ+P!+#rDBM"_=Pcbhm$5Z@`c0$eY^l1"7Ykl:ju$4g0'CZPb4E3Qq($JCS'@<=lF$DM,#:"ht`JLGI,[>;ah"XAmMJ4?![n[?EIpoX]&j5eY:LU1n86W`5Oo3a0llBiIU5j/XAZ>u`=s0k]O`_F7?#<7dq=,+T)c'X!nLNq,JiPj&^-1o.ir5+mOr=/Vs($_D/S.6R3g(+F8ngB(OR4KO%RJ=dQ^4[YhFYjX0dSJZ/r=46;HhMBFS)A940rR'89.sIjF>fU/(mBqB%P'H%5&&<7&Fhq>HcnP$HlJclLlpQIqt2s0t2RP`qWqXX7A&gi+E$5%kNZqTmKZ0IkV.$]2L0m7R<@/'bNg/DC0liOP!k7[$%sYHJo~> +endstream +endobj +201 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 200 0 R +/Annots 202 0 R +>> +endobj +202 0 obj +[ +203 0 R +] +endobj +203 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 156.71 676.732 194.21 666.732 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 29 0 R +/H /I +>> +endobj +204 0 obj +<< /Length 1641 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0D95iQE&AJ$Ckh#rn,"g@cLB=XG1u:@]Fd*Z"TEeVo@f`ms8oiAXq]3AoTg[SZ!Xi@+H8pt(B#p&0jpeJrD1ted;nUXG;dKYrdGRM/>DG(?F&c[OMRQHS57pQeXB_3_#(M"=q)a'D`DQUECV!ZONMA$q?r,$u`%q7cH(^fmeb1Ae6a29!HZ=\R,;Eg/MI%7KY#O#U(Gu=kpJo4L.enjbKB+L%>i[1fmMul46m_#Zs!4f]4dl`g!/k#5n/VQH_6?5>4T/2_]iBCTrBs`:SqU6]cJgAWfoKppH5/l/dqqWk*Vc:CkXeSPsKOo*FZ5j=I<=ph4+Q"-Dti8e`FS$]-J&N$"pYoAki8FWb="<95S+a&5Q'=%8t)p#-%E(#Vsm($pCA%]"VsV$jR9^P>HQWo!B(X/K6SIFkHleb]fCK5j*d!M;GMh*@rm;K-%Pi>S-WM6P5A"aDK&qg?=ka:`Po5pksf>n^C,Rfqi@H)CO8oimdb'H<=^Vg#[Doa@Tb"&1JM(29mRq#"OX^#+!4[GI?B[O)?,]AE@3p$(S*5rh(TXfR+Wp7BGs.OXP33J?TZYbW>/+>_=)rP\s*aFm`B)5I8CW3@d97o\5].K1_H[*$Z;:dANBO4nBJ!m_`!?*>h=\jW\%r$3ctHpS!GgKU/a8/OsYkgc4_eS2j,rf=4_=#9L.?h7Kl*`=BSXYYHh]VJ3q3i]e64]ct]O!c^S:(Jo=;.9;AL^C]-1`M0lRiqlt/!mUZm1"M#X#1iRrHr2t85._-7=-2foM!>@Ddgi=c^Jp\"hUsG%!+p?V_c`Xekj?)r70p]@lB`9qNJ1$a"b\ifn>]:=DgSN?FPnd\ESBGdX)U!KF$qUYblD7(<-mC%9*%h-f%HkJV)^=@)4^p<$i.mRCKbJKtQjIedU`_fC!0a1uLIk3@R6>QV'D=1]Wrg_tm!#A/X&NipguVhpI4QqILnZda*cVtf!P*86_&2'$Fq*4UB\ZQ]dUE#:@ta$h>3i>ORX"-+79fJ`J^?6Y\&X^_6Sm_9n5FRuh:%Y!peoj;)F[U&~> +endstream +endobj +205 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 204 0 R +/Annots 206 0 R +>> +endobj +206 0 obj +[ +207 0 R +208 0 R +209 0 R +] +endobj +207 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 183.66 691.866 229.22 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +208 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 292.52 669.866 338.08 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +209 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 190.6 397.734 236.16 387.734 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +210 0 obj +<< /Length 2148 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=9lo&K%)(h*GkTY4UoM.Gmk)f)[_(ct`c9a#gB/\J2CF[OP*-jcqh=CX"D#r#ZTG^"'EsX1%hA[3@=%YHAiL'*cQ>hnBDu:f((>Tc$O4K[`eOKF$_h_s/I11j"@MYFi*G@>nD_\UcPe+p%?,t^G2uP%>3&qn>/7>NK:de\hdASb"TH\mB6i99:.L0(GF#-7ToC5?ZdsdI^b;$HY$dOMN7?;R0%hI.:MdQm7+Ua<5C^LaA@l(^]]<(P-hJ1UAL7dqG0L_)7F(D#S=H>Z;7kK>L0=[`YUDcSfDoI>P%X_GM9L/[7IdC;Aj'MT9RN@*n(n:UJ:OV>`sU`!_pu_-o6_j+Xi*S*@n0i^og1-*c-=Vk!Vgu_1GO4\dg!&LX]r+)":^?e\h!5pQdG,2h.5/*p3o2,RqB1X-qh<'ih'fq;o5k4'g<0_&\e!5#X.cqP;rY9UDl0E`Z@EaCP!@'Voa6E\#8b(,5#<+WN7Q26&u,k53U!i04J8uu,a"md+qj3j2$a.Ane)uY;^VEH+`okXMeeW1jb!*L4eX9Cf2&6feLF9AfH&/ls$nNWA,hOfOi6=3l!hmi%R%O[0&eg];ZJt(klr3Ya0s3(d,rer2,-tTD*.f+KQa*+K&m=r1+Q(&FEEP!`Up35Ul]5!@g,#?6sAA_pZ!WH0']1cuG=Y9j(A2nFU'!Y8D2^VQanGqHqiQ1_WU\G^XRcJH#Y(70c9iL$p=:0MViT2';24M,W:N;0]fNDD&g`8:U'B23gkpV]No$R=B)X4AO.[q`Y^e"d'TE[mkg[*c>Lejkc'-0.Bd3Ug>e`pPKY/7@G3p`N>BJ(\K0t6_uIH]8R7l[$`S-Bi)(@g-)T!QMWMq,LXI=W.W#oN0_0g:#JI^57R;1&naeu/l`3gFlc\PRqSq[!GSHQEQQ/b[@UlIW,b$da-CN5$ptKWTCB>1NLh=S9Du87MVO(h)AoQWIROb#lB;Mi/RMDV?j+AW]#ZV:$H(@$&j$%4YA_f7o_so.9uj$,-h!+_Zmg_r0b*j)'HNI9aWV`RLP6.FhA*3sF#50:5SLE#ZKmdYiX''$+5]Q3SMh)a4KZjACo@hBDWBf+No9M?.4`??JSm_@?`gmV._Q\d,L]c\A=[nAR-(!04aY'S>N:(1LqEg$"Cp7++f/1h?YO:>A?P=$V5XH9:O!_(and;"\W="'YFV--56(HRa1g,\]`Y$\J\JZ!"<(6HO2fk?/p+[k9K0G#B]X][unX-8uFQ]iCTZ@L_\A$W+?SaSIDR`@qFm"4DAkPZt"hN*DVPs40R2qZnEZPca3O?a.N)M$>_R_hdjQLtSl/s\VPAm+?GQ&#paW/o_0@!)7SE_q)3aH^tq1&e>+Gdctk!^JM!!$#f*gJulq6GP.d46kW4c'0dLmT:p<0Bra4]iu!,&IFFCU`rlp53tc.%UPVsG1jbZR'\M6R0@=)!r*(BA$.Y1pn#*UrL!UP=c^44[(/X8'UdTYU?$Z=NSp*IWNjb##:V_3PDJp?FnT<8e>RfP;^LLC@6 +endstream +endobj +211 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 210 0 R +/Annots 212 0 R +>> +endobj +212 0 obj +[ +213 0 R +214 0 R +] +endobj +213 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 460.8 563.894 506.36 553.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +214 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 180.59 467.922 226.15 457.922 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 121 0 R +/H /I +>> +endobj +215 0 obj +<< /Length 423 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gas2F;/:e<&BE],'_g7sQ6fC,$^fT72X!N?)O(P5AVSIfIUeQj"#D"B\&?A@mB/5%$YC`us>A95$q"-m1UeB#5g]_L5+W9h_/E%Sa&dh:/IQhu1B'H/*&q:#5(99eWtSfL#:@8MMoF-4Q3h;1sl7*@S7onpU/FPl)>/cjs5?"cIAqM[>08,c3+-!+laC-R%S>nlmc:eT/rF-F_T9s$!,n/EQ8I8;?X]W;k1s.mTtre1Z.(ZZPG>J%ll^_S=(UsNhr0fW^T;GPOR+b!]$qadW/ +endstream +endobj +216 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 215 0 R +/Annots 217 0 R +>> +endobj +217 0 obj +[ +218 0 R +] +endobj +218 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 302.82 691.866 348.38 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +219 0 obj +<< /Length 825 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`QbAQ&g&A7ljGdN$jiEK9"Y+$Occm!F^k)eXhbB,:#OUR(9R_LLEgEN%62:<6gFO;_KZbtb'paI/Q>pKD+Ld8Bi+n.2&!8.U0K\0O];M_:Pfd"!IitP8(PTs]T`adlgk4so7HOTF4n91Bm8/Mt&04V\Aa+2DeGKL81&n2Q/u)K3oNo,_PmO.GlW8*pE>HAh_$PDKO];b(]4/p<'hRSY0+3H/_,#K55W\094h**FBU1AXuR-`%oDU.A`,b#=V[]&Zbqo/32IqUJD#Tn5@i>G-,]DEkWpOX??aV)3BmR"T]pCi<@l/2'L*V5C+QuXEC;=>_f;ehDMk@]462_3M^SnaZ'2 +endstream +endobj +220 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 219 0 R +/Annots 221 0 R +>> +endobj +221 0 obj +[ +222 0 R +223 0 R +224 0 R +] +endobj +222 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 250.04 691.866 295.6 681.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 119 0 R +/H /I +>> +endobj +223 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 365.88 526.503 415.88 516.503 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +224 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 365.88 410.612 415.88 400.612 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +225 0 obj +<< /Length 465 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GarVKbAP0N&A7TL5(_:]G+RdI"um_KJ5[6[`ll5&>gCe1>n0K<(6`MHW2!\r6g@C/7Z^8eg%d-+uOSf11m#027Y2mWt#+J&r;o4o`_Y#M&[(4]&[l?!'?-YDN\Qr[H.kQQ?+j8Z46'o^csO\I&/1r/WJ!0ZK"IU-*<"p:CqS3VuW82K>Hib98(p$FiEOZPduOQ#\Oe">fu?SS.dEr~> +endstream +endobj +226 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 225 0 R +>> +endobj +227 0 obj +<< /Length 860 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gas1^92FS%&AIm?jL@aB:&:[+;IE@+G#=)3XUp8%15S_2]CH'f^L*mmob`ro0(F>\r+RJ/U1o'G<%_pc-g1cukUfnZJWEGogI*UI'3WcYA(+CROn'hYR<`@?[YXZ#-KR;rbC[1gJ+2l/1Fq3Y.6AWjVIKWi]D`DiL.FlcjAL%4f.Es]V5`OA9E3_K4-EPYi#_aPq`P>fU1tC#VB-5pJj+.2aocSbu%FprWb9NK-*]*;Yt+$l#>Ht9>aO.^(F1Y2]sQQ1Y-oBPGO^(cknW6HE+i-!=QV.5KI3X\Za"n>5SH/hGN'P-JjCnf(IG]qfsUi[[biiCo0bU?eWNSk;:=RPI[*^b%X@>\m%gLp(O9ub=fP^&Ih!CMksYFU:l1O+^E_^1/44:&:IQ8[LuC<6O%4sLQIjSneF3-$rflU!GrP-f&@f+*_WW)V1\]:h?Ue(a:RRp,7+gRIGOJN\9GGohR9A.q^b%F,PgiA5A]GBGCEPE +endstream +endobj +228 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 227 0 R +>> +endobj +229 0 obj +<< /Length 2112 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%%9llga&A?DnTQ7?=@Kcel@rM?A*%0ij-:r'n3KP-3VKN/%^a,NMp"LBScjbfYQ7k^L^\7`mF\?d6T=qdm*$-fcR6Su%k@qpV(]LoXGq^#E$0-Kubh(.L,L8Gp:*SZ6psI-V>c2`<9>ZeB%,K)s=iu+8X(1>)1VWs$iGc7l-E(>SUDig[rRJI!H1Du;\/QhB7qZDEb[pU]"96q*qP(!`lS^>mI18Xj6aY)EEU3^kI:%sp?1dYY&hc6V\Q-bYf-9VCLX(BCWCKV',-=QJ6k>7"8B](TmBSHL4*WX'=H(K`8I3R71jX-B0<3a@mRU@!7OE^arVIt-(F.\8"_GW>Y9j0!U@e1J0?lK0/ppCe)KZ+_M8LIu\\+5kjl'[EeU'ATQY>Adk42:)6G_#4teF,"h]mIGJD[R*.,eUQ6l&\-*jl.0:,tZ%$=\V$grE_W%4N@'d\lbg)&uk[7Qd`.a=Y67WM?>JN^f?j%S"].HI.KHPlX$R)a7k.p]=;8)s#2^:A8Y8qr<04QLSse:VtF:W?rPW\%.pFRcKBBUarZ20oN,-0uaqm!-^da[,2CJo--:VtS\k%$\aACGg17s;JCT8PJJT1i2u)Rq!*CI_''!rs7C!+P(Z(1MD1;L7NCQ5B\+=jIBq%H^p!"uKDXK]K,$`uH%M7kEt\l]Xsj(=QAX3E,E:N-b%X!D!P6nIg;Mes,]KuN[L'hcE,$'l_7M-!n;QW^#OK%54U-4.?bi;,"VNDRS#a*[6+ZMGZm&6J-H,;>U_mQP.*P;9m6gcl>#^>LWQ1]$*dWW(2;h`C%loY'(lPLHD<*>DH_:4I6AWH`E4b?6\ZVV2U^-9A]"g?=8hcW>#;RV8gD4#hA^Z]CZE/o3l:>qr`;b\c)[jlWGe~> +endstream +endobj +230 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 229 0 R +/Annots 231 0 R +>> +endobj +231 0 obj +[ +232 0 R +233 0 R +234 0 R +235 0 R +236 0 R +237 0 R +238 0 R +239 0 R +] +endobj +232 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 175.77 696.866 419.82 686.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2119.txt) +/S /URI >> +/H /I +>> +endobj +233 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 389.35 669.866 528.98 659.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +234 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.0 658.866 224.65 648.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518.txt) +/S /URI >> +/H /I +>> +endobj +235 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 492.97 642.866 539.54 632.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2616.txt) +/S /URI >> +/H /I +>> +endobj +236 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.0 631.866 250.2 621.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2616.txt) +/S /URI >> +/H /I +>> +endobj +237 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 386.87 615.866 537.6 605.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3253.txt) +/S /URI >> +/H /I +>> +endobj +238 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.0 604.866 308.53 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3253.txt) +/S /URI >> +/H /I +>> +endobj +239 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 314.64 588.866 528.67 578.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc3986.txt) +/S /URI >> +/H /I +>> +endobj +240 0 obj +<< /Length 1787 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%$>Bcf4&:X@Tn@$Hl/4.h@js`\qaXXke2Og8]"(Fcg)qp6i9pn+a61"3eMYo0Eg!g17p-'8cITV>7Sbj`j97S!\!*bCS$Xk5Qr[cMVYYjZmF(]mkAT-0L5tSg[uQeL1OV1[I(ueT`Xr)d?(q<=s:jSD3q9GNVu=adT8!eegY#q>POVUkI'H1uQJ*Don6-XA]8G!6-:5JO/%RhPVf6dA)$UkB@u,!K@r"W,DBq(+eu`/5.4'^R/]+m-FI:'$@aS)7isXiY)*sQoNsk=1R;sgE*dqj8Ed.A_-t[9aGr[AR@^5kY`_HhTRq"]HJW:O[IQ4`r3.C!6QuT.`cOu#4odM^?j,TeRQkO#V.`;+U:j8,HJ^1^nl#@"#JNq(j`_>fQWX`gTo+2,;UFU8@6f/]CpSSt!q[b_Otato3!(?k9Ln(r,(M?MPJZ-`_%s%odt(6M"$6"*Qd?r?`KrR+2Q[IQA`b[]B0npEeMSoPb<$:mXfqg;^^Qs=@<1_Cp7P"Pnt7l,Dl=IAeB4=h3;T_K9-XH]S:=n0pd_iGj:Db1F3b+`L'po2V)t'HQ[Tirjb,QI>9/8?E,asJk?)p=hV>Y%iiEo:B5H5OhhGdgD/_p"q_"[bb_9Q'4F2o8P<.':1cPh?RZ2O)qfO=4tZY>IPgGBqo*WmJ$r>c=&F(YJ,L6aog)V,>%nUZ"1em9-:olo"em*8BT*fj\]a_],QQ@/;QnBM,$-k2eU*9Wa`E_(^-*L?$*?Ki5=V2B.`1JFGjJMK:qENJ]+.nB>e/bZ/)8?L>#-_@"tCH$-C3eI_b0^c$A"Z$]^*a,)j#kFpf(OKC(Nb8eBV?V:YG]4F>f0$3^jl[tElE71am`l)cjM=qrl&*c5o\,>)*Fq/ere+@n'tprkB'#*j>1E7QF27`0cBDYnQ:*blm#bn/&qJ:e73cYJNSZ-qnM"Y+hlK'i_p77[MbC0#ku)dRhHrhe(+%]gH@`#.==NsY0)RTR:.!Rfro"fcRJmHKG#`A~> +endstream +endobj +241 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 240 0 R +/Annots 242 0 R +>> +endobj +242 0 obj +[ +243 0 R +244 0 R +245 0 R +246 0 R +247 0 R +248 0 R +] +endobj +243 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 631.866 178.41 621.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ejw@cse.ucsc.edu) +/S /URI >> +/H /I +>> +endobj +244 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 555.866 224.25 545.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:geoffrey.clemm@us.ibm.com) +/S /URI >> +/H /I +>> +endobj +245 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 479.866 172.92 469.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (tel:+492512807760) +/S /URI >> +/H /I +>> +endobj +246 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 468.866 162.92 458.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (tel:+492512807761) +/S /URI >> +/H /I +>> +endobj +247 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 104.78 457.866 225.07 447.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:julian.reschke@greenbytes.de) +/S /URI >> +/H /I +>> +endobj +248 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 94.5 446.866 229.76 436.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://greenbytes.de/tech/webdav/) +/S /URI >> +/H /I +>> +endobj +249 0 obj +<< /Length 1992 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%%?#SIU'Rf_Zcsm-aW]i#6)hoYE1Shg:/2>_2[L]`5gI,UEV.?M"pEONR]U'?J#lt*!juN2*OI$ZbDHDa?GHnJr4_M0*Gg:Kk4.c6=SMV)-P19/gbZX>(03A.\o?CEd3jhc0m8_mlP\hX(%[Z@eHc!q.Ia;]:H-7-*Vn?ta^?m?^a,_oE&kEKcF+$Jt?Zi_,C#0k$H0r6R@SaA@HJip5H1qBipmrX!;D$AMob>qk7lA3_a46`plc=;(lJ&>if1k),Nn'DD_WdBUJlQ$*LX\glZ@YYfl:K7nFdid'i2;RK-5F>/eoCBDO_o;J3uk::]"fo41d-,=I1kSkqpb"(Va5Y9d^O"b4YI,rdfS/%I1n@BX+l*lI^+<"dQO]nJpiA:3OBRJ&5u*j>ud6a(ALhT2WnlcAKk,9#0\U_r#h84&HdJ66g,>J5_!q_%t"8fA,4D*ga1-X"Zq".$934r!CF=Okf+`2kDZ%5g+o&V.+4B^9DTTDbb6sL72V]V[,JC2h*uHV2BFP,%^_'S!kUf,C@R,e>8f!j_.[qsWO]ui-C_X3'QuOOd@9mi28_;@"S;JMW)U@F-JIsY9@JqtB@[s:K`I<$)gp0Q[6/46TG^#7Kb;Ln1Q31o_SIL<8c\C'K8MdiYhNe4XaUk`8b.!NP=p(\tmh]%hXqj5;B4IIP^R[fX-,LMX:NH#=n&0\AL.VE5Z"B>q%>^3k`Eo#OluiFj58+9I#4Jl.^h#1\cbm]TU8Z'M>H]!k%Ku8hmB('?lSr!J3WhJQXs!Tqs7&'WD;;MA+9sC&fN5<%pYd9C8E$2fVK'#:tH(rZ<%_"W48).Vkf.O7jT>#^rd.a3\`_\<-G&-kR@Jt!YcUb\uMbc9ZUF/38qUe';m)PPMU'bj38:FUgekI#_I%'/QG)feCl8"?r_3p),^ooUkmh`*MM\np.uls>GYe0'doIq'a"S-k`&MSW0.#!teTKm7%PA7>%3--u-"!B$:2bBo`<'>=GUL:W61P1@2$m+frj`471PuU?*_'?9`bn7uOg32tEe.>U:6q;'RX0/\N"-80,&7"0])@Dtcc-,o2q$+k+E:!cKE="8`OesQ\#Jp,a;gfX<.^W':STN(5.^B2"jTt+*.\J$M26&A.3#kOur0lf#>uKAqR,N:-hq[cO,o/.4Sp3Ob[D'bt=7pW+MsY'ua1n05@Tgeep?kc90-9i`%Ot&!/7b.M^^aA)LD%lUOUO`\^aZdBR5E8L`L^)2mC8N28'pPccnq[VHm>@-dC5OI=8+XL2d`c5bHdA- +endstream +endobj +250 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 249 0 R +/Annots 251 0 R +>> +endobj +251 0 obj +[ +252 0 R +253 0 R +254 0 R +] +endobj +252 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 488.89 604.866 535.374 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.ietf.org/ipr) +/S /URI >> +/H /I +>> +endobj +253 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 593.866 144.783 583.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://www.ietf.org/ipr) +/S /URI >> +/H /I +>> +endobj +254 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 247.26 550.866 313.4 540.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (mailto:ietf-ipr@ietf.org) +/S /URI >> +/H /I +>> +endobj +255 0 obj +<< /Length 2383 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>=`4sF&:WeDpl.\Y]m]YI^9J4P]*grf=ce/h_4Dnf^1:"Bq=e)VQb16nRI5(%0-o:PhcE>"d?D=Wci/0!-;5%5RGC!#PM9_d6K(-e0cYXTW$P.$a_X-9HLYmZaF7Jk^/&?j(K.[U6392V,Y<50n?P'Ug5"W(*-NO73kO.K*i?:2oK$2-pXqZaJ)":(VHctXH#]1IMemkMm^,ic.'n4Tg##OC!+mO>=s+7!.Kq2CC*a-eU-/-aLpe"jRT5-PS&I-iDDt(g+4>:TW-$O@f)6[B"AuSbl^.YM\]Sg=eT@G>jQZY%bE"-e?0q:<\'#V8'1o:2(rMqKig\4+94V8h7H:.Lc\bXHf&bW6BJ,*17W7HSZc2Qc[n-+,R]]]G?eOEYdlGmR)*ZK!l($5N#[c^_$n,2TTT'b6:eQQt6&b8op&-&+rl?2mb08c?M*Jit'[foON"a2>,SQg'"u?!',#fhc:,'IKKF12kjVhTmrTa()p5m>"Z5f*=N$)#+%J<<[dXARGJ_+=W^Ro/f3hVu0B"GN[Xj2g$Jio5$K*JS;Ot`M>mC)7oH'@>_M!X)L\(g?B1N/n)2^uRf6aok$bL;W2\p_GeJd1>HdZX+?h,77M616oK1IJ7N(otD<.OBr8U'(d/O]e3>kaXtje/0RW=]-;&?-01=iod0tF1mfJ,hpqJ'bjruA]8;T9e3)3,'ZJhB:-I=;akLS1Z,?ZX(Al1B1LAF.Qnc350rD(s>`Kr,00Y9bta8T;[FOZMr&BmHF])!/St`)unBEH8..\A(eqO/Q1[qf4_e0qilh4KO$@0UC^gh(asW]u$tVf9Bh\`mk1;Mi-G/[-O?FUYbD=0:"+_t:.]ADak$o(XfX0YNW,r;]W!\m`UaF6^f0,FjF5;hq-p_/QRV5*iD`2YGCD8YGMQma:"]=0Si8r0QPMTJ`S1D<3uNah%Y/7<4@H8+u.I&Fp)(W9>jP;Zjk*%8D%OYVk&Ckj\jr'-;n`l0tYQhSl/m_Enb$`$a!D7$XgMpl6Y)DB22u1?DMn3PQ5Zbd@>_G'H(+7d&n8o<^h,`=[%C?k'()C"dE$t(D]&O0^L?[AFX?N^@$%mT/:Zs7>#6G6H_1EW]Jd5>sdDJ"h2'&BEZsCKVDS(2&L7=Ru+.h2+5#>)172#--(Q!nn&N.)cQ8^C5;P1DKks@28ag%s9\rX["g!\5MfntOcDP.;>6c_%^l2dNtU[\a+]o$LU*sCG:Usn(`'-pS0r6+f'WD!HCO6\r1`'"O%Nr,%l@m(XUU4_5`8/@E$5LZ5hQ)f9@/S.IEoE69%9`W`AZHTY:+g0R#87=C&8cS-E7dJ^R,4m:V&;LME:-1X)r.@&Uc);-"J+c":].e^9%AMN3Ej"en@/QF4ftSgN67Ts:mgHl+P;K0QZB$+Q5HeW?rZWOp@lV37PY(?`E%Xr994P_=s%GJUHDWr)?:!SV:nP7JYlM8`lfa3"T%`*(3#n&pJ`+B%Vq?LWWm&^aTQ$S,%`?j:fghN6X+890""+Bk+4@Z\?_9*Ps].69a$c<10`3c@$oWq6CqYmj/8(rb",N/MTH"0;]jeA(5W4h<<]39P))mg,Tc/b"7*10mUIml_/pAC5(E@=;$gmBBBV8-&o0"[lSl#-D,`h=i?ngS3="@VT +endstream +endobj +256 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 255 0 R +/Annots 257 0 R +>> +endobj +257 0 obj +[ +] +endobj +260 0 obj +<< + /Title (\376\377\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\157\0\146\0\40\0\164\0\150\0\151\0\163\0\40\0\115\0\145\0\155\0\157) + /Parent 258 0 R + /Next 262 0 R + /A 259 0 R +>> endobj +262 0 obj +<< + /Title (\376\377\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\116\0\157\0\164\0\151\0\143\0\145) + /Parent 258 0 R + /Prev 260 0 R + /Next 264 0 R + /A 261 0 R +>> endobj +264 0 obj +<< + /Title (\376\377\0\101\0\142\0\163\0\164\0\162\0\141\0\143\0\164) + /Parent 258 0 R + /Prev 262 0 R + /Next 266 0 R + /A 263 0 R +>> endobj +266 0 obj +<< + /Title (\376\377\0\124\0\141\0\142\0\154\0\145\0\40\0\157\0\146\0\40\0\103\0\157\0\156\0\164\0\145\0\156\0\164\0\163) + /Parent 258 0 R + /Prev 264 0 R + /Next 267 0 R + /A 265 0 R +>> endobj +267 0 obj +<< + /Title (\376\377\0\61\0\56\0\40\0\111\0\156\0\164\0\162\0\157\0\144\0\165\0\143\0\164\0\151\0\157\0\156) + /Parent 258 0 R + /Prev 266 0 R + /Next 268 0 R + /A 11 0 R +>> endobj +268 0 obj +<< + /Title (\376\377\0\62\0\56\0\40\0\116\0\157\0\164\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\103\0\157\0\156\0\166\0\145\0\156\0\164\0\151\0\157\0\156\0\163) + /Parent 258 0 R + /Prev 267 0 R + /Next 270 0 R + /A 13 0 R +>> endobj +270 0 obj +<< + /Title (\376\377\0\63\0\56\0\40\0\124\0\145\0\162\0\155\0\151\0\156\0\157\0\154\0\157\0\147\0\171) + /Parent 258 0 R + /Prev 268 0 R + /Next 272 0 R + /A 269 0 R +>> endobj +272 0 obj +<< + /Title (\376\377\0\64\0\56\0\40\0\117\0\166\0\145\0\162\0\166\0\151\0\145\0\167\0\40\0\157\0\146\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 258 0 R + /Prev 270 0 R + /Next 274 0 R + /A 271 0 R +>> endobj +274 0 obj +<< + /Title (\376\377\0\65\0\56\0\40\0\117\0\160\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163\0\40\0\157\0\156\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 258 0 R + /Prev 272 0 R + /Next 276 0 R + /A 273 0 R +>> endobj +276 0 obj +<< + /Title (\376\377\0\66\0\56\0\40\0\115\0\113\0\122\0\105\0\104\0\111\0\122\0\105\0\103\0\124\0\122\0\105\0\106\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 258 0 R + /First 277 0 R + /Last 277 0 R + /Prev 274 0 R + /Next 279 0 R + /Count -1 + /A 275 0 R +>> endobj +277 0 obj +<< + /Title (\376\377\0\66\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\103\0\162\0\145\0\141\0\164\0\151\0\156\0\147\0\40\0\141\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\167\0\151\0\164\0\150\0\40\0\115\0\113\0\122\0\105\0\104\0\111\0\122\0\105\0\103\0\124\0\122\0\105\0\106) + /Parent 276 0 R + /A 23 0 R +>> endobj +279 0 obj +<< + /Title (\376\377\0\67\0\56\0\40\0\125\0\120\0\104\0\101\0\124\0\105\0\122\0\105\0\104\0\111\0\122\0\105\0\103\0\124\0\122\0\105\0\106\0\40\0\115\0\145\0\164\0\150\0\157\0\144) + /Parent 258 0 R + /First 280 0 R + /Last 280 0 R + /Prev 276 0 R + /Next 282 0 R + /Count -1 + /A 278 0 R +>> endobj +280 0 obj +<< + /Title (\376\377\0\67\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\125\0\160\0\144\0\141\0\164\0\151\0\156\0\147\0\40\0\141\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\167\0\151\0\164\0\150\0\40\0\125\0\120\0\104\0\101\0\124\0\105\0\122\0\105\0\104\0\111\0\122\0\105\0\103\0\124\0\122\0\105\0\106) + /Parent 279 0 R + /A 27 0 R +>> endobj +282 0 obj +<< + /Title (\376\377\0\70\0\56\0\40\0\117\0\160\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163\0\40\0\157\0\156\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163\0\40\0\124\0\150\0\141\0\164\0\40\0\103\0\157\0\156\0\164\0\141\0\151\0\156\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 258 0 R + /First 283 0 R + /Last 284 0 R + /Prev 279 0 R + /Next 286 0 R + /Count -2 + /A 281 0 R +>> endobj +283 0 obj +<< + /Title (\376\377\0\70\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\120\0\122\0\117\0\120\0\106\0\111\0\116\0\104\0\40\0\157\0\156\0\40\0\141\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\167\0\151\0\164\0\150\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 282 0 R + /Next 284 0 R + /A 31 0 R +>> endobj +284 0 obj +<< + /Title (\376\377\0\70\0\56\0\62\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\120\0\122\0\117\0\120\0\106\0\111\0\116\0\104\0\40\0\167\0\151\0\164\0\150\0\40\0\101\0\160\0\160\0\154\0\171\0\55\0\124\0\157\0\55\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\55\0\122\0\145\0\146\0\40\0\157\0\156\0\40\0\141\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\167\0\151\0\164\0\150\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 282 0 R + /Prev 283 0 R + /A 33 0 R +>> endobj +286 0 obj +<< + /Title (\376\377\0\71\0\56\0\40\0\117\0\160\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163\0\40\0\157\0\156\0\40\0\124\0\141\0\162\0\147\0\145\0\164\0\163\0\40\0\157\0\146\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 258 0 R + /Prev 282 0 R + /Next 287 0 R + /A 285 0 R +>> endobj +287 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\40\0\122\0\145\0\154\0\141\0\164\0\151\0\166\0\145\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163\0\40\0\151\0\156\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\146\0\164\0\141\0\162\0\147\0\145\0\164) + /Parent 258 0 R + /First 288 0 R + /Last 288 0 R + /Prev 286 0 R + /Next 290 0 R + /Count -1 + /A 37 0 R +>> endobj +288 0 obj +<< + /Title (\376\377\0\61\0\60\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\122\0\145\0\163\0\157\0\154\0\166\0\151\0\156\0\147\0\40\0\141\0\40\0\122\0\145\0\154\0\141\0\164\0\151\0\166\0\145\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\151\0\156\0\40\0\141\0\40\0\115\0\165\0\154\0\164\0\151\0\55\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145) + /Parent 287 0 R + /A 39 0 R +>> endobj +290 0 obj +<< + /Title (\376\377\0\61\0\61\0\56\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163\0\40\0\164\0\157\0\40\0\103\0\157\0\154\0\154\0\145\0\143\0\164\0\151\0\157\0\156\0\163) + /Parent 258 0 R + /Prev 287 0 R + /Next 292 0 R + /A 289 0 R +>> endobj +292 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\40\0\110\0\145\0\141\0\144\0\145\0\162\0\163) + /Parent 258 0 R + /First 294 0 R + /Last 296 0 R + /Prev 290 0 R + /Next 298 0 R + /Count -2 + /A 291 0 R +>> endobj +294 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\61\0\56\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\55\0\122\0\145\0\146\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 292 0 R + /Next 296 0 R + /A 293 0 R +>> endobj +296 0 obj +<< + /Title (\376\377\0\61\0\62\0\56\0\62\0\56\0\40\0\101\0\160\0\160\0\154\0\171\0\55\0\124\0\157\0\55\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\55\0\122\0\145\0\146\0\40\0\122\0\145\0\161\0\165\0\145\0\163\0\164\0\40\0\110\0\145\0\141\0\144\0\145\0\162) + /Parent 292 0 R + /Prev 294 0 R + /A 295 0 R +>> endobj +298 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163) + /Parent 258 0 R + /First 300 0 R + /Last 302 0 R + /Prev 292 0 R + /Next 304 0 R + /Count -2 + /A 297 0 R +>> endobj +300 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\61\0\56\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\55\0\154\0\151\0\146\0\145\0\164\0\151\0\155\0\145\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 298 0 R + /Next 302 0 R + /A 299 0 R +>> endobj +302 0 obj +<< + /Title (\376\377\0\61\0\63\0\56\0\62\0\56\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\146\0\164\0\141\0\162\0\147\0\145\0\164\0\40\0\50\0\160\0\162\0\157\0\164\0\145\0\143\0\164\0\145\0\144\0\51) + /Parent 298 0 R + /Prev 300 0 R + /A 301 0 R +>> endobj +304 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 258 0 R + /First 306 0 R + /Last 306 0 R + /Prev 298 0 R + /Next 308 0 R + /Count -1 + /A 303 0 R +>> endobj +306 0 obj +<< + /Title (\376\377\0\61\0\64\0\56\0\61\0\56\0\40\0\162\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\162\0\145\0\146\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164) + /Parent 304 0 R + /A 305 0 R +>> endobj +308 0 obj +<< + /Title (\376\377\0\61\0\65\0\56\0\40\0\105\0\170\0\164\0\145\0\156\0\163\0\151\0\157\0\156\0\163\0\40\0\164\0\157\0\40\0\164\0\150\0\145\0\40\0\104\0\101\0\126\0\72\0\162\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\40\0\130\0\115\0\114\0\40\0\105\0\154\0\145\0\155\0\145\0\156\0\164\0\40\0\146\0\157\0\162\0\40\0\115\0\165\0\154\0\164\0\151\0\55\0\123\0\164\0\141\0\164\0\165\0\163\0\40\0\122\0\145\0\163\0\160\0\157\0\156\0\163\0\145\0\163) + /Parent 258 0 R + /Prev 304 0 R + /Next 310 0 R + /A 307 0 R +>> endobj +310 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\40\0\103\0\141\0\160\0\141\0\142\0\151\0\154\0\151\0\164\0\171\0\40\0\104\0\151\0\163\0\143\0\157\0\166\0\145\0\162\0\171) + /Parent 258 0 R + /First 311 0 R + /Last 311 0 R + /Prev 308 0 R + /Next 313 0 R + /Count -1 + /A 309 0 R +>> endobj +311 0 obj +<< + /Title (\376\377\0\61\0\66\0\56\0\61\0\56\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\72\0\40\0\104\0\151\0\163\0\143\0\157\0\166\0\145\0\162\0\171\0\40\0\157\0\146\0\40\0\123\0\165\0\160\0\160\0\157\0\162\0\164\0\40\0\146\0\157\0\162\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163) + /Parent 310 0 R + /A 63 0 R +>> endobj +313 0 obj +<< + /Title (\376\377\0\61\0\67\0\56\0\40\0\123\0\145\0\143\0\165\0\162\0\151\0\164\0\171\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 258 0 R + /First 314 0 R + /Last 317 0 R + /Prev 310 0 R + /Next 318 0 R + /Count -4 + /A 312 0 R +>> endobj +314 0 obj +<< + /Title (\376\377\0\61\0\67\0\56\0\61\0\56\0\40\0\120\0\162\0\151\0\166\0\141\0\143\0\171\0\40\0\103\0\157\0\156\0\143\0\145\0\162\0\156\0\163) + /Parent 313 0 R + /Next 315 0 R + /A 67 0 R +>> endobj +315 0 obj +<< + /Title (\376\377\0\61\0\67\0\56\0\62\0\56\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\114\0\157\0\157\0\160\0\163) + /Parent 313 0 R + /Prev 314 0 R + /Next 316 0 R + /A 69 0 R +>> endobj +316 0 obj +<< + /Title (\376\377\0\61\0\67\0\56\0\63\0\56\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\40\0\122\0\145\0\163\0\157\0\165\0\162\0\143\0\145\0\163\0\40\0\141\0\156\0\144\0\40\0\104\0\145\0\156\0\151\0\141\0\154\0\40\0\157\0\146\0\40\0\123\0\145\0\162\0\166\0\151\0\143\0\145) + /Parent 313 0 R + /Prev 315 0 R + /Next 317 0 R + /A 71 0 R +>> endobj +317 0 obj +<< + /Title (\376\377\0\61\0\67\0\56\0\64\0\56\0\40\0\122\0\145\0\166\0\145\0\141\0\154\0\151\0\156\0\147\0\40\0\120\0\162\0\151\0\166\0\141\0\164\0\145\0\40\0\114\0\157\0\143\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 313 0 R + /Prev 316 0 R + /A 73 0 R +>> endobj +318 0 obj +<< + /Title (\376\377\0\61\0\70\0\56\0\40\0\111\0\156\0\164\0\145\0\162\0\156\0\141\0\164\0\151\0\157\0\156\0\141\0\154\0\151\0\172\0\141\0\164\0\151\0\157\0\156\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 258 0 R + /Prev 313 0 R + /Next 320 0 R + /A 75 0 R +>> endobj +320 0 obj +<< + /Title (\376\377\0\61\0\71\0\56\0\40\0\111\0\101\0\116\0\101\0\40\0\103\0\157\0\156\0\163\0\151\0\144\0\145\0\162\0\141\0\164\0\151\0\157\0\156\0\163) + /Parent 258 0 R + /First 321 0 R + /Last 321 0 R + /Prev 318 0 R + /Next 324 0 R + /Count -3 + /A 319 0 R +>> endobj +321 0 obj +<< + /Title (\376\377\0\61\0\71\0\56\0\61\0\56\0\40\0\110\0\124\0\124\0\120\0\40\0\150\0\145\0\141\0\144\0\145\0\162\0\163) + /Parent 320 0 R + /First 322 0 R + /Last 323 0 R + /Count -2 + /A 82 0 R +>> endobj +322 0 obj +<< + /Title (\376\377\0\61\0\71\0\56\0\61\0\56\0\61\0\56\0\40\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\55\0\122\0\145\0\146) + /Parent 321 0 R + /Next 323 0 R + /A 84 0 R +>> endobj +323 0 obj +<< + /Title (\376\377\0\61\0\71\0\56\0\61\0\56\0\62\0\56\0\40\0\101\0\160\0\160\0\154\0\171\0\55\0\124\0\157\0\55\0\122\0\145\0\144\0\151\0\162\0\145\0\143\0\164\0\55\0\122\0\145\0\146) + /Parent 321 0 R + /Prev 322 0 R + /A 86 0 R +>> endobj +324 0 obj +<< + /Title (\376\377\0\62\0\60\0\56\0\40\0\103\0\157\0\156\0\164\0\162\0\151\0\142\0\165\0\164\0\157\0\162\0\163) + /Parent 258 0 R + /Prev 320 0 R + /Next 325 0 R + /A 88 0 R +>> endobj +325 0 obj +<< + /Title (\376\377\0\62\0\61\0\56\0\40\0\101\0\143\0\153\0\156\0\157\0\167\0\154\0\145\0\144\0\147\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 258 0 R + /Prev 324 0 R + /Next 326 0 R + /A 90 0 R +>> endobj +326 0 obj +<< + /Title (\376\377\0\62\0\62\0\56\0\40\0\116\0\157\0\162\0\155\0\141\0\164\0\151\0\166\0\145\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145\0\163) + /Parent 258 0 R + /Prev 325 0 R + /Next 327 0 R + /A 92 0 R +>> endobj +327 0 obj +<< + /Title (\376\377\0\101\0\165\0\164\0\150\0\157\0\162\0\163\0\47\0\40\0\101\0\144\0\144\0\162\0\145\0\163\0\163\0\145\0\163) + /Parent 258 0 R + /Prev 326 0 R + /Next 328 0 R + /A 94 0 R +>> endobj +328 0 obj +<< + /Title (\376\377\0\111\0\156\0\164\0\145\0\154\0\154\0\145\0\143\0\164\0\165\0\141\0\154\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\171\0\40\0\141\0\156\0\144\0\40\0\103\0\157\0\160\0\171\0\162\0\151\0\147\0\150\0\164\0\40\0\123\0\164\0\141\0\164\0\145\0\155\0\145\0\156\0\164\0\163) + /Parent 258 0 R + /Prev 327 0 R + /Next 329 0 R + /A 96 0 R +>> endobj +329 0 obj +<< + /Title (\376\377\0\111\0\156\0\144\0\145\0\170) + /Parent 258 0 R + /Prev 328 0 R + /A 98 0 R +>> endobj +330 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F5 +/BaseFont /Times-Roman +/Encoding /WinAnsiEncoding >> +endobj +331 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F6 +/BaseFont /Times-Italic +/Encoding /WinAnsiEncoding >> +endobj +332 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F9 +/BaseFont /Courier +/Encoding /WinAnsiEncoding >> +endobj +333 0 obj +<< /Type /Font +/Subtype /Type1 +/Name /F7 +/BaseFont /Times-Bold +/Encoding /WinAnsiEncoding >> +endobj +1 0 obj +<< /Type /Pages +/Count 34 +/Kids [6 0 R 8 0 R 77 0 R 100 0 R 116 0 R 126 0 R 135 0 R 144 0 R 149 0 R 155 0 R 157 0 R 164 0 R 166 0 R 176 0 R 178 0 R 180 0 R 182 0 R 184 0 R 186 0 R 188 0 R 192 0 R 197 0 R 199 0 R 201 0 R 205 0 R 211 0 R 216 0 R 220 0 R 226 0 R 228 0 R 230 0 R 241 0 R 250 0 R 256 0 R ] >> +endobj +2 0 obj +<< /Type /Catalog +/Pages 1 0 R + /Outlines 258 0 R + /PageMode /UseOutlines + /Names << /Dests << /Names [ (rfc.status) [ 6 0 R /XYZ 67.0 487.084 null ] (rfc.copyrightnotice) [ 6 0 R /XYZ 67.0 407.95 null ] (rfc.abstract) [ 6 0 R /XYZ 67.0 350.816 null ] (rfc.toc) [ 8 0 R /XYZ 67.0 725.0 null ] (rfc.section.1) [ 100 0 R /XYZ 67.0 725.0 null ] (rfc.section.2) [ 116 0 R /XYZ 67.0 725.0 null ] (rfc.section.3) [ 126 0 R /XYZ 67.0 725.0 null ] (terminology) [ 126 0 R /XYZ 67.0 725.0 null ] (rfc.iref.1) [ 126 0 R /XYZ 67.0 654.866 null ] (rfc.iref.2) [ 126 0 R /XYZ 67.0 606.866 null ] (rfc.iref.3) [ 126 0 R /XYZ 67.0 569.866 null ] (rfc.section.4) [ 135 0 R /XYZ 67.0 725.0 null ] (overview) [ 135 0 R /XYZ 67.0 725.0 null ] (rfc.section.5) [ 144 0 R /XYZ 67.0 725.0 null ] (operations.on.redirect.reference.resources) [ 144 0 R /XYZ 67.0 725.0 null ] (rfc.section.6) [ 149 0 R /XYZ 67.0 725.0 null ] (METHOD_MKREDIRECTREF) [ 149 0 R /XYZ 67.0 725.0 null ] (rfc.iref.6) [ 149 0 R /XYZ 67.0 623.866 null ] (rfc.figure.u.1) [ 149 0 R /XYZ 67.0 594.366 null ] (rfc.iref.7) [ 149 0 R /XYZ 67.0 579.506 null ] (rfc.iref.8) [ 149 0 R /XYZ 67.0 569.646 null ] (rfc.iref.9) [ 149 0 R /XYZ 67.0 549.926 null ] (rfc.iref.10) [ 149 0 R /XYZ 67.0 540.066 null ] (rfc.iref.11) [ 149 0 R /XYZ 67.0 530.206 null ] (rfc.iref.12) [ 149 0 R /XYZ 67.0 520.346 null ] (rfc.iref.13) [ 149 0 R /XYZ 67.0 500.626 null ] (rfc.iref.14) [ 149 0 R /XYZ 67.0 490.766 null ] (rfc.iref.15) [ 149 0 R /XYZ 67.0 480.906 null ] (rfc.iref.16) [ 149 0 R /XYZ 67.0 471.046 null ] (rfc.figure.u.2) [ 149 0 R /XYZ 67.0 327.186 null ] (rfc.iref.17) [ 149 0 R /XYZ 67.0 312.326 null ] (rfc.iref.18) [ 149 0 R /XYZ 67.0 302.466 null ] (rfc.iref.19) [ 149 0 R /XYZ 67.0 270.106 null ] (rfc.iref.20) [ 149 0 R /XYZ 67.0 256.606 null ] (rfc.iref.21) [ 149 0 R /XYZ 67.0 256.606 null ] (rfc.iref.22) [ 149 0 R /XYZ 67.0 240.606 null ] (rfc.iref.23) [ 149 0 R /XYZ 67.0 240.606 null ] (rfc.iref.24) [ 149 0 R /XYZ 67.0 213.606 null ] (rfc.iref.25) [ 149 0 R /XYZ 67.0 213.606 null ] (rfc.iref.26) [ 149 0 R /XYZ 67.0 197.606 null ] (rfc.iref.27) [ 149 0 R /XYZ 67.0 197.606 null ] (rfc.iref.28) [ 149 0 R /XYZ 67.0 170.606 null ] (rfc.iref.29) [ 149 0 R /XYZ 67.0 170.606 null ] (rfc.iref.30) [ 149 0 R /XYZ 67.0 132.606 null ] (rfc.iref.31) [ 149 0 R /XYZ 67.0 132.606 null ] (rfc.iref.32) [ 149 0 R /XYZ 67.0 109.106 null ] (rfc.iref.33) [ 149 0 R /XYZ 67.0 95.606 null ] (rfc.iref.34) [ 149 0 R /XYZ 67.0 95.606 null ] (rfc.section.6.1) [ 155 0 R /XYZ 67.0 692.0 null ] (rfc.figure.u.3) [ 155 0 R /XYZ 67.0 668.028 null ] (rfc.figure.u.4) [ 155 0 R /XYZ 67.0 522.568 null ] (rfc.section.7) [ 157 0 R /XYZ 67.0 725.0 null ] (METHOD_UPDATEREDIRECTREF) [ 157 0 R /XYZ 67.0 725.0 null ] (rfc.iref.37) [ 157 0 R /XYZ 67.0 623.866 null ] (rfc.figure.u.5) [ 157 0 R /XYZ 67.0 594.366 null ] (rfc.figure.u.6) [ 157 0 R /XYZ 67.0 445.506 null ] (rfc.iref.38) [ 157 0 R /XYZ 67.0 430.646 null ] (rfc.iref.39) [ 157 0 R /XYZ 67.0 420.786 null ] (rfc.iref.40) [ 157 0 R /XYZ 67.0 378.566 null ] (rfc.iref.41) [ 157 0 R /XYZ 67.0 365.066 null ] (rfc.iref.42) [ 157 0 R /XYZ 67.0 365.066 null ] (rfc.iref.43) [ 157 0 R /XYZ 67.0 338.066 null ] (rfc.iref.44) [ 157 0 R /XYZ 67.0 338.066 null ] (rfc.iref.45) [ 157 0 R /XYZ 67.0 311.066 null ] (rfc.iref.46) [ 157 0 R /XYZ 67.0 311.066 null ] (rfc.iref.47) [ 157 0 R /XYZ 67.0 295.066 null ] (rfc.iref.48) [ 157 0 R /XYZ 67.0 295.066 null ] (rfc.iref.49) [ 157 0 R /XYZ 67.0 268.066 null ] (rfc.iref.50) [ 157 0 R /XYZ 67.0 268.066 null ] (rfc.iref.51) [ 157 0 R /XYZ 67.0 244.566 null ] (rfc.iref.52) [ 157 0 R /XYZ 67.0 231.066 null ] (rfc.iref.53) [ 157 0 R /XYZ 67.0 231.066 null ] (rfc.section.7.1) [ 157 0 R /XYZ 67.0 189.566 null ] (rfc.figure.u.7) [ 157 0 R /XYZ 67.0 165.594 null ] (rfc.figure.u.8) [ 164 0 R /XYZ 67.0 654.7 null ] (rfc.section.8) [ 166 0 R /XYZ 67.0 725.0 null ] (operations.on.collections.that.contain.redirect.reference.resources) [ 166 0 R /XYZ 67.0 725.0 null ] (rfc.table.u.1) [ 166 0 R /XYZ 67.0 578.866 null ] (rfc.section.8.1) [ 166 0 R /XYZ 67.0 388.056 null ] (rfc.figure.u.9) [ 166 0 R /XYZ 67.0 332.084 null ] (rfc.figure.u.10) [ 166 0 R /XYZ 67.0 281.504 null ] (rfc.figure.u.11) [ 166 0 R /XYZ 67.0 106.464 null ] (rfc.section.8.2) [ 176 0 R /XYZ 67.0 285.76 null ] (rfc.figure.u.12) [ 176 0 R /XYZ 67.0 216.816 null ] (rfc.figure.u.13) [ 176 0 R /XYZ 67.0 166.236 null ] (rfc.figure.u.14) [ 178 0 R /XYZ 67.0 625.12 null ] (rfc.section.9) [ 182 0 R /XYZ 67.0 725.0 null ] (operations.on.targets.of.redirect.reference.resources) [ 182 0 R /XYZ 67.0 725.0 null ] (rfc.section.10) [ 184 0 R /XYZ 67.0 725.0 null ] (rfc.section.10.1) [ 184 0 R /XYZ 67.0 571.866 null ] (rfc.figure.u.15) [ 184 0 R /XYZ 67.0 547.894 null ] (rfc.figure.u.16) [ 184 0 R /XYZ 67.0 372.854 null ] (rfc.section.11) [ 188 0 R /XYZ 67.0 725.0 null ] (redirect.references.to.collections) [ 188 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.17) [ 188 0 R /XYZ 67.0 503.866 null ] (rfc.section.12) [ 192 0 R /XYZ 67.0 725.0 null ] (headers) [ 192 0 R /XYZ 67.0 725.0 null ] (rfc.section.12.1) [ 192 0 R /XYZ 67.0 690.866 null ] (header.redirect-ref) [ 192 0 R /XYZ 67.0 690.866 null ] (rfc.figure.u.18) [ 192 0 R /XYZ 67.0 666.894 null ] (rfc.section.12.2) [ 192 0 R /XYZ 67.0 578.314 null ] (header.apply-to-redirect-ref) [ 192 0 R /XYZ 67.0 578.314 null ] (rfc.figure.u.19) [ 192 0 R /XYZ 67.0 554.342 null ] (rfc.section.13) [ 197 0 R /XYZ 67.0 725.0 null ] (properties) [ 197 0 R /XYZ 67.0 725.0 null ] (rfc.section.13.1) [ 197 0 R /XYZ 67.0 658.866 null ] (redirect-lifetime.property) [ 197 0 R /XYZ 67.0 658.866 null ] (rfc.figure.u.20) [ 197 0 R /XYZ 67.0 602.894 null ] (rfc.iref.60) [ 197 0 R /XYZ 67.0 578.174 null ] (rfc.iref.61) [ 197 0 R /XYZ 67.0 558.454 null ] (rfc.iref.62) [ 197 0 R /XYZ 67.0 548.594 null ] (rfc.iref.63) [ 197 0 R /XYZ 67.0 528.874 null ] (rfc.iref.64) [ 197 0 R /XYZ 67.0 519.014 null ] (rfc.iref.65) [ 197 0 R /XYZ 67.0 499.294 null ] (rfc.section.13.2) [ 197 0 R /XYZ 67.0 467.434 null ] (reftarget.property) [ 197 0 R /XYZ 67.0 467.434 null ] (rfc.figure.u.21) [ 197 0 R /XYZ 67.0 400.462 null ] (rfc.section.14) [ 199 0 R /XYZ 67.0 725.0 null ] (xml.elements) [ 199 0 R /XYZ 67.0 725.0 null ] (rfc.section.14.1) [ 199 0 R /XYZ 67.0 690.866 null ] (redirectref.xml.element) [ 199 0 R /XYZ 67.0 690.866 null ] (rfc.figure.u.22) [ 199 0 R /XYZ 67.0 597.894 null ] (rfc.section.15) [ 201 0 R /XYZ 67.0 725.0 null ] (extensions.to.response.element) [ 201 0 R /XYZ 67.0 725.0 null ] (rfc.figure.u.23) [ 201 0 R /XYZ 67.0 618.732 null ] (rfc.section.16) [ 205 0 R /XYZ 67.0 725.0 null ] (capability.discovery) [ 205 0 R /XYZ 67.0 725.0 null ] (rfc.section.16.1) [ 205 0 R /XYZ 67.0 571.866 null ] (rfc.figure.u.24) [ 205 0 R /XYZ 67.0 547.894 null ] (rfc.figure.u.25) [ 205 0 R /XYZ 67.0 491.174 null ] (rfc.section.17) [ 211 0 R /XYZ 67.0 725.0 null ] (security.considerations) [ 211 0 R /XYZ 67.0 725.0 null ] (rfc.section.17.1) [ 211 0 R /XYZ 67.0 615.866 null ] (rfc.section.17.2) [ 211 0 R /XYZ 67.0 530.894 null ] (rfc.section.17.3) [ 211 0 R /XYZ 67.0 445.922 null ] (rfc.section.17.4) [ 211 0 R /XYZ 67.0 360.95 null ] (rfc.section.18) [ 216 0 R /XYZ 67.0 725.0 null ] (rfc.section.19) [ 220 0 R /XYZ 67.0 725.0 null ] (iana.considerations) [ 220 0 R /XYZ 67.0 725.0 null ] (rfc.section.19.1) [ 220 0 R /XYZ 67.0 669.866 null ] (rfc.section.19.1.1) [ 220 0 R /XYZ 67.0 618.894 null ] (rfc.section.19.1.2) [ 220 0 R /XYZ 67.0 503.003 null ] (rfc.section.20) [ 226 0 R /XYZ 67.0 725.0 null ] (rfc.section.21) [ 228 0 R /XYZ 67.0 725.0 null ] (rfc.references) [ 230 0 R /XYZ 67.0 725.0 null ] (RFC2119) [ 230 0 R /XYZ 67.0 702.866 null ] (RFC2518) [ 230 0 R /XYZ 67.0 675.866 null ] (RFC2616) [ 230 0 R /XYZ 67.0 648.866 null ] (RFC3253) [ 230 0 R /XYZ 67.0 621.866 null ] (RFC3986) [ 230 0 R /XYZ 67.0 594.866 null ] (rfc.authors) [ 241 0 R /XYZ 67.0 725.0 null ] (rfc.copyright) [ 241 0 R /XYZ 67.0 427.866 null ] (rfc.ipr) [ 250 0 R /XYZ 67.0 725.0 null ] (rfc.index) [ 256 0 R /XYZ 67.0 725.0 null ] ] >> >> + >> +endobj +3 0 obj +<< +/Font << /F5 330 0 R /F6 331 0 R /F9 332 0 R /F7 333 0 R >> +/ProcSet [ /PDF /ImageC /Text ] >> +endobj +11 0 obj +<< +/S /GoTo +/D [100 0 R /XYZ 67.0 725.0 null] +>> +endobj +13 0 obj +<< +/S /GoTo +/D [116 0 R /XYZ 67.0 725.0 null] +>> +endobj +15 0 obj +<< +/S /GoTo +/D [126 0 R /XYZ 67.0 725.0 null] +>> +endobj +17 0 obj +<< +/S /GoTo +/D [135 0 R /XYZ 67.0 725.0 null] +>> +endobj +19 0 obj +<< +/S /GoTo +/D [144 0 R /XYZ 67.0 725.0 null] +>> +endobj +21 0 obj +<< +/S /GoTo +/D [149 0 R /XYZ 67.0 725.0 null] +>> +endobj +23 0 obj +<< +/S /GoTo +/D [155 0 R /XYZ 67.0 692.0 null] +>> +endobj +25 0 obj +<< +/S /GoTo +/D [157 0 R /XYZ 67.0 725.0 null] +>> +endobj +27 0 obj +<< +/S /GoTo +/D [157 0 R /XYZ 67.0 189.566 null] +>> +endobj +29 0 obj +<< +/S /GoTo +/D [166 0 R /XYZ 67.0 725.0 null] +>> +endobj +31 0 obj +<< +/S /GoTo +/D [166 0 R /XYZ 67.0 388.056 null] +>> +endobj +33 0 obj +<< +/S /GoTo +/D [176 0 R /XYZ 67.0 285.76 null] +>> +endobj +35 0 obj +<< +/S /GoTo +/D [182 0 R /XYZ 67.0 725.0 null] +>> +endobj +37 0 obj +<< +/S /GoTo +/D [184 0 R /XYZ 67.0 725.0 null] +>> +endobj +39 0 obj +<< +/S /GoTo +/D [184 0 R /XYZ 67.0 571.866 null] +>> +endobj +41 0 obj +<< +/S /GoTo +/D [188 0 R /XYZ 67.0 725.0 null] +>> +endobj +43 0 obj +<< +/S /GoTo +/D [192 0 R /XYZ 67.0 725.0 null] +>> +endobj +45 0 obj +<< +/S /GoTo +/D [192 0 R /XYZ 67.0 690.866 null] +>> +endobj +47 0 obj +<< +/S /GoTo +/D [192 0 R /XYZ 67.0 578.314 null] +>> +endobj +49 0 obj +<< +/S /GoTo +/D [197 0 R /XYZ 67.0 725.0 null] +>> +endobj +51 0 obj +<< +/S /GoTo +/D [197 0 R /XYZ 67.0 658.866 null] +>> +endobj +53 0 obj +<< +/S /GoTo +/D [197 0 R /XYZ 67.0 467.434 null] +>> +endobj +55 0 obj +<< +/S /GoTo +/D [199 0 R /XYZ 67.0 725.0 null] +>> +endobj +57 0 obj +<< +/S /GoTo +/D [199 0 R /XYZ 67.0 690.866 null] +>> +endobj +59 0 obj +<< +/S /GoTo +/D [201 0 R /XYZ 67.0 725.0 null] +>> +endobj +61 0 obj +<< +/S /GoTo +/D [205 0 R /XYZ 67.0 725.0 null] +>> +endobj +63 0 obj +<< +/S /GoTo +/D [205 0 R /XYZ 67.0 571.866 null] +>> +endobj +65 0 obj +<< +/S /GoTo +/D [211 0 R /XYZ 67.0 725.0 null] +>> +endobj +67 0 obj +<< +/S /GoTo +/D [211 0 R /XYZ 67.0 615.866 null] +>> +endobj +69 0 obj +<< +/S /GoTo +/D [211 0 R /XYZ 67.0 530.894 null] +>> +endobj +71 0 obj +<< +/S /GoTo +/D [211 0 R /XYZ 67.0 445.922 null] +>> +endobj +73 0 obj +<< +/S /GoTo +/D [211 0 R /XYZ 67.0 360.95 null] +>> +endobj +75 0 obj +<< +/S /GoTo +/D [216 0 R /XYZ 67.0 725.0 null] +>> +endobj +80 0 obj +<< +/S /GoTo +/D [220 0 R /XYZ 67.0 725.0 null] +>> +endobj +82 0 obj +<< +/S /GoTo +/D [220 0 R /XYZ 67.0 669.866 null] +>> +endobj +84 0 obj +<< +/S /GoTo +/D [220 0 R /XYZ 67.0 618.894 null] +>> +endobj +86 0 obj +<< +/S /GoTo +/D [220 0 R /XYZ 67.0 503.003 null] +>> +endobj +88 0 obj +<< +/S /GoTo +/D [226 0 R /XYZ 67.0 725.0 null] +>> +endobj +90 0 obj +<< +/S /GoTo +/D [228 0 R /XYZ 67.0 725.0 null] +>> +endobj +92 0 obj +<< +/S /GoTo +/D [230 0 R /XYZ 67.0 725.0 null] +>> +endobj +94 0 obj +<< +/S /GoTo +/D [241 0 R /XYZ 67.0 725.0 null] +>> +endobj +96 0 obj +<< +/S /GoTo +/D [250 0 R /XYZ 67.0 725.0 null] +>> +endobj +98 0 obj +<< +/S /GoTo +/D [256 0 R /XYZ 67.0 725.0 null] +>> +endobj +119 0 obj +<< +/S /GoTo +/D [230 0 R /XYZ 67.0 675.866 null] +>> +endobj +121 0 obj +<< +/S /GoTo +/D [230 0 R /XYZ 67.0 648.866 null] +>> +endobj +124 0 obj +<< +/S /GoTo +/D [230 0 R /XYZ 67.0 702.866 null] +>> +endobj +130 0 obj +<< +/S /GoTo +/D [230 0 R /XYZ 67.0 594.866 null] +>> +endobj +133 0 obj +<< +/S /GoTo +/D [230 0 R /XYZ 67.0 621.866 null] +>> +endobj +258 0 obj +<< + /First 260 0 R + /Last 329 0 R +>> endobj +259 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 487.084 null] +>> +endobj +261 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 407.95 null] +>> +endobj +263 0 obj +<< +/S /GoTo +/D [6 0 R /XYZ 67.0 350.816 null] +>> +endobj +265 0 obj +<< +/S /GoTo +/D [8 0 R /XYZ 67.0 725.0 null] +>> +endobj +269 0 obj +<< +/S /GoTo +/D [126 0 R /XYZ 67.0 725.0 null] +>> +endobj +271 0 obj +<< +/S /GoTo +/D [135 0 R /XYZ 67.0 725.0 null] +>> +endobj +273 0 obj +<< +/S /GoTo +/D [144 0 R /XYZ 67.0 725.0 null] +>> +endobj +275 0 obj +<< +/S /GoTo +/D [149 0 R /XYZ 67.0 725.0 null] +>> +endobj +278 0 obj +<< +/S /GoTo +/D [157 0 R /XYZ 67.0 725.0 null] +>> +endobj +281 0 obj +<< +/S /GoTo +/D [166 0 R /XYZ 67.0 725.0 null] +>> +endobj +285 0 obj +<< +/S /GoTo +/D [182 0 R /XYZ 67.0 725.0 null] +>> +endobj +289 0 obj +<< +/S /GoTo +/D [188 0 R /XYZ 67.0 725.0 null] +>> +endobj +291 0 obj +<< +/S /GoTo +/D [192 0 R /XYZ 67.0 725.0 null] +>> +endobj +293 0 obj +<< +/S /GoTo +/D [192 0 R /XYZ 67.0 690.866 null] +>> +endobj +295 0 obj +<< +/S /GoTo +/D [192 0 R /XYZ 67.0 578.314 null] +>> +endobj +297 0 obj +<< +/S /GoTo +/D [197 0 R /XYZ 67.0 725.0 null] +>> +endobj +299 0 obj +<< +/S /GoTo +/D [197 0 R /XYZ 67.0 658.866 null] +>> +endobj +301 0 obj +<< +/S /GoTo +/D [197 0 R /XYZ 67.0 467.434 null] +>> +endobj +303 0 obj +<< +/S /GoTo +/D [199 0 R /XYZ 67.0 725.0 null] +>> +endobj +305 0 obj +<< +/S /GoTo +/D [199 0 R /XYZ 67.0 690.866 null] +>> +endobj +307 0 obj +<< +/S /GoTo +/D [201 0 R /XYZ 67.0 725.0 null] +>> +endobj +309 0 obj +<< +/S /GoTo +/D [205 0 R /XYZ 67.0 725.0 null] +>> +endobj +312 0 obj +<< +/S /GoTo +/D [211 0 R /XYZ 67.0 725.0 null] +>> +endobj +319 0 obj +<< +/S /GoTo +/D [220 0 R /XYZ 67.0 725.0 null] +>> +endobj +xref +0 334 +0000000000 65535 f +0000088634 00000 n +0000088954 00000 n +0000097019 00000 n +0000000015 00000 n +0000000071 00000 n +0000001589 00000 n +0000001695 00000 n +0000003902 00000 n +0000004022 00000 n +0000004272 00000 n +0000097135 00000 n +0000004407 00000 n +0000097200 00000 n +0000004542 00000 n +0000097265 00000 n +0000004676 00000 n +0000097330 00000 n +0000004811 00000 n +0000097395 00000 n +0000004946 00000 n +0000097460 00000 n +0000005081 00000 n +0000097525 00000 n +0000005216 00000 n +0000097590 00000 n +0000005351 00000 n +0000097655 00000 n +0000005486 00000 n +0000097722 00000 n +0000005621 00000 n +0000097787 00000 n +0000005756 00000 n +0000097854 00000 n +0000005891 00000 n +0000097920 00000 n +0000006026 00000 n +0000097985 00000 n +0000006161 00000 n +0000098050 00000 n +0000006296 00000 n +0000098117 00000 n +0000006431 00000 n +0000098182 00000 n +0000006566 00000 n +0000098247 00000 n +0000006701 00000 n +0000098314 00000 n +0000006836 00000 n +0000098381 00000 n +0000006971 00000 n +0000098446 00000 n +0000007106 00000 n +0000098513 00000 n +0000007241 00000 n +0000098580 00000 n +0000007376 00000 n +0000098645 00000 n +0000007511 00000 n +0000098712 00000 n +0000007646 00000 n +0000098777 00000 n +0000007781 00000 n +0000098842 00000 n +0000007915 00000 n +0000098909 00000 n +0000008049 00000 n +0000098974 00000 n +0000008184 00000 n +0000099041 00000 n +0000008319 00000 n +0000099108 00000 n +0000008454 00000 n +0000099175 00000 n +0000008589 00000 n +0000099241 00000 n +0000008722 00000 n +0000009615 00000 n +0000009738 00000 n +0000009828 00000 n +0000099306 00000 n +0000009958 00000 n +0000099371 00000 n +0000010091 00000 n +0000099438 00000 n +0000010225 00000 n +0000099505 00000 n +0000010359 00000 n +0000099572 00000 n +0000010492 00000 n +0000099637 00000 n +0000010625 00000 n +0000099702 00000 n +0000010758 00000 n +0000099767 00000 n +0000010891 00000 n +0000099832 00000 n +0000011024 00000 n +0000099897 00000 n +0000011156 00000 n +0000014121 00000 n +0000014246 00000 n +0000014371 00000 n +0000014509 00000 n +0000014647 00000 n +0000014785 00000 n +0000014921 00000 n +0000015059 00000 n +0000015197 00000 n +0000015335 00000 n +0000015473 00000 n +0000015611 00000 n +0000015749 00000 n +0000015887 00000 n +0000016025 00000 n +0000016163 00000 n +0000017143 00000 n +0000017269 00000 n +0000017322 00000 n +0000099962 00000 n +0000017461 00000 n +0000100030 00000 n +0000017600 00000 n +0000017739 00000 n +0000100098 00000 n +0000017877 00000 n +0000019239 00000 n +0000019365 00000 n +0000019418 00000 n +0000019556 00000 n +0000100166 00000 n +0000019695 00000 n +0000019833 00000 n +0000100234 00000 n +0000019970 00000 n +0000021869 00000 n +0000021995 00000 n +0000022064 00000 n +0000022202 00000 n +0000022340 00000 n +0000022478 00000 n +0000022616 00000 n +0000022754 00000 n +0000022892 00000 n +0000024343 00000 n +0000024469 00000 n +0000024506 00000 n +0000024645 00000 n +0000024784 00000 n +0000026840 00000 n +0000026966 00000 n +0000027011 00000 n +0000027149 00000 n +0000027288 00000 n +0000027427 00000 n +0000028754 00000 n +0000028864 00000 n +0000031001 00000 n +0000031127 00000 n +0000031180 00000 n +0000031319 00000 n +0000031457 00000 n +0000031593 00000 n +0000031731 00000 n +0000032685 00000 n +0000032795 00000 n +0000036069 00000 n +0000036195 00000 n +0000036272 00000 n +0000036411 00000 n +0000036550 00000 n +0000036689 00000 n +0000036828 00000 n +0000036967 00000 n +0000037106 00000 n +0000037245 00000 n +0000039315 00000 n +0000039425 00000 n +0000040839 00000 n +0000040949 00000 n +0000041462 00000 n +0000041572 00000 n +0000042032 00000 n +0000042142 00000 n +0000044132 00000 n +0000044242 00000 n +0000045108 00000 n +0000045218 00000 n +0000047217 00000 n +0000047343 00000 n +0000047372 00000 n +0000047510 00000 n +0000048575 00000 n +0000048701 00000 n +0000048738 00000 n +0000048875 00000 n +0000049012 00000 n +0000050144 00000 n +0000050254 00000 n +0000050883 00000 n +0000050993 00000 n +0000051853 00000 n +0000051979 00000 n +0000052008 00000 n +0000052146 00000 n +0000053881 00000 n +0000054007 00000 n +0000054052 00000 n +0000054191 00000 n +0000054330 00000 n +0000054468 00000 n +0000056710 00000 n +0000056836 00000 n +0000056873 00000 n +0000057011 00000 n +0000057150 00000 n +0000057666 00000 n +0000057792 00000 n +0000057821 00000 n +0000057960 00000 n +0000058878 00000 n +0000059004 00000 n +0000059049 00000 n +0000059187 00000 n +0000059325 00000 n +0000059463 00000 n +0000060021 00000 n +0000060131 00000 n +0000061084 00000 n +0000061194 00000 n +0000063400 00000 n +0000063526 00000 n +0000063611 00000 n +0000063802 00000 n +0000063993 00000 n +0000064183 00000 n +0000064374 00000 n +0000064563 00000 n +0000064753 00000 n +0000064943 00000 n +0000065134 00000 n +0000067015 00000 n +0000067141 00000 n +0000067210 00000 n +0000067386 00000 n +0000067571 00000 n +0000067739 00000 n +0000067907 00000 n +0000068095 00000 n +0000068279 00000 n +0000070365 00000 n +0000070491 00000 n +0000070536 00000 n +0000070713 00000 n +0000070888 00000 n +0000071064 00000 n +0000073541 00000 n +0000073667 00000 n +0000100302 00000 n +0000100356 00000 n +0000073688 00000 n +0000100422 00000 n +0000073885 00000 n +0000100487 00000 n +0000074081 00000 n +0000100553 00000 n +0000074230 00000 n +0000074431 00000 n +0000074618 00000 n +0000100617 00000 n +0000074864 00000 n +0000100683 00000 n +0000075046 00000 n +0000100749 00000 n +0000075398 00000 n +0000100815 00000 n +0000075762 00000 n +0000076039 00000 n +0000100881 00000 n +0000076522 00000 n +0000076823 00000 n +0000100947 00000 n +0000077330 00000 n +0000077883 00000 n +0000078386 00000 n +0000101013 00000 n +0000079046 00000 n +0000079474 00000 n +0000079848 00000 n +0000101079 00000 n +0000080334 00000 n +0000101145 00000 n +0000080656 00000 n +0000101211 00000 n +0000080861 00000 n +0000101279 00000 n +0000081142 00000 n +0000101347 00000 n +0000081469 00000 n +0000101413 00000 n +0000081857 00000 n +0000101481 00000 n +0000082166 00000 n +0000101549 00000 n +0000082428 00000 n +0000101615 00000 n +0000082662 00000 n +0000101683 00000 n +0000082899 00000 n +0000101749 00000 n +0000083424 00000 n +0000083706 00000 n +0000101815 00000 n +0000084170 00000 n +0000084470 00000 n +0000084680 00000 n +0000084893 00000 n +0000085317 00000 n +0000085592 00000 n +0000101881 00000 n +0000085921 00000 n +0000086197 00000 n +0000086410 00000 n +0000086606 00000 n +0000086854 00000 n +0000087046 00000 n +0000087262 00000 n +0000087501 00000 n +0000087707 00000 n +0000088079 00000 n +0000088194 00000 n +0000088305 00000 n +0000088417 00000 n +0000088524 00000 n +trailer +<< +/Size 334 +/Root 2 0 R +/Info 4 0 R +>> +startxref +101947 +%%EOF diff --git a/dav/SabreDAV/docs/rfc4790.txt b/dav/SabreDAV/docs/rfc4790.txt new file mode 100644 index 000000000..d58191c09 --- /dev/null +++ b/dav/SabreDAV/docs/rfc4790.txt @@ -0,0 +1,1459 @@ + + + + + + +Network Working Group C. Newman +Request for Comments: 4790 Sun Microsystems +Category: Standards Track M. Duerst + Aoyama Gakuin University + A. Gulbrandsen + Oryx + March 2007 + + + Internet Application Protocol Collation Registry + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The IETF Trust (2007). + +Abstract + + Many Internet application protocols include string-based lookup, + searching, or sorting operations. However, the problem space for + searching and sorting international strings is large, not fully + explored, and is outside the area of expertise for the Internet + Engineering Task Force (IETF). Rather than attempt to solve such a + large problem, this specification creates an abstraction framework so + that application protocols can precisely identify a comparison + function, and the repertoire of comparison functions can be extended + in the future. + + + + + + + + + + + + + + + + + +Newman, et al. Standards Track [Page 1] + +RFC 4790 Collation Registry March 2007 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 1.1. Conventions Used in This Document . . . . . . . . . . . . 4 + 2. Collation Definition and Purpose . . . . . . . . . . . . . . . 4 + 2.1. Definition . . . . . . . . . . . . . . . . . . . . . . . . 4 + 2.2. Purpose . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 2.3. Some Other Terms Used in this Document . . . . . . . . . . 5 + 2.4. Sort Keys . . . . . . . . . . . . . . . . . . . . . . . . 5 + 3. Collation Identifier Syntax . . . . . . . . . . . . . . . . . 6 + 3.1. Basic Syntax . . . . . . . . . . . . . . . . . . . . . . . 6 + 3.2. Wildcards . . . . . . . . . . . . . . . . . . . . . . . . 6 + 3.3. Ordering Direction . . . . . . . . . . . . . . . . . . . . 7 + 3.4. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 + 3.5. Naming Guidelines . . . . . . . . . . . . . . . . . . . . 7 + 4. Collation Specification Requirements . . . . . . . . . . . . . 8 + 4.1. Collation/Server Interface . . . . . . . . . . . . . . . . 8 + 4.2. Operations Supported . . . . . . . . . . . . . . . . . . . 8 + 4.2.1. Validity . . . . . . . . . . . . . . . . . . . . . . . 9 + 4.2.2. Equality . . . . . . . . . . . . . . . . . . . . . . . 9 + 4.2.3. Substring . . . . . . . . . . . . . . . . . . . . . . 9 + 4.2.4. Ordering . . . . . . . . . . . . . . . . . . . . . . . 10 + 4.3. Sort Keys . . . . . . . . . . . . . . . . . . . . . . . . 10 + 4.4. Use of Lookup Tables . . . . . . . . . . . . . . . . . . . 11 + 5. Application Protocol Requirements . . . . . . . . . . . . . . 11 + 5.1. Character Encoding . . . . . . . . . . . . . . . . . . . . 11 + 5.2. Operations . . . . . . . . . . . . . . . . . . . . . . . . 11 + 5.3. Wildcards . . . . . . . . . . . . . . . . . . . . . . . . 12 + 5.4. String Comparison . . . . . . . . . . . . . . . . . . . . 12 + 5.5. Disconnected Clients . . . . . . . . . . . . . . . . . . . 12 + 5.6. Error Codes . . . . . . . . . . . . . . . . . . . . . . . 13 + 5.7. Octet Collation . . . . . . . . . . . . . . . . . . . . . 13 + 6. Use by Existing Protocols . . . . . . . . . . . . . . . . . . 13 + 7. Collation Registration . . . . . . . . . . . . . . . . . . . . 14 + 7.1. Collation Registration Procedure . . . . . . . . . . . . . 14 + 7.2. Collation Registration Format . . . . . . . . . . . . . . 15 + 7.2.1. Registration Template . . . . . . . . . . . . . . . . 15 + 7.2.2. The Collation Element . . . . . . . . . . . . . . . . 15 + 7.2.3. The Identifier Element . . . . . . . . . . . . . . . . 16 + 7.2.4. The Title Element . . . . . . . . . . . . . . . . . . 16 + 7.2.5. The Operations Element . . . . . . . . . . . . . . . . 16 + 7.2.6. The Specification Element . . . . . . . . . . . . . . 16 + 7.2.7. The Submitter Element . . . . . . . . . . . . . . . . 16 + 7.2.8. The Owner Element . . . . . . . . . . . . . . . . . . 16 + 7.2.9. The Version Element . . . . . . . . . . . . . . . . . 17 + 7.2.10. The Variable Element . . . . . . . . . . . . . . . . . 17 + 7.3. Structure of Collation Registry . . . . . . . . . . . . . 17 + 7.4. Example Initial Registry Summary . . . . . . . . . . . . . 18 + + + +Newman, et al. Standards Track [Page 2] + +RFC 4790 Collation Registry March 2007 + + + 8. Guidelines for Expert Reviewer . . . . . . . . . . . . . . . . 18 + 9. Initial Collations . . . . . . . . . . . . . . . . . . . . . . 19 + 9.1. ASCII Numeric Collation . . . . . . . . . . . . . . . . . 20 + 9.1.1. ASCII Numeric Collation Description . . . . . . . . . 20 + 9.1.2. ASCII Numeric Collation Registration . . . . . . . . . 20 + 9.2. ASCII Casemap Collation . . . . . . . . . . . . . . . . . 21 + 9.2.1. ASCII Casemap Collation Description . . . . . . . . . 21 + 9.2.2. ASCII Casemap Collation Registration . . . . . . . . . 22 + 9.3. Octet Collation . . . . . . . . . . . . . . . . . . . . . 22 + 9.3.1. Octet Collation Description . . . . . . . . . . . . . 22 + 9.3.2. Octet Collation Registration . . . . . . . . . . . . . 23 + 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 23 + 11. Security Considerations . . . . . . . . . . . . . . . . . . . 23 + 12. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 23 + 13. References . . . . . . . . . . . . . . . . . . . . . . . . . . 24 + 13.1. Normative References . . . . . . . . . . . . . . . . . . . 24 + 13.2. Informative References . . . . . . . . . . . . . . . . . . 24 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Newman, et al. Standards Track [Page 3] + +RFC 4790 Collation Registry March 2007 + + +1. Introduction + + The Application Configuration Access Protocol ACAP [11] specification + introduced the concept of a comparator (which we call collation in + this document), but failed to create an IANA registry. With the + introduction of stringprep [6] and the Unicode Collation Algorithm + [7], it is now time to create that registry and populate it with some + initial values appropriate for an international community. This + specification replaces and generalizes the definition of a comparator + in ACAP, and creates a collation registry. + +1.1. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" + in this document are to be interpreted as defined in "Key words for + use in RFCs to Indicate Requirement Levels" [1]. + + The attribute syntax specifications use the Augmented Backus-Naur + Form (ABNF) [2] notation, including the core rules defined in + Appendix A. The ABNF production "Language-tag" is imported from + Language Tags [5] and "reg-name" from URI: Generic Syntax [4]. + +2. Collation Definition and Purpose + +2.1. Definition + + A collation is a named function which takes two arbitrary length + strings as input and can be used to perform one or more of three + basic comparison operations: equality test, substring match, and + ordering test. + +2.2. Purpose + + Collations are an abstraction for comparison functions so that these + comparison functions can be used in multiple protocols. The details + of a particular comparison operation can be specified by someone with + appropriate expertise, independent of the application protocols that + use that collation. This is similar to the way a charset [13] + separates the details of octet to character mapping from a protocol + specification, such as MIME [9], or the way SASL [10] separates the + details of an authentication mechanism from a protocol specification, + such as ACAP [11]. + + + + + + + + + +Newman, et al. Standards Track [Page 4] + +RFC 4790 Collation Registry March 2007 + + + Here is a small diagram to help illustrate the value of this + abstraction: + + +-------------------+ +-----------------+ + | IMAP i18n SEARCH |--+ | Basic | + +-------------------+ | +--| Collation Spec | + | | +-----------------+ + +-------------------+ | +-------------+ | +-----------------+ + | ACAP i18n SEARCH |--+--| Collation |--+--| A stringprep | + +-------------------+ | | Registry | | | Collation Spec | + | +-------------+ | +-----------------+ + +-------------------+ | | +-----------------+ + | ...other protocol |--+ | | locale-specific | + +-------------------+ +--| Collation Spec | + +-----------------+ + + Thus IMAP, ACAP, and future application protocols with international + search capability simply specify how to interface to the collation + registry instead of each protocol specification having to specify all + the collations it supports. + +2.3. Some Other Terms Used in this Document + + The terms client, server, and protocol are used in somewhat unusual + senses. + + Client means a user, or a program acting directly on behalf of a + user. This may be a mail reader acting as an IMAP client, or it may + be an interactive shell, where the user can type protocol commands/ + requests directly, or it may be a script or program written by the + user. + + Server means a program that performs services requested by the + client. This may be a traditional server such as an HTTP server, or + it may be a Sieve [14] interpreter running a Sieve script written by + a user. A server needs to use the operations provided by collations + in order to fulfill the client's requests. + + The protocol describes how the client tells the server what it wants + done, and (if applicable) how the server tells the client about the + results. IMAP is a protocol by this definition, and so is the Sieve + language. + +2.4. Sort Keys + + One component of a collation is a transformation, which turns a + string into a sort key, which is then used while sorting. + + + + +Newman, et al. Standards Track [Page 5] + +RFC 4790 Collation Registry March 2007 + + + The transformation can range from an identity mapping (e.g., the + i;octet collation Section 9.3) to a mapping that makes the string + unreadable to a human. + + This is an implementation detail of collations or servers. A + protocol SHOULD NOT expose it to clients, since some collations leave + the sort key's format up to the implementation, and current + conformant implementations are known to use different formats. + +3. Collation Identifier Syntax + +3.1. Basic Syntax + + The collation identifier itself is a single US-ASCII string. The + identifier MUST NOT be longer than 254 characters, and obeys the + following grammar: + + collation-char = ALPHA / DIGIT / "-" / ";" / "=" / "." + + collation-id = collation-prefix ";" collation-core-name + *collation-arg + + collation-scope = Language-tag / "vnd-" reg-name + + collation-core-name = ALPHA *( ALPHA / DIGIT / "-" ) + + collation-arg = ";" ALPHA *( ALPHA / DIGIT ) "=" + 1*( ALPHA / DIGIT / "." ) + + + Note: the ABNF production "Language-tag" is imported from Language + Tags [5] and "reg-name" from URI: Generic Syntax [4]. + + There is a special identifier called "default". For protocols that + have a default collation, "default" refers to that collation. For + other protocols, the identifier "default" MUST match no collations, + and servers SHOULD treat it in the same way as they treat nonexistent + collations. + +3.2. Wildcards + + The string a client uses to select a collation MAY contain one or + more wildcard ("*") characters that match zero or more collation- + chars. Wildcard characters MUST NOT be adjacent. If the wildcard + string matches multiple collations, the server SHOULD attempt to + select a widely useful collation in preference to a narrowly useful + one. + + + + +Newman, et al. Standards Track [Page 6] + +RFC 4790 Collation Registry March 2007 + + + collation-wild = ("*" / (ALPHA ["*"])) *(collation-char ["*"]) + ; MUST NOT exceed 254 characters total + +3.3. Ordering Direction + + When used as a protocol element for ordering, the collation + identifier MAY be prefixed by either "+" or "-" to explicitly specify + an ordering direction. "+" has no effect on the ordering operation, + while "-" inverts the result of the ordering operation. In general, + collation-order is used when a client requests a collation, and + collation-selected is used when the server informs the client of the + selected collation. + + collation-selected = ["+" / "-"] collation-id + + collation-order = ["+" / "-"] collation-wild + +3.4. URIs + + Some protocols are designed to use URIs [4] to refer to collations + rather than simple tokens. A special section of the IANA URL space + is reserved for such usage. The "collation-uri" form is used to + refer to a specific named collation (the collation registration may + not actually be present). The "collation-auri" form is an abstract + name for an ordering, a collation pattern or a vendor private + collator. + + collation-uri = "http://www.iana.org/assignments/collation/" + collation-id ".xml" + + collation-auri = ( "http://www.iana.org/assignments/collation/" + collation-order ".xml" ) / other-uri + + other-uri = + ; excluding the IANA collation namespace. + +3.5. Naming Guidelines + + While this specification makes no absolute requirements on the + structure of collation identifiers, naming consistency is important, + so the following initial guidelines are provided. + + Collation identifiers with an international audience typically begin + with "i;". Collation identifiers intended for a particular language + or locale typically begin with a language tag [5] followed by a ";". + After the first ";" is normally the name of the general collation + algorithm, followed by a series of algorithm modifications separated + by the ";" delimiter. Parameterized modifications will use "=" to + + + +Newman, et al. Standards Track [Page 7] + +RFC 4790 Collation Registry March 2007 + + + delimit the parameter from the value. The version numbers of any + lookup tables used by the algorithm SHOULD be present as + parameterized modifications. + + Collation identifiers of the form *;vnd-hostname;* are reserved for + vendor-specific collations created by the owner of the hostname + following the "vnd-" prefix (e.g., vnd-example.com for the vendor + example.com). Registration of such collations (or the name space as + a whole), with intended use of the "Vendor", is encouraged when a + public specification or open-source implementation is available, but + is not required. + +4. Collation Specification Requirements + +4.1. Collation/Server Interface + + The collation itself defines what it operates on. Most collations + are expected to operate on character strings. The i;octet + (Section 9.3) collation operates on octet strings. The i;ascii- + numeric (Section 9.1) operation operates on numbers. + + This specification defines the collation interface in terms of octet + strings. However, implementations may choose to use character + strings instead. Such implementations may not be able to implement + e.g., i;octet. Since i;octet is not currently mandatory to implement + for any protocol, this should not be a problem. + +4.2. Operations Supported + + A collation specification MUST state which of the three basic + operations are supported (equality, substring, ordering) and how to + perform each of the supported operations on any two input character + strings, including empty strings. Collations must be deterministic, + i.e., given a collation with a specific identifier, and any two fixed + input strings, the result MUST be the same for the same operation. + + In general, collation operations should behave as their names + suggest. While a collation may be new, the operations are not, so + the new collation's operations should be similar to those of older + collations. For example, a date/time collation should not provide a + "substring" operation that would morph IMAP substring SEARCH into + e.g., a date-range search. + + A non-obvious consequence of the rules for each collation operation + is that, for any single collation, either none or all of the + operations can return "undefined". For example, it is not possible + to have an equality operation that never returns "undefined", and a + substring operation that occasionally does. + + + +Newman, et al. Standards Track [Page 8] + +RFC 4790 Collation Registry March 2007 + + +4.2.1. Validity + + The validity test takes one string as argument. It returns valid if + its input string is a valid input to the collation's other + operations, and invalid if not. (In other words, a string is valid + if it is equal to itself according to the collation's equality + operation.) + + The validity test is provided by all collations. It MUST NOT be + listed separately in the collation registration. + +4.2.2. Equality + + The equality test always returns "match" or "no-match" when it is + supplied valid input, and MAY return "undefined" if one or both input + strings are not valid. + + The equality test MUST be reflexive and symmetric. For valid input, + it MUST be transitive. + + If a collation provides either a substring or an ordering test, it + MUST also provide an equality test. The substring and/or ordering + tests MUST be consistent with the equality test. + + The return values of the equality test are called "match", "no-match" + and "undefined" in this document. + +4.2.3. Substring + + The substring matching operation determines if the first string is a + substring of the second string, i.e., if one or more substrings of + the second string is equal to the first, as defined by the + collation's equality operation. + + A collation that supports substring matching will automatically + support two special cases of substring matching: prefix and suffix + matching, if those special cases are supported by the application + protocol. It returns "match" or "no-match" when it is supplied valid + input and returns "undefined" when supplied invalid input. + + Application protocols MAY return position information for substring + matches. If this is done, the position information SHOULD include + both the starting offset and the ending offset for each match. This + is important because more sophisticated collations can match strings + of unequal length (for example, a pre-composed accented character can + match a decomposed accented character). In general, overlapping + matches SHOULD be reported (as when "ana" occurs twice within + "banana"), although there are cases where a collation may decide not + + + +Newman, et al. Standards Track [Page 9] + +RFC 4790 Collation Registry March 2007 + + + to. For example, in a collation which treats all whitespace + sequences as identical, the substring operation could be defined such + that " 1 " (SP "1" SP) is reported just once within " 1 " (SP SP + "1" SP SP), not four times (SP SP "1" SP, SP "1" SP, SP "1" SP SP and + SP SP "1" SP SP), since the four matches are, in a sense, the same + match. + + A string is a substring of itself. The empty string is a substring + of all strings. + + Note that the substring operation of some collations can match + strings of unequal length. For example, a pre-composed accented + character can match a decomposed accented character. The Unicode + Collation Algorithm [7] discusses this in more detail. + + The return values of the substring operation are called "match", "no- + match", and "undefined" in this document. + +4.2.4. Ordering + + The ordering operation determines how two strings are ordered. It + MUST be reflexive. For valid input, it MUST be transitive and + trichotomous. + + Ordering returns "less" if the first string is listed before the + second string, according to the collation; "greater", if the second + string is listed before the first string; and "equal", if the two + strings are equal, as defined by the collation's equality operation. + If one or both strings are invalid, the result of ordering is + "undefined". + + When the collation is used with a "+" prefix, the behavior is the + same as when used with no prefix. When the collation is used with a + "-" prefix, the result of the ordering operation of the collation + MUST be reversed. + + The return values of the ordering operation are called "less", + "equal", "greater", and "undefined" in this document. + +4.3. Sort Keys + + A collation specification SHOULD describe the internal transformation + algorithm to generate sort keys. This algorithm can be applied to + individual strings, and the result can be stored to potentially + optimize future comparison operations. A collation MAY specify that + the sort key is generated by the identity function. The sort key may + have no meaning to a human. The sort key may not be valid input to + the collation. + + + +Newman, et al. Standards Track [Page 10] + +RFC 4790 Collation Registry March 2007 + + +4.4. Use of Lookup Tables + + Some collations use customizable lookup tables, e.g., because the + tables depend on locale, and may be modified after shipping the + software. Collations that use more than one customizable lookup + table in a documented format MUST assign numbers to the tables they + use. This permits an application protocol command to access the + tables used by a server collation, so that clients and servers use + the same tables. + +5. Application Protocol Requirements + + This section describes the requirements and issues that an + application protocol needs to consider if it offers searching, + substring matching and/or sorting, and permits the use of characters + outside the US-ASCII charset. + +5.1. Character Encoding + + The protocol specification has to make sure that it is clear on which + characters (rather than just octets) the collations are used. This + can be done by specifying the protocol itself in terms of characters + (e.g., in the case of a query language), by specifying a single + character encoding for the protocol (e.g., UTF-8 [3]), or by + carefully describing the relevant issues of character encoding + labeling and conversion. In the later case, details to consider + include how to handle unknown charsets, any charsets that are + mandatory-to-implement, any issues with byte-order that might apply, + and any transfer encodings that need to be supported. + +5.2. Operations + + The protocol must specify which of the operations defined in this + specification (equality matching, substring matching, and ordering) + can be invoked in the protocol, and how they are invoked. There may + be more than one way to invoke an operation. + + The protocol MUST provide a mechanism for the client to select the + collation to use with equality matching, substring matching, and + ordering. + + If a protocol needs a total ordering and the collation chosen does + not provide it because the ordering operation returns "undefined" at + least once, the recommended fallback is to sort all invalid strings + after the valid ones, and use i;octet to order the invalid strings. + + Although the collation's substring function provides a list of + matches, a protocol need not provide all that to the client. It may + + + +Newman, et al. Standards Track [Page 11] + +RFC 4790 Collation Registry March 2007 + + + provide only the first matching substring, or even just the + information that the substring search matched. In this way, + collations can be used with protocols that are defined such that "x + is a substring of y" returns true-false. + + If the protocol provides positional information for the results of a + substring match, that positional information SHOULD fully specify the + substring(s) in the result that matches, independent of the length of + the search string. For example, returning both the starting and + ending offset of the match would suffice, as would the starting + offset and a length. Returning just the starting offset is not + acceptable. This rule is necessary because advanced collations can + treat strings of different lengths as equal (for example, pre- + composed and decomposed accented characters). + +5.3. Wildcards + + The protocol MUST specify whether it allows the use of wildcards in + collation identifiers. If the protocol allows wildcards, then: + The protocol MUST specify how comparisons behave in the absence of + explicit collation negotiation, or when a collation of "default" + is requested. The protocol MAY specify that the default collation + used in such circumstances is sensitive to server configuration. + + The protocol SHOULD provide a way to list available collations + matching a given wildcard pattern, or patterns. + +5.4. String Comparison + + If a protocol compares strings in any nontrivial way, using a + collation may be appropriate. As an example, many protocols use + case-independent strings. In many cases, a simple ASCII mapping to + upper/lower case works well. In other cases, it may be better to use + a specifiable collation; for example, so that a server can treat "i" + and "I" as equivalent in Italy, and different in Turkey (Turkish also + has a dotted upper-case" I" and a dotless lower-case "i"). + + Protocol designers should consider, in each case, whether to use a + specifiable collation. Keywords often have other needs than user + variables, and search arguments may be different again. + +5.5. Disconnected Clients + + If the protocol supports disconnected clients, and a collation is + used that can use configurable tables (e.g., to support + locale-specific extensions), then the client may not be able to + reproduce the server's collation operations while offline. + + + + +Newman, et al. Standards Track [Page 12] + +RFC 4790 Collation Registry March 2007 + + + A mechanism to download such tables has been discussed. Such a + mechanism is not included in the present specification, since the + problem is not yet well understood. + +5.6. Error Codes + + The protocol specification should consider assigning protocol error + codes for the following circumstances: + + o The client requests the use of a collation by identifier or + pattern, but no implemented collation matches that pattern. + + o The client attempts to use a collation for an operation that is + not supported by that collation -- for example, attempting to use + the "i;ascii-numeric" collation for substring matching. + + o The client uses an equality or substring matching collation, and + the result is an error. It may be appropriate to distinguish + between the two input strings, particularly when one is supplied + by the client and the other is stored by the server. It might + also be appropriate to distinguish the specific case of an invalid + UTF-8 string. + +5.7. Octet Collation + + The i;octet (Section 9.3) collation is only usable with protocols + based on octet-strings. Clients and servers MUST NOT use i;octet + with other protocols. + + If the protocol permits the use of collations with data structures + other than strings, the protocol MUST describe the default behavior + for a collation with those data structures. + +6. Use by Existing Protocols + + This section is informative. + + Both ACAP [11] and Sieve [14] are standards track specifications that + used collations prior to the creation of this specification and + registry. Those standards do not meet all the application protocol + requirements described in Section 5. + + These protocols allow the use of the i;octet (Section 9.3) collation + working directly on UTF-8 data, as used in these protocols. + + + + + + + +Newman, et al. Standards Track [Page 13] + +RFC 4790 Collation Registry March 2007 + + + In Sieve, all matches are either true or false. Accordingly, Sieve + servers must treat "undefined" and "no-match" results of the equality + and substring operations as false, and only "match" as true. + + In ACAP and Sieve, there are no invalid strings. In this document's + terms, invalid strings sort after valid strings. + + IMAP [15] also collates, although that is explicit only when the + COMPARATOR [17] extension is used. The built-in IMAP substring + operation and the ordering provided by the SORT [16] extension may + not meet the requirements made in this document. + + Other protocols may be in a similar position. + + In IMAP, the default collation is i;ascii-casemap, because its + operations are understood to match IMAP's built-in operations. + +7. Collation Registration + +7.1. Collation Registration Procedure + + The IETF will create a mailing list, collation@ietf.org, which can be + used for public discussion of collation proposals prior to + registration. Use of the mailing list is strongly encouraged. The + IESG will appoint a designated expert who will monitor the + collation@ietf.org mailing list and review registrations. + + The registration procedure begins when a completed registration + template is sent to iana@iana.org and collation@ietf.org. The + designated expert is expected to tell IANA and the submitter of the + registration within two weeks whether the registration is approved, + approved with minor changes, or rejected with cause. When a + registration is rejected with cause, it can be re-submitted if the + concerns listed in the cause are addressed. Decisions made by the + designated expert can be appealed to the IESG Applications Area + Director, then to the IESG. They follow the normal appeals procedure + for IESG decisions. + + Collation registrations in a standards track, BCP, or IESG-approved + experimental RFC are owned by the IETF, and changes to the + registration follow normal procedures for updating such documents. + Collation registrations in other RFCs are owned by the RFC author(s). + Other collation registrations are owned by the individual(s) listed + in the contact field of the registration, and IANA will preserve this + information. + + If the registration is a change of an existing collation, it MUST be + approved by the owner. In the event the owner cannot be contacted + + + +Newman, et al. Standards Track [Page 14] + +RFC 4790 Collation Registry March 2007 + + + for a period of one month, and the designated expert deems the change + necessary, the IESG MAY re-assign ownership to an appropriate party. + +7.2. Collation Registration Format + + Registration of a collation is done by sending a well-formed XML + document to collation@ietf.org and iana@iana.org. + +7.2.1. Registration Template + + Here is a template for the registration: + + + + + collation identifier + technical title for collation + equality order substring + specification reference + email address of owner or IETF + email address of submitter + 1 + + +7.2.2. The Collation Element + + The root of the registration document MUST be a element. + The collation element contains the other elements in the + registration, which are described in the following sub-subsections, + in the order given here. + + The element MAY include an "rfc=" attribute if the + specification is in an RFC. The "rfc=" attribute gives only the + number of the RFC, without any prefix, such as "RFC", or suffix, such + as ".txt". + + The element MUST include a "scope=" attribute, which MUST + have one of the values "global", "local", or "other". + + The element MUST include an "intendedUse=" attribute, + which must have one of the values "common", "limited", "vendor", or + "deprecated". Collation specifications intended for "common" use are + expected to reference standards from standards bodies with + significant experience dealing with the details of international + character sets. + + Be aware that future revisions of this specification may add + additional function types, as well as additional XML attributes, + + + +Newman, et al. Standards Track [Page 15] + +RFC 4790 Collation Registry March 2007 + + + values, and elements. Any system that automatically parses these XML + documents MUST take this into account to preserve future + compatibility. + +7.2.3. The Identifier Element + + The element gives the precise identifier of the + collation, e.g., i;ascii-casemap. The element is + mandatory. + +7.2.4. The Title Element + + The element gives the title of the collation. The <title> + element is mandatory. + +7.2.5. The Operations Element + + The <operations> element lists which of the three operations + ("equality", "order" or "substring") the collation provides, + separated by single spaces. The <operations> element is mandatory. + +7.2.6. The Specification Element + + The <specification> element describes where to find the + specification. The <specification> element is mandatory. It MAY + have a URI attribute. There may be more than one <specification> + element, in which case, they together form the specification. + + If it is discovered that parts of a collation specification conflict, + a new revision of the collation is necessary, and the + collation@ietf.org mailing list should be notified. + +7.2.7. The Submitter Element + + The <submitter> element provides an RFC 2822 [12] email address for + the person who submitted the registration. It is optional if the + <owner> element contains an email address. + + There may be more than one <submitter> element. + +7.2.8. The Owner Element + + The <owner> element contains either the four letters "IETF" or an + email address of the owner of the registration. The <owner> element + is mandatory. There may be more than one <owner> element. If so, + all owners are equal. Each owner can speak for all. + + + + + +Newman, et al. Standards Track [Page 16] + +RFC 4790 Collation Registry March 2007 + + +7.2.9. The Version Element + + The <version> element MUST be included when the registration is + likely to be revised, or has been revised in such a way that the + results change for one or more input strings. The <version> element + is optional. + +7.2.10. The Variable Element + + The <variable> element specifies an optional variable to control the + collation's behaviour, for example whether it is case sensitive. The + <variable> element is optional. When <variable> is used, it must + contain <name> and <default> elements, and it may contain one or more + <value> elements. + +7.2.10.1. The Name Element + + The <name> element specifies the name value of a variable. The + <name> element is mandatory. + +7.2.10.2. The Default Element + + The <default> element specifies the default value of a variable. The + <default> element is mandatory. + +7.2.10.3. The Value Element + + The <value> element specifies a legal value of a variable. The + <value> element is optional. If one or more <value> elements are + present, only those values are legal. If none are, then the + variable's legal values do not form an enumerated set, and the rules + MUST be specified in an RFC accompanying the registration. + +7.3. Structure of Collation Registry + + Once the registration is approved, IANA will store each XML + registration document in a URL of the form + http://www.iana.org/assignments/collation/collation-id.xml, where + collation-id is the content of the identifier element in the + registration. Both the submitter and the designated expert are + responsible for verifying that the XML is well-formed. The + registration document should avoid using new elements. If any are + necessary, it is important to be consistent with other registrations. + + IANA will also maintain a text summary of the registry under the name + http://www.iana.org/assignments/collation/collation-index.html. This + summary is divided into four sections. The first section is for + collations intended for common use. This section is intended for + + + +Newman, et al. Standards Track [Page 17] + +RFC 4790 Collation Registry March 2007 + + + collation registrations published in IESG-approved RFCs, or for + locally scoped collations from the primary standards body for that + locale. The designated expert is encouraged to reject collation + registrations with an intended use of "common" if the expert believes + it should be "limited", as it is desirable to keep the number of + "common" registrations small and of high quality. The second section + is reserved for limited-use collations. The third section is + reserved for registered vendor-specific collations. The final + section is reserved for deprecated collations. + +7.4. Example Initial Registry Summary + + The following is an example of how IANA might structure the initial + registry summary.html file: + + Collation Functions Scope Reference + --------- --------- ----- --------- + Common Use Collations: + i;ascii-casemap e, o, s Local [RFC 4790] + + Limited Use Collations: + i;octet e, o, s Other [RFC 4790] + i;ascii-numeric e, o Other [RFC 4790] + + Vendor Collations: + + Deprecated Collations: + + + References + ---------- + [RFC 4790] Newman, C., Duerst, M., Gulbrandsen, A., "Internet + Application Protocol Collation Registry", RFC 4790, + Sun Microsystems, March 2007. + +8. Guidelines for Expert Reviewer + + The expert reviewer appointed by the IESG has fairly broad latitude + for this registry. While a number of collations are expected + (particularly customizations of the UCA for localized use), an + explosion of collations (particularly common-use collations) is not + desirable for widespread interoperability. However, it is important + for the expert reviewer to provide cause when rejecting a + registration, and, when possible, to describe corrective action to + + + + + + + +Newman, et al. Standards Track [Page 18] + +RFC 4790 Collation Registry March 2007 + + + permit the registration to proceed. The following table includes + some example reasons to reject a registration with cause: + + o The registration is not a well-formed XML document. + + o The registration has an intended use of "common", but there is no + evidence the collation will be widely deployed, so it should be + listed as "limited". + + o The registration has an intended use of "common", but it is + redundant with the functionality of a previously registered + "common" collation. + + o The registration has an intended use of "common", but the + specification is not detailed enough to allow interoperable + implementations by others. + + o The collation identifier fails to precisely identify the version + numbers of relevant tables to use. + + o The registration fails to meet one of the "MUST" requirements in + Section 4. + + o The collation identifier fails to meet the syntax in Section 3. + + o The collation specification referenced in the registration is + vague or has optional features without a clear behavior specified. + + o The referenced specification does not adequately address security + considerations specific to that collation. + + o The registration's operations are needlessly different from those + of traditional operations. + + o The registration's XML is needlessly different from that of + already registered collations. + +9. Initial Collations + + This section registers the three collations that were originally + defined in [11], and are implemented in most [14] engines. Some of + the behavior of these collations is perhaps not ideal, such as + i;ascii-casemap accepting non-ASCII input. Compatibility with widely + deployed code was judged more important than fixing the collations. + Some of the aspects of these collations are necessary to maintain + compatibility with widely deployed code. + + + + + +Newman, et al. Standards Track [Page 19] + +RFC 4790 Collation Registry March 2007 + + +9.1. ASCII Numeric Collation + +9.1.1. ASCII Numeric Collation Description + + The "i;ascii-numeric" collation is a simple collation intended for + use with arbitrarily-sized, unsigned decimal integer numbers stored + as octet strings. US-ASCII digits (0x30 to 0x39) represent digits of + the numbers. Before converting from string to integer, the input + string is truncated at the first non-digit character. All input is + valid; strings that do not start with a digit represent positive + infinity. + + The collation supports equality and ordering, but does not support + the substring operation. + + The equality operation returns "match" if the two strings represent + the same number (i.e., leading zeroes and trailing non-digits are + disregarded), and "no-match" if the two strings represent different + numbers. + + The ordering operation returns "less" if the first string represents + a smaller number than the second, "equal" if they represent the same + number, and "greater" if the first string represents a larger number + than the second. + + Some examples: "0" is less than "1", and "1" is less than + "4294967298". "4294967298", "04294967298", and "4294967298b" are all + equal. "04294967298" is less than "". "", "x", and "y" are equal. + +9.1.2. ASCII Numeric Collation Registration + + <?xml version='1.0'?> + <!DOCTYPE collation SYSTEM 'collationreg.dtd'> + <collation rfc="4790" scope="other" intendedUse="limited"> + <identifier>i;ascii-numeric</identifier> + <title>ASCII Numeric + equality order + RFC 4790 + IETF + chris.newman@sun.com + + + + + + + + + + + +Newman, et al. Standards Track [Page 20] + +RFC 4790 Collation Registry March 2007 + + +9.2. ASCII Casemap Collation + +9.2.1. ASCII Casemap Collation Description + + The "i;ascii-casemap" collation is a simple collation that operates + on octet strings and treats US-ASCII letters case-insensitively. It + provides equality, substring, and ordering operations. All input is + valid. Note that letters outside ASCII are not treated case- + insensitively. + + Its equality, ordering, and substring operations are as for i;octet, + except that at first, the lower-case letters (octet values 97-122) in + each input string are changed to upper case (octet values 65-90). + + Care should be taken when using OS-supplied functions to implement + this collation, as it is not locale sensitive. Functions, such as + strcasecmp and toupper, are sometimes locale sensitive, and may + inappropriately map lower-case letters other than a-z to upper case. + + The i;ascii-casemap collation is well-suited for use with many + Internet protocols and computer languages. Use with natural language + is often inappropriate; even though the collation apparently supports + languages such as Swahili and English, in real-world use, it tends to + mis-sort a number of types of string: + + o people and place names containing non-ASCII, + + o words such as "naive" (if spelled with an accent, the accented + character could push the word to the wrong spot in a sorted list), + + o names such as "Lloyd" (which, in Welsh, sorts after "Lyon", unlike + in English), + + o strings containing euro and pound sterling symbols, quotation + marks other than '"', dashes/hyphens, etc. + + + + + + + + + + + + + + + + +Newman, et al. Standards Track [Page 21] + +RFC 4790 Collation Registry March 2007 + + +9.2.2. ASCII Casemap Collation Registration + + + + + i;ascii-casemap + ASCII Casemap + equality order substring + RFC 4790 + IETF + chris.newman@sun.com + + +9.3. Octet Collation + +9.3.1. Octet Collation Description + + The "i;octet" collation is a simple and fast collation intended for + use on binary octet strings rather than on character data. Protocols + that want to make this collation available have to do so by + explicitly allowing it. If not explicitly allowed, it MUST NOT be + used. It never returns an "undefined" result. It provides equality, + substring, and ordering operations. + + The ordering algorithm is as follows: + + 1. If both strings are the empty string, return the result "equal". + + 2. If the first string is empty and the second is not, return the + result "less". + + 3. If the second string is empty and the first is not, return the + result "greater". + + 4. If both strings begin with the same octet value, remove the first + octet from both strings and repeat this algorithm from step 1. + + 5. If the unsigned value (0 to 255) of the first octet of the first + string is less than the unsigned value of the first octet of the + second string, then return "less". + + 6. If this step is reached, return "greater". + + This algorithm is roughly equivalent to the C library function + memcmp, with appropriate length checks added. + + + + + + +Newman, et al. Standards Track [Page 22] + +RFC 4790 Collation Registry March 2007 + + + The matching operation returns "match" if the sorting algorithm would + return "equal". Otherwise, the matching operation returns "no- + match". + + The substring operation returns "match" if the first string is the + empty string, or if there exists a substring of the second string of + length equal to the length of the first string, which would result in + a "match" result from the equality function. Otherwise, the + substring operation returns "no-match". + +9.3.2. Octet Collation Registration + + This collation is defined with intendedUse="limited" because it can + only be used by protocols that explicitly allow it. + + + + + i;octet + Octet + equality order substring + RFC 4790 + IETF + chris.newman@sun.com + + +10. IANA Considerations + + Section 7 defines how to register collations with IANA. Section 9 + defines a list of predefined collations that have been registered + with IANA. + +11. Security Considerations + + Collations will normally be used with UTF-8 strings. Thus, the + security considerations for UTF-8 [3], stringprep [6], and Unicode + TR-36 [8] also apply, and are normative to this specification. + +12. Acknowledgements + + The authors want to thank all who have contributed to this document, + including Brian Carpenter, John Cowan, Dave Cridland, Mark Davis, + Spencer Dawkins, Lisa Dusseault, Lars Eggert, Frank Ellermann, Philip + Guenther, Tony Hansen, Ted Hardie, Sam Hartman, Kjetil Torgrim Homme, + Michael Kay, John Klensin, Alexey Melnikov, Jim Melton, and Abhijit + Menon-Sen. + + + + + +Newman, et al. Standards Track [Page 23] + +RFC 4790 Collation Registry March 2007 + + +13. References + +13.1. Normative References + + [1] Bradner, S., "Key words for use in RFCs to Indicate Requirement + Levels", BCP 14, RFC 2119, March 1997. + + [2] Crocker, D. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", RFC 4234, October 2005. + + [3] Yergeau, F., "UTF-8, a transformation format of ISO 10646", + STD 63, RFC 3629, November 2003. + + [4] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform + Resource Identifier (URI): Generic Syntax", RFC 3986, + January 2005. + + [5] Phillips, A. and M. Davis, "Tags for Identifying Languages", + BCP 47, RFC 4646, September 2006. + + [6] Hoffman, P. and M. Blanchet, "Preparation of Internationalized + Strings ("stringprep")", RFC 3454, December 2002. + + [7] Davis, M. and K. Whistler, "Unicode Collation Algorithm version + 14", May 2005, + . + + [8] Davis, M. and M. Suignard, "Unicode Security Considerations", + February 2006, . + +13.2. Informative References + + [9] Freed, N. and N. Borenstein, "Multipurpose Internet Mail + Extensions (MIME) Part One: Format of Internet Message Bodies", + RFC 2045, November 1996. + + [10] Melnikov, A., "Simple Authentication and Security Layer + (SASL)", RFC 4422, June 2006. + + [11] Newman, C. and J. Myers, "ACAP -- Application Configuration + Access Protocol", RFC 2244, November 1997. + + [12] Resnick, P., "Internet Message Format", RFC 2822, April 2001. + + [13] Freed, N. and J. Postel, "IANA Charset Registration + Procedures", BCP 19, RFC 2978, October 2000. + + + + + +Newman, et al. Standards Track [Page 24] + +RFC 4790 Collation Registry March 2007 + + + [14] Showalter, T., "Sieve: A Mail Filtering Language", RFC 3028, + January 2001. + + [15] Crispin, M., "Internet Message Access Protocol - Version + 4rev1", RFC 3501, March 2003. + + [16] Crispin, M. and K. Murchison, "Internet Message Access Protocol + - Sort and Thread Extensions", Work in Progress, May 2004. + + [17] Newman, C. and A. Gulbrandsen, "Internet Message Access + Protocol Internationalization", Work in Progress, January 2006. + +Authors' Addresses + + Chris Newman + Sun Microsystems + 1050 Lakes Drive + West Covina, CA 91790 + USA + + EMail: chris.newman@sun.com + + + Martin Duerst + Aoyama Gakuin University + 5-10-1 Fuchinobe + Sagamihara, Kanagawa 229-8558 + Japan + + Phone: +81 42 759 6329 + Fax: +81 42 759 6495 + EMail: duerst@it.aoyama.ac.jp + URI: http://www.sw.it.aoyama.ac.jp/D%C3%BCrst/ + + Note: Please write "Duerst" with u-umlaut wherever possible, for + example as "Dürst" in XML and HTML. + + + Arnt Gulbrandsen + Oryx Mail Systems GmbH + Schweppermannstr. 8 + 81671 Munich + Germany + + Fax: +49 89 4502 9758 + EMail: arnt@oryx.com + URI: http://www.oryx.com/arnt/ + + + + +Newman, et al. Standards Track [Page 25] + +RFC 4790 Collation Registry March 2007 + + +Full Copyright Statement + + Copyright (C) The IETF Trust (2007). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + +Newman, et al. Standards Track [Page 26] + diff --git a/dav/SabreDAV/docs/rfc4791.txt b/dav/SabreDAV/docs/rfc4791.txt new file mode 100644 index 000000000..7a30bb214 --- /dev/null +++ b/dav/SabreDAV/docs/rfc4791.txt @@ -0,0 +1,5995 @@ + + + + + + +Network Working Group C. Daboo +Request for Comments: 4791 Apple +Category: Standards Track B. Desruisseaux + Oracle + L. Dusseault + CommerceNet + March 2007 + + + Calendaring Extensions to WebDAV (CalDAV) + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The IETF Trust (2007). + +Abstract + + This document defines extensions to the Web Distributed Authoring and + Versioning (WebDAV) protocol to specify a standard way of accessing, + managing, and sharing calendaring and scheduling information based on + the iCalendar format. This document defines the "calendar-access" + feature of CalDAV. + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 1] + +RFC 4791 CalDAV March 2007 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 1.1. Notational Conventions . . . . . . . . . . . . . . . . . . 5 + 1.2. XML Namespaces and Processing . . . . . . . . . . . . . . 5 + 1.3. Method Preconditions and Postconditions . . . . . . . . . 6 + 2. Requirements Overview . . . . . . . . . . . . . . . . . . . . 6 + 3. Calendaring Data Model . . . . . . . . . . . . . . . . . . . . 7 + 3.1. Calendar Server . . . . . . . . . . . . . . . . . . . . . 7 + 3.2. Recurrence and the Data Model . . . . . . . . . . . . . . 8 + 4. Calendar Resources . . . . . . . . . . . . . . . . . . . . . . 9 + 4.1. Calendar Object Resources . . . . . . . . . . . . . . . . 9 + 4.2. Calendar Collection . . . . . . . . . . . . . . . . . . . 10 + 5. Calendar Access Feature . . . . . . . . . . . . . . . . . . . 11 + 5.1. Calendar Access Support . . . . . . . . . . . . . . . . . 11 + 5.1.1. Example: Using OPTIONS for the Discovery of + Calendar Access Support . . . . . . . . . . . . . . . 12 + 5.2. Calendar Collection Properties . . . . . . . . . . . . . . 12 + 5.2.1. CALDAV:calendar-description Property . . . . . . . . . 12 + 5.2.2. CALDAV:calendar-timezone Property . . . . . . . . . . 13 + 5.2.3. CALDAV:supported-calendar-component-set Property . . . 14 + 5.2.4. CALDAV:supported-calendar-data Property . . . . . . . 15 + 5.2.5. CALDAV:max-resource-size Property . . . . . . . . . . 16 + 5.2.6. CALDAV:min-date-time Property . . . . . . . . . . . . 17 + 5.2.7. CALDAV:max-date-time Property . . . . . . . . . . . . 18 + 5.2.8. CALDAV:max-instances Property . . . . . . . . . . . . 19 + 5.2.9. CALDAV:max-attendees-per-instance Property . . . . . . 19 + 5.2.10. Additional Precondition for PROPPATCH . . . . . . . . 20 + 5.3. Creating Resources . . . . . . . . . . . . . . . . . . . . 20 + 5.3.1. MKCALENDAR Method . . . . . . . . . . . . . . . . . . 20 + 5.3.1.1. Status Codes . . . . . . . . . . . . . . . . . . . 22 + 5.3.1.2. Example: Successful MKCALENDAR Request . . . . . . 23 + 5.3.2. Creating Calendar Object Resources . . . . . . . . . . 25 + 5.3.2.1. Additional Preconditions for PUT, COPY, and + MOVE . . . . . . . . . . . . . . . . . . . . . . . 26 + 5.3.3. Non-Standard Components, Properties, and Parameters . 28 + 5.3.4. Calendar Object Resource Entity Tag . . . . . . . . . 28 + 6. Calendaring Access Control . . . . . . . . . . . . . . . . . . 29 + 6.1. Calendaring Privilege . . . . . . . . . . . . . . . . . . 29 + 6.1.1. CALDAV:read-free-busy Privilege . . . . . . . . . . . 29 + 6.2. Additional Principal Property . . . . . . . . . . . . . . 30 + 6.2.1. CALDAV:calendar-home-set Property . . . . . . . . . . 30 + 7. Calendaring Reports . . . . . . . . . . . . . . . . . . . . . 31 + 7.1. REPORT Method . . . . . . . . . . . . . . . . . . . . . . 31 + 7.2. Ordinary Collections . . . . . . . . . . . . . . . . . . . 31 + 7.3. Date and Floating Time . . . . . . . . . . . . . . . . . . 32 + 7.4. Time Range Filtering . . . . . . . . . . . . . . . . . . . 32 + 7.5. Searching Text: Collations . . . . . . . . . . . . . . . . 33 + + + +Daboo, et al. Standards Track [Page 2] + +RFC 4791 CalDAV March 2007 + + + 7.5.1. CALDAV:supported-collation-set Property . . . . . . . 34 + 7.6. Partial Retrieval . . . . . . . . . . . . . . . . . . . . 34 + 7.7. Non-Standard Components, Properties, and Parameters . . . 35 + 7.8. CALDAV:calendar-query REPORT . . . . . . . . . . . . . . . 36 + 7.8.1. Example: Partial Retrieval of Events by Time Range . . 38 + 7.8.2. Example: Partial Retrieval of Recurring Events . . . . 42 + 7.8.3. Example: Expanded Retrieval of Recurring Events . . . 45 + 7.8.4. Example: Partial Retrieval of Stored Free Busy + Components . . . . . . . . . . . . . . . . . . . . . . 48 + 7.8.5. Example: Retrieval of To-Dos by Alarm Time Range . . . 50 + 7.8.6. Example: Retrieval of Event by UID . . . . . . . . . . 51 + 7.8.7. Example: Retrieval of Events by PARTSTAT . . . . . . . 53 + 7.8.8. Example: Retrieval of Events Only . . . . . . . . . . 55 + 7.8.9. Example: Retrieval of All Pending To-Dos . . . . . . . 59 + 7.8.10. Example: Attempt to Query Unsupported Property . . . . 62 + 7.9. CALDAV:calendar-multiget REPORT . . . . . . . . . . . . . 63 + 7.9.1. Example: Successful CALDAV:calendar-multiget REPORT . 64 + 7.10. CALDAV:free-busy-query REPORT . . . . . . . . . . . . . . 66 + 7.10.1. Example: Successful CALDAV:free-busy-query REPORT . . 68 + 8. Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . 69 + 8.1. Client-to-Client Interoperability . . . . . . . . . . . . 69 + 8.2. Synchronization Operations . . . . . . . . . . . . . . . . 69 + 8.2.1. Use of Reports . . . . . . . . . . . . . . . . . . . . 69 + 8.2.1.1. Restrict the Time Range . . . . . . . . . . . . . 69 + 8.2.1.2. Synchronize by Time Range . . . . . . . . . . . . 70 + 8.2.1.3. Synchronization Process . . . . . . . . . . . . . 70 + 8.2.2. Restrict the Properties Returned . . . . . . . . . . . 72 + 8.3. Use of Locking . . . . . . . . . . . . . . . . . . . . . . 72 + 8.4. Finding Calendars . . . . . . . . . . . . . . . . . . . . 72 + 8.5. Storing and Using Attachments . . . . . . . . . . . . . . 74 + 8.5.1. Inline Attachments . . . . . . . . . . . . . . . . . . 74 + 8.5.2. External Attachments . . . . . . . . . . . . . . . . . 75 + 8.6. Storing and Using Alarms . . . . . . . . . . . . . . . . . 76 + 9. XML Element Definitions . . . . . . . . . . . . . . . . . . . 77 + 9.1. CALDAV:calendar XML Element . . . . . . . . . . . . . . . 77 + 9.2. CALDAV:mkcalendar XML Element . . . . . . . . . . . . . . 77 + 9.3. CALDAV:mkcalendar-response XML Element . . . . . . . . . . 78 + 9.4. CALDAV:supported-collation XML Element . . . . . . . . . . 78 + 9.5. CALDAV:calendar-query XML Element . . . . . . . . . . . . 78 + 9.6. CALDAV:calendar-data XML Element . . . . . . . . . . . . . 79 + 9.6.1. CALDAV:comp XML Element . . . . . . . . . . . . . . . 80 + 9.6.2. CALDAV:allcomp XML Element . . . . . . . . . . . . . . 81 + 9.6.3. CALDAV:allprop XML Element . . . . . . . . . . . . . . 81 + 9.6.4. CALDAV:prop XML Element . . . . . . . . . . . . . . . 82 + 9.6.5. CALDAV:expand XML Element . . . . . . . . . . . . . . 82 + 9.6.6. CALDAV:limit-recurrence-set XML Element . . . . . . . 83 + 9.6.7. CALDAV:limit-freebusy-set XML Element . . . . . . . . 84 + 9.7. CALDAV:filter XML Element . . . . . . . . . . . . . . . . 85 + + + +Daboo, et al. Standards Track [Page 3] + +RFC 4791 CalDAV March 2007 + + + 9.7.1. CALDAV:comp-filter XML Element . . . . . . . . . . . . 85 + 9.7.2. CALDAV:prop-filter XML Element . . . . . . . . . . . . 86 + 9.7.3. CALDAV:param-filter XML Element . . . . . . . . . . . 87 + 9.7.4. CALDAV:is-not-defined XML Element . . . . . . . . . . 88 + 9.7.5. CALDAV:text-match XML Element . . . . . . . . . . . . 88 + 9.8. CALDAV:timezone XML Element . . . . . . . . . . . . . . . 89 + 9.9. CALDAV:time-range XML Element . . . . . . . . . . . . . . 90 + 9.10. CALDAV:calendar-multiget XML Element . . . . . . . . . . . 94 + 9.11. CALDAV:free-busy-query XML Element . . . . . . . . . . . . 95 + 10. Internationalization Considerations . . . . . . . . . . . . . 95 + 11. Security Considerations . . . . . . . . . . . . . . . . . . . 95 + 12. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 96 + 12.1. Namespace Registration . . . . . . . . . . . . . . . . . . 96 + 13. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 96 + 14. References . . . . . . . . . . . . . . . . . . . . . . . . . . 97 + 14.1. Normative References . . . . . . . . . . . . . . . . . . . 97 + 14.2. Informative References . . . . . . . . . . . . . . . . . . 98 + Appendix A. CalDAV Method Privilege Table (Normative) . . . . . . 99 + Appendix B. Calendar Collections Used in the Examples . . . . . . 99 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 4] + +RFC 4791 CalDAV March 2007 + + +1. Introduction + + The concept of using HTTP [RFC2616] and WebDAV [RFC2518] as a basis + for a calendar access protocol is by no means a new concept: it was + discussed in the IETF CALSCH working group as early as 1997 or 1998. + Several companies have implemented calendar access protocols using + HTTP to upload and download iCalendar [RFC2445] objects, and using + WebDAV to get listings of resources. However, those implementations + do not interoperate because there are many small and big decisions to + be made in how to model calendaring data as WebDAV resources, as well + as how to implement required features that aren't already part of + WebDAV. This document proposes a way to model calendar data in + WebDAV, with additional features to make an interoperable calendar + access protocol. + +1.1. Notational Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The term "protected" is used in the Conformance field of property + definitions as defined in Section 1.4.2 of [RFC3253]. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:caldav" are referenced in this document + outside of the context of an XML fragment, the string "DAV:" and + "CALDAV:" will be prefixed to the element type names, respectively. + +1.2. XML Namespaces and Processing + + Definitions of XML elements in this document use XML element type + declarations (as found in XML Document Type Declarations), described + in Section 3.2 of [W3C.REC-xml-20060816]. + + The namespace "urn:ietf:params:xml:ns:caldav" is reserved for the XML + elements defined in this specification, its revisions, and related + CalDAV specifications. XML elements defined by individual + implementations MUST NOT use the "urn:ietf:params:xml:ns:caldav" + namespace, and instead should use a namespace that they control. + + The XML declarations used in this document do not include namespace + information. Thus, implementers must not use these declarations as + the only way to create valid CalDAV properties or to validate CalDAV + XML element types. Some of the declarations refer to XML elements + defined by WebDAV [RFC2518], which use the "DAV:" namespace. + Wherever such XML elements appear, they are explicitly prefixed with + "DAV:" to avoid confusion. + + + +Daboo, et al. Standards Track [Page 5] + +RFC 4791 CalDAV March 2007 + + + Also note that some CalDAV XML element names are identical to WebDAV + XML element names, though their namespace differs. Care must be + taken not to confuse the two sets of names. + + Processing of XML by CalDAV clients and servers MUST follow the rules + described in [RFC2518]; in particular, Section 14, and Appendix 3 of + that specification. + +1.3. Method Preconditions and Postconditions + + A "precondition" of a method describes the state of the server that + must be true for that method to be performed. A "postcondition" of a + method describes the state of the server that must be true after that + method has been completed. If a method precondition or postcondition + for a request is not satisfied, the response status of the request + MUST either be 403 (Forbidden), if the request should not be repeated + because it will always fail, or 409 (Conflict), if it is expected + that the user might be able to resolve the conflict and resubmit the + request. + + In order to allow better client handling of 403 and 409 responses, a + distinct XML element type is associated with each method precondition + and postcondition of a request. When a particular precondition is + not satisfied or a particular postcondition cannot be achieved, the + appropriate XML element MUST be returned as the child of a top-level + DAV:error element in the response body, unless otherwise negotiated + by the request. + +2. Requirements Overview + + This section lists what functionality is required of a CalDAV server. + To advertise support for CalDAV, a server: + + o MUST support iCalendar [RFC2445] as a media type for the calendar + object resource format; + + o MUST support WebDAV Class 1 [RFC2518] (note that [rfc2518bis] + describes clarifications to [RFC2518] that aid interoperability); + + o MUST support WebDAV ACL [RFC3744] with the additional privilege + defined in Section 6.1 of this document; + + o MUST support transport over TLS [RFC2246] as defined in [RFC2818] + (note that [RFC2246] has been obsoleted by [RFC4346]); + + o MUST support ETags [RFC2616] with additional requirements + specified in Section 5.3.4 of this document; + + + + +Daboo, et al. Standards Track [Page 6] + +RFC 4791 CalDAV March 2007 + + + o MUST support all calendaring reports defined in Section 7 of this + document; and + + o MUST advertise support on all calendar collections and calendar + object resources for the calendaring reports in the DAV:supported- + report-set property, as defined in Versioning Extensions to WebDAV + [RFC3253]. + + In addition, a server: + + o SHOULD support the MKCALENDAR method defined in Section 5.3.1 of + this document. + +3. Calendaring Data Model + + One of the features that has made WebDAV a successful protocol is its + firm data model. This makes it a useful framework for other + applications such as calendaring. This specification follows the + same pattern by developing all features based on a well-described + data model. + + As a brief overview, a CalDAV calendar is modeled as a WebDAV + collection with a defined structure; each calendar collection + contains a number of resources representing calendar objects as its + direct child resource. Each resource representing a calendar object + (event, to-do, journal entry, or other calendar components) is called + a "calendar object resource". Each calendar object resource and each + calendar collection can be individually locked and have individual + WebDAV properties. Requirements derived from this model are provided + in Section 4.1 and Section 4.2. + +3.1. Calendar Server + + A CalDAV server is a calendaring-aware engine combined with a WebDAV + repository. A WebDAV repository is a set of WebDAV collections, + containing other WebDAV resources, within a unified URL namespace. + For example, the repository "http://www.example.com/webdav/" may + contain WebDAV collections and resources, all of which have URLs + beginning with "http://www.example.com/webdav/". Note that the root + URL, "http://www.example.com/", may not itself be a WebDAV repository + (for example, if the WebDAV support is implemented through a servlet + or other Web server extension). + + A WebDAV repository MAY include calendar data in some parts of its + URL namespace, and non-calendaring data in other parts. + + A WebDAV repository can advertise itself as a CalDAV server if it + supports the functionality defined in this specification at any point + + + +Daboo, et al. Standards Track [Page 7] + +RFC 4791 CalDAV March 2007 + + + within the root of the repository. That might mean that calendaring + data is spread throughout the repository and mixed with non-calendar + data in nearby collections (e.g., calendar data may be found in + /home/lisa/calendars/ as well as in /home/bernard/calendars/, and + non-calendar data in /home/lisa/contacts/). Or, it might mean that + calendar data can be found only in certain sections of the repository + (e.g., /calendar/). Calendaring features are only required in the + repository sections that are or contain calendar object resources. + Therefore, a repository confining calendar data to the /calendar/ + collection would only need to support the CalDAV required features + within that collection. + + The CalDAV server or repository is the canonical location for + calendar data and state information. Clients may submit requests to + change data or download data. Clients may store calendar objects + offline and attempt to synchronize at a later time. However, clients + MUST be prepared for calendar data on the server to change between + the time of last synchronization and when attempting an update, as + calendar collections may be shared and accessible via multiple + clients. Entity tags and other features make this possible. + +3.2. Recurrence and the Data Model + + Recurrence is an important part of the data model because it governs + how many resources are expected to exist. This specification models + a recurring calendar component and its recurrence exceptions as a + single resource. In this model, recurrence rules, recurrence dates, + exception rules, and exception dates are all part of the data in a + single calendar object resource. This model avoids problems of + limiting how many recurrence instances to store in the repository, + how to keep recurrence instances in sync with the recurring calendar + component, and how to link recurrence exceptions with the recurring + calendar component. It also results in less data to synchronize + between client and server, and makes it easier to make changes to all + recurrence instances or to a recurrence rule. It makes it easier to + create a recurring calendar component and to delete all recurrence + instances. + + Clients are not forced to retrieve information about all recurrence + instances of a recurring component. The CALDAV:calendar-query and + CALDAV:calendar-multiget reports defined in this document allow + clients to retrieve only recurrence instances that overlap a given + time range. + + + + + + + + +Daboo, et al. Standards Track [Page 8] + +RFC 4791 CalDAV March 2007 + + +4. Calendar Resources + +4.1. Calendar Object Resources + + Calendar object resources contained in calendar collections MUST NOT + contain more than one type of calendar component (e.g., VEVENT, + VTODO, VJOURNAL, VFREEBUSY, etc.) with the exception of VTIMEZONE + components, which MUST be specified for each unique TZID parameter + value specified in the iCalendar object. For instance, a calendar + object resource can contain one VEVENT component and one VTIMEZONE + component, but it cannot contain one VEVENT component and one VTODO + component. Instead, the VEVENT and VTODO components would have to be + stored in separate calendar object resources in the same collection. + + Calendar object resources contained in calendar collections MUST NOT + specify the iCalendar METHOD property. + + The UID property value of the calendar components contained in a + calendar object resource MUST be unique in the scope of the calendar + collection in which they are stored. + + Calendar components in a calendar collection that have different UID + property values MUST be stored in separate calendar object resources. + + Calendar components with the same UID property value, in a given + calendar collection, MUST be contained in the same calendar object + resource. This ensures that all components in a recurrence "set" are + contained in the same calendar object resource. It is possible for a + calendar object resource to just contain components that represent + "overridden" instances (ones that modify the behavior of a regular + instance, and thus include a RECURRENCE-ID property) without also + including the "master" recurring component (the one that defines the + recurrence "set" and does not contain any RECURRENCE-ID property). + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 9] + +RFC 4791 CalDAV March 2007 + + + For example, given the following iCalendar object: + + BEGIN:VCALENDAR + PRODID:-//Example Corp.//CalDAV Client//EN + VERSION:2.0 + BEGIN:VEVENT + UID:1@example.com + SUMMARY:One-off Meeting + DTSTAMP:20041210T183904Z + DTSTART:20041207T120000Z + DTEND:20041207T130000Z + END:VEVENT + BEGIN:VEVENT + UID:2@example.com + SUMMARY:Weekly Meeting + DTSTAMP:20041210T183838Z + DTSTART:20041206T120000Z + DTEND:20041206T130000Z + RRULE:FREQ=WEEKLY + END:VEVENT + BEGIN:VEVENT + UID:2@example.com + SUMMARY:Weekly Meeting + RECURRENCE-ID:20041213T120000Z + DTSTAMP:20041210T183838Z + DTSTART:20041213T130000Z + DTEND:20041213T140000Z + END:VEVENT + END:VCALENDAR + + The VEVENT component with the UID value "1@example.com" would be + stored in its own calendar object resource. The two VEVENT + components with the UID value "2@example.com", which represent a + recurring event where one recurrence instance has been overridden, + would be stored in the same calendar object resource. + +4.2. Calendar Collection + + A calendar collection contains calendar object resources that + represent calendar components within a calendar. A calendar + collection is manifested to clients as a WebDAV resource collection + identified by a URL. A calendar collection MUST report the DAV: + collection and CALDAV:calendar XML elements in the value of the DAV: + resourcetype property. The element type declaration for CALDAV: + calendar is: + + + + + + +Daboo, et al. Standards Track [Page 10] + +RFC 4791 CalDAV March 2007 + + + A calendar collection can be created through provisioning (i.e., + automatically created when a user's account is provisioned), or it + can be created with the MKCALENDAR method (see Section 5.3.1). This + method can be useful for a user to create additional calendars (e.g., + soccer schedule) or for users to share a calendar (e.g., team events + or conference rooms). However, note that this document doesn't + define the purpose of extra calendar collections. Users must rely on + non-standard cues to find out what a calendar collection is for, or + use the CALDAV:calendar-description property defined in Section 5.2.1 + to provide such a cue. + + The following restrictions are applied to the resources within a + calendar collection: + + a. Calendar collections MUST only contain calendar object resources + and collections that are not calendar collections, i.e., the only + "top-level" non-collection resources allowed in a calendar + collection are calendar object resources. This ensures that + calendar clients do not have to deal with non-calendar data in a + calendar collection, though they do have to distinguish between + calendar object resources and collections when using standard + WebDAV techniques to examine the contents of a collection. + + b. Collections contained in calendar collections MUST NOT contain + calendar collections at any depth, i.e., "nesting" of calendar + collections within other calendar collections at any depth is not + allowed. This specification does not define how collections + contained in a calendar collection are used or how they relate to + any calendar object resources contained in the calendar + collection. + + Multiple calendar collections MAY be children of the same collection. + +5. Calendar Access Feature + +5.1. Calendar Access Support + + A server supporting the features described in this document MUST + include "calendar-access" as a field in the DAV response header from + an OPTIONS request on any resource that supports any calendar + properties, reports, method, or privilege. A value of "calendar- + access" in the DAV response header MUST indicate that the server + supports all MUST level requirements specified in this document. + + + + + + + + +Daboo, et al. Standards Track [Page 11] + +RFC 4791 CalDAV March 2007 + + +5.1.1. Example: Using OPTIONS for the Discovery of Calendar Access + Support + + >> Request << + + OPTIONS /home/bernard/calendars/ HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 200 OK + Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE + Allow: PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL + DAV: 1, 2, access-control, calendar-access + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Length: 0 + + In this example, the OPTIONS method returns the value "calendar- + access" in the DAV response header to indicate that the collection + "/home/bernard/calendars/" supports the properties, reports, method, + or privilege defined in this specification. + +5.2. Calendar Collection Properties + + This section defines properties for calendar collections. + +5.2.1. CALDAV:calendar-description Property + + Name: calendar-description + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a human-readable description of the calendar + collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MAY be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). An xml:lang attribute indicating the human + language of the description SHOULD be set for this property by + clients or through server provisioning. Servers MUST return any + xml:lang attribute if set for the property. + + Description: If present, the property contains a description of the + calendar collection that is suitable for presentation to a user. + If not present, the client should assume no description for the + calendar collection. + + + + +Daboo, et al. Standards Track [Page 12] + +RFC 4791 CalDAV March 2007 + + + Definition: + + + PCDATA value: string + + Example: + + Calendrier de Mathilde Desruisseaux + +5.2.2. CALDAV:calendar-timezone Property + + Name: calendar-timezone + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time zone on a calendar collection. + + Conformance: This property SHOULD be defined on all calendar + collections. If defined, it SHOULD NOT be returned by a PROPFIND + DAV:allprop request (as defined in Section 12.14.1 of [RFC2518]). + + Description: The CALDAV:calendar-timezone property is used to + specify the time zone the server should rely on to resolve "date" + values and "date with local time" values (i.e., floating time) to + "date with UTC time" values. The server will require this + information to determine if a calendar component scheduled with + "date" values or "date with local time" values overlaps a CALDAV: + time-range specified in a CALDAV:calendar-query REPORT. The + server will also require this information to compute the proper + FREEBUSY time period as "date with UTC time" in the VFREEBUSY + component returned in a response to a CALDAV:free-busy-query + REPORT request that takes into account calendar components + scheduled with "date" values or "date with local time" values. In + the absence of this property, the server MAY rely on the time zone + of their choice. + + Note: The iCalendar data embedded within the CALDAV:calendar- + timezone XML element MUST follow the standard XML character data + encoding rules, including use of <, >, & etc. entity + encoding or the use of a construct. In the + later case, the iCalendar data cannot contain the character + sequence "]]>", which is the end delimiter for the CDATA section. + + + + + + + +Daboo, et al. Standards Track [Page 13] + +RFC 4791 CalDAV March 2007 + + + Definition: + + + PCDATA value: an iCalendar object with exactly one VTIMEZONE + component. + + Example: + + BEGIN:VCALENDAR + PRODID:-//Example Corp.//CalDAV Client//EN + VERSION:2.0 + BEGIN:VTIMEZONE + TZID:US-Eastern + LAST-MODIFIED:19870101T000000Z + BEGIN:STANDARD + DTSTART:19671029T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:Eastern Standard Time (US & Canada) + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19870405T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:Eastern Daylight Time (US & Canada) + END:DAYLIGHT + END:VTIMEZONE + END:VCALENDAR + + +5.2.3. CALDAV:supported-calendar-component-set Property + + Name: supported-calendar-component-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies the calendar component types (e.g., VEVENT, + VTODO, etc.) that calendar object resources can contain in the + calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + + + +Daboo, et al. Standards Track [Page 14] + +RFC 4791 CalDAV March 2007 + + + Description: The CALDAV:supported-calendar-component-set property is + used to specify restrictions on the calendar component types that + calendar object resources may contain in a calendar collection. + Any attempt by the client to store calendar object resources with + component types not listed in this property, if it exists, MUST + result in an error, with the CALDAV:supported-calendar-component + precondition (Section 5.3.2.1) being violated. Since this + property is protected, it cannot be changed by clients using a + PROPPATCH request. However, clients can initialize the value of + this property when creating a new calendar collection with + MKCALENDAR. The empty-element tag MUST + only be specified if support for calendar object resources that + only contain VTIMEZONE components is provided or desired. Support + for VTIMEZONE components in calendar object resources that contain + VEVENT or VTODO components is always assumed. In the absence of + this property, the server MUST accept all component types, and the + client can assume that all component types are accepted. + + Definition: + + + + Example: + + + + + + +5.2.4. CALDAV:supported-calendar-data Property + + Name: supported-calendar-data + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies what media types are allowed for calendar object + resources in a calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:supported-calendar-data property is used to + specify the media type supported for the calendar object resources + contained in a given calendar collection (e.g., iCalendar version + 2.0). Any attempt by the client to store calendar object + + + +Daboo, et al. Standards Track [Page 15] + +RFC 4791 CalDAV March 2007 + + + resources with a media type not listed in this property MUST + result in an error, with the CALDAV:supported-calendar-data + precondition (Section 5.3.2.1) being violated. In the absence of + this property, the server MUST only accept data with the media + type "text/calendar" and iCalendar version 2.0, and clients can + assume that the server will only accept this data. + + Definition: + + + + Example: + + + + + +5.2.5. CALDAV:max-resource-size Property + + Name: max-resource-size + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a numeric value indicating the maximum size of a + resource in octets that the server is willing to accept when a + calendar object resource is stored in a calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-resource-size is used to specify a + numeric value that represents the maximum size in octets that the + server is willing to accept when a calendar object resource is + stored in a calendar collection. Any attempt to store a calendar + object resource exceeding this size MUST result in an error, with + the CALDAV:max-resource-size precondition (Section 5.3.2.1) being + violated. In the absence of this property, the client can assume + that the server will allow storing a resource of any reasonable + size. + + Definition: + + + PCDATA value: a numeric value (positive integer) + + + + +Daboo, et al. Standards Track [Page 16] + +RFC 4791 CalDAV March 2007 + + + Example: + + 102400 + +5.2.6. CALDAV:min-date-time Property + + Name: min-date-time + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a DATE-TIME value indicating the earliest date and + time (in UTC) that the server is willing to accept for any DATE or + DATE-TIME value in a calendar object resource stored in a calendar + collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:min-date-time is used to specify an + iCalendar DATE-TIME value in UTC that indicates the earliest + inclusive date that the server is willing to accept for any + explicit DATE or DATE-TIME value in a calendar object resource + stored in a calendar collection. Any attempt to store a calendar + object resource using a DATE or DATE-TIME value earlier than this + value MUST result in an error, with the CALDAV:min-date-time + precondition (Section 5.3.2.1) being violated. Note that servers + MUST accept recurring components that specify instances beyond + this limit, provided none of those instances have been overridden. + In that case, the server MAY simply ignore those instances outside + of the acceptable range when processing reports on the calendar + object resource. In the absence of this property, the client can + assume any valid iCalendar date may be used at least up to the + CALDAV:max-date-time value, if that is defined. + + Definition: + + + PCDATA value: an iCalendar format DATE-TIME value in UTC + + Example: + + 19000101T000000Z + + + + + +Daboo, et al. Standards Track [Page 17] + +RFC 4791 CalDAV March 2007 + + +5.2.7. CALDAV:max-date-time Property + + Name: max-date-time + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a DATE-TIME value indicating the latest date and + time (in UTC) that the server is willing to accept for any DATE or + DATE-TIME value in a calendar object resource stored in a calendar + collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-date-time is used to specify an + iCalendar DATE-TIME value in UTC that indicates the inclusive + latest date that the server is willing to accept for any date or + time value in a calendar object resource stored in a calendar + collection. Any attempt to store a calendar object resource using + a DATE or DATE-TIME value later than this value MUST result in an + error, with the CALDAV:max-date-time precondition + (Section 5.3.2.1) being violated. Note that servers MUST accept + recurring components that specify instances beyond this limit, + provided none of those instances have been overridden. In that + case, the server MAY simply ignore those instances outside of the + acceptable range when processing reports on the calendar object + resource. In the absence of this property, the client can assume + any valid iCalendar date may be used at least down to the CALDAV: + min-date-time value, if that is defined. + + Definition: + + + PCDATA value: an iCalendar format DATE-TIME value in UTC + + Example: + + 20491231T235959Z + + + + + + + + + + +Daboo, et al. Standards Track [Page 18] + +RFC 4791 CalDAV March 2007 + + +5.2.8. CALDAV:max-instances Property + + Name: max-instances + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a numeric value indicating the maximum number of + recurrence instances that a calendar object resource stored in a + calendar collection can generate. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-instances is used to specify a numeric + value that indicates the maximum number of recurrence instances + that a calendar object resource stored in a calendar collection + can generate. Any attempt to store a calendar object resource + with a recurrence pattern that generates more instances than this + value MUST result in an error, with the CALDAV:max-instances + precondition (Section 5.3.2.1) being violated. In the absence of + this property, the client can assume that the server has no limits + on the number of recurrence instances it can handle or expand. + + Definition: + + + PCDATA value: a numeric value (integer greater than zero) + + Example: + + 100 + +5.2.9. CALDAV:max-attendees-per-instance Property + + Name: max-attendees-per-instance + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a numeric value indicating the maximum number of + ATTENDEE properties in any instance of a calendar object resource + stored in a calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + + + + +Daboo, et al. Standards Track [Page 19] + +RFC 4791 CalDAV March 2007 + + + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-attendees-per-instance is used to + specify a numeric value that indicates the maximum number of + iCalendar ATTENDEE properties on any one instance of a calendar + object resource stored in a calendar collection. Any attempt to + store a calendar object resource with more ATTENDEE properties per + instance than this value MUST result in an error, with the CALDAV: + max-attendees-per-instance precondition (Section 5.3.2.1) being + violated. In the absence of this property, the client can assume + that the server can handle any number of ATTENDEE properties in a + calendar component. + + Definition: + + + PCDATA value: a numeric value (integer greater than zero) + + Example: + + 25 + +5.2.10. Additional Precondition for PROPPATCH + + This specification requires an additional Precondition for the + PROPPATCH method. The precondition is: + + (CALDAV:valid-calendar-data): The time zone specified in CALDAV: + calendar-timezone property MUST be a valid iCalendar object + containing a single valid VTIMEZONE component. + +5.3. Creating Resources + + Calendar collections and calendar object resources may be created by + either a CalDAV client or by the CalDAV server. This specification + defines restrictions and a data model that both clients and servers + MUST adhere to when manipulating such calendar data. + +5.3.1. MKCALENDAR Method + + An HTTP request using the MKCALENDAR method creates a new calendar + collection resource. A server MAY restrict calendar collection + creation to particular collections. + + + + + +Daboo, et al. Standards Track [Page 20] + +RFC 4791 CalDAV March 2007 + + + Support for MKCALENDAR on the server is only RECOMMENDED and not + REQUIRED because some calendar stores only support one calendar per + user (or principal), and those are typically pre-created for each + account. However, servers and clients are strongly encouraged to + support MKCALENDAR whenever possible to allow users to create + multiple calendar collections to help organize their data better. + + Clients SHOULD use the DAV:displayname property for a human-readable + name of the calendar. Clients can either specify the value of the + DAV:displayname property in the request body of the MKCALENDAR + request, or alternatively issue a PROPPATCH request to change the + DAV:displayname property to the appropriate value immediately after + issuing the MKCALENDAR request. Clients SHOULD NOT set the DAV: + displayname property to be the same as any other calendar collection + at the same URI "level". When displaying calendar collections to + users, clients SHOULD check the DAV:displayname property and use that + value as the name of the calendar. In the event that the DAV: + displayname property is empty, the client MAY use the last part of + the calendar collection URI as the name; however, that path segment + may be "opaque" and not represent any meaningful human-readable text. + + If a MKCALENDAR request fails, the server state preceding the request + MUST be restored. + + Marshalling: + If a request body is included, it MUST be a CALDAV:mkcalendar XML + element. Instruction processing MUST occur in the order + instructions are received (i.e., from top to bottom). + Instructions MUST either all be executed or none executed. Thus, + if any error occurs during processing, all executed instructions + MUST be undone and a proper error result returned. Instruction + processing details can be found in the definition of the DAV:set + instruction in Section 12.13.2 of [RFC2518]. + + + + If a response body for a successful request is included, it MUST + be a CALDAV:mkcalendar-response XML element. + + + + The response MUST include a Cache-Control:no-cache header. + + Preconditions: + + (DAV:resource-must-be-null): A resource MUST NOT exist at the + Request-URI; + + + + +Daboo, et al. Standards Track [Page 21] + +RFC 4791 CalDAV March 2007 + + + (CALDAV:calendar-collection-location-ok): The Request-URI MUST + identify a location where a calendar collection can be created; + + (CALDAV:valid-calendar-data): The time zone specified in the + CALDAV:calendar-timezone property MUST be a valid iCalendar object + containing a single valid VTIMEZONE component; + + (DAV:needs-privilege): The DAV:bind privilege MUST be granted to + the current user on the parent collection of the Request-URI. + + Postconditions: + + (CALDAV:initialize-calendar-collection): A new calendar collection + exists at the Request-URI. The DAV:resourcetype of the calendar + collection MUST contain both DAV:collection and CALDAV:calendar + XML elements. + +5.3.1.1. Status Codes + + The following are examples of response codes one would expect to get + in a response to a MKCALENDAR request. Note that this list is by no + means exhaustive. + + 201 (Created) - The calendar collection resource was created in + its entirety; + + 207 (Multi-Status) - The calendar collection resource was not + created since one or more DAV:set instructions specified in the + request body could not be processed successfully. The following + are examples of response codes one would expect to be used in a + 207 (Multi-Status) response in this situation: + + 403 (Forbidden) - The client, for reasons the server chooses + not to specify, cannot alter one of the properties; + + 409 (Conflict) - The client has provided a value whose + semantics are not appropriate for the property. This includes + trying to set read-only properties; + + 424 (Failed Dependency) - The DAV:set instruction on the + specified resource would have succeeded if it were not for the + failure of another DAV:set instruction specified in the request + body; + + 423 (Locked) - The specified resource is locked and the client + either is not a lock owner or the lock type requires a lock + token to be submitted and the client did not submit it; and + + + + +Daboo, et al. Standards Track [Page 22] + +RFC 4791 CalDAV March 2007 + + + 507 (Insufficient Storage) - The server did not have sufficient + space to record the property; + + 403 (Forbidden) - This indicates at least one of two conditions: + 1) the server does not allow the creation of calendar collections + at the given location in its namespace, or 2) the parent + collection of the Request-URI exists but cannot accept members; + + 409 (Conflict) - A collection cannot be made at the Request-URI + until one or more intermediate collections have been created; + + 415 (Unsupported Media Type) - The server does not support the + request type of the body; and + + 507 (Insufficient Storage) - The resource does not have sufficient + space to record the state of the resource after the execution of + this method. + +5.3.1.2. Example: Successful MKCALENDAR Request + + This example creates a calendar collection called /home/lisa/ + calendars/events/ on the server cal.example.com with specific values + for the properties DAV:displayname, CALDAV:calendar-description, + CALDAV:supported-calendar-component-set, and CALDAV:calendar- + timezone. + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 23] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + MKCALENDAR /home/lisa/calendars/events/ HTTP/1.1 + Host: cal.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + Lisa's Events + Calendar restricted to events. + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 24] + +RFC 4791 CalDAV March 2007 + + + >> Response << + + HTTP/1.1 201 Created + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Length: 0 + +5.3.2. Creating Calendar Object Resources + + Clients populate calendar collections with calendar object resources. + The URL for each calendar object resource is entirely arbitrary and + does not need to bear a specific relationship to the calendar object + resource's iCalendar properties or other metadata. New calendar + object resources MUST be created with a PUT request targeted at an + unmapped URI. A PUT request targeted at a mapped URI updates an + existing calendar object resource. + + When servers create new resources, it's not hard for the server to + choose an unmapped URI. It's slightly tougher for clients, because a + client might not want to examine all resources in the collection and + might not want to lock the entire collection to ensure that a new + resource isn't created with a name collision. However, there is an + HTTP feature to mitigate this. If the client intends to create a new + non-collection resource, such as a new VEVENT, the client SHOULD use + the HTTP request header "If-None-Match: *" on the PUT request. The + Request-URI on the PUT request MUST include the target collection, + where the resource is to be created, plus the name of the resource in + the last path segment. The "If-None-Match: *" request header ensures + that the client will not inadvertently overwrite an existing resource + if the last path segment turned out to already be used. + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 25] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + PUT /home/lisa/calendars/events/qwue23489.ics HTTP/1.1 + If-None-Match: * + Host: cal.example.com + Content-Type: text/calendar + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + UID:20010712T182145Z-123401@example.com + DTSTAMP:20060712T182145Z + DTSTART:20060714T170000Z + DTEND:20060715T040000Z + SUMMARY:Bastille Day Party + END:VEVENT + END:VCALENDAR + + >> Response << + + HTTP/1.1 201 Created + Content-Length: 0 + Date: Sat, 11 Nov 2006 09:32:12 GMT + ETag: "123456789-000-111" + + The request to change an existing event is the same, but with a + specific ETag in the "If-Match" header, rather than the "If-None- + Match" header. + + As indicated in Section 3.10 of [RFC2445], the URL of calendar object + resources containing (an arbitrary set of) calendaring and scheduling + information may be suffixed by ".ics", and the URL of calendar object + resources containing free or busy time information may be suffixed by + ".ifb". + +5.3.2.1. Additional Preconditions for PUT, COPY, and MOVE + + This specification creates additional Preconditions for PUT, COPY, + and MOVE methods. These preconditions apply when a PUT operation of + a calendar object resource into a calendar collection occurs, or when + a COPY or MOVE operation of a calendar object resource into a + calendar collection occurs, or when a COPY or MOVE operation occurs + on a calendar collection. + + + + + + +Daboo, et al. Standards Track [Page 26] + +RFC 4791 CalDAV March 2007 + + + The new preconditions are: + + (CALDAV:supported-calendar-data): The resource submitted in the + PUT request, or targeted by a COPY or MOVE request, MUST be a + supported media type (i.e., iCalendar) for calendar object + resources; + + (CALDAV:valid-calendar-data): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST be valid data + for the media type being specified (i.e., MUST contain valid + iCalendar data); + + (CALDAV:valid-calendar-object-resource): The resource submitted in + the PUT request, or targeted by a COPY or MOVE request, MUST obey + all restrictions specified in Section 4.1 (e.g., calendar object + resources MUST NOT contain more than one type of calendar + component, calendar object resources MUST NOT specify the + iCalendar METHOD property, etc.); + + (CALDAV:supported-calendar-component): The resource submitted in + the PUT request, or targeted by a COPY or MOVE request, MUST + contain a type of calendar component that is supported in the + targeted calendar collection; + + (CALDAV:no-uid-conflict): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST NOT specify + an iCalendar UID property value already in use in the targeted + calendar collection or overwrite an existing calendar object + resource with one that has a different UID property value. + Servers SHOULD report the URL of the resource that is already + making use of the same UID property value in the DAV:href element; + + + + (CALDAV:calendar-collection-location-ok): In a COPY or MOVE + request, when the Request-URI is a calendar collection, the + Destination-URI MUST identify a location where a calendar + collection can be created; + + (CALDAV:max-resource-size): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST have an octet + size less than or equal to the value of the CALDAV:max-resource- + size property value (Section 5.2.5) on the calendar collection + where the resource will be stored; + + (CALDAV:min-date-time): The resource submitted in the PUT request, + or targeted by a COPY or MOVE request, MUST have all of its + iCalendar DATE or DATE-TIME property values (for each recurring + + + +Daboo, et al. Standards Track [Page 27] + +RFC 4791 CalDAV March 2007 + + + instance) greater than or equal to the value of the CALDAV:min- + date-time property value (Section 5.2.6) on the calendar + collection where the resource will be stored; + + (CALDAV:max-date-time): The resource submitted in the PUT request, + or targeted by a COPY or MOVE request, MUST have all of its + iCalendar DATE or DATE-TIME property values (for each recurring + instance) less than the value of the CALDAV:max-date-time property + value (Section 5.2.7) on the calendar collection where the + resource will be stored; + + (CALDAV:max-instances): The resource submitted in the PUT request, + or targeted by a COPY or MOVE request, MUST generate a number of + recurring instances less than or equal to the value of the CALDAV: + max-instances property value (Section 5.2.8) on the calendar + collection where the resource will be stored; + + (CALDAV:max-attendees-per-instance): The resource submitted in the + PUT request, or targeted by a COPY or MOVE request, MUST have a + number of ATTENDEE properties on any one instance less than or + equal to the value of the CALDAV:max-attendees-per-instance + property value (Section 5.2.9) on the calendar collection where + the resource will be stored; + +5.3.3. Non-Standard Components, Properties, and Parameters + + iCalendar provides a "standard mechanism for doing non-standard + things". This extension support allows implementers to make use of + non-standard components, properties, and parameters whose names are + prefixed with the text "X-". + + Servers MUST support the use of non-standard components, properties, + and parameters in calendar object resources stored via the PUT + method. + + Servers may need to enforce rules for their own "private" components, + properties, or parameters, so servers MAY reject any attempt by the + client to change those or use values for those outside of any + restrictions the server may have. Servers SHOULD ensure that any + "private" components, properties, or parameters it uses follow the + convention of including a vendor id in the "X-" name, as described in + Section 4.2 of [RFC2445], e.g., "X-ABC-PRIVATE". + +5.3.4. Calendar Object Resource Entity Tag + + The DAV:getetag property MUST be defined and set to a strong entity + tag on all calendar object resources. + + + + +Daboo, et al. Standards Track [Page 28] + +RFC 4791 CalDAV March 2007 + + + A response to a GET request targeted at a calendar object resource + MUST contain an ETag response header field indicating the current + value of the strong entity tag of the calendar object resource. + + Servers SHOULD return a strong entity tag (ETag header) in a PUT + response when the stored calendar object resource is equivalent by + octet equality to the calendar object resource submitted in the body + of the PUT request. This allows clients to reliably use the returned + strong entity tag for data synchronization purposes. For instance, + the client can do a PROPFIND request on the stored calendar object + resource and have the DAV:getetag property returned, and compare that + value with the strong entity tag it received on the PUT response, and + know that if they are equal, then the calendar object resource on the + server has not been changed. + + In the case where the data stored by a server as a result of a PUT + request is not equivalent by octet equality to the submitted calendar + object resource, the behavior of the ETag response header is not + specified here, with the exception that a strong entity tag MUST NOT + be returned in the response. As a result, clients may need to + retrieve the modified calendar object resource (and ETag) as a basis + for further changes, rather than use the calendar object resource it + had sent with the PUT request. + +6. Calendaring Access Control + +6.1. Calendaring Privilege + + CalDAV servers MUST support and adhere to the requirements of WebDAV + ACL [RFC3744]. WebDAV ACL provides a framework for an extensible set + of privileges that can be applied to WebDAV collections and ordinary + resources. CalDAV servers MUST also support the calendaring + privilege defined in this section. + +6.1.1. CALDAV:read-free-busy Privilege + + Calendar users often wish to allow other users to see their busy time + information, without viewing the other details of the calendar + components (e.g., location, summary, attendees). This allows a + significant amount of privacy while still allowing other users to + schedule meetings at times when the user is likely to be free. + + The CALDAV:read-free-busy privilege controls which calendar + collections, regular collections, and calendar object resources are + examined when a CALDAV:free-busy-query REPORT request is processed + (see Section 7.10). This privilege can be granted on calendar + collections, regular collections, or calendar object resources. + + + + +Daboo, et al. Standards Track [Page 29] + +RFC 4791 CalDAV March 2007 + + + Servers MUST support this privilege on all calendar collections, + regular collections, and calendar object resources. + + + + + The CALDAV:read-free-busy privilege MUST be aggregated in the DAV: + read privilege. Servers MUST allow the CALDAV:read-free-busy to be + granted without the DAV:read privilege being granted. + + Clients should note that when only the CALDAV:read-free-busy + privilege has been granted on a resource, access to GET, HEAD, + OPTIONS, and PROPFIND on the resource is not implied (those + operations are governed by the DAV:read privilege). + +6.2. Additional Principal Property + + This section defines an additional property for WebDAV principal + resources, as defined in [RFC3744]. + +6.2.1. CALDAV:calendar-home-set Property + + Name: calendar-home-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identifies the URL of any WebDAV collections that contain + calendar collections owned by the associated principal resource. + + Conformance: This property SHOULD be defined on a principal + resource. If defined, it MAY be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:calendar-home-set property is meant to allow + users to easily find the calendar collections owned by the + principal. Typically, users will group all the calendar + collections that they own under a common collection. This + property specifies the URL of collections that are either calendar + collections or ordinary collections that have child or descendant + calendar collections owned by the principal. + + Definition: + + + + + + + + +Daboo, et al. Standards Track [Page 30] + +RFC 4791 CalDAV March 2007 + + + Example: + + + http://cal.example.com/home/bernard/calendars/ + + +7. Calendaring Reports + + This section defines the reports that CalDAV servers MUST support on + calendar collections and calendar object resources. + + CalDAV servers MUST advertise support for these reports on all + calendar collections and calendar object resources with the DAV: + supported-report-set property, defined in Section 3.1.5 of [RFC3253]. + CalDAV servers MAY also advertise support for these reports on + ordinary collections. + + Some of these reports allow calendar data (from possibly multiple + resources) to be returned. + +7.1. REPORT Method + + The REPORT method (defined in Section 3.6 of [RFC3253]) provides an + extensible mechanism for obtaining information about one or more + resources. Unlike the PROPFIND method, which returns the value of + one or more named properties, the REPORT method can involve more + complex processing. REPORT is valuable in cases where the server has + access to all of the information needed to perform the complex + request (such as a query), and where it would require multiple + requests for the client to retrieve the information needed to perform + the same request. + + CalDAV servers MUST support the DAV:expand-property REPORT defined in + Section 3.8 of [RFC3253]. + +7.2. Ordinary Collections + + Servers MAY support the reports defined in this document on ordinary + collections (collections that are not calendar collections), in + addition to calendar collections or calendar object resources. In + computing responses to the reports on ordinary collections, servers + MUST only consider calendar object resources contained in calendar + collections that are targeted by the REPORT request, based on the + value of the Depth request header. + + + + + + +Daboo, et al. Standards Track [Page 31] + +RFC 4791 CalDAV March 2007 + + +7.3. Date and Floating Time + + iCalendar provides a way to specify DATE and DATE-TIME values that + are not bound to any time zone in particular, hereafter called + "floating date" and "floating time", respectively. These values are + used to represent the same day, hour, minute, and second value, + regardless of which time zone is being observed. For instance, the + DATE value "20051111", represents November 11, 2005 in no specific + time zone, while the DATE-TIME value "20051111T111100" represents + November 11, 2005, at 11:11 A.M. in no specific time zone. + + CalDAV servers may need to convert "floating date" and "floating + time" values in date with UTC time values in the processing of + calendaring REPORT requests. + + For the CALDAV:calendar-query REPORT, CalDAV servers MUST rely on the + value of the CALDAV:timezone XML element, if specified as part of the + request body, to perform the proper conversion of "floating date" and + "floating time" values to date with UTC time values. If the CALDAV: + timezone XML element is not specified in the request body, CalDAV + servers MUST rely on the value of the CALDAV:calendar-timezone + property, if defined, or else the CalDAV servers MAY rely on the time + zone of their choice. + + For the CALDAV:free-busy-query REPORT, CalDAV servers MUST rely on + the value of the CALDAV:calendar-timezone property, if defined, to + compute the proper FREEBUSY time period value as date with UTC time + for calendar components scheduled with "floating date" or "floating + time". If the CALDAV:calendar-timezone property is not defined, + CalDAV servers MAY rely on the time zone of their choice. + +7.4. Time Range Filtering + + Some of the reports defined in this section can include a time range + filter that is used to restrict the set of calendar object resources + returned to just those that overlap the specified time range. The + time range filter can be applied to a calendar component as a whole, + or to specific calendar component properties with DATE or DATE-TIME + value types. + + To determine whether a calendar object resource matches the time + range filter element, the start and end times for the targeted + component or property are determined and then compared to the + requested time range. If there is an overlap with the requested time + range, then the calendar object resource matches the filter element. + The rules defined in [RFC2445] for determining the actual start and + end times of calendar components MUST be used, and these are fully + enumerated in Section 9.9 of this document. + + + +Daboo, et al. Standards Track [Page 32] + +RFC 4791 CalDAV March 2007 + + + When such time range filtering is used, special consideration must be + given to recurring calendar components, such as VEVENT and VTODO. + The server MUST expand recurring components to determine whether any + recurrence instances overlap the specified time range. If one or + more recurrence instances overlap the time range, then the calendar + object resource matches the filter element. + +7.5. Searching Text: Collations + + Some of the reports defined in this section do text matches of + character strings provided by the client and are compared to stored + calendar data. Since iCalendar data is, by default, encoded in the + UTF-8 charset and may include characters outside the US-ASCII charset + range in some property and parameter values, there is a need to + ensure that text matching follows well-defined rules. + + To deal with this, this specification makes use of the IANA Collation + Registry defined in [RFC4790] to specify collations that may be used + to carry out the text comparison operations with a well-defined rule. + + The comparisons used in CalDAV are all "substring" matches, as per + [RFC4790], Section 4.2. Collations supported by the server MUST + support "substring" match operations. + + CalDAV servers are REQUIRED to support the "i;ascii-casemap" and + "i;octet" collations, as described in [RFC4790], and MAY support + other collations. + + Servers MUST advertise the set of collations that they support via + the CALDAV:supported-collation-set property defined on any resource + that supports reports that use collations. + + Clients MUST only use collations from the list advertised by the + server. + + In the absence of a collation explicitly specified by the client, or + if the client specifies the "default" collation identifier (as + defined in [RFC4790], Section 3.1), the server MUST default to using + "i;ascii-casemap" as the collation. + + Wildcards (as defined in [RFC4790], Section 3.2) MUST NOT be used in + the collation identifier. + + If the client chooses a collation not supported by the server, the + server MUST respond with a CALDAV:supported-collation precondition + error response. + + + + + +Daboo, et al. Standards Track [Page 33] + +RFC 4791 CalDAV March 2007 + + +7.5.1. CALDAV:supported-collation-set Property + + Name: supported-collation-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identifies the set of collations supported by the server + for text matching operations. + + Conformance: This property MUST be defined on any resource that + supports a report that does text matching. If defined, it MUST be + protected and SHOULD NOT be returned by a PROPFIND DAV:allprop + request (as defined in Section 12.14.1 of [RFC2518]). + + Description: The CALDAV:supported-collation-set property contains + zero or more CALDAV:supported-collation elements, which specify + the collection identifiers of the collations supported by the + server. + + Definition: + + + + + + Example: + + + i;ascii-casemap + i;octet + + +7.6. Partial Retrieval + + Some calendaring reports defined in this document allow partial + retrieval of calendar object resources. A CalDAV client can specify + what information to return in the body of a calendaring REPORT + request. + + A CalDAV client can request particular WebDAV property values, all + WebDAV property values, or a list of the names of the resource's + WebDAV properties. A CalDAV client can also request calendar data to + be returned and specify whether all calendar components and + properties should be returned, or only particular ones. See CALDAV: + calendar-data in Section 9.6. + + + + + +Daboo, et al. Standards Track [Page 34] + +RFC 4791 CalDAV March 2007 + + + By default, the returned calendar data will include the component + that defines the recurrence set, referred to as the "master + component", as well as the components that define exceptions to the + recurrence set, referred to as the "overridden components". + + A CalDAV client that is only interested in the recurrence instances + that overlap a specified time range can request to receive only the + "master component", along with the "overridden components" that + impact the specified time range, and thus, limit the data returned by + the server (see CALDAV:limit-recurrence-set in Section 9.6.6). An + overridden component impacts a time range if its current start and + end times overlap the time range, or if the original start and end + times -- the ones that would have been used if the instance were not + overridden -- overlap the time range, or if it affects other + instances that overlap the time range. + + A CalDAV client with no support for recurrence properties (i.e., + EXDATE, EXRULE, RDATE, and RRULE) and possibly VTIMEZONE components, + or a client unwilling to perform recurrence expansion because of + limited processing capability, can request to receive only the + recurrence instances that overlap a specified time range as separate + calendar components that each define exactly one recurrence instance + (see CALDAV:expand in Section 9.6.5.) + + Finally, in the case of VFREEBUSY components, a CalDAV client can + request to receive only the FREEBUSY property values that overlap a + specified time range (see CALDAV:limit-freebusy-set in + Section 9.6.7.) + +7.7. Non-Standard Components, Properties, and Parameters + + Servers MUST support the use of non-standard component, property, or + parameter names in the CALDAV:calendar-data XML element in + calendaring REPORT requests to allow clients to request that non- + standard components, properties, and parameters be returned in the + calendar data provided in the response. + + Servers MAY support the use of non-standard component, property, or + parameter names in the CALDAV:comp-filter, CALDAV:prop-filter, and + CALDAV:param-filter XML elements specified in the CALDAV:filter XML + element of calendaring REPORT requests. + + Servers MUST fail with the CALDAV:supported-filter precondition if a + calendaring REPORT request uses a CALDAV:comp-filter, CALDAV:prop- + filter, or CALDAV:param-filter XML element that makes reference to a + non-standard component, property, or parameter name on which the + server does not support queries. + + + + +Daboo, et al. Standards Track [Page 35] + +RFC 4791 CalDAV March 2007 + + +7.8. CALDAV:calendar-query REPORT + + The CALDAV:calendar-query REPORT performs a search for all calendar + object resources that match a specified filter. The response of this + report will contain all the WebDAV properties and calendar object + resource data specified in the request. In the case of the CALDAV: + calendar-data XML element, one can explicitly specify the calendar + components and properties that should be returned in the calendar + object resource data that matches the filter. + + The format of this report is modeled on the PROPFIND method. The + request and response bodies of the CALDAV:calendar-query REPORT use + XML elements that are also used by PROPFIND. In particular, the + request can include XML elements to request WebDAV properties to be + returned. When that occurs, the response should follow the same + behavior as PROPFIND with respect to the DAV:multistatus response + elements used to return specific property results. For instance, a + request to retrieve the value of a property that does not exist is an + error and MUST be noted with a response XML element that contains a + 404 (Not Found) status value. + + Support for the CALDAV:calendar-query REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CALDAV:calendar-query XML element, as + defined in Section 9.5. + + The request MAY include a Depth header. If no Depth header is + included, Depth:0 is assumed. + + The response body for a successful request MUST be a DAV: + multistatus XML element (i.e., the response uses the same format + as the response for PROPFIND). In the case where there are no + response elements, the returned DAV:multistatus XML element is + empty. + + The response body for a successful CALDAV:calendar-query REPORT + request MUST contain a DAV:response element for each iCalendar + object that matched the search filter. Calendar data is being + returned in the CALDAV:calendar-data XML element inside the DAV: + propstat XML element. + + Preconditions: + + (CALDAV:supported-calendar-data): The attributes "content-type" + and "version" of the CALDAV:calendar-data XML element (see + + + + +Daboo, et al. Standards Track [Page 36] + +RFC 4791 CalDAV March 2007 + + + Section 9.6) specify a media type supported by the server for + calendar object resources. + + (CALDAV:valid-filter): The CALDAV:filter XML element (see + Section 9.7) specified in the REPORT request MUST be valid. For + instance, a CALDAV:filter cannot nest a + element in a element, and a CALDAV:filter + cannot nest a element in a + element. + + (CALDAV:supported-filter): The CALDAV:comp-filter (see + Section 9.7.1), CALDAV:prop-filter (see Section 9.7.2), and + CALDAV:param-filter (see Section 9.7.3) XML elements used in the + CALDAV:filter XML element (see Section 9.7) in the REPORT request + only make reference to components, properties, and parameters for + which queries are supported by the server, i.e., if the CALDAV: + filter element attempts to reference an unsupported component, + property, or parameter, this precondition is violated. Servers + SHOULD report the CALDAV:comp-filter, CALDAV:prop-filter, or + CALDAV:param-filter for which it does not provide support. + + + + (CALDAV:valid-calendar-data): The time zone specified in the + REPORT request MUST be a valid iCalendar object containing a + single valid VTIMEZONE component. + + (CALDAV:min-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values greater than + or equal to the value of the CALDAV:min-date-time property value + (Section 5.2.6) on the calendar collections being targeted by the + REPORT request; + + (CALDAV:max-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values less than or + equal to the value of the CALDAV:max-date-time property value + (Section 5.2.7) on the calendar collections being targeted by the + REPORT request; + + (CALDAV:supported-collation): Any XML attribute specifying a + collation MUST specify a collation supported by the server as + described in Section 7.5. + + + + + + + +Daboo, et al. Standards Track [Page 37] + +RFC 4791 CalDAV March 2007 + + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + calendar object resources must fall within server-specific, + predefined limits. For example, this condition might be triggered + if a search specification would cause the return of an extremely + large number of responses. + +7.8.1. Example: Partial Retrieval of Events by Time Range + + In this example, the client requests the server to return specific + components and properties of the VEVENT components that overlap the + time range from January 4, 2006, at 00:00:00 A.M. UTC to January 5, + 2006, at 00:00:00 A.M. UTC. In addition, the DAV:getetag property is + also requested and returned as part of the response. Note that the + first calendar object returned is a recurring event whose first + instance lies outside the requested time range, but whose third + instance does overlap the time range. Note that due to the CALDAV: + calendar-data element restrictions, the DTSTAMP property in VEVENT + components has not been returned, and the only property returned in + the VCALENDAR object is VERSION. + + See Appendix B for the calendar data being targeted by this example. + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 38] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + +Daboo, et al. Standards Track [Page 39] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060106T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060106T120000 + SUMMARY:Event #2 bis bis + UID:00959BC664CA650E933C892C@example.com + + + +Daboo, et al. Standards Track [Page 40] + +RFC 4791 CalDAV March 2007 + + + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + +Daboo, et al. Standards Track [Page 41] + +RFC 4791 CalDAV March 2007 + + +7.8.2. Example: Partial Retrieval of Recurring Events + + In this example, the client requests the server to return VEVENT + components that overlap the time range from January 3, 2006, at 00: + 00:00 A.M. UTC to January 5, 2006, at 00:00:00 A.M. UTC. Use of the + CALDAV:limit-recurrence-set element causes the server to only return + overridden recurrence components that overlap the time range + specified in that element or that affect other instances that overlap + the time range (e.g., in the case of a THISANDFUTURE behavior). In + this example, the first overridden component in the matching resource + is returned, but the second one is not. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + +Daboo, et al. Standards Track [Page 42] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + + + +Daboo, et al. Standards Track [Page 43] + +RFC 4791 CalDAV March 2007 + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +Daboo, et al. Standards Track [Page 44] + +RFC 4791 CalDAV March 2007 + + + + + +7.8.3. Example: Expanded Retrieval of Recurring Events + + In this example, the client requests the server to return VEVENT + components that overlap the time range from January 2, 2006, at 00: + 00:00 A.M. UTC to January 5, 2006, at 00:00:00 A.M. UTC and to return + recurring calendar components expanded into individual recurrence + instance calendar components. Use of the CALDAV:expand element + causes the server to only return overridden recurrence instances that + overlap the time range specified in that element. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + +Daboo, et al. Standards Track [Page 45] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART:20060103T170000 + DURATION:PT1H + RECURRENCE-ID:20060103T170000 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART:20060104T190000 + DURATION:PT1H + RECURRENCE-ID:20060104T170000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART:20060104T150000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + + + +Daboo, et al. Standards Track [Page 46] + +RFC 4791 CalDAV March 2007 + + + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 47] + +RFC 4791 CalDAV March 2007 + + +7.8.4. Example: Partial Retrieval of Stored Free Busy Components + + In this example, the client requests the server to return the + VFREEBUSY components that have free busy information that overlap the + time range from January 2, 2006, at 00:00:00 A.M. UTC (inclusively) + to January 3, 2006, at 00:00:00 A.M. UTC (exclusively). Use of the + CALDAV:limit-freebusy-set element causes the server to only return + the FREEBUSY property values that overlap the time range specified in + that element. Note that this is not an example of discovering when + the calendar owner is busy. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 48] + +RFC 4791 CalDAV March 2007 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd8.ics + + + "fffff-abcd8" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VFREEBUSY + ORGANIZER;CN="Bernard Desruisseaux":mailto:bernard@example.com + UID:76ef34-54a3d2@example.com + DTSTAMP:20050530T123421Z + DTSTART:20060101T100000Z + DTEND:20060108T100000Z + FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z + END:VFREEBUSY + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 49] + +RFC 4791 CalDAV March 2007 + + +7.8.5. Example: Retrieval of To-Dos by Alarm Time Range + + In this example, the client requests the server to return the VTODO + components that have an alarm trigger scheduled in the specified time + range. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 50] + +RFC 4791 CalDAV March 2007 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd4.ics + + + "fffff-abcd4" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235300Z + DUE;TZID=US/Eastern:20060106T120000 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:NEEDS-ACTION + SUMMARY:Task #2 + UID:E10BA47467C5C69BB74E8720@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.6. Example: Retrieval of Event by UID + + In this example, the client requests the server to return the VEVENT + component that has the UID property set to + "DC6C50A017428C5216A2F1CD@example.com". + + See Appendix B for the calendar data being targeted by this example. + + + + + +Daboo, et al. Standards Track [Page 51] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + DC6C50A017428C5216A2F1CD@example.com + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + + + +Daboo, et al. Standards Track [Page 52] + +RFC 4791 CalDAV March 2007 + + + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.7. Example: Retrieval of Events by PARTSTAT + + In this example, the client requests the server to return the VEVENT + components that have the ATTENDEE property with the value + "mailto:lisa@example.com" and for which the PARTSTAT parameter is set + to NEEDS-ACTION. + + See Appendix B for the calendar data being targeted by this example. + + + + + + + +Daboo, et al. Standards Track [Page 53] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + mailto:lisa@example.com + + NEEDS-ACTION + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + + + +Daboo, et al. Standards Track [Page 54] + +RFC 4791 CalDAV March 2007 + + + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.8. Example: Retrieval of Events Only + + In this example, the client requests the server to return all VEVENT + components. + + See Appendix B for the calendar data being targeted by this example. + + + + + +Daboo, et al. Standards Track [Page 55] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd1.ics + + + "fffff-abcd1" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + + + +Daboo, et al. Standards Track [Page 56] + +RFC 4791 CalDAV March 2007 + + + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001102Z + DTSTART;TZID=US/Eastern:20060102T100000 + DURATION:PT1H + SUMMARY:Event #1 + Description:Go Steelers! + UID:74855313FA803DA593CD579A@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + + + +Daboo, et al. Standards Track [Page 57] + +RFC 4791 CalDAV March 2007 + + + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060106T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060106T120000 + SUMMARY:Event #2 bis bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + + + +Daboo, et al. Standards Track [Page 58] + +RFC 4791 CalDAV March 2007 + + + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.9. Example: Retrieval of All Pending To-Dos + + In this example, the client requests the server to return all VTODO + components that do not include a COMPLETED property and do not have a + STATUS property value matching CANCELLED, i.e., VTODOs that still + need to be worked on. + + See Appendix B for the calendar data being targeted by this example. + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 59] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + CANCELLED + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd4.ics + + + "fffff-abcd4" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + + + +Daboo, et al. Standards Track [Page 60] + +RFC 4791 CalDAV March 2007 + + + DTSTAMP:20060205T235335Z + DUE;VALUE=DATE:20060104 + STATUS:NEEDS-ACTION + SUMMARY:Task #1 + UID:DDDEEB7915FA61233B861457@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd5.ics + + + "fffff-abcd5" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235300Z + DUE;VALUE=DATE:20060106 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:NEEDS-ACTION + SUMMARY:Task #2 + UID:E10BA47467C5C69BB74E8720@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + +Daboo, et al. Standards Track [Page 61] + +RFC 4791 CalDAV March 2007 + + +7.8.10. Example: Attempt to Query Unsupported Property + + In this example, the client requests the server to return all VEVENT + components that include an X-ABC-GUID property with a value matching + "ABC". However, the server does not support querying that non- + standard property, and instead returns an error response. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + ABC + + + + + + + >> Response << + + HTTP/1.1 403 Forbidden + Date: Sat, 11 Nov 2005 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + +Daboo, et al. Standards Track [Page 62] + +RFC 4791 CalDAV March 2007 + + +7.9. CALDAV:calendar-multiget REPORT + + The CALDAV:calendar-multiget REPORT is used to retrieve specific + calendar object resources from within a collection, if the Request- + URI is a collection, or to retrieve a specific calendar object + resource, if the Request-URI is a calendar object resource. This + report is similar to the CALDAV:calendar-query REPORT (see + Section 7.8), except that it takes a list of DAV:href elements, + instead of a CALDAV:filter element, to determine which calendar + object resources to return. + + Support for the CALDAV:calendar-multiget REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CALDAV:calendar-multiget XML element + (see Section 9.10). If the Request-URI is a collection resource, + then the DAV:href elements MUST refer to calendar object resources + within that collection, and they MAY refer to calendar object + resources at any depth within the collection. As a result, the + "Depth" header MUST be ignored by the server and SHOULD NOT be + sent by the client. If the Request-URI refers to a non-collection + resource, then there MUST be a single DAV:href element that is + equivalent to the Request-URI. + + The response body for a successful request MUST be a DAV: + multistatus XML element. + + The response body for a successful CALDAV:calendar-multiget REPORT + request MUST contain a DAV:response element for each calendar + object resource referenced by the provided set of DAV:href + elements. Calendar data is being returned in the CALDAV:calendar- + data element inside the DAV:prop element. + + In the case of an error accessing any of the provided DAV:href + resources, the server MUST return the appropriate error status + code in the DAV:status element of the corresponding DAV:response + element. + + Preconditions: + + (CALDAV:supported-calendar-data): The attributes "content-type" + and "version" of the CALDAV:calendar-data XML elements (see + Section 9.6) specify a media type supported by the server for + calendar object resources. + + (CALDAV:min-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values greater than + + + +Daboo, et al. Standards Track [Page 63] + +RFC 4791 CalDAV March 2007 + + + or equal to the value of the CALDAV:min-date-time property value + (Section 5.2.6) on the calendar collections being targeted by the + REPORT request; + + (CALDAV:max-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values less than or + equal to the value of the CALDAV:max-date-time property value + (Section 5.2.7) on the calendar collections being targeted by the + REPORT request; + + Postconditions: + + None. + +7.9.1. Example: Successful CALDAV:calendar-multiget REPORT + + In this example, the client requests the server to return specific + properties of the VEVENT components referenced by specific URIs. In + addition, the DAV:getetag property is also requested and returned as + part of the response. Note that in this example, the resource at + http://cal.example.com/bernard/work/mtg1.ics does not exist, + resulting in an error status response. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + /bernard/work/abcd1.ics + /bernard/work/mtg1.ics + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + + + +Daboo, et al. Standards Track [Page 64] + +RFC 4791 CalDAV March 2007 + + + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd1.ics + + + "fffff-abcd1" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001102Z + DTSTART;TZID=US/Eastern:20060102T100000 + DURATION:PT1H + SUMMARY:Event #1 + Description:Go Steelers! + UID:74855313FA803DA593CD579A@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/mtg1.ics + HTTP/1.1 404 Not Found + + + +Daboo, et al. Standards Track [Page 65] + +RFC 4791 CalDAV March 2007 + + + + + +7.10. CALDAV:free-busy-query REPORT + + The CALDAV:free-busy-query REPORT generates a VFREEBUSY component + containing free busy information for all the calendar object + resources targeted by the request and that have the CALDAV:read-free- + busy or DAV:read privilege granted to the current user. + + Only VEVENT components without a TRANSP property or with the TRANSP + property set to OPAQUE, and VFREEBUSY components SHOULD be considered + in generating the free busy time information. + + In the case of VEVENT components, the free or busy time type (FBTYPE) + of the FREEBUSY properties in the returned VFREEBUSY component SHOULD + be derived from the value of the TRANSP and STATUS properties, as + outlined in the table below: + + +---------------------------++------------------+ + | VEVENT || VFREEBUSY | + +-------------+-------------++------------------+ + | TRANSP | STATUS || FBTYPE | + +=============+=============++==================+ + | | CONFIRMED || BUSY | + | | (default) || | + | OPAQUE +-------------++------------------+ + | (default) | CANCELLED || FREE | + | +-------------++------------------+ + | | TENTATIVE || BUSY-TENTATIVE | + | +-------------++------------------+ + | | x-name || BUSY or | + | | || x-name | + +-------------+-------------++------------------+ + | | CONFIRMED || | + | TRANSPARENT | CANCELLED || FREE | + | | TENTATIVE || | + | | x-name || | + +-------------+-------------++------------------+ + + Duplicate busy time periods with the same FBTYPE parameter value + SHOULD NOT be specified in the returned VFREEBUSY component. Servers + SHOULD coalesce consecutive or overlapping busy time periods of the + same type. Busy time periods with different FBTYPE parameter values + MAY overlap. + + Support for the CALDAV:free-busy-query REPORT is REQUIRED. + + + + +Daboo, et al. Standards Track [Page 66] + +RFC 4791 CalDAV March 2007 + + + Marshalling: + + The request body MUST be a CALDAV:free-busy-query XML element (see + Section 9.11), which MUST contain exactly one CALDAV:time-range + XML element, as defined in Section 9.9. + + The request MAY include a Depth header. If no Depth header is + included, Depth:0 is assumed. + + The response body for a successful request MUST be an iCalendar + object that contains exactly one VFREEBUSY component that + describes the busy time intervals for the calendar object + resources containing VEVENT, or VFREEBUSY components that satisfy + the Depth value and for which the current user is at least granted + the CALDAV:read-free-busy privilege. If no calendar object + resources are found to satisfy these conditions, a VFREEBUSY + component with no FREEBUSY property MUST be returned. This report + only returns busy time information. Free time information can be + inferred from the returned busy time information. + + If the current user is not granted the CALDAV:read-free-busy or + DAV:read privileges on the Request-URI, the CALDAV:free-busy-query + REPORT request MUST fail and return a 404 (Not Found) status + value. This restriction will prevent users from discovering URLs + of resources for which they are only granted the CALDAV:read-free- + busy privilege. + + The CALDAV:free-busy-query REPORT request can only be run against + a collection (either a regular collection or a calendar + collection). An attempt to run the report on a calendar object + resource MUST fail and return a 403 (Forbidden) status value. + + Preconditions: + + None. + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + calendar object resources must fall within server-specific, + predefined limits. For example, this postcondition might fail if + the specified CALDAV:time-range would cause an extremely large + number of calendar object resources to be considered in computing + the response. + + + + + + + +Daboo, et al. Standards Track [Page 67] + +RFC 4791 CalDAV March 2007 + + +7.10.1. Example: Successful CALDAV:free-busy-query REPORT + + In this example, the client requests the server to return free busy + information on the calendar collection /bernard/work/, between 9:00 + A.M. and 5:00 P.M. EST (2:00 P.M. and 10:00 P.M. UTC) on the January + 4, 2006. The server responds, indicating two busy time intervals of + one hour, one of which is tentative. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + >> Response << + + HTTP/1.1 200 OK + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/calendar + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + BEGIN:VFREEBUSY + DTSTAMP:20050125T090000Z + DTSTART:20060104T140000Z + DTEND:20060105T220000Z + FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060104T150000Z/PT1H + FREEBUSY:20060104T190000Z/PT1H + END:VFREEBUSY + END:VCALENDAR + + + + + + + + + +Daboo, et al. Standards Track [Page 68] + +RFC 4791 CalDAV March 2007 + + +8. Guidelines + +8.1. Client-to-Client Interoperability + + There are a number of actions clients can take that will be legal + (the server will not return errors), but that can degrade + interoperability with other client implementations accessing the same + data. For example, a recurrence rule could be replaced with a set of + recurrence dates, a single recurring event could be replaced with a + set of independent resources to represent each recurrence, or the + start/end time values can be translated from the original time zone + to another time zone. Although this advice amounts to iCalendar + interoperability best practices and is not limited only to CalDAV + usage, interoperability problems are likely to be more evident in + CalDAV use cases. + +8.2. Synchronization Operations + + WebDAV already provides functionality required to synchronize a + collection or set of collections, to make changes offline, and + provides a simple way to resolve conflicts when reconnected. ETags + are the key to making this work, but these are not required of all + WebDAV servers. Since offline functionality is more important to + calendar applications than to some other WebDAV applications, CalDAV + servers MUST support ETags, as specified in Section 5.3.4. + +8.2.1. Use of Reports + +8.2.1.1. Restrict the Time Range + + The reports provided in CalDAV can be used by clients to optimize + their performance in terms of network bandwidth usage and resource + consumption on the local client machine. Both are certainly major + considerations for mobile or handheld devices with limited capacity, + but they are also relevant to desktop client applications in cases + where the calendar collections contain large amounts of data. + + Typically, clients present calendar data to users in views that span + a finite time interval, so whenever possible, clients should only + retrieve calendar components from the server using CALDAV:calendar- + query REPORT, combined with a CALDAV:time-range element, to limit the + set of returned components to just those needed to populate the + current view. + + + + + + + + +Daboo, et al. Standards Track [Page 69] + +RFC 4791 CalDAV March 2007 + + +8.2.1.2. Synchronize by Time Range + + Typically in a calendar, historical data (events, to-dos, etc. that + have completed prior to the current date) do not change, though they + may be deleted. As a result, a client can speed up the + synchronization process by only considering data for the present time + and the future up to a reasonable limit (e.g., one week, one month). + If the user then tries to examine a portion of the calendar outside + the range that has been synchronized, the client can perform another + synchronization operation on the new time interval being examined. + This "just-in-time" synchronization can minimize bandwidth for common + user interaction behaviors. + +8.2.1.3. Synchronization Process + + If a client wants to support calendar data synchronization, as + opposed to downloading calendar data each time it is needed, the + client needs to cache the calendar object resource's URI and ETag, + along with the actual calendar data. While the URI remains static + for the lifetime of the calendar object resource, the ETag will + change with each successive change to the calendar object resource. + Thus, to synchronize a local data cache with the server, the client + can first fetch the URI/ETag pairs for the time interval being + considered, and compare those results with the cached data. Any + cached component whose ETag differs from that on the server needs to + be refreshed. + + In order to properly detect the changes between the server and client + data, the client will need to keep a record of which calendar object + resources have been created, changed, or deleted since the last + synchronization operation so that it can reconcile those changes with + the data on the server. + + Here's an example of how to do that: + + The client issues a CALDAV:calendar-query REPORT request for a + specific time range and asks for only the DAV:getetag property to be + returned: + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 70] + +RFC 4791 CalDAV March 2007 + + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + The client then uses the results to determine which calendar object + resources have changed, been created, or deleted on the server, and + how those relate to locally cached calendar object resources that may + have changed, been created, or deleted. If the client determines + that there are calendar object resources on the server that need to + be fetched, the client issues a CALDAV:calendar-multiget REPORT + request to fetch its calendar data: + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + /bernard/work/abcd1.ics + /bernard/work/mtg1.ics + + + + + + + +Daboo, et al. Standards Track [Page 71] + +RFC 4791 CalDAV March 2007 + + +8.2.2. Restrict the Properties Returned + + A client may not need all the calendar properties of a calendar + object resource when presenting information to the user. Since some + calendar property values can be large (e.g., ATTACH or ATTENDEE), a + client can choose to restrict the calendar properties to be returned + in a calendaring REPORT request to those it knows it will use. + + However, if a client needs to make a change to a calendar object + resource, it can only change the entire calendar object resource via + a PUT request. There is currently no way to incrementally make a + change to a set of calendar properties of a calendar object resource. + As a result, the client will have to get the entire calendar object + resource that is being changed. + +8.3. Use of Locking + + WebDAV locks can be used to prevent two clients that are modifying + the same resource from either overwriting each others' changes + (though that problem can also be solved by using ETags) or wasting + time making changes that will conflict with another set of changes. + In a multi-user calendar system, an interactive calendar client could + lock an event while the user is editing the event, and unlock the + event when the user finishes or cancels. Locks can also be used to + prevent changes while data is being reorganized. For example, a + calendar client might lock two calendar collections prior to moving a + bunch of calendar resources from one to another. + + Clients are responsible for requesting a lock timeout period that is + appropriate to the use case. When the user explicitly decides to + reserve a resource and prevent other changes, a long timeout might be + appropriate, but in cases where the client automatically decides to + lock the resource, the timeout should be short (and the client can + always refresh the lock should it need to). A short lock timeout + means that if the client is unable to remove the lock, the other + calendar users aren't prevented from making changes. + +8.4. Finding Calendars + + Much of the time, a calendar client (or agent) will discover a new + calendar's location by being provided directly with the URL. For + example, a user will type his or her own calendar location into + client configuration information or copy and paste a URL from email + into the calendar application. The client need only confirm that the + URL points to a resource that is a calendar collection. The client + may also be able to browse WebDAV collections to find calendar + collections. + + + + +Daboo, et al. Standards Track [Page 72] + +RFC 4791 CalDAV March 2007 + + + The choice of HTTP URLs means that calendar object resources are + backward compatible with existing software, but does have the + disadvantage that existing software does not usually know to look at + the OPTIONS response to that URL to determine what can be done with + it. This is somewhat of a barrier for WebDAV usage as well as with + CalDAV usage. This specification does not offer a way through this + other than making the information available in the OPTIONS response + should this be requested. + + For calendar sharing and scheduling use cases, one might wish to find + the calendar belonging to another user. If the other user has a + calendar in the same repository, that calendar can be found by using + the principal namespace required by WebDAV ACL support. For other + cases, the authors have no universal solution, but implementers can + consider whether to use vCard [RFC2426] or LDAP [RFC4511] standards + together with calendar attributes [RFC2739]. + + Because CalDAV requires servers to support WebDAV ACL [RFC3744], + including principal namespaces, and with the addition of the CALDAV: + calendar-home-set property, there are a couple options for CalDAV + clients to find one's own calendar or another user's calendar. + + In this case, a DAV:principal-match REPORT is used to find a named + property (the CALDAV:calendar-home-set) on the Principal-URL of the + current user. Using this, a WebDAV client can learn "who am I" and + "where are my calendars". The REPORT request body looks like this: + + + + + + + + + + To find other users' calendars, the DAV:principal-property-search + REPORT can be used to filter on some properties and return others. + To search for a calendar owned by a user named "Laurie", the REPORT + request body would look like this: + + + + + + + + + + + +Daboo, et al. Standards Track [Page 73] + +RFC 4791 CalDAV March 2007 + + + + + + + + + Laurie + + + + + + + + The server performs a case-sensitive or caseless search for a + matching string subset of "Laurie" within the DAV:displayname + property. Thus, the server might return "Laurie Dusseault", "Laurier + Desruisseaux", or "Wilfrid Laurier" as matching DAV:displayname + values, and return the calendars for each of these. + +8.5. Storing and Using Attachments + + CalDAV clients MAY create attachments in calendar components either + as inline or external. This section contains some guidelines for + creating and managing attachments. + +8.5.1. Inline Attachments + + CalDAV clients MUST support inline attachments as specified in + iCalendar [RFC2445]. CalDAV servers MUST support inline attachments, + so clients can rely on being able to create attachments this way. On + the other hand, inline attachments have some drawbacks: + + o Servers MAY impose limitations on the size of calendar object + resources (i.e., refusing PUT requests of very large iCalendar + objects). Servers that impose such limitations MUST use the + CALDAV:max-resource-size property on a calendar collection to + inform the client as to what the limitation is (see + Section 5.2.5). + + o Servers MAY impose storage quota limitations on calendar + collections (See [RFC4331]). + + o Any change to a calendar object resource containing an inline + attachment requires the entire inline attachment to be re- + uploaded. + + + + +Daboo, et al. Standards Track [Page 74] + +RFC 4791 CalDAV March 2007 + + + o Clients synchronizing a changed calendar object resource have to + download the entire calendar object resource, even if the + attachment is unchanged. + +8.5.2. External Attachments + + CalDAV clients SHOULD support downloading of external attachments + referenced by arbitrary URI schemes, by either processing them + directly, or by passing the attachment URI to a suitable "helper + application" for processing, if such an application exists. CalDAV + clients MUST support downloading of external attachments referenced + by the "http" or "https" URI schemes. An external attachment could + be: + + o In a collection in the calendar collection containing the calendar + object resource; + + o Somewhere else in the same repository that hosts the calendar + collection; or + + o On an HTTP or FTP server elsewhere. + + CalDAV servers MAY provide support for child collections in calendar + collections. CalDAV servers MAY allow the MKCOL method to create + child collections in calendar collections. Child collections of + calendar collections MAY contain any type of resource except calendar + collections that they MUST NOT contain. Some CalDAV servers won't + allow child collections in calendar collections, and it may be + possible on such a server to discover other locations where + attachments can be stored. + + Clients are entirely responsible for maintaining reference + consistency with calendar components that link to external + attachments. A client deleting a calendar component with an external + attachment might therefore also delete the attachment if that's + appropriate; however, appropriateness can be very hard to determine. + A new component might easily reference some pre-existing Web resource + that is intended to have independent existence from the calendar + component (the "attachment" could be a major proposal to be discussed + in a meeting, for instance). Best practices will probably emerge and + should probably be documented, but for now, clients should be wary of + engaging in aggressive "cleanup" of external attachments. A client + could involve the user in making decisions about removing + unreferenced documents, or a client could be conservative in only + deleting attachments it had created. + + Also, clients are responsible for consistency of permissions when + using external attachments. One reason for servers to support the + + + +Daboo, et al. Standards Track [Page 75] + +RFC 4791 CalDAV March 2007 + + + storage of attachments within child collections of calendar + collections is that ACL inheritance might make it easier to grant the + same permissions to attachments that are granted on the calendar + collection. Otherwise, it can be very difficult to keep permissions + synchronized. With attachments stored on separate repositories, it + can be impossible to keep permissions consistent -- the two + repositories may not support the same permissions or have the same + set of principals. Some systems have used tickets or other anonymous + access control mechanisms to provide partially satisfactory solutions + to these kinds of problems. + +8.6. Storing and Using Alarms + + Note that all CalDAV calendar collections (including those the user + might treat as public or group calendars) can contain alarm + information on events and to-dos. Users can synchronize a calendar + between multiple devices and decide to have alarms execute on a + different device than the device that created the alarm. Not all + alarm action types are completely interoperable (e.g., those that + name a sound file to play). + + When the action is AUDIO and the client is configured to execute + the alarm, the client SHOULD play the suggested sound if it's + available or play another sound, but SHOULD NOT rewrite the alarm + just to replace the suggested sound with a sound that's locally + available. + + When the action is DISPLAY and the client is configured to execute + the alarm, the client SHOULD execute a display alarm by displaying + according to the suggested description or some reasonable + replacement, but SHOULD NOT rewrite the alarm for its own + convenience. + + When the action is EMAIL and the client is incapable of sending + email, it SHOULD ignore the alarm, but it MUST continue to + synchronize the alarm itself. + + This specification makes no recommendations about executing alarms + of type PROCEDURE, except to note that clients are advised to take + care to avoid creating security holes by executing these. + + Non-interoperable alarm information (e.g., should somebody define a + color to be used in a display alarm) should be put in non-standard + properties inside the VALARM component in order to keep the basic + alarm usable on all devices. + + Clients that allow changes to calendar object resources MUST + synchronize the alarm data that already exists in the resources. + + + +Daboo, et al. Standards Track [Page 76] + +RFC 4791 CalDAV March 2007 + + + Clients MAY execute alarms that are downloaded in this fashion, + possibly based on user preference. If a client is only doing read + operations on a calendar and there is no risk of losing alarm + information, then the client MAY discard alarm information. + + This specification makes no attempt to provide multi-user alarms on + group calendars or to find out for whom an alarm is intended. + Addressing those issues might require extensions to iCalendar; for + example, to store alarms per-user, or to indicate for which user a + VALARM was intended. In the meantime, clients might maximize + interoperability by generally not uploading alarm information to + public, group, or resource calendars. + +9. XML Element Definitions + +9.1. CALDAV:calendar XML Element + + Name: calendar + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies the resource type of a calendar collection. + + Description: See Section 4.2. + + Definition: + + + +9.2. CALDAV:mkcalendar XML Element + + Name: mkcalendar + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a request that includes the WebDAV property + values to be set for a calendar collection resource when it is + created. + + Description: See Section 5.3.1. + + Definition: + + + + + + + + + +Daboo, et al. Standards Track [Page 77] + +RFC 4791 CalDAV March 2007 + + +9.3. CALDAV:mkcalendar-response XML Element + + Name: mkcalendar-response + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a response body for a successful MKCALENDAR + request. + + Description: See Section 5.3.1. + + Definition: + + + +9.4. CALDAV:supported-collation XML Element + + Name: supported-collation + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identifies a single collation via its collation identifier, + as defined by [RFC4790]. + + Description: The CALDAV:supported-collation contains the text of a + collation identifier, as described in Section 7.5.1. + + Definition: + + + PCDATA value: collation identifier + +9.5. CALDAV:calendar-query XML Element + + Name: calendar-query + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Defines a report for querying calendar object resources. + + Description: See Section 7.8. + + Definition: + + + + + + +Daboo, et al. Standards Track [Page 78] + +RFC 4791 CalDAV March 2007 + + +9.6. CALDAV:calendar-data XML Element + + Name: calendar-data + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specified one of the following: + + 1. A supported media type for calendar object resources when + nested in the CALDAV:supported-calendar-data property; + + 2. The parts of a calendar object resource should be returned by + a calendaring report; + + 3. The content of a calendar object resource in a response to a + calendaring report. + + Description: When nested in the CALDAV:supported-calendar-data + property, the CALDAV:calendar-data XML element specifies a media + type supported by the CalDAV server for calendar object resources. + + When used in a calendaring REPORT request, the CALDAV:calendar- + data XML element specifies which parts of calendar object + resources need to be returned in the response. If the CALDAV: + calendar-data XML element doesn't contain any CALDAV:comp element, + calendar object resources will be returned in their entirety. + + Finally, when used in a calendaring REPORT response, the CALDAV: + calendar-data XML element specifies the content of a calendar + object resource. Given that XML parsers normalize the two- + character sequence CRLF (US-ASCII decimal 13 and US-ASCII decimal + 10) to a single LF character (US-ASCII decimal 10), the CR + character (US-ASCII decimal 13) MAY be omitted in calendar object + resources specified in the CALDAV:calendar-data XML element. + Furthermore, calendar object resources specified in the CALDAV: + calendar-data XML element MAY be invalid per their media type + specification if the CALDAV:calendar-data XML element part of the + calendaring REPORT request did not specify required properties + (e.g., UID, DTSTAMP, etc.), or specified a CALDAV:prop XML element + with the "novalue" attribute set to "yes". + + Note: The CALDAV:calendar-data XML element is specified in requests + and responses inside the DAV:prop XML element as if it were a + WebDAV property. However, the CALDAV:calendar-data XML element is + not a WebDAV property and, as such, is not returned in PROPFIND + responses, nor used in PROPPATCH requests. + + + + + +Daboo, et al. Standards Track [Page 79] + +RFC 4791 CalDAV March 2007 + + + Note: The iCalendar data embedded within the CALDAV:calendar-data + XML element MUST follow the standard XML character data encoding + rules, including use of <, >, & etc. entity encoding or + the use of a construct. In the later case, the + iCalendar data cannot contain the character sequence "]]>", which + is the end delimiter for the CDATA section. + + Definition: + + + + when nested in the CALDAV:supported-calendar-data property + to specify a supported media type for calendar object + resources; + + + + when nested in the DAV:prop XML element in a calendaring + REPORT request to specify which parts of calendar object + resources should be returned in the response; + + + PCDATA value: iCalendar object + + when nested in the DAV:prop XML element in a calendaring + REPORT response to specify the content of a returned + calendar object resource. + + + content-type value: a MIME media type + version value: a version string + + attributes can be used on all three variants of the + CALDAV:calendar-data XML element. + +9.6.1. CALDAV:comp XML Element + + Name: comp + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Defines which component types to return. + + + + + + +Daboo, et al. Standards Track [Page 80] + +RFC 4791 CalDAV March 2007 + + + Description: The name value is a calendar component name (e.g., + VEVENT). + + Definition: + + + + + name value: a calendar component name + + Note: The CALDAV:prop and CALDAV:allprop elements have the same name + as the DAV:prop and DAV:allprop elements defined in [RFC2518]. + However, the CALDAV:prop and CALDAV:allprop elements are defined + in the "urn:ietf:params:xml:ns:caldav" namespace instead of the + "DAV:" namespace. + +9.6.2. CALDAV:allcomp XML Element + + Name: allcomp + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies that all components shall be returned. + + Description: The CALDAV:allcomp XML element can be used when the + client wants all types of components returned by a calendaring + REPORT request. + + Definition: + + + +9.6.3. CALDAV:allprop XML Element + + Name: allprop + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies that all properties shall be returned. + + Description: The CALDAV:allprop XML element can be used when the + client wants all properties of components returned by a + calendaring REPORT request. + + Definition: + + + + + + +Daboo, et al. Standards Track [Page 81] + +RFC 4791 CalDAV March 2007 + + + Note: The CALDAV:allprop element has the same name as the DAV: + allprop element defined in [RFC2518]. However, the CALDAV:allprop + element is defined in the "urn:ietf:params:xml:ns:caldav" + namespace instead of the "DAV:" namespace. + +9.6.4. CALDAV:prop XML Element + + Name: prop + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Defines which properties to return in the response. + + Description: The "name" attribute specifies the name of the calendar + property to return (e.g., ATTENDEE). The "novalue" attribute can + be used by clients to request that the actual value of the + property not be returned (if the "novalue" attribute is set to + "yes"). In that case, the server will return just the iCalendar + property name and any iCalendar parameters and a trailing ":" + without the subsequent value data. + + Definition: + + + + + name value: a calendar property name + novalue value: "yes" or "no" + + Note: The CALDAV:prop element has the same name as the DAV:prop + element defined in [RFC2518]. However, the CALDAV:prop element is + defined in the "urn:ietf:params:xml:ns:caldav" namespace instead + of the "DAV:" namespace. + +9.6.5. CALDAV:expand XML Element + + Name: expand + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Forces the server to expand recurring components into + individual recurrence instances. + + Description: The CALDAV:expand XML element specifies that for a + given calendaring REPORT request, the server MUST expand the + recurrence set into calendar components that define exactly one + + + + +Daboo, et al. Standards Track [Page 82] + +RFC 4791 CalDAV March 2007 + + + recurrence instance, and MUST return only those whose scheduled + time intersect a specified time range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + the time range. Both attributes are specified as date with UTC + time value. The value of the "end" attribute MUST be greater than + the value of the "start" attribute. + + The server MUST use the same logic as defined for CALDAV:time- + range to determine if a recurrence instance intersects the + specified time range. + + Recurring components, other than the initial instance, MUST + include a RECURRENCE-ID property indicating which instance they + refer to. + + The returned calendar components MUST NOT use recurrence + properties (i.e., EXDATE, EXRULE, RDATE, and RRULE) and MUST NOT + have reference to or include VTIMEZONE components. Date and local + time with reference to time zone information MUST be converted + into date with UTC time. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.6.6. CALDAV:limit-recurrence-set XML Element + + Name: limit-recurrence-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time range to limit the set of "overridden + components" returned by the server. + + Description: The CALDAV:limit-recurrence-set XML element specifies + that for a given calendaring REPORT request, the server MUST + return, in addition to the "master component", only the + "overridden components" that impact a specified time range. An + overridden component impacts a time range if its current start and + end times overlap the time range, or if the original start and end + + + + +Daboo, et al. Standards Track [Page 83] + +RFC 4791 CalDAV March 2007 + + + times -- the ones that would have been used if the instance were + not overridden -- overlap the time range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + the time range. Both attributes are specified as date with UTC + time value. The value of the "end" attribute MUST be greater than + the value of the "start" attribute. + + The server MUST use the same logic as defined for CALDAV:time- + range to determine if the current or original scheduled time of an + "overridden" recurrence instance intersects the specified time + range. + + Overridden components that have a RANGE parameter on their + RECURRENCE-ID property may specify one or more instances in the + recurrence set, and some of those instances may fall within the + specified time range or may have originally fallen within the + specified time range prior to being overridden. If that is the + case, the overridden component MUST be included in the results, as + it has a direct impact on the interpretation of instances within + the specified time range. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.6.7. CALDAV:limit-freebusy-set XML Element + + Name: limit-freebusy-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time range to limit the set of FREEBUSY values + returned by the server. + + Description: The CALDAV:limit-freebusy-set XML element specifies + that for a given calendaring REPORT request, the server MUST only + return the FREEBUSY property values of a VFREEBUSY component that + intersects a specified time range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + + + +Daboo, et al. Standards Track [Page 84] + +RFC 4791 CalDAV March 2007 + + + the time range. Both attributes are specified as "date with UTC + time" value. The value of the "end" attribute MUST be greater + than the value of the "start" attribute. + + The server MUST use the same logic as defined for CALDAV:time- + range to determine if a FREEBUSY property value intersects the + specified time range. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.7. CALDAV:filter XML Element + + Name: filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a filter to limit the set of calendar components + returned by the server. + + Description: The CALDAV:filter XML element specifies the search + filter used to limit the calendar components returned by a + calendaring REPORT request. + + Definition: + + + +9.7.1. CALDAV:comp-filter XML Element + + Name: comp-filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies search criteria on calendar components. + + Description: The CALDAV:comp-filter XML element specifies a query + targeted at the calendar object (i.e., VCALENDAR) or at a specific + calendar component type (e.g., VEVENT). The scope of the + CALDAV:comp-filter XML element is the calendar object when used as + a child of the CALDAV:filter XML element. The scope of the + CALDAV:comp-filter XML element is the enclosing calendar component + + + +Daboo, et al. Standards Track [Page 85] + +RFC 4791 CalDAV March 2007 + + + when used as a child of another CALDAV:comp-filter XML element. A + CALDAV:comp-filter is said to match if: + + * The CALDAV:comp-filter XML element is empty and the calendar + object or calendar component type specified by the "name" + attribute exists in the current scope; + + or: + + * The CALDAV:comp-filter XML element contains a CALDAV:is-not- + defined XML element and the calendar object or calendar + component type specified by the "name" attribute does not exist + in the current scope; + + or: + + * The CALDAV:comp-filter XML element contains a CALDAV:time-range + XML element and at least one recurrence instance in the + targeted calendar component is scheduled to overlap the + specified time range, and all specified CALDAV:prop-filter and + CALDAV:comp-filter child XML elements also match the targeted + calendar component; + + or: + + * The CALDAV:comp-filter XML element only contains CALDAV:prop- + filter and CALDAV:comp-filter child XML elements that all match + the targeted calendar component. + + Definition: + + + + + name value: a calendar object or calendar component + type (e.g., VEVENT) + +9.7.2. CALDAV:prop-filter XML Element + + Name: prop-filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies search criteria on calendar properties. + + Description: The CALDAV:prop-filter XML element specifies a query + targeted at a specific calendar property (e.g., CATEGORIES) in the + + + +Daboo, et al. Standards Track [Page 86] + +RFC 4791 CalDAV March 2007 + + + scope of the enclosing calendar component. A calendar property is + said to match a CALDAV:prop-filter if: + + * The CALDAV:prop-filter XML element is empty and a property of + the type specified by the "name" attribute exists in the + enclosing calendar component; + + or: + + * The CALDAV:prop-filter XML element contains a CALDAV:is-not- + defined XML element and no property of the type specified by + the "name" attribute exists in the enclosing calendar + component; + + or: + + * The CALDAV:prop-filter XML element contains a CALDAV:time-range + XML element and the property value overlaps the specified time + range, and all specified CALDAV:param-filter child XML elements + also match the targeted property; + + or: + + * The CALDAV:prop-filter XML element contains a CALDAV:text-match + XML element and the property value matches it, and all + specified CALDAV:param-filter child XML elements also match the + targeted property; + + Definition: + + + + + name value: a calendar property name (e.g., ATTENDEE) + +9.7.3. CALDAV:param-filter XML Element + + Name: param-filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Limits the search to specific parameter values. + + Description: The CALDAV:param-filter XML element specifies a query + targeted at a specific calendar property parameter (e.g., + PARTSTAT) in the scope of the calendar property on which it is + + + +Daboo, et al. Standards Track [Page 87] + +RFC 4791 CalDAV March 2007 + + + defined. A calendar property parameter is said to match a CALDAV: + param-filter if: + + * The CALDAV:param-filter XML element is empty and a parameter of + the type specified by the "name" attribute exists on the + calendar property being examined; + + or: + + * The CALDAV:param-filter XML element contains a CALDAV:is-not- + defined XML element and no parameter of the type specified by + the "name" attribute exists on the calendar property being + examined; + + Definition: + + + + + name value: a property parameter name (e.g., PARTSTAT) + +9.7.4. CALDAV:is-not-defined XML Element + + Name: is-not-defined + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies that a match should occur if the enclosing + component, property, or parameter does not exist. + + Description: The CALDAV:is-not-defined XML element specifies that a + match occurs if the enclosing component, property, or parameter + value specified in a calendaring REPORT request does not exist in + the calendar data being tested. + + Definition: + + + +9.7.5. CALDAV:text-match XML Element + + Name: text-match + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a substring match on a property or parameter + value. + + + + +Daboo, et al. Standards Track [Page 88] + +RFC 4791 CalDAV March 2007 + + + Description: The CALDAV:text-match XML element specifies text used + for a substring match against the property or parameter value + specified in a calendaring REPORT request. + + The "collation" attribute is used to select the collation that the + server MUST use for character string matching. In the absence of + this attribute, the server MUST use the "i;ascii-casemap" + collation. + + The "negate-condition" attribute is used to indicate that this + test returns a match if the text matches when the attribute value + is set to "no", or return a match if the text does not match, if + the attribute value is set to "yes". For example, this can be + used to match components with a STATUS property not set to + CANCELLED. + + Definition: + + + PCDATA value: string + + + +9.8. CALDAV:timezone XML Element + + Name: timezone + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies the time zone component to use when determining + the results of a report. + + Description: The CALDAV:timezone XML element specifies that for a + given calendaring REPORT request, the server MUST rely on the + specified VTIMEZONE component instead of the CALDAV:calendar- + timezone property of the calendar collection, in which the + calendar object resource is contained to resolve "date" values and + "date with local time" values (i.e., floating time) to "date with + UTC time" values. The server will require this information to + determine if a calendar component scheduled with "date" values or + "date with local time" values intersects a CALDAV:time-range + specified in a CALDAV:calendar-query REPORT. + + Note: The iCalendar data embedded within the CALDAV:timezone XML + element MUST follow the standard XML character data encoding + rules, including use of <, >, & etc. entity encoding or + the use of a construct. In the later case, the + + + +Daboo, et al. Standards Track [Page 89] + +RFC 4791 CalDAV March 2007 + + + iCalendar data cannot contain the character sequence "]]>", which + is the end delimiter for the CDATA section. + + Definition: + + + PCDATA value: an iCalendar object with exactly one VTIMEZONE + +9.9. CALDAV:time-range XML Element + + Name: time-range + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time range to limit the set of calendar + components returned by the server. + + Description: The CALDAV:time-range XML element specifies that for a + given calendaring REPORT request, the server MUST only return the + calendar object resources that, depending on the context, have a + component or property whose value intersects a specified time + range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + the time range. Both attributes MUST be specified as "date with + UTC time" value. Time ranges open at one end can be specified by + including only one attribute; however, at least one attribute MUST + always be present in the CALDAV:time-range element. If either the + "start" or "end" attribute is not specified in the CALDAV:time- + range XML element, assume "-infinity" and "+infinity" as their + value, respectively. If both "start" and "end" are present, the + value of the "end" attribute MUST be greater than the value of the + "start" attribute. + + Time range tests MUST consider every recurrence instance when + testing the time range condition; if any one instance matches, + then the test returns true. Testing recurrence instances requires + the server to infer an effective value for DTSTART, DTEND, + DURATION, and DUE properties for an instance based on the + recurrence patterns and any overrides. + + A VEVENT component overlaps a given time range if the condition + for the corresponding component state specified in the table below + is satisfied. Note that, as specified in [RFC2445], the DTSTART + property is REQUIRED in the VEVENT component. The conditions + depend on the presence of the DTEND and DURATION properties in the + VEVENT component. Furthermore, the value of the DTEND property + + + +Daboo, et al. Standards Track [Page 90] + +RFC 4791 CalDAV March 2007 + + + MUST be later in time than the value of the DTSTART property. The + duration of a VEVENT component with no DTEND and DURATION + properties is 1 day (+P1D) when the DTSTART is a DATE value, and 0 + seconds when the DTSTART is a DATE-TIME value. + + +---------------------------------------------------------------+ + | VEVENT has the DTEND property? | + | +-----------------------------------------------------------+ + | | VEVENT has the DURATION property? | + | | +-------------------------------------------------------+ + | | | DURATION property value is greater than 0 seconds? | + | | | +---------------------------------------------------+ + | | | | DTSTART property is a DATE-TIME value? | + | | | | +-----------------------------------------------+ + | | | | | Condition to evaluate | + +---+---+---+---+-----------------------------------------------+ + | Y | N | N | * | (start < DTEND AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + | N | Y | Y | * | (start < DTSTART+DURATION AND end > DTSTART) | + | | +---+---+-----------------------------------------------+ + | | | N | * | (start <= DTSTART AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + | N | N | N | Y | (start <= DTSTART AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + | N | N | N | N | (start < DTSTART+P1D AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + + A VTODO component is said to overlap a given time range if the + condition for the corresponding component state specified in the + table below is satisfied. The conditions depend on the presence + of the DTSTART, DURATION, DUE, COMPLETED, and CREATED properties + in the VTODO component. Note that, as specified in [RFC2445], the + DUE value MUST be a DATE-TIME value equal to or after the DTSTART + value if specified. + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 91] + +RFC 4791 CalDAV March 2007 + + + +-------------------------------------------------------------------+ + | VTODO has the DTSTART property? | + | +---------------------------------------------------------------+ + | | VTODO has the DURATION property? | + | | +-----------------------------------------------------------+ + | | | VTODO has the DUE property? | + | | | +-------------------------------------------------------+ + | | | | VTODO has the COMPLETED property? | + | | | | +---------------------------------------------------+ + | | | | | VTODO has the CREATED property? | + | | | | | +-----------------------------------------------+ + | | | | | | Condition to evaluate | + +---+---+---+---+---+-----------------------------------------------+ + | Y | Y | N | * | * | (start <= DTSTART+DURATION) AND | + | | | | | | ((end > DTSTART) OR | + | | | | | | (end >= DTSTART+DURATION)) | + +---+---+---+---+---+-----------------------------------------------+ + | Y | N | Y | * | * | ((start < DUE) OR (start <= DTSTART)) | + | | | | | | AND | + | | | | | | ((end > DTSTART) OR (end >= DUE)) | + +---+---+---+---+---+-----------------------------------------------+ + | Y | N | N | * | * | (start <= DTSTART) AND (end > DTSTART) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | Y | * | * | (start < DUE) AND (end >= DUE) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | Y | Y | ((start <= CREATED) OR (start <= COMPLETED))| + | | | | | | AND | + | | | | | | ((end >= CREATED) OR (end >= COMPLETED))| + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | Y | N | (start <= COMPLETED) AND (end >= COMPLETED) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | N | Y | (end > CREATED) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | N | N | TRUE | + +---+---+---+---+---+-----------------------------------------------+ + + A VJOURNAL component overlaps a given time range if the condition + for the corresponding component state specified in the table below + is satisfied. The conditions depend on the presence of the + DTSTART property in the VJOURNAL component and on whether the + DTSTART is a DATE-TIME or DATE value. The effective "duration" of + a VJOURNAL component is 1 day (+P1D) when the DTSTART is a DATE + value, and 0 seconds when the DTSTART is a DATE-TIME value. + + + + + + + + +Daboo, et al. Standards Track [Page 92] + +RFC 4791 CalDAV March 2007 + + + +----------------------------------------------------+ + | VJOURNAL has the DTSTART property? | + | +------------------------------------------------+ + | | DTSTART property is a DATE-TIME value? | + | | +--------------------------------------------+ + | | | Condition to evaluate | + +---+---+--------------------------------------------+ + | Y | Y | (start <= DTSTART) AND (end > DTSTART) | + +---+---+--------------------------------------------+ + | Y | N | (start < DTSTART+P1D) AND (end > DTSTART) | + +---+---+--------------------------------------------+ + | N | * | FALSE | + +---+---+--------------------------------------------+ + + A VFREEBUSY component overlaps a given time range if the condition + for the corresponding component state specified in the table below + is satisfied. The conditions depend on the presence in the + VFREEBUSY component of the DTSTART and DTEND properties, and any + FREEBUSY properties in the absence of DTSTART and DTEND. Any + DURATION property is ignored, as it has a special meaning when + used in a VFREEBUSY component. + + When only FREEBUSY properties are used, each period in each + FREEBUSY property is compared against the time range, irrespective + of the type of free busy information (free, busy, busy-tentative, + busy-unavailable) represented by the property. + + + +------------------------------------------------------+ + | VFREEBUSY has both the DTSTART and DTEND properties? | + | +--------------------------------------------------+ + | | VFREEBUSY has the FREEBUSY property? | + | | +----------------------------------------------+ + | | | Condition to evaluate | + +---+---+----------------------------------------------+ + | Y | * | (start <= DTEND) AND (end > DTSTART) | + +---+---+----------------------------------------------+ + | N | Y | (start < freebusy-period-end) AND | + | | | (end > freebusy-period-start) | + +---+---+----------------------------------------------+ + | N | N | FALSE | + +---+---+----------------------------------------------+ + + A VALARM component is said to overlap a given time range if the + following condition holds: + + (start <= trigger-time) AND (end > trigger-time) + + + + +Daboo, et al. Standards Track [Page 93] + +RFC 4791 CalDAV March 2007 + + + A VALARM component can be defined such that it triggers repeatedly. + Such a VALARM component is said to overlap a given time range if at + least one of its triggers overlaps the time range. + + The calendar properties COMPLETED, CREATED, DTEND, DTSTAMP, + DTSTART, DUE, and LAST-MODIFIED overlap a given time range if the + following condition holds: + + (start <= date-time) AND (end > date-time) + + Note that if DTEND is not present in a VEVENT, but DURATION is, then + the test should instead operate on the 'effective' DTEND, i.e., + DTSTART+DURATION. Similarly, if DUE is not present in a VTODO, but + DTSTART and DURATION are, then the test should instead operate on the + 'effective' DUE, i.e., DTSTART+DURATION. + + The semantic of CALDAV:time-range is not defined for any other + calendar components and properties. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.10. CALDAV:calendar-multiget XML Element + + Name: calendar-multiget + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: CalDAV report used to retrieve specific calendar object + resources. + + Description: See Section 7.9. + + Definition: + + + + + + + + + +Daboo, et al. Standards Track [Page 94] + +RFC 4791 CalDAV March 2007 + + +9.11. CALDAV:free-busy-query XML Element + + Name: free-busy-query + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: CalDAV report used to generate a VFREEBUSY to determine + busy time over a specific time range. + + Description: See Section 7.10. + + Definition: + + + +10. Internationalization Considerations + + CalDAV allows internationalized strings to be stored and retrieved + for the description of calendar collections (see Section 5.2.1). + + The CALDAV:calendar-query REPORT (Section 7.8) includes a text + searching option controlled by the CALDAV:text-match element, and + details of character handling are covered in the description of that + element (see Section 9.7.5). + +11. Security Considerations + + HTTP protocol transactions are sent in the clear over the network + unless protection from snooping is negotiated. This can be + accomplished by use of TLS, as defined in [RFC2818]. In particular, + HTTP Basic authentication MUST NOT be used unless TLS is in effect. + + Servers MUST take adequate precautions to ensure that malicious + clients cannot consume excessive server resources (CPU, memory, disk, + etc.) through carefully crafted reports. For example, a client could + upload an event with a recurrence rule that specifies a recurring + event occurring every second for the next 100 years, which would + result in approximately 3 x 10^9 instances! A report that asks for + recurrences to be expanded over that range would likely constitute a + denial-of-service attack on the server. + + When creating new resources (including calendar collections), clients + MUST ensure that the resource name (the last path segment of the + resource URI) assigned to the new resource does not expose any data + from within the iCalendar resource itself or information about the + nature of a calendar collection. This is required to ensure that the + presence of a specific iCalendar component or nature of components in + a collection cannot be inferred based on the name of a resource. + + + +Daboo, et al. Standards Track [Page 95] + +RFC 4791 CalDAV March 2007 + + + When rolling up free-busy information, more information about a + user's events is exposed if busy periods overlap or are adjacent + (this tells the client requesting the free-busy information that the + calendar owner has at least two events, rather than knowing only that + the calendar owner has one or more events during the busy period). + Thus, a conservative approach to calendar data privacy would have + servers always coalesce such busy periods when they are the same + type. + + Procedure alarms are a known security risk for either clients or + servers to handle, particularly when the alarm was created by another + agent. Clients and servers are not required to execute such + procedure alarms. + + Security considerations described in iCalendar [RFC2445] and iTIP + [RFC2446] are also applicable to CalDAV. + + Beyond these, CalDAV does not raise any security considerations that + are not present in HTTP [RFC2616] and WebDAV [RFC2518], [RFC3253], + [RFC3744]. + +12. IANA Considerations + + This document uses one new URN to identify a new XML namespace. The + URN conforms to a registry mechanism described in [RFC3688]. + +12.1. Namespace Registration + + Registration request for the CalDAV namespace: + + URI: urn:ietf:params:xml:ns:caldav + + Registrant Contact: See the "Authors' Addresses" section of this + document. + + XML: None. Namespace URIs do not represent an XML specification. + +13. Acknowledgements + + The authors would like to thank the following individuals for + contributing their ideas and support for writing this specification: + Michael Arick, Mario Bonin, Chris Bryant, Scott Carr, Andre + Courtemanche, Mike Douglass, Ted Hardie, Marten den Haring, Jeffrey + Harris, Sam Hartman, Helge Hess, Jeff McCullough, Alexey Melnikov, + Dan Mosedale, Brian Moseley, Francois Perrault, Kervin L. Pierre, + Julian F. Reschke, Wilfredo Sanchez Vega, Mike Shaver, Jari + Urpalainen, Simon Vaillancourt, and Jim Whitehead. + + + + +Daboo, et al. Standards Track [Page 96] + +RFC 4791 CalDAV March 2007 + + + The authors would also like to thank the Calendaring and Scheduling + Consortium for advice with this specification, and for organizing + interoperability testing events to help refine it. + +14. References + +14.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, + RFC 2119, March 1997. + + [RFC2246] Dierks, T. and C. Allen, "The TLS Protocol + Version 1.0", RFC 2246, January 1999. + + [RFC2445] Dawson, F. and Stenerson, D., "Internet + Calendaring and Scheduling Core Object + Specification (iCalendar)", RFC 2445, + November 1998. + + [RFC2446] Silverberg, S., Mansour, S., Dawson, F., and + R. Hopson, "iCalendar Transport-Independent + Interoperability Protocol (iTIP) Scheduling + Events, BusyTime, To-dos and Journal + Entries", RFC 2446, November 1998. + + [RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, + S., and D. Jensen, "HTTP Extensions for + Distributed Authoring -- WEBDAV", RFC 2518, + February 1999. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, + H., Masinter, L., Leach, P., and T. Berners- + Lee, "Hypertext Transfer Protocol -- + HTTP/1.1", RFC 2616, June 1999. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, + May 2000. + + [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, + C., and J. Whitehead, "Versioning Extensions + to WebDAV (Web Distributed Authoring and + Versioning)", RFC 3253, March 2002. + + [RFC3688] Mealling, M., "The IETF XML Registry", + BCP 81, RFC 3688, January 2004. + + + + + +Daboo, et al. Standards Track [Page 97] + +RFC 4791 CalDAV March 2007 + + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. + Whitehead, "Web Distributed Authoring and + Versioning (WebDAV) Access Control Protocol", + RFC 3744, May 2004. + + [RFC4346] Dierks, T. and E. Rescorla, "The Transport + Layer Security (TLS) Protocol Version 1.1", + RFC 4346, April 2006. + + [RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, + "Internet Application Protocol Collation + Registry", RFC 4790, March 2007. + + [W3C.REC-xml-20060816] Paoli, J., Maler, E., Yergeau, F., Sperberg- + McQueen, C., and T. Bray, "Extensible Markup + Language (XML) 1.0 (Fourth Edition)", World + Wide Web Consortium Recommendation REC-xml- + 20060816, August 2006, + . + +14.2. Informative References + + [RFC2426] Dawson, F. and T. Howes, "vCard MIME + Directory Profile", RFC 2426, September 1998. + + [RFC2739] Small, T., Hennessy, D., and F. Dawson, + "Calendar Attributes for vCard and LDAP", + RFC 2739, January 2000. + + [RFC4331] Korver, B. and L. Dusseault, "Quota and Size + Properties for Distributed Authoring and + Versioning (DAV) Collections", RFC 4331, + February 2006. + + [RFC4511] Sermersheim, J., "Lightweight Directory + Access Protocol (LDAP): The Protocol", + RFC 4511, June 2006. + + [rfc2518bis] Dusseault, L., "HTTP Extensions for + Distributed Authoring - WebDAV", Work + in Progress, December 2006. + + + + + + + + + + +Daboo, et al. Standards Track [Page 98] + +RFC 4791 CalDAV March 2007 + + +Appendix A. CalDAV Method Privilege Table (Normative) + + The following table extends the WebDAV Method Privilege Table + specified in Appendix B of [RFC3744]. + + +------------+------------------------------------------------------+ + | METHOD | PRIVILEGES | + +------------+------------------------------------------------------+ + | MKCALENDAR | DAV:bind | + | REPORT | DAV:read or CALDAV:read-free-busy (on all referenced | + | | resources) | + +------------+------------------------------------------------------+ + +Appendix B. Calendar Collections Used in the Examples + + This appendix shows the calendar object resources contained in the + calendar collection queried in the examples throughout this document. + + The content of the calendar collection is being shown as if it were + returned by a CALDAV:calendar-query REPORT request designed to return + all the calendar data in the collection: + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + +Daboo, et al. Standards Track [Page 99] + +RFC 4791 CalDAV March 2007 + + + + + + + http://cal.example.com/bernard/work/abcd1.ics + + + "fffff-abcd1" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001102Z + DTSTART;TZID=US/Eastern:20060102T100000 + DURATION:PT1H + SUMMARY:Event #1 + Description:Go Steelers! + UID:74855313FA803DA593CD579A@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + + +Daboo, et al. Standards Track [Page 100] + +RFC 4791 CalDAV March 2007 + + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd3.ics + + + +Daboo, et al. Standards Track [Page 101] + +RFC 4791 CalDAV March 2007 + + + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd4.ics + + + + + +Daboo, et al. Standards Track [Page 102] + +RFC 4791 CalDAV March 2007 + + + "fffff-abcd4" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235335Z + DUE;VALUE=DATE:20060104 + STATUS:NEEDS-ACTION + SUMMARY:Task #1 + UID:DDDEEB7915FA61233B861457@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd5.ics + + + "fffff-abcd5" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235300Z + DUE;VALUE=DATE:20060106 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:NEEDS-ACTION + SUMMARY:Task #2 + UID:E10BA47467C5C69BB74E8720@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +Daboo, et al. Standards Track [Page 103] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd6.ics + + + "fffff-abcd6" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + COMPLETED:20051223T122322Z + DTSTAMP:20060205T235400Z + DUE;VALUE=DATE:20051225 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:COMPLETED + SUMMARY:Task #3 + UID:E10BA47467C5C69BB74E8722@example.com + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd7.ics + + + "fffff-abcd7" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235600Z + DUE;VALUE=DATE:20060101 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:CANCELLED + SUMMARY:Task #4 + UID:E10BA47467C5C69BB74E8725@example.com + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + +Daboo, et al. Standards Track [Page 104] + +RFC 4791 CalDAV March 2007 + + + + + + + http://cal.example.com/bernard/work/abcd8.ics + + + "fffff-abcd8" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VFREEBUSY + ORGANIZER;CN="Bernard Desruisseaux":mailto:bernard@example.com + UID:76ef34-54a3d2@example.com + DTSTAMP:20050530T123421Z + DTSTART:20060101T000000Z + DTEND:20060108T000000Z + FREEBUSY:20050531T230000Z/20050601T010000Z + FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z + FREEBUSY:20060103T100000Z/20060103T120000Z + FREEBUSY:20060104T100000Z/20060104T120000Z + FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20060105T100000Z/20060105T120000Z + FREEBUSY:20060106T100000Z/20060106T120000Z + END:VFREEBUSY + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 105] + +RFC 4791 CalDAV March 2007 + + +Authors' Addresses + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + Bernard Desruisseaux + Oracle Corporation + 600 Blvd. de Maisonneuve West + Suite 1900 + Montreal, QC H3A 3J2 + CANADA + + EMail: bernard.desruisseaux@oracle.com + URI: http://www.oracle.com/ + + + Lisa Dusseault + CommerceNet + 169 University Ave. + Palo Alto, CA 94301 + USA + + EMail: ldusseault@commerce.net + URI: http://commerce.net/ + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 106] + +RFC 4791 CalDAV March 2007 + + +Full Copyright Statement + + Copyright (C) The IETF Trust (2007). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + +Daboo, et al. Standards Track [Page 107] + diff --git a/dav/SabreDAV/docs/rfc4918.pdf b/dav/SabreDAV/docs/rfc4918.pdf new file mode 100644 index 000000000..566ac4ddd --- /dev/null +++ b/dav/SabreDAV/docs/rfc4918.pdf @@ -0,0 +1,13609 @@ +%PDF-1.3 +%ª«¬­ +4 0 obj +<< /Type /Info +/Producer (FOP 0.20.5) >> +endobj +5 0 obj +<< /Length 1281 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=*99Yi)&AJ$CkW#_;(r0NeDj,HW2jMG%6]sg,",=7d)F0<.B&`d^gVO.5`fI)N+7,4kRrhOc.kjm#3Nc!H#?B40&Gq`SqD\e@*i'T#'k&Pj[=m`htPo78Z=LeR.-q(HJ1o,1d.U/M[LJE%'JUYTN?Hd>0A1UR:N9uG'I'[2tA4(ku:3$*EY>'GB)Xl=00["6m,WG%^]a-H-2?K+;K@X;K;`2,4.'n@#oL"6[[@NNp5Qp7;Q.Zs-rZ>i#`ktZ2a*TDu,=5H8EVifV9UIDJK"nGsP(9h-ZF-00%+-F`*("ZPlU5W[B]/a]E7UU"GJs!&S6u+9k:Rh6+?A)guk@6j7DH5"NDeo(n$[Di#Xa>bmF5?8:^?i.cIh4Mub94Z"gQn)C.Oa.M0nP@p#mHiNK7kCb&4Z]Rtp>HKB@F-p!*aYAF+53usPY1bVHVgF!KSopX7N9A3_cT(r;=Fb#Jpm-#jNdQ'UUfrnu-[4U:hb@LLW7rN&*i,QU^3q^_8>sU0`>p6iTH&iD.p`?d +endstream +endobj +6 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 5 0 R +/Annots 7 0 R +>> +endobj +7 0 obj +[ +8 0 R +] +endobj +8 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 116.72 697.0 136.72 687.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A << /URI (http://tools.ietf.org/html/rfc2518) +/S /URI >> +/H /I +>> +endobj +9 0 obj +<< /Length 2108 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$I?$"IS'Sc)R.stJP&<\TtdO!Y6"f2FK'5:[PX=k'JgdFTdnhqFm.9k5nSPVB\Ra#6H"h^O\Pkp2.dLZkX(m7H.dlDgAW-Zr9=I]C"qB!EO`@L;jqLF($?F9L.VFtbPJ$.kXciF06hjH$Ul*NFLA*n?I&<;,(3O._X](>-d^OLjE=JgKh;Zko'Wr'G8'3Y'%2-(1ap`M/lf%mR_PV2<-BZ$GsMMlM3^N-\@+PU,MU2FJB3-ooW>]^r*5jjX8H].^5Q_mNX4lY6J:NiT0bR]8!!cN1SG_Y@Brcss/ra*t1*o"^\`)+h;Y7?G>c9ag,R:m=,3!+%n`GjiPVfG)6C8"^)8Ei'F;6r)IT#*9d/i#6W,"C5^L-XTQZ%r\f@$@FW)e5Tb`ZfLnAnI`l#k[Y476pl!ICL'RCMY3^o8Knp2a;b$M'(Z4i['';G4qe#(W7K!MSD"6=TpG+(f/#JOZKhm(QKIl'k6gjV._'-_4YgIb;'^\9a,G4]k*E4,9_K/30FKlW!:EV>/"q6B,G%/9G3ZM>l5-F55](R2[mq=W$@iD!.\EjoFfSQ<-6/!;]-(@OIUo2:Z6J;A&G&C<#"l?bgI=`$VOBZW:R_W+(NlQH&?HtLM(h@HemiX#g^bA\L]741J_QmSq_DChc13j^*?ibU\X=!%mWI-:-DO7i"@uJ)%1X8r11QLIF$=a9/YALEM,:I0Va,=Y(@YV"Ec)Vjj/!JGUZo=k6fML=/rh.T:4iuleW;#e,[Nf6j.o<#]Ro8_=Z8Xu";s-c8G["OQJ_;[hGp"0nRIorhl09U7N3/)!WuYJ(GCZ,U+LSHG(Q"aBEOUjO^0JNKq=a[5^Dgbu99.=S&j8^RTY\=4@4it((R>FRd51![DP!anf//MMnM-+KG\rY3H^hsD$cjqO'h4p1YcH_`j#bTgFiBi$PJ#^%o0M.#!&m'+qm<4*hX@eTX\?I#e]g_q9QQr820;c$L3YW[Gth?$9p3ba1em=6.Q[NKPs<(U]ni,,^MZemOe)CX;,1du~> +endstream +endobj +10 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 9 0 R +/Annots 11 0 R +>> +endobj +11 0 obj +[ +12 0 R +14 0 R +16 0 R +18 0 R +20 0 R +22 0 R +24 0 R +26 0 R +28 0 R +30 0 R +32 0 R +34 0 R +36 0 R +38 0 R +40 0 R +42 0 R +44 0 R +46 0 R +48 0 R +50 0 R +52 0 R +54 0 R +56 0 R +58 0 R +60 0 R +62 0 R +64 0 R +66 0 R +68 0 R +70 0 R +72 0 R +74 0 R +76 0 R +78 0 R +80 0 R +82 0 R +84 0 R +] +endobj +12 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 82.0 686.866 136.45 676.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 13 0 R +/H /I +>> +endobj +14 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 666.056 182.84 656.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 15 0 R +/H /I +>> +endobj +16 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 82.0 645.246 137.0 635.246 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 17 0 R +/H /I +>> +endobj +18 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 624.436 236.4 614.436 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +20 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 608.626 215.31 598.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 21 0 R +/H /I +>> +endobj +22 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 592.626 214.75 582.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 23 0 R +/H /I +>> +endobj +24 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 576.626 159.21 566.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 25 0 R +/H /I +>> +endobj +26 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 565.626 268.38 555.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 27 0 R +/H /I +>> +endobj +28 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 549.626 159.21 539.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 29 0 R +/H /I +>> +endobj +30 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 533.626 256.69 523.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 31 0 R +/H /I +>> +endobj +32 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 512.626 208.37 502.626 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +34 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 496.816 219.2 486.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 35 0 R +/H /I +>> +endobj +36 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 480.816 179.77 470.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 37 0 R +/H /I +>> +endobj +38 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 82.0 459.816 117.01 449.816 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +40 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 444.006 143.66 434.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 41 0 R +/H /I +>> +endobj +42 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 428.006 205.04 418.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 43 0 R +/H /I +>> +endobj +44 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 412.006 165.33 402.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 45 0 R +/H /I +>> +endobj +46 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 396.006 206.98 386.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 47 0 R +/H /I +>> +endobj +48 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 380.006 146.99 370.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 49 0 R +/H /I +>> +endobj +50 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 364.006 151.44 354.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 51 0 R +/H /I +>> +endobj +52 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 348.006 202.82 338.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 53 0 R +/H /I +>> +endobj +54 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 332.006 187.81 322.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 55 0 R +/H /I +>> +endobj +56 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 311.006 131.16 301.006 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +58 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 295.196 204.2 285.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 59 0 R +/H /I +>> +endobj +60 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 279.196 187.83 269.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 61 0 R +/H /I +>> +endobj +62 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 263.196 233.92 253.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 63 0 R +/H /I +>> +endobj +64 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 247.196 209.21 237.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +66 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 231.196 251.12 221.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 67 0 R +/H /I +>> +endobj +68 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 220.196 242.81 210.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 69 0 R +/H /I +>> +endobj +70 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 209.196 323.07 199.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 71 0 R +/H /I +>> +endobj +72 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 193.196 222.54 183.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 73 0 R +/H /I +>> +endobj +74 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 177.196 190.59 167.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 75 0 R +/H /I +>> +endobj +76 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 156.196 257.02 146.196 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 77 0 R +/H /I +>> +endobj +78 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 140.386 213.63 130.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 79 0 R +/H /I +>> +endobj +80 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 124.386 145.6 114.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 81 0 R +/H /I +>> +endobj +82 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 108.386 154.22 98.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 83 0 R +/H /I +>> +endobj +84 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 97.386 243.09 87.386 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 85 0 R +/H /I +>> +endobj +86 0 obj +<< /Length 2593 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$JhfIL2&BE]"=7DN!k2K"=/6S#_+kIfM?kUnWRnb%FiZt*?QntW,rV%ZI'CUNhMdBncqTo6A9'4$+GI&11%OBNl8Kp;I#LfTeQWKm$?Osj\VB:hU$]#T1Wl/;,4Udo>ML,BP>fD2rP:XFp4ZHk91!$E)l!4q*Wn5@eqRa6u?KJ6N@B-qJP^/s`qY'c%`D'96gbb5Vm36L9;#i4f!XCUOVUJ*?k$8t_`>P=lT4ZW3RoO:fq+#gZG5Bl]P;>ua1ghs>)/UKE"$f\m&HnQM1jZBRF\6dh!aXLT=$O?3=OcLCYQ=u98N0a:o6rE`YO@Xn6Y/BT/;$E8'sEqf!a=<$-LY0*Y3JEbB\ks*UKLn88ai^p0;Bmh/=HTIj(4`LH(l_h$(.m5B_D]aruB:I]-pH2)e5L\:GR#N08;/K"Q,`mkBMPl1?L=*\JNf91>Gl;L\E7C+4Ih_LNf&cngq=Wk:j-1Y-fN2-G48Z,4LCRj1P`#)M!2kF4E(_TS=R$ShE]f*d+eZTUB&E-g0qOcZV>&gn5'=L"0H@EG1'<,6Aqt-9"Wem-[TG,#"]UHDaaI;ELtA,?W/%(6I(>3@l+0?=rGBV0b6-6"V0&`J`M@=W8bdkUsG48X3`K'.Z`?,F6%g9->2PLn9oR^utn*=mai#D^Z)3JHWKnM1uj[Gtm@bqALFbf)[_3oQ%6gRp_%VlUmo4#%UH`DH7%2Z*]5I"tV?%pFq66:?V43`;f0,F'B=gQkrS@S%fH21,u6JB0['Sf(DIg'?C[!H(CPNUA5d4iN[i;5X)UoTcQWo1tXI4EX9@o2t31L*[_5"ldDM\#@rVB@RY&/f(UG>";`=1pPu@(L%GG$L@1'Fj!O=(!NB6CihD#Nppp[P*ml;MFoTVA>k9t$(5D*+e]p?ECL)BI,XG_JS^\&+]FM>>T*@p\GZUJj!;q+^A\)?D`XEC,2NSXE:#Xlrfaj,a;tmmYM7bJQO$+1"YXqAG*lE@\Y-=TW"o/g!mE]=h(Gsm1mF]N'mgk[r"=-)3-(o\u^VfrH!W_ff'dQ1]-T`=N47+K!:cC,`lG@HZO4Nn=116B4usYGu524!2C\=juO`k2Q,H?3`i]+c9F4,2EW60a0X59lWqk[nEV;OgE`BP4Mc]b7k^[rY#EriB(FBo\#%1kiJ3!%Z5acC6Vtf4qJ>=jo`IG$j46kV4UVnEJ]Grk!TWBTea2Wln5do5[>7[`J%k#aY]qF,UA2WS(O3k;3r$=D_[O9j`mP8q'(mpIM*3s()S'jL0DTK)5+b6[4=pZm%/+$hsn;LCY$Q<%rUn1It^^fm7'S@<[7Asc]N"E,SY=t)e24qm\5t.QBjKCegb3i-62,hL))@VYBi8AS&W\c[X_mg1JfgJ]gJ6hhBX(YL1KDS\YPTsXt8M65b&8Nea;3)(6=5UOGJ_V/O`(.T_;d:`g^ehn6_fMQfn`06#<_oTBA;Z-+JR!K/Kj-L\Mn1YhkEtO$fn#P1P]fcr]n+DPc7>]HQXcm#AW;%Wa9l^WkAI9DH2uQpB[@7b1"p/VOs#46R4jg'B7;Gc#PY#Z"*VWn6/6]Hl[]-VTHg>02>ba4MF89@iCBg4_`S)*Q(=[`CW>5,SWtoLNfK26L[9Z!*SiE-,J71b`cR?grW3kEJ2%1ss7qeYE#79:IEdls`R$2Y6aEf+eW<22`0t4`ZmJ4g-(!rMj9=X[`#t]]lroceUnrjc1e2OI'.o$;J#%Eg)@dSH^L)A9#;gCR#[s3dr96cQlk3BgPo.o;3e5riHMQNLS;G(@om0SRL"_?7q%F_5Op27@]4Fq.LVpXnelC4_$kc0)T#:ut!3j+`J(h-P5abF4-A2mJ)T?=LP/)bIX[69+-XkFQQeh[P/@S6#&2+%\kRPM27"Fl*,YC$Pb-4f,DNm\0BZ3Fi&!@3@o@+FtulgnQXXgk*66!Z5d]N"Q7E:H]%MK_7c(Ouf:`&`Kn9**kgKCrso8*bH71&BlhOAb3@jPhf7i59oj/2E$\OAb;C_SN9+PNW%+Vo+Yms(aXYBR9;(86Ceu~> +endstream +endobj +87 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 86 0 R +/Annots 88 0 R +>> +endobj +88 0 obj +[ +89 0 R +91 0 R +93 0 R +95 0 R +97 0 R +99 0 R +101 0 R +103 0 R +105 0 R +107 0 R +109 0 R +111 0 R +113 0 R +115 0 R +117 0 R +119 0 R +121 0 R +123 0 R +125 0 R +127 0 R +129 0 R +131 0 R +133 0 R +135 0 R +137 0 R +139 0 R +141 0 R +143 0 R +145 0 R +147 0 R +149 0 R +151 0 R +153 0 R +155 0 R +157 0 R +159 0 R +161 0 R +163 0 R +165 0 R +167 0 R +169 0 R +171 0 R +173 0 R +175 0 R +177 0 R +179 0 R +181 0 R +183 0 R +185 0 R +187 0 R +189 0 R +] +endobj +89 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 714.0 210.33 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 90 0 R +/H /I +>> +endobj +91 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 698.0 240.29 688.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 92 0 R +/H /I +>> +endobj +93 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 94.5 682.0 116.16 672.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 94 0 R +/H /I +>> +endobj +95 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 666.0 227.54 656.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 96 0 R +/H /I +>> +endobj +97 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 650.0 312.22 640.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 98 0 R +/H /I +>> +endobj +99 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 629.0 262.56 619.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 100 0 R +/H /I +>> +endobj +101 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 613.19 176.45 603.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 102 0 R +/H /I +>> +endobj +103 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 602.19 209.79 592.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 104 0 R +/H /I +>> +endobj +105 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 591.19 275.59 581.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 106 0 R +/H /I +>> +endobj +107 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 580.19 267.53 570.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 108 0 R +/H /I +>> +endobj +109 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 569.19 350.01 559.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 110 0 R +/H /I +>> +endobj +111 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 558.19 249.47 548.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 112 0 R +/H /I +>> +endobj +113 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 547.19 265.02 537.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 114 0 R +/H /I +>> +endobj +115 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 531.19 185.9 521.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 116 0 R +/H /I +>> +endobj +117 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 520.19 275.59 510.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 118 0 R +/H /I +>> +endobj +119 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 509.19 208.67 499.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 120 0 R +/H /I +>> +endobj +121 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 493.19 164.22 483.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 122 0 R +/H /I +>> +endobj +123 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 482.19 197.56 472.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 124 0 R +/H /I +>> +endobj +125 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 471.19 186.99 461.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 126 0 R +/H /I +>> +endobj +127 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 455.19 208.93 445.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 128 0 R +/H /I +>> +endobj +129 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 439.19 181.17 429.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 130 0 R +/H /I +>> +endobj +131 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 423.19 190.32 413.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 132 0 R +/H /I +>> +endobj +133 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 412.19 206.99 402.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 134 0 R +/H /I +>> +endobj +135 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 401.19 188.65 391.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 136 0 R +/H /I +>> +endobj +137 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 385.19 171.44 375.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 138 0 R +/H /I +>> +endobj +139 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 374.19 248.37 364.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 140 0 R +/H /I +>> +endobj +141 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 363.19 188.11 353.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 142 0 R +/H /I +>> +endobj +143 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 347.19 154.78 337.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 144 0 R +/H /I +>> +endobj +145 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 336.19 253.92 326.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 146 0 R +/H /I +>> +endobj +147 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 325.19 190.88 315.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 148 0 R +/H /I +>> +endobj +149 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 314.19 195.89 304.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 150 0 R +/H /I +>> +endobj +151 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 303.19 294.2 293.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 152 0 R +/H /I +>> +endobj +153 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 292.19 158.95 282.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 154 0 R +/H /I +>> +endobj +155 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 281.19 240.87 271.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 156 0 R +/H /I +>> +endobj +157 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 270.19 255.59 260.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 158 0 R +/H /I +>> +endobj +159 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 259.19 239.49 249.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 160 0 R +/H /I +>> +endobj +161 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 243.19 157.55 233.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 162 0 R +/H /I +>> +endobj +163 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 232.19 193.65 222.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 164 0 R +/H /I +>> +endobj +165 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 221.19 198.66 211.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 166 0 R +/H /I +>> +endobj +167 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 210.19 242.51 200.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 168 0 R +/H /I +>> +endobj +169 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 199.19 158.95 189.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 170 0 R +/H /I +>> +endobj +171 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 188.19 262.81 178.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 172 0 R +/H /I +>> +endobj +173 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 177.19 242.26 167.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 174 0 R +/H /I +>> +endobj +175 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 161.19 160.33 151.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 176 0 R +/H /I +>> +endobj +177 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 150.19 276.42 140.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 178 0 R +/H /I +>> +endobj +179 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 139.19 182.82 129.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 180 0 R +/H /I +>> +endobj +181 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 128.19 189.21 118.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 182 0 R +/H /I +>> +endobj +183 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 117.19 218.1 107.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 184 0 R +/H /I +>> +endobj +185 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 106.19 215.89 96.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 186 0 R +/H /I +>> +endobj +187 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 95.19 183.94 85.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 188 0 R +/H /I +>> +endobj +189 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 84.19 241.99 74.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 190 0 R +/H /I +>> +endobj +191 0 obj +<< /Length 2416 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$I?#uJp'Sc)J/"""B/khChKQPnla#)U/c(rX.G<3F12GTW#i!%o3s*b+gF,hh;fh'Gg1MuLL$@&<:.K)FnH?6nCijkX:>o\.8r=O$3rJ*?uTYCj?F\1Lb+.Y(*b12j#'BcDPI=G4&j13qi8iTRhmqcQ'/$SI>3IG'`UD%9,H$Q%B8c.Ob[)f!>+\qI[n&"e=cWOKQiDhr9^:f$nUm)*:Vs'`?iCQU45fm5hZ=Q^\'#4^b9&FH`#=>!er\FK;@e_"a-GpYZU'?Wmc&2jX*kl\;VZsfQ;:,[B+T`0&^;Fe.NSEobh8GlLq@(e,Rb@4Cb_O\1W>G]t"EWd/Y+2UFp-NP7qkjRLF?bn#"R'(=TlDS4=H!Ef2bi5Ff]13PIZY3?$=Rc`'8W\%12MP)C#Z9Ef\7-#9DhSRVc?+4@-XdUbM%-gW\P/_(`[1hCIK<1oB#b77/f-F%Qrhube;__LHN7j:6WSZ\7=^KQVAW3SZd)Tc*Z7f]2k]BoZ%7!+%4[H&S@HA)/Fs7Jqt)Zn_Ik)?*T&-RG&//SUKg[(%5K.V`\FDXO0nulFB!Vg*/7uE=0(XOR`_P"O;$(dD0ELFiq)#`CeHAi5=t7mY%ZZRTMB"RC6S]T\,+^M"f6ml]4[ngE:^trVdF)7qVBHUP'd*3'=9_L3i";!@taUID0MPTQ[;M=e#c[7nAY[$HlQSUJm+V:'/278SbRR]'=^_.R4oi@A=ZJFE@e)Ik1j&6A-h%p+c4m'le4iaaZ8Gm@[9R3#!A[`9:qA>fYlb':KGa]uN(h^TP$Hg]V;tr0+M`-^nl=m[bjZ,NV0<`a_^)C$PLC[hSWj?ideZo>bFa[M;Cq,8.3Y'A^mP>'W:I!_6D/oVS\;[(nGnjup4?_64;okMGGd&>XaKDbVVn3bGEAr)#<+GMa9pa%:L=^IF2ank(CgAR`@'9j@G>[qeP/$OE!+R&QB%JDc,"@!N$ZIB1*FBS`eIKR2C+Z@GnL1b9?a&b6iV:sjQjc/T#FP.@d'^6c,+g;-XB;Fi;(CTj_i6??2rdo7]DE,Jo:>mqQhZ16HNLC**9_e&JDQ:D2p_TcS'L.i>opm$(JnK.%8S7\3[H::G&=j'JT[K_bCokp=gjP;&^;r+_\Hlfsrt(W=V7PN,sg!/u1fG,A]%5S_W%-_X]jD0uSI:JZpn]^kp+O`ci5)QO3Pjgek6]%S#+)KRj9hCb<_UV:/eHe!&>E%\.$.<+*)A._J&OeTm'f$4,P?)`i(_q@G=CBFS!t/Bo,B3`6+E*X=&>r,,t3$:rAnnV*^\Ukl<%>eW=V&oHVJ:NfPkh4s8@nK%5fn7q(L7rQ:`0Fb1T)GpA)iu;,$F*tFEG'usE"p(0+Cf&l+dN#SP'@WW_!4+3@JL8MpGei"$=4Ilfo8HDC*SW3&R9(Mq[Yi +endstream +endobj +192 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 191 0 R +/Annots 193 0 R +>> +endobj +193 0 obj +[ +194 0 R +196 0 R +198 0 R +200 0 R +202 0 R +204 0 R +206 0 R +208 0 R +210 0 R +212 0 R +214 0 R +216 0 R +218 0 R +220 0 R +222 0 R +224 0 R +226 0 R +228 0 R +230 0 R +232 0 R +234 0 R +236 0 R +238 0 R +240 0 R +242 0 R +244 0 R +246 0 R +248 0 R +250 0 R +252 0 R +254 0 R +256 0 R +258 0 R +260 0 R +262 0 R +264 0 R +266 0 R +268 0 R +270 0 R +272 0 R +274 0 R +276 0 R +278 0 R +] +endobj +194 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 719.0 255.02 709.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 195 0 R +/H /I +>> +endobj +196 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 708.0 276.42 698.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 197 0 R +/H /I +>> +endobj +198 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 692.0 174.77 682.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 199 0 R +/H /I +>> +endobj +200 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 681.0 163.95 671.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 201 0 R +/H /I +>> +endobj +202 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 670.0 197.54 660.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 203 0 R +/H /I +>> +endobj +204 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 649.0 265.89 639.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 205 0 R +/H /I +>> +endobj +206 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 633.19 152.53 623.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 207 0 R +/H /I +>> +endobj +208 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 617.19 155.31 607.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 209 0 R +/H /I +>> +endobj +210 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 601.19 176.98 591.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 211 0 R +/H /I +>> +endobj +212 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 585.19 137.53 575.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 213 0 R +/H /I +>> +endobj +214 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 574.19 144.22 564.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 215 0 R +/H /I +>> +endobj +216 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 563.19 139.78 553.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 217 0 R +/H /I +>> +endobj +218 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 552.19 173.39 542.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 219 0 R +/H /I +>> +endobj +220 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 541.19 249.76 531.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 221 0 R +/H /I +>> +endobj +222 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 530.19 274.16 520.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 223 0 R +/H /I +>> +endobj +224 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 519.19 230.04 509.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 225 0 R +/H /I +>> +endobj +226 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 508.19 302.37 498.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 227 0 R +/H /I +>> +endobj +228 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 497.19 350.59 487.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 229 0 R +/H /I +>> +endobj +230 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 486.19 283.91 476.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 231 0 R +/H /I +>> +endobj +232 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 475.19 345.59 465.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 233 0 R +/H /I +>> +endobj +234 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 464.19 312.53 454.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 235 0 R +/H /I +>> +endobj +236 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 448.19 180.3 438.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 237 0 R +/H /I +>> +endobj +238 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 432.19 171.41 422.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 239 0 R +/H /I +>> +endobj +240 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 416.19 199.48 406.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 241 0 R +/H /I +>> +endobj +242 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 395.19 242.85 385.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 243 0 R +/H /I +>> +endobj +244 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 379.38 167.01 369.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 245 0 R +/H /I +>> +endobj +246 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 363.38 202.82 353.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 247 0 R +/H /I +>> +endobj +248 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 347.38 146.99 337.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 249 0 R +/H /I +>> +endobj +250 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 331.38 194.48 321.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 251 0 R +/H /I +>> +endobj +252 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 315.38 196.15 305.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 253 0 R +/H /I +>> +endobj +254 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 294.38 200.89 284.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 255 0 R +/H /I +>> +endobj +256 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 278.57 195.61 268.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 257 0 R +/H /I +>> +endobj +258 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 262.57 211.99 252.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 259 0 R +/H /I +>> +endobj +260 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 241.57 183.39 231.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 261 0 R +/H /I +>> +endobj +262 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 225.76 173.09 215.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 263 0 R +/H /I +>> +endobj +264 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 209.76 250.87 199.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 265 0 R +/H /I +>> +endobj +266 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 193.76 185.05 183.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 267 0 R +/H /I +>> +endobj +268 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 172.76 197.55 162.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 269 0 R +/H /I +>> +endobj +270 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 156.95 201.15 146.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 271 0 R +/H /I +>> +endobj +272 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 140.95 188.38 130.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 273 0 R +/H /I +>> +endobj +274 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 124.95 199.49 114.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 275 0 R +/H /I +>> +endobj +276 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 108.95 182.27 98.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 277 0 R +/H /I +>> +endobj +278 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 92.95 179.48 82.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 279 0 R +/H /I +>> +endobj +280 0 obj +<< /Length 1891 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!$I_/H).'ZTV?.l_\^Fg2Ammqm9H4cFQ\C,VMdBf$&AEDn\#6pNg#J%mn'n6X*<#H;k%6^oL]KD]J6I*_<&io9em%VSDM?u^Z.#3uX8K_gH.K'E"t-jUR(@N0^.U_-0^"Q+gMV[N*QOMi<-8J"kei$abO`(6\qQCE;n5H571'oU=F0)thGYMmIF7Vu/Q*:"*TB44=j6cFlU@8<>ik/7qaT/5Jl8.'VK-XKXA:oX>u;,NI="P??L$H,i@k0!RlH?6%_uABaLf!Eo6):t:O&`EMHan&al%nh3'YMX>jJj7g?>Cfcn6o6I1;\Mh;c^/5mT\3aA]71ri(ABOA&6(($9WoeTA>@l%f-Y#1]8lX1_.nN65V9&+nA%m^R_s0E=6]6B5.:j1d);Y@?RtDRRUKB34$#Z=hAXVZBXab3&SM7#]jnR-ZMG`FeG2sn8oD*-@3sg8g&X,$mDNK#pQ"JZb%>gWgVUa)hi'9tjWhISQ\0$)OSbDd&s+A#UFK`fTA)a+t7;5HB0$(IV2]$e=d,WZQ(V'I%#<\)MW_(Xpi7Go$)F-81^b30AUdrQB`VY3RQY"[oE(hJKQ2gfTT2OsH3hKPs?Y(clUtWAoL*-9J#`'G.BHZRh#(He'Mof)':N#)pr_#ZSl$UpP1sBnaK(#\gRrl6$*>J>A'FoAG?ON(JWDpdEVA3]OgD'4[-QUq_,DJ8!koDg@;D4Pa_L$ruhWM4bgBG#BJ=F192XTLb8E9t#44P]V_uGN\"A1#N\<%9FLP#@).B1I7/C[E7]G>H[Po*-O`FlVFuS#qFu)'5SRo+=3,A]04%77HT&o^A9G)7g5M>)-qg)-&YS3hiu>%2K&^]-"V*C9fO=SSZ!rJ-b>eFCqJF%UUhBCr2c>:=-%V-(9,T_^RYB(FkW\'q2Wg1hLZL(0W)@cKYEnG^>M#M*W5bl:E#S,NC)^nrIDh%aMIO/,IgBWYI^^i.7]jm6EO;`B7g3#42^T@W`%Ul]g\&TR%*E,^SdOX3^_=AH"Xj[E7sTM\.0fHSs/[#;`-Z*[TqQ=qJ`=,(fb7?b0N&_9#\B#Bu.HmVcYb6KHbGCS$q[*HGLS%JdR_7W$sagr84SL.J59up")':i\D)`05715J=2.JjROcaSB6h_pY[Bo+>i(hZ5@QiF)Sh0+a+8$>1[Z>'R:@2DqpHD/Wi,13#TK?LHBW7f\lU&Fql5NpAOid]b][or~> +endstream +endobj +281 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 280 0 R +/Annots 282 0 R +>> +endobj +282 0 obj +[ +283 0 R +285 0 R +287 0 R +289 0 R +291 0 R +293 0 R +295 0 R +297 0 R +299 0 R +301 0 R +303 0 R +305 0 R +307 0 R +309 0 R +311 0 R +313 0 R +315 0 R +317 0 R +319 0 R +321 0 R +323 0 R +325 0 R +327 0 R +329 0 R +331 0 R +333 0 R +335 0 R +337 0 R +339 0 R +341 0 R +343 0 R +345 0 R +347 0 R +349 0 R +351 0 R +353 0 R +355 0 R +357 0 R +359 0 R +361 0 R +] +endobj +283 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 714.0 197.82 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 284 0 R +/H /I +>> +endobj +285 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 698.0 176.15 688.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 286 0 R +/H /I +>> +endobj +287 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 682.0 189.49 672.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 288 0 R +/H /I +>> +endobj +289 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 666.0 192.27 656.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 290 0 R +/H /I +>> +endobj +291 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 650.0 202.82 640.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 292 0 R +/H /I +>> +endobj +293 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 634.0 198.38 624.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 294 0 R +/H /I +>> +endobj +295 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 618.0 198.38 608.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 296 0 R +/H /I +>> +endobj +297 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 602.0 205.04 592.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 298 0 R +/H /I +>> +endobj +299 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 586.0 204.49 576.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 300 0 R +/H /I +>> +endobj +301 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 570.0 199.49 560.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 302 0 R +/H /I +>> +endobj +303 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 554.0 208.95 544.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 304 0 R +/H /I +>> +endobj +305 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 538.0 190.04 528.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 306 0 R +/H /I +>> +endobj +307 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 522.0 183.38 512.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 308 0 R +/H /I +>> +endobj +309 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 506.0 225.59 496.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 310 0 R +/H /I +>> +endobj +311 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 490.0 199.49 480.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 312 0 R +/H /I +>> +endobj +313 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 474.0 205.04 464.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 314 0 R +/H /I +>> +endobj +315 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 458.0 197.27 448.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 316 0 R +/H /I +>> +endobj +317 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 442.0 195.04 432.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 318 0 R +/H /I +>> +endobj +319 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 426.0 200.04 416.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 320 0 R +/H /I +>> +endobj +321 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 410.0 244.48 400.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 322 0 R +/H /I +>> +endobj +323 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 394.0 176.16 384.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 324 0 R +/H /I +>> +endobj +325 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 378.0 191.15 368.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 326 0 R +/H /I +>> +endobj +327 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 362.0 187.83 352.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 328 0 R +/H /I +>> +endobj +329 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 346.0 195.61 336.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 330 0 R +/H /I +>> +endobj +331 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 330.0 185.6 320.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 332 0 R +/H /I +>> +endobj +333 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 309.0 155.59 299.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 334 0 R +/H /I +>> +endobj +335 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 293.19 185.31 283.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 336 0 R +/H /I +>> +endobj +337 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 277.19 186.99 267.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 338 0 R +/H /I +>> +endobj +339 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 261.19 214.2 251.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 340 0 R +/H /I +>> +endobj +341 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 245.19 203.1 235.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 342 0 R +/H /I +>> +endobj +343 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 229.19 195.32 219.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 344 0 R +/H /I +>> +endobj +345 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 213.19 165.32 203.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 346 0 R +/H /I +>> +endobj +347 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 197.19 198.66 187.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 348 0 R +/H /I +>> +endobj +349 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 181.19 192.54 171.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 350 0 R +/H /I +>> +endobj +351 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 170.19 281.14 160.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 352 0 R +/H /I +>> +endobj +353 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 154.19 187.53 144.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 354 0 R +/H /I +>> +endobj +355 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 138.19 198.1 128.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 356 0 R +/H /I +>> +endobj +357 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 127.19 286.7 117.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 358 0 R +/H /I +>> +endobj +359 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 106.19 270.89 96.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 360 0 R +/H /I +>> +endobj +361 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 85.38 201.73 75.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 362 0 R +/H /I +>> +endobj +363 0 obj +<< /Length 2146 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauaA9lJcG&;KZL'tS7.#V/N36lt/26!^kN+E;TA.!e^+`XuZQL*S/6rq[oifq,IJO(*ZZ$8dUAQa@eELGM;GR%:q*:-@eThjZd#q;1\$HG95G]d-FX-%8)\k0;K6pN+L7GsAspk;3(N:ElEgf^m7^\!QUF89-`dl^`537!eY"O2ZFOb0kYi_/$*YWarLZpb4iGS:hlA7bjL0j>>FqcBB9.pXdfa+(?&XR"Y!i*MGjmKHLj>))qk%e4(m_KI07O)H"7`J7466@VCT/T`sJL!RVLdTEcB3\]3'9K?teHTnVO"!RVLdYQl(C\]3'9(2,DO@=[W0(:F4^W%\H&e%CuXT%7&,VlT/ko*^M(H'KslBjS6%\q$d+5gbB_E%td7$*0!3JAabFi<"KK+X"=^o50oG^OCeB(!#p[4VXBb>A3GTqa3G1,[uq@*OLqj!#//9oomVcCjtGC`?X/)1NeQ)YPaJYM?\/3c`B_k&3c&3q[hGjp^]f[1=+C6*S%hS?S\1$S2sk@M;W%if;,WbY7#Z\`=-2p[f\nCklRuZb$qu13kXtF6aaPgeCBf`F,i_3cDs[h3e/ppJ['41JEXGc?ICjl26Em\XAFGjjh-leJ@%<6rhYuHgGac)'>[XCkk07+(dN>n3-[^\J)T4L`C?/9B!!&)L_F-gq;)l-("BI_&>u-k!M-A$C%GdDh9,is%nq=nUS=@BWi4KGWk2Y?CBH4-DN;ne*dC$[+hgNK/"c3=M5D"s"D+.]PPQQNnF9:lN(fMg!84lp(Q)&9^I]089fh_CV;aYd\qU;]h$!J!37/"P)OKYSd8sINkn:?G5KQkVai!#fR9V?1"_g@kM?\,29:LcM_G8=.,G-,g_on1ji(.MW\<`FW9PBA;F:G"kbS"IBTL'6":kO\1!5^+Z":=8bm:3jf*22)+`:ehIqXEI8D`L$k1bSCl=9cn4ooVNP%lWgb52R*+HQ'V9h&ZrmTZD@_H3bQ2]WOgWXbi49,]4Q(mB&t=%OI3hF#4';F'%$UQ,0SRnX0$fS8ED6.?1&Vfc1IUJiZ:cSgd4\ej^O,Sk[rLO(0b!/6Q%8F5Sq$:dUB!\7dZ_ir<=[3O6_B`dAP[a(*r'c>2u&j@F%3LRL9YeRaf0t&r[C.cK[+Up&>K8_/-q%LHmS^5Ng#O8hQF0U9Sq3WL>,(TZAu2Ckp"tCicdFIEtm"Hj4Wp_ZreU9;0QN[WPFKVh0?4fi&+3\_kJRp:*=<3Gh0_!\@kq$_D?1/\%T:(A5nM@E\A/u\[5V;#@[o/u`*?-E<.E]&%G;#VnmUmuIf"rS8o4gtn+"2_q6)j2ZJ/[%;JYHlXptMa3gP';Pf9!?WpI^J$$U.266,[0,4=3W."4eP:@SIVr;H?g6.PXXWP.Tl'@JXk8S.aDE4H1R0U)(OPQsq,"Spk_kD`Et=n:7GBhK6/g9DBJ2_?YM=ie4"CjMQi0iJW$O;%E()GG6%(5]lZT?#NbO)n>+IFoNtqe"=GQb-EFt-Z+In.@bqCj(fS/*V=GJZ_o`eGrG;FW/ofE>lNLuSaP4_YP%,\5AD/9Y%tg@->#bl.oW2A(ei-Blie[ftD>N#Uqi6X*(o$R3MAs+g7Z'=pB=:doUG&E02[/K^Ddg=7]YkZ9rdF5VS"n(S42omP1F>IiNiPt\Zf\nTEFJ8cZ*k9OE1jT9fOZSFBK+OE)>@W`F[cG/82P:KXSS"Dq!8Zu`=rrGokL33~> +endstream +endobj +364 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 363 0 R +/Annots 365 0 R +>> +endobj +365 0 obj +[ +366 0 R +368 0 R +370 0 R +372 0 R +374 0 R +376 0 R +378 0 R +380 0 R +382 0 R +384 0 R +386 0 R +388 0 R +390 0 R +392 0 R +394 0 R +396 0 R +398 0 R +400 0 R +402 0 R +404 0 R +406 0 R +408 0 R +410 0 R +412 0 R +414 0 R +416 0 R +418 0 R +420 0 R +422 0 R +424 0 R +426 0 R +428 0 R +430 0 R +432 0 R +434 0 R +436 0 R +438 0 R +440 0 R +442 0 R +] +endobj +366 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 709.0 195.88 699.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 367 0 R +/H /I +>> +endobj +368 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 693.19 128.67 683.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 369 0 R +/H /I +>> +endobj +370 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 677.19 128.67 667.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 371 0 R +/H /I +>> +endobj +372 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 661.19 128.67 651.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 373 0 R +/H /I +>> +endobj +374 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 640.19 239.51 630.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 375 0 R +/H /I +>> +endobj +376 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 619.38 189.5 609.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 377 0 R +/H /I +>> +endobj +378 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 603.57 200.61 593.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 379 0 R +/H /I +>> +endobj +380 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 587.57 169.48 577.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 381 0 R +/H /I +>> +endobj +382 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 571.57 208.38 561.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 383 0 R +/H /I +>> +endobj +384 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 555.57 239.48 545.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 385 0 R +/H /I +>> +endobj +386 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 539.57 255.59 529.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 387 0 R +/H /I +>> +endobj +388 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 523.57 218.11 513.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 389 0 R +/H /I +>> +endobj +390 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 507.57 242.27 497.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 391 0 R +/H /I +>> +endobj +392 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 491.57 207.84 481.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 393 0 R +/H /I +>> +endobj +394 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 470.57 179.5 460.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 395 0 R +/H /I +>> +endobj +396 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 454.76 176.15 444.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 397 0 R +/H /I +>> +endobj +398 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 438.76 174.2 428.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 399 0 R +/H /I +>> +endobj +400 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 422.76 192.81 412.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 401 0 R +/H /I +>> +endobj +402 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 411.76 133.66 401.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 403 0 R +/H /I +>> +endobj +404 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 400.76 136.44 390.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 405 0 R +/H /I +>> +endobj +406 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 389.76 158.11 379.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 407 0 R +/H /I +>> +endobj +408 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 378.76 118.66 368.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 409 0 R +/H /I +>> +endobj +410 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 367.76 161.43 357.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 411 0 R +/H /I +>> +endobj +412 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 356.76 152.54 346.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 413 0 R +/H /I +>> +endobj +414 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 112.0 345.76 145.89 335.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 415 0 R +/H /I +>> +endobj +416 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 329.76 178.95 319.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 417 0 R +/H /I +>> +endobj +418 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 87.0 308.76 169.77 298.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 419 0 R +/H /I +>> +endobj +420 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 287.95 232.84 277.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 421 0 R +/H /I +>> +endobj +422 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 267.14 178.38 257.14 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 423 0 R +/H /I +>> +endobj +424 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 87.0 246.33 133.64 236.33 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 425 0 R +/H /I +>> +endobj +426 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 230.52 189.19 220.52 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 427 0 R +/H /I +>> +endobj +428 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 214.52 193.63 204.52 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 429 0 R +/H /I +>> +endobj +430 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 193.52 147.28 183.52 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 431 0 R +/H /I +>> +endobj +432 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 172.71 236.98 162.71 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 433 0 R +/H /I +>> +endobj +434 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 156.9 226.16 146.9 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 435 0 R +/H /I +>> +endobj +436 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 140.9 231.15 130.9 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 437 0 R +/H /I +>> +endobj +438 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 124.9 216.7 114.9 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 439 0 R +/H /I +>> +endobj +440 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 108.9 248.91 98.9 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 441 0 R +/H /I +>> +endobj +442 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 87.9 240.9 77.9 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 443 0 R +/H /I +>> +endobj +444 0 obj +<< /Length 900 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauI69lldX&;KZQ'tV@E=@!":*>9gg,gB"<0l7Gh*%7+i-5]tgSbh5FI)JQ1Nlat2q;;iug"hF-[Go=N>YGu"%Wm4c+#mX_5etc3";o_AR"HRUJA203_soa3!WLoJ]^7uD+@-$)$a@%s*lQ\dnX&edN%c\&5T$1eACaM)4PaURf55o&@`XDf%\@F"_')\\-V+o>KoL*U4gtpd\fWiZjBa&L,t/f_&>!Eu(+PPS,S8^U_T>@@)*//F.p008raJ4&R^9h-!XWSV6$*C'TTEU*kZ\YDJg<1[1f%+5)97k5o#qYH(+"l^HN,\uhTk]AYZ*C!A\T&@@K_g/`H;lpaCXhEZ9;;Q%oQY&EcY'P5=&fi!'0:3Y).F14YC:?bej&h%]&Jum]8!U6Rau=Z^Dk*sh.U"B_g72o#:$ZB75Jlo#b7#(l&Re31X=mlPG+cF'(!!9EmqU="b!hrGA2<&Z):d=eKBH53JshI'$F(7eJ5@L +endstream +endobj +445 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 444 0 R +/Annots 446 0 R +>> +endobj +446 0 obj +[ +447 0 R +449 0 R +451 0 R +453 0 R +455 0 R +457 0 R +459 0 R +461 0 R +463 0 R +465 0 R +] +endobj +447 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 709.0 260.35 699.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 448 0 R +/H /I +>> +endobj +449 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 688.19 171.72 678.19 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 450 0 R +/H /I +>> +endobj +451 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 672.38 318.63 662.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 452 0 R +/H /I +>> +endobj +453 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 651.38 279.49 641.38 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 454 0 R +/H /I +>> +endobj +455 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 630.57 244.48 620.57 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 456 0 R +/H /I +>> +endobj +457 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 614.76 307.82 604.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 458 0 R +/H /I +>> +endobj +459 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 598.76 241.98 588.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 460 0 R +/H /I +>> +endobj +461 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 582.76 154.77 572.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 462 0 R +/H /I +>> +endobj +463 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 561.76 275.87 551.76 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 464 0 R +/H /I +>> +endobj +465 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 72.0 540.95 96.45 530.95 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 466 0 R +/H /I +>> +endobj +467 0 obj +<< /Length 2858 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GatU699\*g'#+6EW-l^sb(C7QMRd,RE?(bs.]Sk]/(tV41PE58L;,@&%8KKB#Ga:%E:fFIBAk]ZFod3nPcQ%bm5Z(i%ark_\X5UXg'k,b!t9q7W+hG<#6mW7U8%SX_nAA$u1.,A2CMdMgs-6r3Atr'`4YX1m4Z`V$>b@[_E+JJA8"lEDkV%[>Vi>H"dj#;LM%Ss39b/\5%Tl!J]da>K827fAL7I!ioKdDm^M=,WQ/1cja\I^k)Z7Oprsl>%)o1%)J!`F'Fnst6,)mp#7PmPppD$DZ&*[955[HM5r9:SZcdjIk>VM@)IWEROLQgL8c+(,^ZMOs_#Al'DWZmU85:VU+]mNQQBJ4cf!7D)=Ume'M`:dZB#GgY$&KgZ]>DY*&L15p='"k"q[V(Un*I'slq=LTsOf&8c7VJ8,$>mEu92PJV=n.1sGn,Y0mNN$r39B)ha9C#4$042>nGRr*-_`3rs"7BdkQGD_4o5KA$Peg]=i/cjrROEa'D)=AjhqoC'9/en'MV%rG]u=^M?GSppY%h[Z`2EgQ_m-Y;5N&>c4M^@/&Lp6K"d\R[;4u26rKm]dP`MlBlO=Os51bs0O(B\TV:u66DM`5C\#Tk#'D5`?6E[IRG"d;o4MoaZN(%E.-fIINImO!;.E&tfC%\\!sP8Sjp?bn:uC;_ln*BP:R2YmLR4V-=0KUQaEl**!\kJ]C'VTuV3P4Kf/"1qs,kJp?@1O0aGi7LM5B2AOs6$Rq+OlX$1$*eb)+'(A"h/Bi;-W(45==WUF7^4(D.-4Ftpa5c0Y_h]HQj.*jPFHSWDcY+FX-5U&='S!59eE4TBs5&=!bC@*3$hQbZZ[TE?9pW+:Tpe*#9pi/@e+ZET_YIukK'8C@pg?k+>hagOM@-R_[_e-]jDoL3gV#\X,9BS[DgM%E9Nf63:a9XNQp`RRU9S@Ukkf7QMK8&[KVAjtghR3Z+\C"eD89/+lig?hKM-9Rm"/\"N7((+@tPF)E.OD,_.QptP+C;SJj'G6Ms-n]dS_<5R".[M>?VEL]gD]r9N=4_Zcu`0o^ud8_jIct?][3da;[HkS+A#gXe=a'Q7*?+).-\]-$=KO_HFIW7<&S$<]33:Q*3\?P[],.LH*Nr:G1jm3HRfDPblK,#DYNa<&#JOH0h!"#A:hAGtJ.HPO8G@AGI,VG<8c57b[8uG_j/iZV]*ej$3p;WJA=2ZZ$@/(36G"%+j:"%cWjeejA^BZsCYnaR9+hXb$X_4JOtIGb1ZJ#E)(ih.EN%%pY'U"`.nT`2X7e2VQe7ppos+&sOgEILHq75EM7)kG2be!/5ApOc2b(J!tT77B-Gj4R5'>Yg>-FdkfuBUrXPpX%'eAX5kq;XeRS:si?BoG&5aH:R/h]0/_8J3:E5:_pf.Ec3NopjL@ZOkAaCp!?X[H#8:%R9'I1H#`!6WEqdOBoc`!DDYuF+lstM5LsGF>?kTmje;Z#]?`&@\`c-!`WD[3`2;/ndbE]BhRQdOrmCIJR^j50lQ'%=,UhsjTGAo4Vl)mjNp=^[WYt+i@M;F"VuGN.>`2GQXpfLnN@Q!-FBo*q8jO]/R4'O/r'eUR9"@Bdf;P^)nr)Pm+n;H04$$3;,L[4pESXAU&=-OD-l\uisT/'5K'ZP^*pH$.t5[88LjY(YZ7hKf[r+aN+,#-$E2UMr&QZK18\72Vd\o\-hZ=YpXFf-#T6#Mu8sq,0,5hn"oZ&QeL7bhH$E^(qaSMP;Bq1KAZ*cB)[ZmK`&8,N5@F6skMW5/,*HpsohQfGa_fkbOu=0Khhc=1Q8R3$*H?:iV9m]Y.?/2=c1D7/W#,Bkui>gc8c]>dFi3-.Z`OINHp&Q9Ht+nqU1KM%ai*aXpaFNoR"jcZI6/Z0U=a'YjC3Qq$\FHQ@tc*VP^O1)[^=VuiD7k=^;`Z(ttbqB15,RK)GF.E3mF@4YO!S-PH:!19iQT8jbV12F('SB;c!STQHQ";DW[9VL7r@%.m.gU@os'4'd>J9erXibs#~> +endstream +endobj +468 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 467 0 R +/Annots 469 0 R +>> +endobj +469 0 obj +[ +470 0 R +472 0 R +473 0 R +475 0 R +476 0 R +477 0 R +478 0 R +479 0 R +480 0 R +481 0 R +482 0 R +483 0 R +484 0 R +485 0 R +486 0 R +488 0 R +489 0 R +490 0 R +491 0 R +492 0 R +493 0 R +] +endobj +470 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 389.93 498.866 435.49 488.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 471 0 R +/H /I +>> +endobj +472 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 379.75 477.866 425.31 467.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 471 0 R +/H /I +>> +endobj +473 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 326.51 466.866 372.07 456.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 474 0 R +/H /I +>> +endobj +475 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 95.33 434.866 132.83 424.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 19 0 R +/H /I +>> +endobj +476 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 241.41 434.866 278.91 424.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +477 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 314.18 434.866 351.68 424.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 39 0 R +/H /I +>> +endobj +478 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 469.15 434.866 506.65 424.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +479 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 407.21 402.866 444.71 392.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 100 0 R +/H /I +>> +endobj +480 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 128.37 391.866 170.87 381.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 205 0 R +/H /I +>> +endobj +481 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 237.52 380.866 275.02 370.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 77 0 R +/H /I +>> +endobj +482 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 336.94 337.866 379.44 327.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 243 0 R +/H /I +>> +endobj +483 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 120.6 326.866 163.1 316.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 255 0 R +/H /I +>> +endobj +484 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 185.33 315.866 227.83 305.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 261 0 R +/H /I +>> +endobj +485 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 402.53 304.866 445.03 294.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 360 0 R +/H /I +>> +endobj +486 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 182.81 272.866 234.47 262.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 487 0 R +/H /I +>> +endobj +488 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 95.33 250.866 137.83 240.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 334 0 R +/H /I +>> +endobj +489 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 262.25 250.866 304.75 240.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 269 0 R +/H /I +>> +endobj +490 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 415.55 239.866 458.05 229.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 362 0 R +/H /I +>> +endobj +491 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 148.93 207.866 191.43 197.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 367 0 R +/H /I +>> +endobj +492 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 328.36 207.866 370.86 197.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 375 0 R +/H /I +>> +endobj +493 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 446.12 207.866 488.62 197.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 377 0 R +/H /I +>> +endobj +494 0 obj +<< /Length 1017 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%"=\n'3&:WeD=FDY3)r`^;^(jfSWu`f$HO00"->p(ZPKJ&.rr"l,XiG9V=ReX=Z,cGV1X/@9Ede`Glrl<)(FM+ESmZ*05E#Y6`<&-,E6h&E$9`JOlP+d=Cg4ljPPH)ddaUB\3$dOl+3H$]&ruJ=B^lQ.Ee&03-gohdE&cRdg=./."RN5C7R(M5fRh:\6cp60hK?Q,h)n>*&I:)$mSpub_]_aiIeMme)1bJ`r"^[JR3i&=U#R>A$/fspm`/+74TNQSeBnWZ?X\AH#"?4XB?2QCOun9)t9Pq='1H;**XMB@>3#AnFg&T']A88mq6Y'CbGe`R@8'_L-P-FmY1fg34nU>7Yd-"/@4[,rFeDc'l!lKI\M#$%dUZqNlT&Z@'?AGs"@V-M87=!D1".1oLGJ7DY_dW**NRA&C,0j3hNT92>[=\6hbiGeV&0E\CYX97udJu$S9U_pG[E=$N`T`?:tj_9N+\sal2DK7e1+^Q5&n-9W0i1&m-kucc/C]d7H="5@duO, +endstream +endobj +495 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 494 0 R +/Annots 496 0 R +>> +endobj +496 0 obj +[ +497 0 R +499 0 R +500 0 R +] +endobj +497 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 400.28 680.866 445.84 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +499 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 117.83 658.866 163.39 648.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +500 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 92.0 604.866 137.56 594.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 501 0 R +/H /I +>> +endobj +502 0 obj +<< /Length 2087 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau`U968iG&AI=/E.!OU*1H=dHV0L3FiKj>3(eNgEe#Q/ig9ic!-Ft7qLXqXmRdI.47.-3Tg3"0Z2+S6>6,IMo`[cE#Uho_?TI'm7WC`E/OMkGpCcYN7$imqq1QAE9;?,`>rH-f`\3omV@;1uJ$_2bELrb7XQan%2G=W8V>^6Q!=9Lf_7;gBuqh>H`h&:TRfA;%EbQ<:X'(F;VPp:o;BH8]_SHlqqVte8o-GcP+>2.q`cj&BA[S?qW]@?+gR9/GZka4MWZCFGL#]hjoF**$WI0p7U%Uq11@sa];n"8[l3]@a$54Zm$/),k-(3H.=D&_EK-%i#r@=4_t*<53gMBdE09s!DO-4d5*Uu6.?V9Y[kOB'FYIJCSE@>?lZgfg&YI66]F^&oRY4:$=HU/HG@q/1;?\%N_k4=_Zeg!GQo?1/nM#)%jO+_:\:1:_t0k*DI7[F87i"A>j[8\ViGB[kn`["@ZcC?4;=:;1Q3#XuRWUK=<7U-J@UZMgJeX[4_<.)sL#d3k19r*E9i4>=]0(_i>r!XP[dMfH]/hhZVPB:C#L>LJPN0a(=Z6%!F@2->bGgBXqe9-;Obg9"i?qepNos1C4@t=]Dg59U]pLm<`:['AE%9#j=c9hmOt_e%Qj>Ai5\.^!,!%gfH[!iHUk7m3@7dG->ulkn"6BYHN8]d1R)n;LI5K]k-V9SK2`m0B8VmZ5Xk`%j<5VDa[Ci[l79kNAk?D-tqC(4YNX0`rcfgep0^8rJMV0B3:/#Ir"l7*%#7IG.+TSXq2:FD##cQ#jBKbf,Al"@2E8q4^q1<&\i]EqSUoGg'OTi.)d%Fq&.A"Je?1pIY(<3TUTRE>KN&$A04[SsaNa!nKQP$aHnX)glE(\?0RPB-.b!J#tZ-;A5i:VY)@pBmna.`0KDuBi[U?iCT/$la)"X=bE3RER\'!nkE"+V^L_M[N2b<$ZDV:U'OV"j'R_>r]aEN#=,&+14Y:OKqXk[SSr;\M0XUaa2;nuE92DGL0P)>$d3WO4&5sIorTZ\Fa\EY)V%/V^Z*TZ`khnK*]QPEM+W-*Bd-sOM3\0gG1!rV:kA@:MaN&AklM@-Ht<4.>2S-;1Mqn!_NeR*>^->sXf&6X*ta647_.B-qpD+2t'Ji!Mb&G0gX.06/=!H/s#'T[qF+5fRhP!=T[g$r8H%qZ,Gi55Y9_8X:B],e,EKs):@%Qi;@C9'X#M&S\d+X`b^s0^a?es\T\p9_;eNt&H[q/"9@A!\7VM&D@I@0`X"cO@(.P%Q+=*b=bp:["+WCe-EeXRoXV_s>O^hL/K,ACT27eUYptGhoa,00uh>PM)_-*oEBk6F;(?2il+MER/,Y4-dXO"@d"n_WI\^Ne0/ofdn?Mjn)L.\?~> +endstream +endobj +503 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 502 0 R +/Annots 504 0 R +>> +endobj +504 0 obj +[ +505 0 R +507 0 R +508 0 R +] +endobj +505 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 255.86 680.866 301.42 670.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +507 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 117.83 594.866 163.39 584.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +508 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 134.77 551.866 172.27 541.866 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 33 0 R +/H /I +>> +endobj +509 0 obj +<< /Length 2955 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHN;01JM&q9SYi:t:]9Iu/tbi.l(#r1=ULm$_)LN134n"ins:KsAjBpT/%#^%Zg1`mncW22Wtb@Hk\G*Ztg4B_3%4'S`O1Q&oY[@4\oo=?M*KG-/-5X6(1aO$Y@bF(s#G<[$D/H5%gs#roY(MF>87AEcKFEfK(08Rt98,[9f/]MmhX!$_mk0^:eTBG9fj.8MMPAGi9F#Hph+pld*!ME'/ogK^i2a=*?cC>7c-#pLNkr^:-?EKk%j84$];T#n>@Xg0n^AID'7;F/YP`D@OZmIpI-ijN?lZ/8cKLH@SZgM?M=k1XQY/XQ6LOfZr6.OhA?F_4?)o,IE1>J7cJj/D:.9+rT\,Yelp8=&l+,KgEf92'"=O_""QKp1-T>e:OoWU\O$DBZaB)@#u3V#Z%4[XE6(Hu'%7>p8GG?HuQ)q)TSU=1_&c2.D.id9MX]0iV[CRIuga?caYm23i6m".W6(DBaum-1EfgbQ5Z:V\4&Y3OpOZC:3Kh[hX_dG4U2,jEZW3Oc5.1b5H-64bkO&d8pAV%A&!Qq2_].6&$Z8Lq+)\Z@iF)QMgU'Xcs?9j,_Y5@`O98>o1cjCdqul[S2JA)DCltjeD(gC&jcdIEZ?qSI\]uK>5)n.\rK`k%>*kD[t@'](HjjDo46+A0Y)PA%^<=6.,,G'k&gY:9H-5T&5\59Yfsd^HeJAa1_meg5`+4HsUN^4@S$Ue\)<=?7,)Mhi?[N?28Y'GL4L/`M2SDn/I`u)GBc',4gMW;MuW(D!Y+[lP'+'".^7=1dX)?cb/\bd/bOC_YiCn*/-HElR7G#QO(SmqNhO*A>q(JE"Rq(C[=(lh)GKB_GK;M>o,$^@IB&;d#+X>'FZ_=kBjN66\H(Z[9gn8*BVk9D)Or5@kE!KOs87Sg3[#T/C9HtA2[7VSqL1l6J[MD07Ok9q;ji+_7ND`cZ=!q/+61-:\U#DdS]b_f,LouWY:#5nQdHf5_V1"\Y;DGrA(BsA;m,;^^=SOc!q"Z4h%Hf#0QSGB\+n0cCjg*b"8@Aem1)6_@cl\&HZ*+=t#J[!Zp8E;95Y:s`E1F?0,BQ)+]KkUq.k2Pq\cYgpmn7e0DMsm!-<6"G/'i8XZL/lOho,OcOa[,,>HuVm@d!i%Y5!h=Q,.%Zk.SK-HV6!/-$-$Y!!5g&nV/D3b=Y7"3IKNYR'Hbu?B"?39+L]+kMc4<8*!fYH*\B6cGi?!B%cFDVG`NL%101GToY:/[`HN-V(!@r@(&l3?/T))D,$]hVITi]pW7*$\jt^)>WU;1bOQD-\HF"NQZ6GhG#[C9IHFohW!P(M]_,on`=[_\mt\)<"<0A#>X3+&5JnaYD.`0I`D)sBH3:9ShY8pp26O=mG\#Fe+3gpNM,X.pV2FXro)E8>OD+%Y7%6b[h!u!>[+hN*=fR5m9h"IK1+fgBnhs=Hp?E$pVWsDL;9Dk_TCE#0V$3%W1BH@K/-!6Zqhg8=do5nULe&_cpX8YM1#(BY[/Y`m[I\s=,>/]"'C5"9)1b;/+(%O'&$J:o(<*r^`-H)l$#/XKUHGe!eTAup#uIm9Ui/A6Zm;T\5[$t6L:a8lM:q";>^8lmZ'ZULRtu@j)8D!NXBcf^:B=T;_-r'N`N0FbB4t(r*VGQ4MC:qVM%cKU+nRgj27?f2dl7%gL'FLp>O9oo3mO=a(;#j3gc#\G0SQPM%S,Jck.UOO?gN6I!I(EIo$5Pb(-A5Ya0$/-JYJSc?5LV##"?.oAeMoY9kQ0-LU&@@eT[fV&j[W1V&C)gPMN0umg/t[E:$[*Hu +endstream +endobj +510 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 509 0 R +/Annots 511 0 R +>> +endobj +511 0 obj +[ +512 0 R +] +endobj +512 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 282.54 93.09 378.64 83.09 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 513 0 R +/H /I +>> +endobj +514 0 obj +<< /Length 1747 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHK>Bf'b&:WeDG^1GmV\mVPJiO.8fs'-fB@EXcgYJGP&&K*TY-=iX5l0e.^4=5CUcE_QPD:9+B(Y>WdAPCh8ZR&FCW+R/D)-#Ig7h"m"9+,=R5!e:;^\_+=_UZElU\0]P].BKcaB`1\ZM;]*]]VEaQQmpRW]J?/6O_c."aX_R?4SupCDkX=I?%D?c0F5<]&bj7$@p]W=_p8-K]GIK]K'O=*I89:YAY9Rej+sqR"r)-H]7XT8H]qokieZP9f.o1h#p]l5Q[1HaaVT6,Q,qc!gAob/)uQ&"`eL[qrF.&dbraaW$XkPqNh%2C=-/em%thcM;8mC],hCs+e8-r%^Yp#?]tON)rZ*EV'XbECu$of-1$>E6%$U#R,)RhYd-I!g":Fl/]Rb;:16]2ka-GlIQr!#O&q&mkjf]BQs$@5j60oIBpoSF7<38gEsgNA4cTJWa.2^YXB*>CZk$;F)Ofj*j)eqEP^qJ73C@^n\YT/,GHuR,c%r41\T'p?`h;`%9WH#d*gqbPgU"j$kdD$f-[kpH5i_7DE2NR@VLl93K5;;P^'JM8VR8Y-=2MMA]:u,tR6a87WifSD_'.RG8phG?99n_5pfk3F0^Kp3^>i77Iin!!njii#lhAhqdJ9M&Q>9Ns610BG"CDGh#?MTrtF6j5H_@$bDdr@,*UlU/YM=[k\\c3"\e?>-BYY9MN=b1_kgs7,i<)N[4_O%O*P8!Cg=Na$Z@%$!]l&*](Kd&9E@)TRKk?eFNhl,s.7=9&2@H0NgJ:'6QLBOcSt;M?JTKK@LC+#Bu$Le:@s]$Y6_Pnt^`:J4UlV#b%0$Po>$aE5j:%KNM`T8&&PAP0^[7_dpN&n&C&KYNOa<*eg)YYT2J>X9+-QpN6OWCnk[E$Fs)d]B\Rs&Y^kt&pjl3NYoKr@?U4hU/>ia.OV*oS$#hc!M>_a3&),6+Uqc;VaJpD.AIAF=N"nD.KLtD!\K>?SP(-ep@+=B0PUIf>KEC#+'Sh+gB-X*No=]<4b-Cr=RaYb,,WUg^UN&q$BOPie6m.WG%Nb<><4J,fXg$d\I#OR11o@jL;(QMSS>fcC"]k>n&@H3&M^?q1N[ki;C:X;4NtkOJfNfi&@6T&AXeD@b&eI2a-Z2`eXsbQkib3N^kfd)d'ECsGJbVYobbSh$>O1b+p4RoSdLoDD51;R1)5!afgW+BdN$.N]%c,_Gh;=4h^hYQ_Oqm\8o_qo?.X9-!YQWhJQpkCcFQSN6i\T_.CYD%(f/'f5s'KbVPC!^B(AV8G4h`0&&?:Fomp4]r:lRW\'^2^sk;cdlW-&m>T1-DRWjKeSq0=[e_D;Z=l'Ejfpq47i$F*f4D>Q&^U&p]4J8\a&QU1BV?TTp\\*n@nG&d1WDa=gHXrVhL'r4K+aLaf2F]?)#l4/S3ZbA!hNECo$*iTS!K;hM6D8\YfbZOKBdmWu?8ckoL.Kgfp=pRhiW989,>PMZ3/qEKG"Zcu1Ep4NB9*ASj"q+i2)'Rtd.c> +endstream +endobj +515 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 514 0 R +>> +endobj +516 0 obj +<< /Length 2668 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat%%=``=U&:XAWi:s"+B4=IIDoMG"M3C[iD5U+Y@j]J'6p`)$?7%q\lEB[I&@nG6eTpWm8M(^9n(SM)]/k;R3dWuqN%Ou+E#/62h!hGmF:H8"i;!_O"4*pEYs/'XltnC>#Os%d3BVlJAh)u[G:sA!b@sB\o3UBTcuiU?ne0&flX!Dqp[!NJ=EF8Mg9gF:!>:,uKSsGJ@%"H7X>8hZX$78d?Q)uR[h4'8'#qu$Z78IMM>d2Qs'!gjRW:^SfAeeF5&sgid;"'TfJX=d[A^-RH"hX`g?sRR:Kl$c)A]q3S1?>\>J_ip#!43Em;tJ]-kMmh/6lnCpU(K1FimOflJ@Co\t%oB@1,Rbrp,s_!ZK9PDn@I+upYE]J&LAXT(_g_@8ZW$3q;g`F,qcFK7tQe+i'Q0qZ'ZQ?BV)c:?,g?dbEbRLtjW:Gk4nKF4kp)Y8@&FSlrDk9$,$UMGj/SnE@4"1JR3(Ye%tVN6fl"Q`.:h+iuC#!KaW2[SI'bF5m(VgmG9eNYVBU(:d!^0E$YRb`jq\=U*(j"G+t(DTK98>t6))^;b(WEFXDMhN4>/2GNrj9*M6XApiJJA1U!(2:8l/i6&F(SRW-"q%[PsS8D@rEe+@;$*>UV:""-2.DY":FhS/\D-_,Y!Mh*+6EqLH-KL"mj)>UMjt!+6/KOIOLl/L?`qKYX4'9I/Y&>h&'R/Usi@LW7%2m/VtT>)4d@pn(afd!h*JiaRsMYUSHZbb8N-TS*#f\AV*6N_ED[<`Cg9377TU2pp1ZYF7?u$4\rkUkOt';\GS6MII3o#lfX0UFFH$Nm9_G^^'Y/M+=(:GQdn7!B.>V6)5OT"('$&8pbQ8itmg*S/2fAKX4(_R:$jPVF(]X/W6-ko#8L>"EFqK#;^lM&^cImo^tlI$O2tmn5Z.t/4TAP?uI(cfGe\Rds9t+JNCkF&kR@8lm+4tA1Jr@XZI"YJS9S=@,GR!lq&kaFpJ6lqRk/qfU_=>)b8r:6*ERGgBRUG.cGuf-C!JOYZ>%1A;Oh5e,krZ0*.&U-,K)<#0oak^E^_f4'e:sfnjZL!^c(h37eYIP*c$ML77%PGJ,T#7P];@M*EQ[UJ19"h''`=n.U7/15Paog#s`T4<<bp*19+C'(2NYMo!H#"2!'."I[7a(97Zad3P'dmA'1Ys9iqoLSOK*lJ*(C>YVAiGP1&Q3S"!`@5FGh-fJPFp;&hE2Y/:>cR9I.V4&!;G^ma,"LD9NB9Z#U=#F")]&_\&qm)jKKR<^Atf*6Y^r-$K8sCHBP.+Hl>l`HI+3:0BYAmTGnh83HeU><<^DXTM>Y@;n;?Jl3bZX3ioc\k7W[CO>GQ=At'6/,CJYA\e]4;gC8R`HK<'nDN)n??upWKeKO$Y,?!5/%WJA1=aM.uiM%:a,[F*m*baBEZ2LL(h%7X/Ri5jHJ\YgKQ&r`;aTk>4meeeh)D/ahp2Z:h^&+Aj=n@fBja8\``5#`#!QBqf!.3hAq@T__CdM'Qt1n%,/&t<2OFI:f+%U8/?V<"Ad3\&Rb]e&4_f/lar;FYpUeR#9'1#5lpC9fUO1N45qN59Y/,V*n4'rfoJX?bf9l>epGT]Fl^)SOOb8&SVlfL_a5Ot')tl8,F'OtS''PXS9A;Ni(80Ne?JA(2g-OrBh*0Q*cuIA]IBL?c%b8f@;tY1[#q3DcO^/:u3rV7+tm=D;0)D]Fd6ct>(r9:0jS/PfK*"(OCL@h00`6:DT+c'<8_H6[YJ/nlRg9sP:$s$VMI9CNeGp:&:Pb%*VI\:8B`[[+O[[I!L)*U,/;[?3EQH6gPld\U[`.ZWC50-(--TSE.O%rd\2K^'%m9j\q]p";^U>YM*Cc3S +endstream +endobj +517 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 516 0 R +/Annots 518 0 R +>> +endobj +518 0 obj +[ +519 0 R +] +endobj +519 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 332.51 157.408 378.07 147.408 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +520 0 obj +<< /Length 1005 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gasao966RV&AJ$CE,.4m&g0*"A(oG]aebDYBb-UOA.V4/+Ye;qotR\@8@5\a,f<%`X8R4VIHarn]3[(`1l7EpC1g,FRAEbi^,.;qF&jPTS/+uP8'_u%_@WR@X39^B[]gQ#L]VAVer55'".,1P.<[(r]6/a1_Vi0=CaA;^D@h326LB?nNmp=E1Ufh=$Xjt[0;E=eu/PS-Qh%51S$n'dYjq&>62AV*QU8FDo6HjlP6k#)7e:7m4e2?Y/J(1Yfc.[tD=c'oDD8s0.1@b5n'@gC(0`>!GHg)D':",>a=C%SGfbcWNFDWsl>DB>1"Ch=Q@c-_%qj.Y"0QWeaT9t.W`'9npCfG9O\$2Fh0mr!UWZ$nC"R&R>IJbNi9+VYDub$nL7LNfQ1d(T\pPj`#oh%@g2$tQKIhg2CL?:,(*"<:]oLkpAD3J4mp4@$29f7EAJN"<(i4EM.Y2cb*69lqPpYk$LODXdN.^-8)V>,k2_kWpHbIB+$Von&b]=TsSe"t`o7 +endstream +endobj +521 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 520 0 R +>> +endobj +522 0 obj +<< /Length 2903 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gau0FgN)%<&q0LUYkV\.aoVm2"Nec(Em_#%h^5S9t5`ln+CWf/FB3XEQiG%aFj>pK8KZW)Qo'R)NI=?-LmnoC$^6^YHmSX)oW3Z[_Za^D`N(Sj2#P4NO#Do"s(u)]n&l2@9pIG2hjTY-SA@P44:3+3f,-K5cK%*o;lmR.6*jOfE)h=5ad09fh078*9"uU@:btLl7^Y$q&cpQBA^CbhRs.8>H8U40:n&!-Kmd5p1IRNipMcBVGc"#6L"fT?PeY34P#O4@-tI[A%V3I`Gap:QK5:`C"^K9,^d?)Z2b?64O5kNpjli+_gad0Q]YH%#naP,>,p(6hK4re!d`>)R3a\jPKET]=aZ]B3Y]/?/+a_=_3n=NF0cdF%r(-D>Hdg4ZVfl=l=cq:&U3YmXMJoOiGcjO0@!\!&lhQe>pY:!#CX+$)c:5_WS)NcKQE'r7J]eF;[%6gOh"C#2V>''7Q.i+_TC=jX^IWDnT,cG@7G^XG5b#tKBG;l7O!Sq\r,0a@,DjU'j[PKQZr3j0E;cD2,#Cmh(7Jo-0j#*W/>X]6\FR;XZ4c_>N@G%WG\R7/ZhupDLr\>+@_Em*B#:ZB=/bOFQHY5"P[BAAq->=-+LTd'iXhJmq4P##2X;iY/jR37#)H,iJbX^ubo<3c?u[YjPph;dnWQ#/sTo8@nQq]r+)-_B8hoKDBEO7aK>VdeUFPns2?5-&+STCf5d40uUACo1\[>uS0=#)-dB@cQ#=SmY58u+"`"N-'bYfW%,.l@UedVV=q)H"Y@g:K.W>VKSEg69.t[(%jDX2dQ*_',i:TjqJ.28DbJR^c9BO3V9Pd:qA814@8=l;A@`U.Q6EaF1W]@sQ%,1pS$+E8'4aX=-NjAs\Nl1<,uDm/g31*%=O0HX_TU'Q?$Z.Eb5^Ymm_[MP_Xg/*]V*,Eq/&M`cnn8CC13E\0mnG,hFo@DTHP/DYjZpojV7ajZ(4o2.cojh=qd2Ti#`c.baAmekJkqPGW/mBH\oWm::u:IpDKW)Z`+7FL#'eOdpmF"1XIEnd]T\M'f?B_X+c2D;T>Fs0qu3U.en*WFc6N?(fVVAe(JXQGFt7!r/mT/A,ml#f[OZ`k3FV1+/Zb#$*+;?aCD-i#0sbp*/W:m`X-li!P5T/nYE0U7ku]+`EhU4fr(8ni:FY>6Ea8V3^+pD%i[$f#_iqH4VfNdTr'82;@oo&IsH]G`M!ro[/Yf+<@RS#MW_i#g79]h]B>*:-YBjj8)hem*hY-8f%>V0"^5ip-C&hq+3eY!.rRWjlU0nq!c<[hc9PDiR06@5XB$IL(dYi^W3)00A#RpS`]-cXN-.K"rb2+`c+8dUJL%F_`L#L:'DUZE[C3SJ.1"#e$>N`\2eidEu.*7qq1q]'F]?$feVXH1._h[C]eIL3%8sTC0q:\U8>)NT^;bI.O8P..HB7&C$*sgWdmE'tPS+%]>hMqLt0Ks*`aZqWFAXH]cVPY\%aY\N/kHBsNuMpo\2%7J,4*WF-Y4T)*abmQmF/3U:NH*8Y+Q>eKnTSuJ1S~> +endstream +endobj +523 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 522 0 R +/Annots 524 0 R +>> +endobj +524 0 obj +[ +525 0 R +526 0 R +] +endobj +525 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 158.68 467.894 204.24 457.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +526 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 223.68 467.894 269.24 457.894 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 506 0 R +/H /I +>> +endobj +527 0 obj +<< /Length 2155 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm=gN)%,&:O:SB_3"bfXbG=-T?l$VbZT8PpW%I%hJo$R#hGDJ9(;V"XQf$$NZE\G4Ztb!:6:?]6aPW4umCbpM9.sUK*f-cW[`q=hE6?^NOJL1&$F)@A>ue:"%bI(U;m%/#%92?G#RrWt@"^V7%2Ai:\@Yn>,"BPbR(lHUga+Ig5q^rS?ekL9itYUV:9eXHYr>6Jhn5SG2Np]5P+.*X2"#/R5IbCb7LakG0'N\OS*a*I_^9:I)[rok(5*cqiIXG_BUXCG2`+=eDGPQA"IrT:)P_g6V&70^gab5?MdWdk9=-_)tQCnJ;+^bUt(fdf7QA-kJ>F@!%+&(T^BcKAdVg)AJ1,>?=YSf-[@c7Ku-p,V&5MF4.4k/R%1FGX.320H+`QhpXAnh/.u.W3G8[5M!s:(0_NKCEmYuNDJ/.],j%P2%[^(,PBDOQOj'+kIRrH]"3Vbm11';ElP=lnMm1Bd#sp-\7[j4;e6&*@?m6cNW._/F_MP#m\5%AUSDqMN6]/UmqA[PIbn;)U728_'cnm$;4>86m1jD[qR2_0SpcqWb+d#+,,Gn"U!7N"GhL2t`NOJ/YTOIN/A=DKnVH;1kD5Jt)A]/9'A'SZl?nc__-b7HWM7GINJ:N1b+eqW/F,.`A6]c<\B)U=".Pa.!]ZU,"M#-0%%O.#ejc;Zn8\(NLA-)eYe&"dU[kE+RFYuFWDs&bAGE3c!P:!XC$Ipc9`g7&DobG>O]k`0ED87M$cMXbEqL^9A@Y#/3nF$\IU2D)L\Y'f+J8#R3/XRG6LB-r,5>lZ.gJ3>E\TGo(-Ba)cY+7?!_NcZ7uH?)N(JZ>'C4TdcpM-Apre#oI(?6P&l3M:HUH`mc@KTkuCKKW%+6j\sUWlZnA-OVGml+:nJsMLL$"JS?hX1JhqHB.FQqaS&0-8OYQaHXK_P0*'240?R!QIUMt`I:Z5!#+eg-T?5k*qjjg1nYdI:H,4ECVk48`g0=<6ep-/rFlb0A>b_4u>!f9O$&`8H86-364H9rm6,tR9b*rdP^(i6(P;d'i!"J$Y76EBW8;m_8>!u2(X1^SMe5V0$l?bCG@I&\7HTPe124>r1=HV8[[egKXN]i\e,@E]g3-Ff;H"i`iYn8DXc3d?liW_cQea-FBC3<5H3a>*(dH#6FlGk)tTOg$%G_]WoB[PF;R.KRZVEhbQN:7\]p:/:$e4O[M]j8l&nO+]6bB/6W_E,7_/4IoL\g>8J%OZ4lZk+)aLQBa@`C?5!WcJ&i?,:e`pAEj`I"=B3YB$U\#p%ZY&$3oKt7gP]kKM+L*rX)+ZokH,a6`Z=2=AANcSXe8)$Pag0l@hC3R$NRE3N>_aE.d\fJJ+_;_p3TKH,Lq4>1[C5*bYaY@YkH,e<*I_?^dS(N?Zn^8Sa_$VWrnt$jmq6bLeqkgAeOb$BRQ9PLQ_pYWPKOh#3;45EYA$p.(&B/HdZ]IOQte%^XO'frHd^HL4#n@DSV1K0R=Ba/EarFg!oHDj$&YQBkU-3] +endstream +endobj +528 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 527 0 R +>> +endobj +529 0 obj +<< /Length 2952 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>fok*u&q0LUW9.f!2:a(QKi6cT*P8UNQAXeiLJ/a3&s?'<$&'KJ.pjB[>'Ls%84q^&X'?l%5kjO2dgPs.`N0#dL^2m`(^q1\=2A;C.NATb?Te@ENQE:ONbb0BT/Z47B9QbCIXeVbG(F>T/g-H0;q7SR;!IE:bcVG:G(5h+kdjGqSSb?M.[>a:4I=/Z'[#LmV3N[F+%DPgAEp@!PKWhp,)tObX#?^;9/GoeI55mrWm_X:.8JMm]]*pdQ`-sE0$/E2VIT]?,gA(S'HZ1cd=k\7E-klK;A>C\55dmCX,SZM#?<$a/$eTk?=iWL^&[B_<36XSg2Xl).%>g2P[U8SN]O,)P/&>AI*1g7EDa5mujDK[crK(^qpZcTF.eu=ZS-e:EJn]3a^m`HGqiT#cNWOc?@GR$lYnF6f[9*ISI-%W#b651LOh$&Z&0.?D@n+*\j_5BqSA7%e1Z7+oZCQnr`m7SB7+RaN#DuI3<=je:I8pmIdGR:XYPq7m"If/7mHRm9EsanVA36^e=n9n1Tjb\h"2aWqo3dkMg%183u?c#8HId:<7-r4"ltcURB,+M!^UZ'-"uc^(r\Sn(B_K_g&K-eB[B8nB:_4KdB82]5CQA;nes&P>0b!C:VNd>/a-EeS6Z"e(A3G'O6`;cu)KBL[f,en&aT(&=eT%RnJ*`K]9Rq3Q:i3heF?N$]PtT73[+=?>;P=P]9*Bh$8Mb[!bJ.pXB#WpjEO2ngW"C4]\@&*AY\%89uV@%D6QE6!!PJKl(WE$R9$M;mHK7\EDW77.o*Qf]ZbQ((mJS2Uiq%u?9b]VQbTr2(k]V5gm>&j@r\q#5[hO#,<"/*_@;mR3%Z;>]J,;C42@$B?PuK"j#?6TqITbfZ%V6C:F;]bBWG;L4[Z9(S;2+C8aD?U"i>H,C9>Ff\cL7hjE\f%M?e4P`l,bf7=U3D;>NW"5kp'J*bXQGpa#8(W,JX=1<\*0E:1[Dp>POIn!;_H#G;P)Tmj3i:%o#,AYn;\[?e48n$M1%k4g[l_Qh\P3Db_aOJ>md5^IE@b='/]eIRZW3K';ZhaX`m0m7B9db>LFTdVgi=R-/crVOSnQ/3Dnh)DHXQb(UnnZLio't;O@a;EQ!3;.6Q2a4\!s#9d++cf+.1_V"mpB$2hT:TjH>ZB#4Crt!iRu&j;b@Z'7Bb_\l$Y9]+hmSgLJnX&E6Q3UL+k;^S6RGJI1(L&8X"UZ%#U?8LVk0p2D.S(=K'gB#.IN$K;'9OUUFb4CA259:]s&]u%rmZTO0YVo#O'Ao'6A^HL9psOq%-9q>^+X8,+:kb(#RKP3Z`u95+$YaX3.FKrK-T84H;.1a+tu>VT07E-^j#aio0bLCqR$@!mPiApW(*l3/T+24l;;\cQA4&1Zq32oY\q5-dcm.?iSnT@tBJ?)tZ(\c\A^2n\Z%+3>&_!WA7%^%)rdrhm%3;=CVUJb"uk'1+aZu`B@4u/W)^8&5@^=B&JcB[+=iD2K`BeKXP=PP8@qd>dsGTFnEM]E-l'K8`,m5k_:qcM9Ipa%@i8,GBYAt'$`Z,E5M,,SD2U4_hn'X'5Q?/oK;;cJ)fnW@sdo9h:^SPKL`jl(sF4:n0taBK/S[smQ>Bb'cFUN81fP@PY-72MJ:3KWtdd1D#5,'DSkQ91K^!(s,!n[CWG^tSfe^H:UD`Q.67Prct+AXOt0o)['ecDb?Ref0R0R[o[ue"B(4'TQ`jg1qc@h"lt]44VYbQ!)T^lY\lK!dUGiFU%.#0LIO'\>TrOgE*[E[e2W7rpqWZ?"TL[Dg>77*0nJ_>dsbVfM=bc"AtGi#dVAE)Yea_n@^R3^6$1KY'5PRX=i:5I[T6)RGusrBmg4,0hm"+#0??Od*FT,rpbFD9+O)UD$Pd\k8sFM]?#HbAbE@4erUA&h3SM"fIjIXP6-9i\Jp;"?RHu(I,=Q(XSC^i4>EZ6cRtsB#f#)p"Ya=J[&Y$M?m!pH9-F[&I1;+;q9k!0I>JJ4qN`GK+/ls&Xf::-Ao6G]dlaIDn\i7Y#E+XTREAVG,d)XIQqJr7e--GeF1/02=).5h:b*m3:6Qkn`k%5:eFd~> +endstream +endobj +530 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 529 0 R +/Annots 531 0 R +>> +endobj +531 0 obj +[ +532 0 R +533 0 R +534 0 R +535 0 R +] +endobj +532 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 185.04 453.394 230.04 443.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 43 0 R +/H /I +>> +endobj +533 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 357.81 345.394 402.81 335.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 49 0 R +/H /I +>> +endobj +534 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 406.32 302.394 443.82 292.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +535 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 446.32 302.394 498.72 292.394 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 57 0 R +/H /I +>> +endobj +536 0 obj +<< /Length 3117 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gat=._33ie&\[X7kiR#Q8!YQ[P*4:)mB,#\]NZH1Y1/IB&.+68O8oB5RaL!tUbq(H&tm5s+*L2=@/!%_ldlUEA,#EGO/pBM2P!Qcft5qun8:FX\c:fUD<1,eBC"f04kZ*a0CKu6doOQ-f9[$*8;\pisp[X0f*=X3/MA`b2Y_qiAke7uC*TNN`+>MZQ#)$GMn6,:rZW4q]('qeqR]:In9ukCBUe,,u4_(bF,MAnJ29=;![3XVu\e:#E,[d1!"^_,cW9F?>4!dIWZoJ4dblmHrg*C]6^8-="2=h'q"?Bj7:ktj7]!N9LHBob6(hY\AEr,^hE@prFi-2>aN=4R?EV<6ZgEairN'\m*&`HVDJ^q5oW2n'ImXjFD:a@0%ugunoYHsX69nLLPJG2mQ'*5Z3rt<#"%m.IPJTdIYVk?cM-5Ps+ZBk_T)5H*;8Wh]A7*a=cqX-*'qUK.]n^VM[22?&;;I2/8H\tI[R1&bmf-2+[q-.d-[bJO_?Z.OFYf:;E>PUNT;9VX>gP;3-*733ED([AbdM`l.fnc#nn6J'$G!+mo4J>D_o>lqJ:M$S_shrk=eKSr+.kaeAS8?.JT46K\opTfDLQ[k;5[2Ulq+>NXIDtkQ6_WTUZe.'=RW8c'TX[X6o$V!bJgY'e2&Unq'rRP-d2/FuHF7.PI2XA5((!BFJQ*_!XXC*>")Q2>-FQ%0D:X0hn7KfEcUJ4V'mIO__3ZSOQ/Qb\W)J(icP&cGKtk-D,.Lq1f`@UE>EMq'_0dZpWua]OQL,A.5*8XnKZACqY*O2n:rP'Q-4R+!N0$"=WcV%s;u![les7)Jg=Isi%dX&lWj@G/R)6e6Ci'rQW;.FnNR@I_4WWo-:9EBeTU`=JDgd>;H^-`!s'Yfo,[4^^^L=EE#ciD$+K-WQ"VGFX0a>0CM`Z:fYtlXYU_2LVd5X1+haDL)$8j84bZNQ&6gC,XthPDMIsY"gNKRqWiS2]d31.ssI=YKM#TLZN@"IkrL`=]u0cYbWe%QtretV1c\(hHs@HSNIAQN96r\++eW8Q>ssQ#^S/e,o9;!#@`QL.Rt<]UL6f+7p"@]RAS?0C'61cruT8Gb9js^6\T8[n/sRRj=3VDq$cTq5!qM'P1Jrn[iR3SSS#Nil;tGnLo`4JS,QK1CkYHH1W0^oa10FjpTG)hP!a?m*tY-;keJLr4_Yk^=\])]ZUV*dW#uPQ&.n,]SR)d,oL\8CGH%TA];4Ssn^&mEhBM`Bu5CKc%n^#SK#G:BFbUqm2/O%]p]@gotAm#45O.qOCG91ME4HKp&W%5J]O;Hg2AIWhLl$b0+^PY8'&LB57SfNL>$[fn\Aq,fFmtn,PS$#[7GA;&;eMLhKZca$//:p[CQb4/#US*DhA=7"Uml=?)&ArtYF01`C/"nePj_;b'9N_NT`$ce#\*#%e#:,6"W-c)LCJSr)pfnQ&'Bd$23$0T2pVQ`VBh!VS*>5?^IjZ#-V8b2lKCUMaX*595<&Pn0Q4[($48qbcA(lmI86;LtGV#1%*j&3p^$uJ#$:F@Akd9D"+)$\e\@/D?Wp7FYUjFWRQ2jT-,09&:PHS]=sQcnE.]r309VQ[a8"@?!+$2tZoYjJ8#Q6GE8oPNH7sWpPe\q0#Nm:\Ug*=6V@>O\/ud^$"#WrS#55)ATp\e#*`VU5QX^_H)5XRZN0WIZ3UN>>iRA8MkMcXril#XMa+3AV8XtlkgYVdcdSL-g?\]?_Cd4B6E/`5(B8dKPCV?Q1<`[OnsGK&.`$HD$QE2>1/:ZK)Tef6V^4[WXh+AV+Y_e%V0uhAI.[1a06u>k`h6mOml`kj`,[Y!QPF0KN!5]eDXc-NReP?A*U5q%P"Zm'75a=?Bs+lUqCm<>j@WXg,`BE=q8P;UP9s_5km:e"2EU1dM%&?*6g-,CMkXYd[o8Sp$8/aI8@G4&_Z=S;F29-1:d";D2/_.=`k#D4M)#Y6S5tlQ@eRVq(S613$1gYDV07aRp`D!h*djGNA@\+*a@$tn`XrB-[`:"X:<64_XE(XjfmZI>oJEumj;q#&X.cX^Y9=P*QB>,/ji^&("p'""5~> +endstream +endobj +537 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 536 0 R +/Annots 538 0 R +>> +endobj +538 0 obj +[ +539 0 R +540 0 R +] +endobj +539 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 358.62 529.0 403.62 519.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 51 0 R +/H /I +>> +endobj +540 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 303.62 219.056 349.18 209.056 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 541 0 R +/H /I +>> +endobj +542 0 obj +<< /Length 3117 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gb!#^968iI'#*O1W,`cf.b9E?6qY"8mC:0BR:Ih6B5/rg"%7_beg/'48^$u)gX(l7GUW.6*4C5,9q[(:]$^J!KE#&dqtY+j#Y)_Z@$/,A%9DqL_$L$l_*!DN`M7Yp`.LK=pjVa9-I8$2X)P'D?aTBl=8h1>\,3%PqX*f]d6B4.s1>>M?QFH)M38=:ablgB:qVp]8iV-h8`^iFMNSMIa$hU!F4lH2V%QogX^\#>M+"mD@u@D6Ih!7/;I#rAFMqaK7"^VAX57sA;RA#OrRs=IAec6AE:q'_HFjr0/j#jo`YehKAjkIVCP8c#8.VFe\>_'b0]%Ylm.^UpFi#u)*aqYJ_(jHS?2rZtL.FD2WW#ao(odN/4]4E*/EI_5b.5duHShtOn[pd#/&44tP*9l;(>F#4_F>@/d"taZ3F_YOcJ=ufr:T3EB\Ah)edAKTU,ZqFs7AN+^8A,`n>Kh+o]+i!eC98?%OG0a.LP[KiC%_@E"8tHj!.SRc\/^kG)DkF)GWf=4;nXXC2fCLN$lM&-;kn06@jfO3'GFKULGp`VW'sn%kB.KGm5$WR;Ue-Q4,jjj7]$`Lbphq"?C-AHTq>BfA3_D8[6+$?n#:q8A`gZ+0L>?cHu1VqY-%s]i6hFKH28A[F1Z%DOrIXD#0%8#_PDe[Ca]?B/kp:HF>8e`s.G0A%l@U'Zi+94I'^P'P61h]p?q[B=JaHD9HE!5.(""$(?!"*/G814$Nf8?,T;<]Q>8ZPU9c@SMjdiC(EQM5l.`82TL;>G;+1'es6#T@b"NVGFB%YAp.uato\!bMkUN>O=?+5C_L;N.oX:RNMsItWAFG]fpsjaFmb6h9KR6T`J9-s*ifA%)O`@FnkOs657I+!97D*p5,Ulp[L'&4r>GG$dr3[>u\&;2P*A9f'[rd[,iK9BB'HgK_S/AC2$UP7hpe%^*c7P;p^-VA,nc`[/L>=VM6+&'ZM?"H](;Ys\UWj2euiR[Ob&^t_qRK)[SO)_p%OU$JW=Wf*RLdrdUX>5XS]/fV]g;,DaT?qB=\T"<2/3Y*Hs9YBDN<^Aa,+d\5@&^Ib,!@KgHD%gU%6*YDGnE5/_&:.8XE0Nb*W[UlUbBC`g"5RKV\3[0DRTubRkpPj1Cl]bUdZit#c0<@W3_ek@Ok4Rf5*]aq0*[t!S@g#3NOWYD0:8fA^^j9ANK-RPB>COiss)9/sm6nE3`VT'Q,a8Wti.&?#Pk(a:@SVcAk)S\OhFM79'%hjk._-kg:8Se%,93Bf`6pAZN%!LpE3M4(L%AKkrs*4i96JJl#=7B;=CqEm]KL/)mDKa7C#Jt2goaV7_a,+"P7B;dgfXe%q+I&#CH#Ls&JO6@e^1&QDaN"\F<,)Qq'uY+hQ!ddocBa[uXO5WU/GWDZSJI-[.*"]9YkL#Hj^;9fooJZ0WR$l7.gTXN_k(liODuJJZf6!D)Tl$tN+mO_CO;$hrHRYP:^!CX:%FIrsUdOM,kYB=U:(mK]SFpj`&snOUqqrbS6dP;Tf7HO2=]1[O*VMe>2\6hVPdan_!e13T[^$EuD!tiMHQZ"Hp[7"cP!KZR0[[L:r;4$fCMZr8ZT2fN)[?GS'(YoBnVT9BU\b+2`kR')]`+YL9&Wlm;7fDXR#YMpi6DBtq_H%bVuo.Zofq"N)Y@LQ7*b>d"#^V$nT)uVkQn/s+=t#3D380Agr\#\fq@URAEj5L^\AS +endstream +endobj +543 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 542 0 R +/Annots 544 0 R +>> +endobj +544 0 obj +[ +545 0 R +546 0 R +548 0 R +549 0 R +] +endobj +545 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 134.77 660.0 184.77 650.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 237 0 R +/H /I +>> +endobj +546 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 457.57 574.0 503.13 564.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 547 0 R +/H /I +>> +endobj +548 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 134.77 541.0 183.38 531.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 448 0 R +/H /I +>> +endobj +549 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 423.03 425.028 480.53 415.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 180 0 R +/H /I +>> +endobj +550 0 obj +<< /Length 660 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gasamhf"u<&:Vr4iQ">i4\PneHcqjH>1[K;au2so*um7lW7V6Lpabs8#GoM%bV_%oeC%ZO^ES<78b^c4<;Qq>h!r)9aN#gB)&p.]t`:`!r&SRPNt>$&k^"ePDPf8"OHWS8K7fl-5ef?Mp"9HchKif86$Vto-DFC)A:`o<75MLf!OMDCIYmaVfAF\'=o>&FT2PCpf6*%O8SJHtagou@5TJF]e^$e?&Fj(HmeP_P4lhjj;)r`A3$i;Q`Np/N8)M5L6TUL`Yi;VIQI_J[+oIrWjWCp%`l5O[U<;ZFKP,OKdCI(ZYBkcQfb7Pout(ZLM#[3JjCXM*g7pZF`"aRhE-)t#nrN7uKM>p<^kkOC82hkL2[H\-BW%#DK.Rq!UWZnA,Ncs=7Z6Dqo<=Gq']/G+5?s)n?Iliqhnd_.c5=?jcgBCKe]56~> +endstream +endobj +551 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 550 0 R +>> +endobj +552 0 obj +<< /Length 2570 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>?#uLV&q/*0cs,KZRfF'f\o3Vhh#23CY#sQ1ep8E-0Tr:7%*OD@n#sKcON_F+Od3%QIPI4(]$L#=52H"Oim%?riQ.KJmV&kOj,V]/]TGiOhC/$\?KfZTD`f$lda=]U6K`0,&\'ILJ+p?IQ)%&;fNm@tkhFeSYL2>GZ58H^\$^Fa2/d;*B7](aCb7QFm$iqZjj#58k?jg.cDAuq/l#`/!Jt<8WN<0h(4dXB@S:o)EpUY7LY^>7F',$QAsX>93_Q'*!AN:8[$m[gA_7+4m&"8$,T(`V>_4-p4\G!;4e7IR?Kd9P`f,0d93i?rr!tbmiEH%&1L(l97(1*O>\nKpX)*20bf;M\(A_?mkgds?nE0>[rLtCC0s9XJ(Hq];"U"ib6.FFS([<)q&36c1%CGPE+oe]V=Y[;A.b-8E5\WCu]+]>7\1Psnee%QVQbsuPO^Y!2$`(#XLR&,5OQe,bb4sM/l+BDDL`YR<5S(O,$Xg0D%/W#$K`Kj#=$7]1h=F3r6/s/1:^AJE&p$tW)=s_9E,`]8]8Na2o-mB(!b`=u[_56L._A!57UFZ]ri`O2#F!4KA)n^f-6b\Y2Zk=ZFYTd_bBGp%'ZUT8%<>j=uaYtt26UN'o"WE$;6)RQ*5sgu*=Qqm4.CJ!tS!VSS?#MnINJdDm`?0Tp3MFJ6]'jQVkY,=LF:H+>SIUr(X6jFUl(/`]q>'])^rSJMH-WA@qIH;-V+SGP&jYSMeIs@4HM#_1+]6I?LJ'F?>%PdA$'g)Y":(smj7U[iin>T!-39A'>:Z;.Ue(uO[&6-p*r0NT\?f`0=1S6Rq)^Q[pTVH6gO",>jnA-1EcLacgq1,1q@tV)Id>hbI/Ee`^FHXRNoqPMDX[S?d]m8.Yi-$dV6XFe*HpK5Ck/jofD>F2ea3hDN@@)$PGkcY*tnYtM'p2I_[sLGl_8;Uk^C&X5I/:I!_dq&;2oI]Y/BQssi.@6L6KeMT4Z'n8?rKPnL=q21;D>Q,gZ/1eX4&6o8%6E#h,U6DFH,:6Ue`MKSX0p^3:[t9?j*eTkD[=*$%g9-SE$.\S*/F]cs801G,=g*`+L%mN]Z(3md!?9"J]44r@5dbCNGtfF:OB>a;id8MPNRB#(o:RLfnGZ,J:0CLdU[!k*gr'PFQdtH4ie$/PSV`F,)b>SkM1UF4_%&R;%!i/8%c2Z#9BWZqUli*S6UkM:YmF9,2.nhFdP-XTApRBHpH,7!Hh/;T=0=lS\:DA3:oWfGOJgAnAc0ib7YnG^WkUJiYQAb?5;b\&!s6+j6k`GkFJ:U^Ul7iC*S!M8"AcMGH`[1cF\2Gq>uIc`fX/^%_.D&bde1VC"367X^jdQ]NTDM+@?+$%94;PTFY&i3`DX1MS27q4Cb0)No!32D5R\)G5;gK.Qu%68G49&'-Y3!MEdo7_YP.@F"\?P4To9sCaq72jRhh6nWpDOO+b@@EMZ.g4;l.T?"Jdt#"?0JIB2tZ()'bEb,;VdKOPM#)3Y_JVWO6XXJ#JlHNJYRDmTYE,J;F><$K0=6>IC2ij@nbCLg10teo:snclqt,NqZ$%@$r6AW.Z>i9EbP,FGBjunHc;l"5jGt!amO0>_Q(k-$;"kS*lMG]+V,jC]o7%KT*Y=#jC48eP[tV,bgd)f(i:oouYA),SuX?%9;Wo#N0-bSW(%_R>@QieW7hn+rQdZd&5lZ4)$J"G1g9@VZo]u[V>)iI.EP!l75(--s,$V'Zi.^$WY8te[`[`JSFp*8f:#&Dl5MY87+^8FHV8pAqo$:iqe"u/s+t5HGNSM$Fnj.XX)? +endstream +endobj +553 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 552 0 R +/Annots 554 0 R +>> +endobj +554 0 obj +[ +555 0 R +] +endobj +555 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 261.71 507.366 306.71 497.366 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 65 0 R +/H /I +>> +endobj +556 0 obj +<< /Length 2802 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +Gatm>;0/3r&:Vs/d.AXSN3*@r+ji7#W@)PQg(Q"(FJ:shi$@+]3!tF@p"+DMD"ne6A0o^pMnsAAp[,H%h_+nBrp&mqADiH%UVV?NC,B&-ncg?8nHP[_pSO_Md$kO-l/9aS5NMCkeaI.'$\)&=rhbq!UFaR]L=lh7J$/?I(N:VHd8uU8gDc9rg5A;;dR!tg2P",&#UMV#(6Zk:tp_N!Xe&,(4D$@_KIYsrf%nEj+T7Dot..p[\IWK,MDa#5%Bdp[V!:t:>AqJE:6"FucR=bkn9F%Xc/f=`.+.j(H)]rMS>fnad-^9BraHOVgHX=c[DK1TPt7Egr"G@/qCGd90Ob0\6hLPS,SS4B@*b`";c\gKEf-Shr=VPXDq(WONQ#AZ$fp@;?b+WR2YdQEY+%b?p_6UsR:@E!7%RAkT/t.qS)Hl6RrUlIpq-i('-9I=9GA1mPURXE4000Q"?Z@u&(c#eKpZ]().J@FL6fWjefD7S`EI8VU=%oMg3N*G0s5HZ^sHWfO"*5f[94Ofr!/'S=MO&V6dFgdf[^4.EkH_D;/e(u9XFd6GV)0N"c1=dD+[kqYk>h'osHWr*I7SP-t@:_e%1tEeIJcbTske6Q>"CAI873n6]\TWl-6L8>^GHhXIR75QJ$$n!U,KpKYVJ.rQdpK\85r>W(fR$B#mW?;\`GA5A->unhgetE)&h+:`InGOTh%jfYYKQ)-^Boh5>27>McH0lO&YY7ATe&7S6`M,>BckY>Xm1PV1j+KF_pI_W=6a*)l>.s%C\oPk>j*MGi+"B++\dd0nQ0qMaBnQjZB6Pap_KoLJ%!p1(FE1g\ZUaWXgoL)+">bjD+LBg1L0`K-uq,"0BZ;'E\kkg!8_-FP7\cLP&sXGI\`j)$hVFo(g7/C-.*&pF\"7YG`)um0QU;_pI@u4"Z,rfiu7F*1rk8,feOB-'mgu-MHHM/\rIj4e))=D%E6G:=,q&hk/8BT(h;0f7]F1(P=/IQm=H(SWoQK]OlPJFKXDK`;gZIAVhFCZT<[+%%VO+#Y9AKSQHnF6"r5tU3&i`9,'$`P0.m'<kq[PI!*MeF\NR3'=[^nAnSB1Y6Bq&%al1YLYOP`2TMsnAeJ*T0HZ\l#:Y`-#Q/g:aHuK#;?K,W4UgXlJ:"Z5>pP:Ue?nXDWW.LhO8S>98W#@9]^F1TADZd%$4S,_pRi8EtQH8of5;\d@Q[:Y&6$Em=4#q(].uNE?LP=;UC!.kL''`Z1!n4.iBrOBQ>Vbol@[g6q]c5e?rjl_W=oLg2Sm'"JE7c7`oj6F6)ABi4hSG5-Sr:bbMfOi8pI;`;NXN,ulKX@,(2@_\Sjhm!W@)m^2iGrqaAsD@_U:9Pik37NSYUrJ.DFEK0b*T.YF7itKp+ph)sfNl)(+175n)hhI?rW&t%!^^:n$/%I9Jc"?nZV%DcHpRgmAn#tm*n%3Q,^N32'E#==*T@cBa>ds$+L>K",3)b=n%n$,X:3U;YIHT2hLVq'tWGMlTW[;'A`4Ds"V)X-`(O0KV/>Rt@%%U'+0fWuULjc/m!e2+.T)*KR`/jAZ%JIP!J+ +endstream +endobj +557 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 556 0 R +/Annots 558 0 R +>> +endobj +558 0 obj +[ +559 0 R +] +endobj +559 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 317.22 522.028 362.78 512.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 498 0 R +/H /I +>> +endobj +560 0 obj +<< /Length 2443 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHN>B?8n'RnB3nFWR%9L9-lA+hLDbP'pPS]#BCe+>p13S"fDS+Aa5`p06GJ8-`s-t+#?$c[#BFP3Q`R/YoG[q>>lCU3F*;.T!qNgLZB$'!.S+Bb14=-.Re+i$LE9_]0W])2_RYM'mRtj!1T2m4u7.Vnh8Z$ZuZ^==gLZHL.++r8DU[mWBjULTb4;d.?K3B1+&m2l'jS=7KIGoGEj@?nl&UlS-XHAK+5A?,k1k2A_&F8KV>P#Xn/>`_JAb0ZZ_l(tE73"7im\o&4*`B7'*PjWD`q?kQDl+WF`5=;h+;L%PJD,X=Lc1;+?*KPb``'BZ01([[dmHWtB,ODgp9WH7;M17p='-W6O0*$=7EcG_d"hf17YtN7%P1U2i>Kms"=h[,>W,A.#+p(?+dW=>08_=@\^\JYnS43J&fer%J8W4K$4L!>_n]Tc7nse%'b)@%hMX%U^L+s;,k,$,A<>jIQ8#KB/E$PRMS)R(E]m&;Rs1JgcbKbnKemu'io@X.51A8E&(ohZ#b&Md1a.`55`m$GASp4'NY'cE@+smAsgQ(*UFZaE=WTA?ps2[p6>r)ck.sjpVm4MuY3Y>js_M@Hjti5-KgBfHQ>cftlg`fokLU#Qc+/$EFIS3$9os?5+9i/%R!5mg41K04$;%#$u!=XZ5@tAaLhNRn[SgSOqd$qkWVgW%6fZC2*Oif[]4V:URG0"(-RUS_^PbK_1Et!:&nO%l%2h0S4?3L'6k9/-FT+?n/^k/K[S3WD-:&=aVPscSgkOkLjgMHT34QVe.X+$]**X'^qVVWFN:'lZ\)(I'!704>l4nKX\KJ-9$>#E-?`PGK06n/kiufX4r,iSW-X!V.jjJ:(B/38!CB[bDM1Ag2PeJkC?Qou.@>2![u;FQ$Ddt+r4P4T?`>NElt&XWMHnnb+5g+Ie2B^$PUG$u[n#e_JTbjV*eLM[DsFJ1VU/!QgM\rQSR-aF["XmoHQ#KQAa1>?-=]=aS=c*$c=n/=h40i_W)rSX5[,uKAu8WoRt02R[]fZX`&\$I5Tl@ngV^I:-T"ojo%\P>qWi!DE_]K]*DZH"c>]m\>/7b/jP`U0RA^BQod4=C2DKX8oeJGM'(cF"Rehii3nME[elO8=rdhMPg%jA-IV0_/VDD?/tDX::$`#sl5"H1D,A\kR"+JcZBcn@S)TYkeC_/kXb@+e;C$2_9fi.#_)FJaHWsIr@6CL@h2*fr\q0JV5j-J^efHD7@MA(sFJR[KF$`gSqL@%g@_P<1KXXo6>HbJ"jp/WK3oq2m?A.SVOU'W4u[_(kA*1Kn'rs0r+Dqa'R#IK9X<0I0A[O9`I/6-Sd&^h]&g,-[p?@2rP:`'gKb(1qlo9LGkd6'NWT5?IQK(D:C6jJb\X,O!L(cPLBY@l$HMc9SWXU(RgDrZeCi;cQ;^CE44GHt%obKE(/OMmY0/T@#3/B^$DSbQ=QHJ.l/]IMiCHM.6AaI'[r`3<<'0%H.R[4""#9+"f6H-'FJF]X%^F=RD:DQWWO\pJc:bhV>lZrB.dK+ff&Ml]e#Z9s)K^>2&DP(5*!_OfCGd?'j_XQaXs6?C/Ygg%!US'Z)h0mPA8*c%s`ck<2JF?U"5=5Gkp'6O0.h)j>jI<2Z,/#a@[H7(#CU.`4Pk3A`=>*p4Gbs7@ZfIo0Te3NI!RaLImEe4&LUE]-/2a88$kekR#sJT_goO(_NuOD^=kU![D>1TCVUCu)qaS_8Pu`f]#$&%)(_fH.ViDi_@D`+@nRp]WAR)9.KEFsqT.8Q8dG``ltRG<=)>-?(Q6U'UZc^a#:VURO?$>XT5?<[IaY`fhk`@iE/qGt0"eCtGt8V&g[3__KG[$is]!1UsChBSYZkB)XcoJme2^LV)j[OIZH"U7Q2Y)*(`jCe3`Uk?leY-:.IiI0$O"S`.rIS`$Hr^*Ua0+Ts)[>[hQ2D.TA3TJjBJM? +endstream +endobj +561 0 obj +<< /Type /Page +/Parent 1 0 R +/MediaBox [ 0 0 612 792 ] +/Resources 3 0 R +/Contents 560 0 R +/Annots 562 0 R +>> +endobj +562 0 obj +[ +563 0 R +565 0 R +566 0 R +] +endobj +563 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 289.75 714.0 335.31 704.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 564 0 R +/H /I +>> +endobj +565 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 226.15 703.0 275.31 693.0 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 450 0 R +/H /I +>> +endobj +566 0 obj +<< /Type /Annot +/Subtype /Link +/Rect [ 388.6 217.028 433.6 207.028 ] +/C [ 0 0 0 ] +/Border [ 0 0 0 ] +/A 41 0 R +/H /I +>> +endobj +567 0 obj +<< /Length 2711 /Filter [ /ASCII85Decode /FlateDecode ] + >> +stream +GauHN=``=W&q9SY;!DP^WefY_GTBR+Z+ZPSc]34Dm*sTs*Zu)[:a$,S&'NgbrU&@t+Z(TDO__#KIP;;*]CGRe!VD/t`U_lT=f,WQ