changeset 7:430dbd5346f7

vendor sabre as distributed
author Charlie Root
date Sat, 13 Jan 2018 09:06:10 -0500
parents cec75ba50afc
children 899c8c6a0519
files vendor/sabre/vobject/.gitignore vendor/sabre/vobject/.travis.yml vendor/sabre/vobject/ChangeLog.md vendor/sabre/vobject/LICENSE vendor/sabre/vobject/README.md vendor/sabre/vobject/bin/bench.php vendor/sabre/vobject/bin/fetch_windows_zones.php vendor/sabre/vobject/bin/generate_vcards vendor/sabre/vobject/bin/generateicalendardata.php vendor/sabre/vobject/bin/vobject vendor/sabre/vobject/lib/Cli.php vendor/sabre/vobject/lib/Component.php vendor/sabre/vobject/lib/Component/VAlarm.php vendor/sabre/vobject/lib/Component/VCalendar.php vendor/sabre/vobject/lib/Component/VCard.php vendor/sabre/vobject/lib/Component/VEvent.php vendor/sabre/vobject/lib/Component/VFreeBusy.php vendor/sabre/vobject/lib/Component/VJournal.php vendor/sabre/vobject/lib/Component/VTimeZone.php vendor/sabre/vobject/lib/Component/VTodo.php vendor/sabre/vobject/lib/DateTimeParser.php vendor/sabre/vobject/lib/Document.php vendor/sabre/vobject/lib/ElementList.php vendor/sabre/vobject/lib/EofException.php vendor/sabre/vobject/lib/FreeBusyGenerator.php vendor/sabre/vobject/lib/ITip/Broker.php vendor/sabre/vobject/lib/ITip/ITipException.php vendor/sabre/vobject/lib/ITip/Message.php vendor/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php vendor/sabre/vobject/lib/Node.php vendor/sabre/vobject/lib/Parameter.php vendor/sabre/vobject/lib/ParseException.php vendor/sabre/vobject/lib/Parser/Json.php vendor/sabre/vobject/lib/Parser/MimeDir.php vendor/sabre/vobject/lib/Parser/Parser.php vendor/sabre/vobject/lib/Property.php vendor/sabre/vobject/lib/Property/Binary.php vendor/sabre/vobject/lib/Property/Boolean.php vendor/sabre/vobject/lib/Property/FlatText.php vendor/sabre/vobject/lib/Property/Float.php vendor/sabre/vobject/lib/Property/ICalendar/CalAddress.php vendor/sabre/vobject/lib/Property/ICalendar/Date.php vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php vendor/sabre/vobject/lib/Property/ICalendar/Duration.php vendor/sabre/vobject/lib/Property/ICalendar/Period.php vendor/sabre/vobject/lib/Property/ICalendar/Recur.php vendor/sabre/vobject/lib/Property/Integer.php vendor/sabre/vobject/lib/Property/Text.php vendor/sabre/vobject/lib/Property/Time.php vendor/sabre/vobject/lib/Property/Unknown.php vendor/sabre/vobject/lib/Property/Uri.php vendor/sabre/vobject/lib/Property/UtcOffset.php vendor/sabre/vobject/lib/Property/VCard/Date.php vendor/sabre/vobject/lib/Property/VCard/DateAndOrTime.php vendor/sabre/vobject/lib/Property/VCard/DateTime.php vendor/sabre/vobject/lib/Property/VCard/LanguageTag.php vendor/sabre/vobject/lib/Property/VCard/TimeStamp.php vendor/sabre/vobject/lib/Reader.php vendor/sabre/vobject/lib/Recur/EventIterator.php vendor/sabre/vobject/lib/Recur/NoInstancesException.php vendor/sabre/vobject/lib/Recur/RDateIterator.php vendor/sabre/vobject/lib/Recur/RRuleIterator.php vendor/sabre/vobject/lib/RecurrenceIterator.php vendor/sabre/vobject/lib/Splitter/ICalendar.php vendor/sabre/vobject/lib/Splitter/SplitterInterface.php vendor/sabre/vobject/lib/Splitter/VCard.php vendor/sabre/vobject/lib/StringUtil.php vendor/sabre/vobject/lib/TimeZoneUtil.php vendor/sabre/vobject/lib/UUIDUtil.php vendor/sabre/vobject/lib/VCardConverter.php vendor/sabre/vobject/lib/Version.php vendor/sabre/vobject/lib/timezonedata/exchangezones.php vendor/sabre/vobject/lib/timezonedata/lotuszones.php vendor/sabre/vobject/lib/timezonedata/php-bc.php vendor/sabre/vobject/lib/timezonedata/php-workaround.php vendor/sabre/vobject/lib/timezonedata/windowszones.php vendor/sabre/vobject/tests/VObject/AttachIssueTest.php vendor/sabre/vobject/tests/VObject/CliTest.php vendor/sabre/vobject/tests/VObject/Component/VAlarmTest.php vendor/sabre/vobject/tests/VObject/Component/VCalendarTest.php vendor/sabre/vobject/tests/VObject/Component/VCardTest.php vendor/sabre/vobject/tests/VObject/Component/VEventTest.php vendor/sabre/vobject/tests/VObject/Component/VFreeBusyTest.php vendor/sabre/vobject/tests/VObject/Component/VJournalTest.php vendor/sabre/vobject/tests/VObject/Component/VTimeZoneTest.php vendor/sabre/vobject/tests/VObject/Component/VTodoTest.php vendor/sabre/vobject/tests/VObject/ComponentTest.php vendor/sabre/vobject/tests/VObject/DateTimeParserTest.php vendor/sabre/vobject/tests/VObject/DocumentTest.php vendor/sabre/vobject/tests/VObject/ElementListTest.php vendor/sabre/vobject/tests/VObject/EmClientTest.php vendor/sabre/vobject/tests/VObject/EmptyParameterTest.php vendor/sabre/vobject/tests/VObject/EmptyValueIssueTest.php vendor/sabre/vobject/tests/VObject/FreeBusyGeneratorTest.php vendor/sabre/vobject/tests/VObject/GoogleColonEscapingTest.php vendor/sabre/vobject/tests/VObject/ICalendar/AttachParseTest.php vendor/sabre/vobject/tests/VObject/ITip/BrokerAttendeeReplyTest.php vendor/sabre/vobject/tests/VObject/ITip/BrokerDeleteEventTest.php vendor/sabre/vobject/tests/VObject/ITip/BrokerNewEventTest.php vendor/sabre/vobject/tests/VObject/ITip/BrokerProcessMessageTest.php vendor/sabre/vobject/tests/VObject/ITip/BrokerProcessReplyTest.php vendor/sabre/vobject/tests/VObject/ITip/BrokerTester.php vendor/sabre/vobject/tests/VObject/ITip/BrokerUpdateEventTest.php vendor/sabre/vobject/tests/VObject/ITip/EvolutionTest.php vendor/sabre/vobject/tests/VObject/ITip/MessageTest.php vendor/sabre/vobject/tests/VObject/Issue153Test.php vendor/sabre/vobject/tests/VObject/Issue26Test.php vendor/sabre/vobject/tests/VObject/Issue36WorkAroundTest.php vendor/sabre/vobject/tests/VObject/Issue40Test.php vendor/sabre/vobject/tests/VObject/Issue64Test.php vendor/sabre/vobject/tests/VObject/Issue96Test.php vendor/sabre/vobject/tests/VObject/JCalTest.php vendor/sabre/vobject/tests/VObject/JCardTest.php vendor/sabre/vobject/tests/VObject/LineFoldingIssueTest.php vendor/sabre/vobject/tests/VObject/ParameterTest.php vendor/sabre/vobject/tests/VObject/Parser/JsonTest.php vendor/sabre/vobject/tests/VObject/Parser/MimeDirTest.php vendor/sabre/vobject/tests/VObject/Parser/QuotedPrintableTest.php vendor/sabre/vobject/tests/VObject/Property/BinaryTest.php vendor/sabre/vobject/tests/VObject/Property/BooleanTest.php vendor/sabre/vobject/tests/VObject/Property/CompoundTest.php vendor/sabre/vobject/tests/VObject/Property/FloatTest.php vendor/sabre/vobject/tests/VObject/Property/ICalendar/CalAddressTest.php vendor/sabre/vobject/tests/VObject/Property/ICalendar/DateTimeTest.php vendor/sabre/vobject/tests/VObject/Property/ICalendar/DurationTest.php vendor/sabre/vobject/tests/VObject/Property/ICalendar/RecurTest.php vendor/sabre/vobject/tests/VObject/Property/TextTest.php vendor/sabre/vobject/tests/VObject/Property/VCard/DateAndOrTimeTest.php vendor/sabre/vobject/tests/VObject/Property/VCard/LanguageTagTest.php vendor/sabre/vobject/tests/VObject/PropertyTest.php vendor/sabre/vobject/tests/VObject/ReaderTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/ByMonthInDailyTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/FifthTuesdayProblemTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/IncorrectExpandTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/Issue48Test.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/Issue50Test.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/MainTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/MissingOverriddenTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/NoInstancesTest.php vendor/sabre/vobject/tests/VObject/Recur/EventIterator/OverrideFirstEventTest.php vendor/sabre/vobject/tests/VObject/Recur/RDateIteratorTest.php vendor/sabre/vobject/tests/VObject/Recur/RRuleIteratorTest.php vendor/sabre/vobject/tests/VObject/RecurrenceIterator/UntilRespectsTimezoneTest.ics vendor/sabre/vobject/tests/VObject/SlashRTest.php vendor/sabre/vobject/tests/VObject/Splitter/ICalendarTest.php vendor/sabre/vobject/tests/VObject/Splitter/VCardTest.php vendor/sabre/vobject/tests/VObject/StringUtilTest.php vendor/sabre/vobject/tests/VObject/TestCase.php vendor/sabre/vobject/tests/VObject/TimeZoneUtilTest.php vendor/sabre/vobject/tests/VObject/UUIDUtilTest.php vendor/sabre/vobject/tests/VObject/VCard21Test.php vendor/sabre/vobject/tests/VObject/VCardConverterTest.php vendor/sabre/vobject/tests/VObject/VersionTest.php vendor/sabre/vobject/tests/VObject/issue153.vcf vendor/sabre/vobject/tests/VObject/issue64.vcf vendor/sabre/vobject/tests/bootstrap.php vendor/sabre/vobject/tests/phpcs/ruleset.xml vendor/sabre/vobject/tests/phpunit.xml
diffstat 159 files changed, 32089 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/.gitignore	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,18 @@
+# Composer stuff
+vendor/
+composer.lock
+tests/cov/
+tests/temp
+
+#vim
+.*.swp
+
+#binaries
+bin/phpunit
+bin/phpcs
+
+# Development stuff
+testdata/
+
+# OS X
+.DS_Store
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/.travis.yml	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,17 @@
+language: php
+php:
+  - 5.3
+  - 5.4
+  - 5.5
+  - 5.6
+  - hhvm
+
+matrix:
+  fast_finish: true
+
+script:
+  - phpunit --configuration tests/phpunit.xml
+  - ./bin/phpcs -p --standard=tests/phpcs/ruleset.xml lib/
+
+
+before_script: composer install
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/ChangeLog.md	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,494 @@
+ChangeLog
+=========
+
+3.3.5 (2015-01-09)
+------------------
+
+* #168: Expanding calendars now removes objects with recurrence rules that
+  don't have a valid recurrence instance.
+* #177: SCHEDULE-STATUS should not contain a reason phrase, only a status
+  code.
+* #175: Parser can now read and skip the UTF-8 BOM.
+* #179: Added `isFloating` to `DATE-TIME` properties.
+* #179: Fixed jCal serialization of floating `DATE-TIME` properties.
+* #173: vCard converter failed for `X-ABDATE` properties that had no
+  `X-ABLABEL`.
+
+
+3.3.4 (2014-11-19)
+------------------
+
+* #154: Converting `ANNIVERSARY` to `X-ANNIVERSARY` and `X-ABDATE` and
+  vice-versa when converting to/from vCard 4.
+* #154: It's now possible to easily select all vCard properties belonging to
+  a single group with `$vcard->{'ITEM1.'}` syntax. (@armin-hackmann)
+* #156: Simpler way to check if a string is UTF-8. (@Hywan)
+* Unittest improvements.
+* #159: The recurrence iterator, freebusy generator and iCalendar DATE and
+  DATE-TIME properties can now all accept a reference timezone when working
+  floating times or all-day events.
+* #159: Master events will no longer get a `RECURRENCE-ID` when expanding.
+* #159: `RECURRENCE-ID` for all-day events will now be correct when expanding.
+* #163: Added a `getTimeZone()` method to `VTIMEZONE` components.
+
+
+3.3.3 (2014-10-09)
+------------------
+
+* #142: `CANCEL` and `REPLY` messages now include the `DTSTART` from the
+  original event.
+* #143: `SCHEDULE-AGENT` on the `ORGANIZER` property is respected.
+* #144: `PARTSTAT=NEEDS-ACTION` is now set for new invites, if no `PARTSTAT` is
+  set to support the inbox feature of iOS.
+* #147: Bugs related to scheduling all-day events.
+* #148: Ignore events that have attendees but no organizer.
+* #149: Avoiding logging errors during timezone detection. This is a workaround
+  for a PHP bug.
+* Support for "Line Islands Standard Time" windows timezone.
+* #154: Correctly work around vCard parameters that have a value but no name.
+
+
+3.3.2 (2014-09-19)
+------------------
+
+* Changed: iTip broker now sets RSVP status to false when replies are received.
+* #118: iTip Message now has a `getScheduleStatus()` method.
+* #119: Support for detecting 'significant changes'.
+* #120: Support for `SCHEDULE-FORCE-SEND`.
+* #121: iCal demands parameters containing the + sign to be quoted.
+* #122: Don't generate REPLY messages for events that have been cancelled.
+* #123: Added `SUMMARY` to iTip messages.
+* #130: Incorrect validation rules for `RELATED` (should be `RELATED-TO`).
+* #128: `ATTACH` in iCalendar is `URI` by default, not `BINARY`.
+* #131: RRULE that doesn't provide a single valid instance now throws an
+  exception.
+* #136: Validator rejects *all* control characters. We were missing a few.
+* #133: Splitter objects will throw exceptions when receiving incompatible
+  objects.
+* #127: Attendees who delete recurring event instances events they had already
+  declined earlier will no longer generate another reply.
+* #125: Send CANCEL messages when ORGANIZER property gets deleted.
+
+
+3.3.1 (2014-08-18)
+------------------
+
+* Changed: It's now possible to pass DateTime objects when using the magic
+  setters on properties. (`$event->DTSTART = new DateTime('now')`).
+* #111: iTip Broker does not process attendee adding events to EXDATE.
+* #112: EventIterator now sets TZID on RECURRENCE-ID.
+* #113: Timezone support during creation of iTip REPLY messages.
+* #114: VTIMEZONE is retained when generating new REQUEST objects.
+* #114: Support for 'MAILTO:' style email addresses (in uppercase) in the iTip
+  broker. This improves evolution support.
+* #115: Using REQUEST-STATUS from REPLY messages and now propegating that into
+  SCHEDULE-STATUS.
+
+
+3.3.0 (2014-08-07)
+------------------
+
+* We now use PSR-4 for the directory structure. This means that everything
+  that was used to be in the `lib/Sabre/VObject` directory is now moved to
+  `lib/`. If you use composer to load this library, you shouldn't have to do
+  anything about that though.
+* VEVENT now get populated with a DTSTAMP and UID property by default.
+* BC Break: Removed the 'includes.php' file. Use composer instead.
+* #103: Added support for processing [iTip][iTip] messages. This allows a user
+  to parse incoming iTip messages and apply the result on existing calendars,
+  or automatically generate invites/replies/cancellations based on changes that
+  a user made on objects.
+* #75, #58, #18: Fixes related to overriding the first event in recurrences.
+* Added: VCalendar::getBaseComponent to find the 'master' component in a
+  calendar.
+* #51: Support for iterating RDATE properties.
+* Fixed: Issue #101: RecurrenceIterator::nextMonthly() shows events that are
+  excluded events with wrong time
+
+
+3.2.4 (2014-07-14)
+------------------
+
+* Added: Issue #98. The VCardConverter now takes `X-APPLE-OMIT-YEAR` into
+  consideration when converting between vCard 3 and 4.
+* Fixed: Issue #96. Some support for Yahoo's broken vcards.
+* Fixed: PHP 5.3 support was broken in the cli tool.
+
+
+3.2.3 (2014-06-12)
+------------------
+
+* Validator now checks if DUE and DTSTART are of the same type in VTODO, and
+  ensures that DUE is always after DTSTART.
+* Removed documentation from source repository, to http://sabre.io/vobject/
+* Expanded the vobject cli tool validation output to make it easier to find
+  issues.
+* Fixed: vobject repair. It was not working for iCalendar objects.
+
+
+3.2.2 (2014-05-07)
+------------------
+
+* Minor tweak in unittests to make it run on PHP 5.5.12. Json-prettifying
+  slightly changed which caused the test to fail.
+
+
+3.2.1 (2014-05-03)
+------------------
+
+* Minor tweak to make the unittests run with the latest hhvm on travis.
+* Updated timezone definitions.
+* Updated copyright links to point to http://sabre.io/
+
+
+3.2.0 (2014-04-02)
+------------------
+
+* Now hhvm compatible!
+* The validator can now detect a _lot_ more problems. Many rules for both
+  iCalendar and vCard were added.
+* Added: bin/generate_vcards, a utility to generate random vcards for testing
+  purposes. Patches are welcome to add more data.
+* Updated: Windows timezone mapping to latest version from unicode.org
+* Changed: The timezone maps are now loaded in from external files, in
+  lib/Sabre/VObject/timezonedata.
+* Added: Fixing badly encoded URL's from google contacts vcards.
+* Fixed: Issue #68. Couldn't decode properties ending in a colon.
+* Fixed: Issue #72. RecurrenceIterator should respect timezone in the UNTIL
+  clause.
+* Fixed: Issue #67. BYMONTH limit on DAILY recurrences.
+* Fixed: Issue #26. Return a more descriptive error when coming across broken
+  BYDAY rules.
+* Fixed: Issue #28. Incorrect timezone detection for some timezones.
+* Fixed: Issue #70. Casting a parameter with a null value to string would fail.
+* Added: Support for rfc6715 and rfc6474.
+* Added: Support for DateTime objects in the VCard DATE-AND-OR-TIME property.
+* Added: UUIDUtil, for easily creating unique identifiers.
+* Fixed: Issue #83. Creating new VALUE=DATE objects using php's DateTime.
+* Fixed: Issue #86. Don't go into an infinite loop when php errors are
+  disabled and an invalid file is read.
+
+
+3.1.4 (2014-03-30)
+------------------
+
+* Fixed: Issue #87: Several compatibility fixes related to timezone handling
+  changes in PHP 5.5.10.
+
+
+3.1.3 (2013-10-02)
+------------------
+
+* Fixed: Support from properties from draft-daboo-valarm-extensions-04. Issue
+  #56.
+* Fixed: Issue #54. Parsing a stream of multiple vcards separated by more than
+  one newline. Thanks @Vedmak for the patch.
+* Fixed: Serializing vcard 2.1 parameters with no name caused a literal '1' to
+  be inserted.
+* Added: VCardConverter removed properties that are no longer supported in vCard
+  4.0.
+* Added: vCards with a minimum number of values (such as N), but don't have that
+  many, are now automatically padded with empty components.
+* Added: The vCard validator now also checks for a minimum number of components,
+  and has the ability to repair these.
+* Added: Some support for vCard 2.1 in the VCard converter, to upgrade to vCard
+  3.0 or 4.0.
+* Fixed: Issue 60 Use Document::$componentMap when instantiating the top-level
+  VCalendar and VCard components.
+* Fixed: Issue 62: Parsing iCalendar parameters with no value.
+* Added: --forgiving option to vobject utility.
+* Fixed: Compound properties such as ADR were not correctly split up in vCard
+  2.1 quoted printable-encoded properties.
+* Fixed: Issue 64: Encoding of binary properties of converted vCards. Thanks
+  @DominikTo for the patch.
+
+
+3.1.2 (2013-08-13)
+------------------
+
+* Fixed: Setting correct property group on VCard conversion
+
+
+3.1.1 (2013-08-02)
+------------------
+
+* Fixed: Issue #53. A regression in RecurrenceIterator.
+
+
+3.1.0 (2013-07-27)
+------------------
+
+* Added: bad-ass new cli debugging utility (in bin/vobject).
+* Added: jCal and jCard parser.
+* Fixed: URI properties should not escape ; and ,.
+* Fixed: VCard 4 documents now correctly use URI as a default value-type for
+  PHOTO and others. BINARY no longer exists in vCard 4.
+* Added: Utility to convert between 2.1, 3.0 and 4.0 vCards.
+* Added: You can now add() multiple parameters to a property in one call.
+* Added: Parameter::has() for easily checking if a parameter value exists.
+* Added: VCard::preferred() to find a preferred email, phone number, etc for a
+  contact.
+* Changed: All $duration properties are now public.
+* Added: A few validators for iCalendar documents.
+* Fixed: Issue #50. RecurrenceIterator gives incorrect result when exception
+  events are out of order in the iCalendar file.
+* Fixed: Issue #48. Overridden events in the recurrence iterator that were past
+  the UNTIL date were ignored.
+* Added: getDuration for DURATION values such as TRIGGER. Thanks to
+  @SimonSimCity.
+* Fixed: Issue #52. vCard 2.1 parameters with no name may lose values if there's
+  more than 1. Thanks to @Vedmak.
+
+
+3.0.0 (2013-06-21)
+------------------
+
+* Fixed: includes.php file was still broken. Our tool to generate it had some
+  bugs.
+
+
+3.0.0-beta4 (2013-06-21)
+------------------------
+
+* Fixed: includes.php was no longer up to date.
+
+
+3.0.0-beta3 (2013-06-17)
+------------------------
+
+* Added: OPTION_FORGIVING now also allows slashes in property names.
+* Fixed: DateTimeParser no longer fails on dates with years < 1000 & > 4999
+* Fixed: Issue 36: Workaround for the recurrenceiterator and caldav events with
+  a missing base event.
+* Fixed: jCard encoding of TIME properties.
+* Fixed: jCal encoding of REQUEST-STATUS, GEO and PERIOD values.
+
+
+3.0.0-beta2 (2013-06-10)
+------------------------
+
+* Fixed: Corrected includes.php file.
+* Fixed: vCard date-time parser supported extended-format dates as well.
+* Changed: Properties have been moved to an ICalendar or VCard directory.
+* Fixed: Couldn't parse vCard 3 extended format dates and times.
+* Fixed: Couldn't export jCard DATE values correctly.
+* Fixed: Recursive loop in ICalendar\DateTime property.
+
+
+3.0.0-beta1 (2013-06-07)
+------------------------
+
+* Added: jsonSerialize() for creating jCal and jCard documents.
+* Added: helper method to parse vCard dates and times.
+* Added: Specialized classes for FLOAT, LANGUAGE-TAG, TIME, TIMESTAMP,
+  DATE-AND-OR-TIME, CAL-ADDRESS, UNKNOWN and UTC-OFFSET properties.
+* Removed: CommaSeparatedText property. Now included into Text.
+* Fixed: Multiple parameters with the same name are now correctly encoded.
+* Fixed: Parameter values containing a comma are now enclosed in double-quotes.
+* Fixed: Iterating parameter values should now fully work as expected.
+* Fixed: Support for vCard 2.1 nameless parameters.
+* Changed: $valueMap, $componentMap and $propertyMap now all use fully-qualified
+  class names, so they are actually overridable.
+* Fixed: Updating DATE-TIME to DATE values now behaves like expected.
+
+
+3.0.0-alpha4 (2013-05-31)
+-------------------------
+
+* Added: It's now possible to send parser options to the splitter classes.
+* Added: A few tweaks to improve component and property creation.
+
+
+3.0.0-alpha3 (2013-05-13)
+-------------------------
+
+* Changed: propertyMap, valueMap and componentMap are now static properties.
+* Changed: Component::remove() will throw an exception when trying to a node
+  that's not a child of said component.
+* Added: Splitter objects are now faster, line numbers are accurately reported
+  and use less memory.
+* Added: MimeDir parser can now continue parsing with the same stream buffer.
+* Fixed: vobjectvalidate.php is operational again.
+* Fixed: \r is properly stripped in text values.
+* Fixed: QUOTED-PRINTABLE is now correctly encoded as well as encoded, for
+  vCards 2.1.
+* Fixed: Parser assumes vCard 2.1, if no version was supplied.
+
+
+3.0.0-alpha2 (2013-05-22)
+-------------------------
+
+* Fixed: vCard URL properties were referencing a non-existant class.
+
+
+3.0.0-alpha1 (2013-05-21)
+-------------------------
+
+* Fixed: Now correctly dealing with escaping of properties. This solves the
+  problem with double-backslashes where they don't belong.
+* Added: Easy support for properties with more than one value, using setParts
+  and getParts.
+* Added: Support for broken 2.1 vCards produced by microsoft.
+* Added: Automatically decoding quoted-printable values.
+* Added: Automatically decoding base64 values.
+* Added: Decoding RFC6868 parameter values (uses ^ as an escape character).
+* Added: Fancy new MimeDir parser that can also parse streams.
+* Added: Automatically mapping many, many properties to a property-class with
+  specialized API's.
+* Added: remove() method for easily removing properties and sub-components
+  components.
+* Changed: Components, Properties and Parameters can no longer be created with
+  Component::create, Property::create and Parameter::create. They must instead
+  be created through the root component. (A VCalendar or VCard object).
+* Changed: API for DateTime properties has slightly changed.
+* Changed: the ->value property is now protected everywhere. Use getParts() and
+  getValue() instead.
+* BC Break: No support for mac newlines (\r). Never came across these anyway.
+* Added: add() method to the Property class.
+* Added: It's now possible to easy set multi-value properties as arrays.
+* Added: When setting date-time properties you can just pass PHP's DateTime
+  object.
+* Added: New components automatically get a bunch of default properties, such as
+  VERSION and CALSCALE.
+* Added: You can add new sub-components much quicker with the magic setters, and
+  add() method.
+
+
+2.1.6 (2014-12-10)
+------------------
+
+* Fixed: Minor change to make sure that unittests succeed on every PHP version.
+
+
+2.1.5 (2014-06-03)
+------------------
+
+* Fixed: #94: Better parameter escaping.
+* Changed: Documentation cleanups.
+
+
+2.1.4 (2014-03-30)
+------------------
+
+* Fixed: Issue #87: Several compatibility fixes related to timezone handling
+  changes in PHP 5.5.10.
+
+
+2.1.3 (2013-10-02)
+------------------
+
+* Fixed: Issue #55. \r must be stripped from property values.
+* Fixed: Issue #65. Putting quotes around parameter values that contain a colon.
+
+
+2.1.2 (2013-08-02)
+------------------
+
+* Fixed: Issue #53. A regression in RecurrenceIterator.
+
+
+2.1.1 (2013-07-27)
+------------------
+
+* Fixed: Issue #50. RecurrenceIterator gives incorrect result when exception
+  events are out of order in the iCalendar file.
+* Fixed: Issue #48. Overridden events in the recurrence iterator that were past
+  the UNTIL date were ignored.
+
+
+2.1.0 (2013-06-17)
+------------------
+
+* This version is fully backwards compatible with 2.0.\*. However, it contains a
+  few new API's that mimic the VObject 3 API. This allows it to be used a
+  'bridge' version. Specifically, this new version exists so SabreDAV 1.7 and
+  1.8 can run with both the 2 and 3 versions of this library.
+* Added: Property\DateTime::hasTime().
+* Added: Property\MultiDateTime::hasTime().
+* Added: Property::getValue().
+* Added: Document class.
+* Added: Document::createComponent and Document::createProperty.
+* Added: Parameter::getValue().
+
+
+2.0.7 (2013-03-05)
+------------------
+
+* Fixed: Microsoft re-uses their magic numbers for different timezones,
+  specifically id 2 for both Sarajevo and Lisbon). A workaround was added to
+  deal with this.
+
+
+2.0.6 (2013-02-17)
+------------------
+
+* Fixed: The reader now properly parses parameters without a value.
+
+
+2.0.5 (2012-11-05)
+------------------
+
+* Fixed: The FreeBusyGenerator is now properly using the factory methods for
+  creation of components and properties.
+
+
+2.0.4 (2012-11-02)
+------------------
+
+* Added: Known Lotus Notes / Domino timezone id's.
+
+
+2.0.3 (2012-10-29)
+------------------
+
+* Added: Support for 'GMT+????' format in TZID's.
+* Added: Support for formats like SystemV/EST5EDT in TZID's.
+* Fixed: RecurrenceIterator now repairs recurrence rules where UNTIL < DTSTART.
+* Added: Support for BYHOUR in FREQ=DAILY (@hollodk).
+* Added: Support for BYHOUR and BYDAY in FREQ=WEEKLY.
+
+
+2.0.2 (2012-10-06)
+------------------
+
+* Added: includes.php file, to load the entire library in one go.
+* Fixed: A problem with determining alarm triggers for TODO's.
+
+
+2.0.1 (2012-09-22)
+------------------
+
+* Removed: Element class. It wasn't used.
+* Added: Basic validation and repair methods for broken input data.
+* Fixed: RecurrenceIterator could infinitely loop when an INTERVAL of 0 was
+  specified.
+* Added: A cli script that can validate and automatically repair vcards and
+  iCalendar objects.
+* Added: A new 'Compound' property, that can automatically split up parts for
+  properties such as N, ADR, ORG and CATEGORIES.
+* Added: Splitter classes, that can split up large objects (such as exports)
+  into individual objects (thanks @DominikTO and @armin-hackmann).
+* Added: VFREEBUSY component, which allows easily checking wether timeslots are
+  available.
+* Added: The Reader class now has a 'FORGIVING' option, which allows it to parse
+  properties with incorrect characters in the name (at this time, it just allows
+  underscores).
+* Added: Also added the 'IGNORE_INVALID_LINES' option, to completely disregard
+  any invalid lines.
+* Fixed: A bug in Windows timezone-id mappings for times created in Greenlands
+  timezone (sorry Greenlanders! I do care!).
+* Fixed: DTEND was not generated correctly for VFREEBUSY reports.
+* Fixed: Parser is at least 25% faster with real-world data.
+
+
+2.0.0 (2012-08-08)
+------------------
+
+* VObject is now a separate project from SabreDAV. See the SabreDAV changelog
+  for version information before 2.0.
+* New: VObject library now uses PHP 5.3 namespaces.
+* New: It's possible to specify lists of parameters when constructing
+  properties.
+* New: made it easier to construct the FreeBusyGenerator.
+
+[iTip]: http://tools.ietf.org/html/rfc5546
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/LICENSE	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,27 @@
+Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/)
+
+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 Sabre 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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/README.md	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,50 @@
+sabre/vobject
+=============
+
+The VObject library allows you to easily parse and manipulate [iCalendar](https://tools.ietf.org/html/rfc5545)
+and [vCard](https://tools.ietf.org/html/rfc6350) objects using PHP.
+
+The goal of the VObject library is to create a very complete library, with an easy to use API.
+
+Build status
+------------
+
+| branch | status |
+| ------ | ------ |
+| master | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.png?branch=master)](https://travis-ci.org/fruux/sabre-vobject) |
+| 3.3    | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.png?branch=3.3)](https://travis-ci.org/fruux/sabre-vobject) |
+| 3.1    | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.png?branch=3.1)](https://travis-ci.org/fruux/sabre-vobject) |
+| 2.1    | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.png?branch=2.1)](https://travis-ci.org/fruux/sabre-vobject) |
+| 2.0    | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.png?branch=2.0)](https://travis-ci.org/fruux/sabre-vobject) |
+
+
+Installation
+------------
+
+VObject requires PHP 5.3, and should be installed using composer.
+The general composer instructions can be found on the [composer website](http://getcomposer.org/doc/00-intro.md composer website).
+
+After that, just declare the vobject dependency as follows:
+
+    "require" : {
+        "sabre/vobject" : "~3.3"
+    }
+
+Then, run `composer.phar update` and you should be good.
+
+Usage
+-----
+
+* [3.x documentation](http://sabre.io/vobject/usage/)
+* [2.x documentation](http://sabre.io/vobject/usage_2/)
+* [Migrating from 2.x to 3.x](http://sabre.io/vobject/upgrade/)
+
+Support
+-------
+
+Head over to the [SabreDAV mailing list](http://groups.google.com/group/sabredav-discuss) for any questions.
+
+Made at fruux
+-------------
+
+This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/bin/bench.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,12 @@
+#!/usr/bin/env php
+<?php
+
+include __DIR__ . '/../vendor/autoload.php';
+
+$data = stream_get_contents(STDIN);
+
+$start = microtime(true); 
+
+$lol = Sabre\VObject\Reader::read($data);
+
+echo "time: " . (microtime(true)-$start) . "\n";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/bin/fetch_windows_zones.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,47 @@
+#!/usr/bin/env php
+<?php
+
+$windowsZonesUrl = 'http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml';
+$outputFile = __DIR__ . '/../lib/timezonedata/windowszones.php';
+
+echo "Fetching timezone map from: " . $windowsZonesUrl, "\n";
+
+$data = file_get_contents($windowsZonesUrl);
+
+$xml = simplexml_load_string($data);
+
+$map = [];
+
+foreach($xml->xpath('//mapZone') as $mapZone) {
+
+    $from = (string)$mapZone['other'];
+    $to = (string)$mapZone['type'];
+
+    list($to) = explode(' ', $to, 2);
+
+    if (!isset($map[$from])) {
+        $map[$from] = $to;
+    }
+
+}
+
+ksort($map);
+echo "Writing to: $outputFile\n";
+
+$f = fopen($outputFile,'w');
+fwrite($f, "<?php\n\n");
+fwrite($f, "/**\n");
+fwrite($f, " * Automatically generated timezone file\n");
+fwrite($f, " *\n");
+fwrite($f, " * Last update: " . date(DATE_W3C) . "\n");
+fwrite($f, " * Source: " .$windowsZonesUrl . "\n");
+fwrite($f, " *\n");
+fwrite($f, " * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).\n");
+fwrite($f, " * @license http://sabre.io/license/ Modified BSD License\n");
+fwrite($f, " */\n");
+fwrite($f, "\n");
+fwrite($f, "return ");
+fwrite($f, var_export($map, true) . ';');
+fclose($f);
+
+echo "Done\n";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/bin/generate_vcards	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,241 @@
+#!/usr/bin/env php
+<?php
+
+namespace Sabre\VObject;
+
+// This sucks.. we have to try to find the composer autoloader. But chances
+// are, we can't find it this way. So we'll do our bestest
+$paths = array(
+    __DIR__ . '/../vendor/autoload.php',  // In case vobject is cloned directly
+    __DIR__ . '/../../../autoload.php',   // In case vobject is a composer dependency.
+);
+
+foreach($paths as $path) {
+    if (file_exists($path)) {
+        include $path;
+        break;
+    }
+}
+
+if (!class_exists('Sabre\\VObject\\Version')) {
+    fwrite(STDERR, "Composer autoloader could not be properly loaded.\n");
+    die(1);
+}
+
+if ($argc < 2) {
+
+    $version = Version::VERSION;
+
+    $help = <<<HI
+sabre/vobject $version
+Usage:
+    generate_vcards [count]
+
+Options:
+    count   The number of random vcards to generate
+
+Examples:
+    generate_vcards 1000 > testdata.vcf
+
+HI;
+
+    fwrite(STDERR, $help);
+    exit(2);
+}
+
+$count = (int)$argv[1];
+if ($count < 1) {
+    fwrite(STDERR, "Count must be at least 1\n");
+    exit(2);
+}
+
+fwrite(STDERR, "sabre/vobject " . Version::VERSION . "\n");
+fwrite(STDERR, "Generating " . $count . " vcards in vCard 4.0 format\n");
+
+/**
+ * The following list is just some random data we compiled from various
+ * sources online.
+ *
+ * Very little thought went into compiling this list, and certainly nothing
+ * political or ethical.
+ *
+ * We would _love_ more additions to this to add more variation to this list.
+ *
+ * Send us PR's and don't be shy adding your own first and last name for fun.
+ */
+
+$sets = [
+    "nl" => [
+        "country" => "Netherlands",
+        "boys" => [
+            "Anno",
+            "Bram",
+            "Daan",
+            "Evert",
+            "Finn",
+            "Jayden",
+            "Jens",
+            "Jesse",
+            "Levi",
+            "Lucas",
+            "Luuk",
+            "Milan",
+            "René",
+            "Sem",
+            "Sibrand",
+            "Willem",
+        ],
+        "girls" => [
+            "Celia",
+            "Emma",
+            "Fenna",
+            "Geke",
+            "Inge",
+            "Julia",
+            "Lisa",
+            "Lotte",
+            "Mila",
+            "Sara",
+            "Sophie",
+            "Tess",
+            "Zoë",
+        ],
+        "last" => [
+            "Bakker",
+            "Bos",
+            "De Boer",
+            "De Groot",
+            "De Jong",
+            "De Vries",
+            "Jansen",
+            "Janssen",
+            "Meyer",
+            "Mulder",
+            "Peters",
+            "Smit",
+            "Van Dijk",
+            "Van den Berg",
+            "Visser",
+            "Vos",
+        ],
+    ],
+    "us" => [
+        "country" => "United States",
+        "boys" => [
+            "Aiden",
+            "Alexander",
+            "Charles",
+            "David",
+            "Ethan",
+            "Jacob",
+            "James",
+            "Jayden",
+            "John",
+            "Joseph",
+            "Liam",
+            "Mason",
+            "Michael",
+            "Noah",
+            "Richard",
+            "Robert",
+            "Thomas",
+            "William",
+        ],
+        "girls" => [
+            "Ava",
+            "Barbara",
+            "Chloe",
+            "Dorothy",
+            "Elizabeth",
+            "Emily",
+            "Emma",
+            "Isabella",
+            "Jennifer",
+            "Lily",
+            "Linda",
+            "Margaret",
+            "Maria",
+            "Mary",
+            "Mia",
+            "Olivia",
+            "Patricia",
+            "Roxy",
+            "Sophia",
+            "Susan",
+            "Zoe",
+        ],
+        "last" => [
+            "Smith",
+            "Johnson",
+            "Williams",
+            "Jones",
+            "Brown",
+            "Davis",
+            "Miller",
+            "Wilson",
+            "Moore",
+            "Taylor",
+            "Anderson",
+            "Thomas",
+            "Jackson",
+            "White",
+            "Harris",
+            "Martin",
+            "Thompson",
+            "Garcia",
+            "Martinez",
+            "Robinson",
+        ],
+    ],
+];
+
+$current = 0;
+
+$r = function($arr) {
+
+    return $arr[mt_rand(0,count($arr)-1)];
+
+};
+
+$bdayStart = strtotime('-85 years');
+$bdayEnd = strtotime('-20 years');
+
+while($current < $count) {
+
+    $current++;
+    fwrite(STDERR, "\033[100D$current/$count");
+
+    $country = array_rand($sets);
+    $gender = mt_rand(0,1)?'girls':'boys';
+
+    $vcard = new Component\VCard([
+        'VERSION' => '4.0',
+        'FN' => $r($sets[$country][$gender]) . ' ' . $r($sets[$country]['last']),
+        'UID' => UUIDUtil::getUUID(),
+    ]);
+
+    $bdayRatio = mt_rand(0,9);
+
+    if($bdayRatio < 2) {
+        // 20% has a birthday property with a full date
+        $dt = new \DateTime('@' . mt_rand($bdayStart, $bdayEnd));
+        $vcard->add('BDAY', $dt->format('Ymd'));
+
+    } elseif ($bdayRatio < 3) {
+        // 10% we only know the month and date of
+        $dt = new \DateTime('@' . mt_rand($bdayStart, $bdayEnd));
+        $vcard->add('BDAY', '--' . $dt->format('md'));
+    }
+    if ($result = $vcard->validate()) {
+        ob_start();
+        echo "\nWe produced an invalid vcard somehow!\n";
+        foreach($result as $message) {
+            echo "  " . $message['message'] . "\n";
+        }
+        fwrite(STDERR, ob_get_clean());
+    }
+    echo $vcard->serialize();
+
+}
+
+fwrite(STDERR,"\nDone.\n");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/bin/generateicalendardata.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,91 @@
+#!/usr/bin/env php
+<?php
+
+use Sabre\VObject;
+
+if ($argc<2) {
+    $cmd = $argv[0];
+    fwrite(STDERR, <<<HI
+Fruux test data generator
+
+This script generates a lot of test data. This is used for profiling and stuff.
+Currently it just generates events in a single calendar.
+
+The iCalendar output goes to stdout. Other messages to stderr.
+
+{$cmd} [events]
+
+
+HI
+    );
+    die();
+}
+
+$events = 100;
+
+if (isset($argv[1])) $events = (int)$argv[1];
+
+include __DIR__ . '/../vendor/autoload.php';
+
+fwrite(STDERR, "Generating " . $events . " events\n");
+
+$currentDate = new DateTime('-' . round($events/2) .  ' days');
+
+$calendar = VObject\Component::create('VCALENDAR');
+$calendar->version = '2.0';
+$calendar->calscale = 'GREGORIAN';
+
+$ii=0;
+
+while($ii < $events) {
+
+    $ii++;
+
+    $event = VObject\Component::create('VEVENT');
+    $event->DTSTART = 'bla';
+    $event->SUMMARY = 'Event #' . $ii;
+    $event->UID = md5(microtime(true));
+
+    $doctorRandom = mt_rand(1,1000);
+
+    switch($doctorRandom) {
+        // All-day event
+        case 1 : 
+            $event->DTEND = 'bla';
+            $dtStart = clone $currentDate;
+            $dtEnd = clone $currentDate;
+            $dtEnd->modify('+' . mt_rand(1,3) . ' days');
+            $event->DTSTART->setDateTime($dtStart, VObject\Property\DateTime::DATE);
+            $event->DTEND->setDateTime($dtEnd, VObject\Property\DateTime::DATE);
+            break;
+        case 2 :
+            $event->RRULE = 'FREQ=DAILY;COUNT=' . mt_rand(1,10);
+            // No break intentional
+        default :
+            $dtStart = clone $currentDate;
+            $dtStart->setTime(mt_rand(1,23), mt_rand(0,59), mt_rand(0,59));
+            $event->DTSTART->setDateTime($dtStart, VObject\Property\DateTime::UTC);
+            $event->DURATION = 'PT'.mt_rand(1,3).'H';
+            break;
+
+    }
+    
+    $calendar->add($event);
+    $currentDate->modify('+ ' . mt_rand(0,3) . ' days');
+
+}
+fwrite(STDERR, "Validating\n");
+
+$result = $calendar->validate();
+if ($result) {
+    fwrite(STDERR, "Errors!\n");
+    fwrite(STDERR, print_r($result,true));
+    die(-1);
+}
+
+fwrite(STDERR, "Serializing this beast\n");
+
+echo $calendar->serialize();
+
+fwrite(STDERR, "done.\n");
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/bin/vobject	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,27 @@
+#!/usr/bin/env php
+<?php
+
+namespace Sabre\VObject;
+
+// This sucks.. we have to try to find the composer autoloader. But chances
+// are, we can't find it this way. So we'll do our bestest
+$paths = array(
+    __DIR__ . '/../vendor/autoload.php',  // In case vobject is cloned directly
+    __DIR__ . '/../../../autoload.php',   // In case vobject is a composer dependency.
+);
+
+foreach($paths as $path) {
+    if (file_exists($path)) {
+        include $path;
+        break;
+    }
+}
+
+if (!class_exists('Sabre\\VObject\\Version')) {
+    fwrite(STDERR, "Composer autoloader could not be loaded.\n");
+    die(1);
+}
+
+$cli = new Cli();
+exit($cli->main($argv));
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Cli.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,761 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    InvalidArgumentException;
+
+/**
+ * This is the CLI interface for sabre-vobject.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Cli {
+
+    /**
+     * No output
+     *
+     * @var bool
+     */
+    protected $quiet = false;
+
+    /**
+     * Help display
+     *
+     * @var bool
+     */
+    protected $showHelp = false;
+
+    /**
+     * Wether to spit out 'mimedir' or 'json' format.
+     *
+     * @var string
+     */
+    protected $format;
+
+    /**
+     * JSON pretty print
+     *
+     * @var bool
+     */
+    protected $pretty;
+
+    /**
+     * Source file
+     *
+     * @var string
+     */
+    protected $inputPath;
+
+    /**
+     * Destination file
+     *
+     * @var string
+     */
+    protected $outputPath;
+
+    /**
+     * output stream
+     *
+     * @var resource
+     */
+    protected $stdout;
+
+    /**
+     * stdin
+     *
+     * @var resource
+     */
+    protected $stdin;
+
+    /**
+     * stderr
+     *
+     * @var resource
+     */
+    protected $stderr;
+
+    /**
+     * Input format (one of json or mimedir)
+     *
+     * @var string
+     */
+    protected $inputFormat;
+
+    /**
+     * Makes the parser less strict.
+     *
+     * @var bool
+     */
+    protected $forgiving = false;
+
+    /**
+     * Main function
+     *
+     * @return int
+     */
+    public function main(array $argv) {
+
+        // @codeCoverageIgnoreStart
+        // We cannot easily test this, so we'll skip it. Pretty basic anyway.
+
+        if (!$this->stderr) {
+            $this->stderr = fopen('php://stderr', 'w');
+        }
+        if (!$this->stdout) {
+            $this->stdout = fopen('php://stdout', 'w');
+        }
+        if (!$this->stdin) {
+            $this->stdin = fopen('php://stdin', 'r');
+        }
+
+        // @codeCoverageIgnoreEnd
+
+
+        try {
+
+            list($options, $positional) = $this->parseArguments($argv);
+
+            if (isset($options['q'])) {
+                $this->quiet = true;
+            }
+            $this->log($this->colorize('green', "sabre/vobject ") . $this->colorize('yellow', Version::VERSION));
+
+            foreach($options as $name=>$value) {
+
+                switch($name) {
+
+                    case 'q' :
+                        // Already handled earlier.
+                        break;
+                    case 'h' :
+                    case 'help' :
+                        $this->showHelp();
+                        return 0;
+                        break;
+                    case 'format' :
+                        switch($value) {
+
+                            // jcard/jcal documents
+                            case 'jcard' :
+                            case 'jcal' :
+
+                            // specific document versions
+                            case 'vcard21' :
+                            case 'vcard30' :
+                            case 'vcard40' :
+                            case 'icalendar20' :
+
+                            // specific formats
+                            case 'json' :
+                            case 'mimedir' :
+
+                            // icalendar/vcad
+                            case 'icalendar' :
+                            case 'vcard' :
+                                $this->format = $value;
+                                break;
+
+                            default :
+                                throw new InvalidArgumentException('Unknown format: ' . $value);
+
+                        }
+                        break;
+                    case 'pretty' :
+                        if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
+                            $this->pretty = true;
+                        }
+                        break;
+                    case 'forgiving' :
+                        $this->forgiving = true;
+                        break;
+                    case 'inputformat' :
+                        switch($value) {
+                            // json formats
+                            case 'jcard' :
+                            case 'jcal' :
+                            case 'json' :
+                                $this->inputFormat = 'json';
+                                break;
+
+                            // mimedir formats
+                            case 'mimedir' :
+                            case 'icalendar' :
+                            case 'vcard' :
+                            case 'vcard21' :
+                            case 'vcard30' :
+                            case 'vcard40' :
+                            case 'icalendar20' :
+
+                                $this->inputFormat = 'mimedir';
+                                break;
+
+                            default :
+                                throw new InvalidArgumentException('Unknown format: ' . $value);
+
+                        }
+                        break;
+                    default :
+                        throw new InvalidArgumentException('Unknown option: ' . $name);
+
+                }
+
+            }
+
+            if (count($positional) === 0) {
+                $this->showHelp();
+                return 1;
+            }
+
+            if (count($positional) === 1) {
+                throw new InvalidArgumentException('Inputfile is a required argument');
+            }
+
+            if (count($positional) > 3) {
+                throw new InvalidArgumentException('Too many arguments');
+            }
+
+            if (!in_array($positional[0], array('validate','repair','convert','color'))) {
+                throw new InvalidArgumentException('Uknown command: ' . $positional[0]);
+            }
+
+        } catch (InvalidArgumentException $e) {
+            $this->showHelp();
+            $this->log('Error: ' . $e->getMessage(), 'red');
+            return 1;
+        }
+
+        $command = $positional[0];
+
+        $this->inputPath = $positional[1];
+        $this->outputPath = isset($positional[2])?$positional[2]:'-';
+
+        if ($this->outputPath !== '-') {
+            $this->stdout = fopen($this->outputPath, 'w');
+        }
+
+        if (!$this->inputFormat) {
+            if (substr($this->inputPath, -5)==='.json') {
+                $this->inputFormat = 'json';
+            } else {
+                $this->inputFormat = 'mimedir';
+            }
+        }
+        if (!$this->format) {
+            if (substr($this->outputPath,-5)==='.json') {
+                $this->format = 'json';
+            } else {
+                $this->format = 'mimedir';
+            }
+        }
+
+
+        $realCode = 0;
+
+        try {
+
+            while($input = $this->readInput()) {
+
+                $returnCode = $this->$command($input);
+                if ($returnCode!==0) $realCode = $returnCode;
+
+            }
+
+        } catch (EofException $e) {
+            // end of file
+        } catch (\Exception $e) {
+            $this->log('Error: ' . $e->getMessage(),'red');
+            return 2;
+        }
+
+        return $realCode;
+
+    }
+
+    /**
+     * Shows the help message.
+     *
+     * @return void
+     */
+    protected function showHelp() {
+
+        $this->log('Usage:', 'yellow');
+        $this->log("  vobject [options] command [arguments]");
+        $this->log('');
+        $this->log('Options:', 'yellow');
+        $this->log($this->colorize('green', '  -q            ') . "Don't output anything.");
+        $this->log($this->colorize('green', '  -help -h      ') . "Display this help message.");
+        $this->log($this->colorize('green', '  --format      ') . "Convert to a specific format. Must be one of: vcard, vcard21,");
+        $this->log($this->colorize('green', '  --forgiving   ') . "Makes the parser less strict.");
+        $this->log("                vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir.");
+        $this->log($this->colorize('green', '  --inputformat ') . "If the input format cannot be guessed from the extension, it");
+        $this->log("                must be specified here.");
+        // Only PHP 5.4 and up
+        if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
+            $this->log($this->colorize('green', '  --pretty      ') . "json pretty-print.");
+        }
+        $this->log('');
+        $this->log('Commands:', 'yellow');
+        $this->log($this->colorize('green', '  validate') . ' source_file              Validates a file for correctness.');
+        $this->log($this->colorize('green', '  repair') . ' source_file [output_file]  Repairs a file.');
+        $this->log($this->colorize('green', '  convert') . ' source_file [output_file] Converts a file.');
+        $this->log($this->colorize('green', '  color') . ' source_file                 Colorize a file, useful for debbugging.');
+        $this->log(
+        <<<HELP
+
+If source_file is set as '-', STDIN will be used.
+If output_file is omitted, STDOUT will be used.
+All other output is sent to STDERR.
+
+HELP
+        );
+
+        $this->log('Examples:', 'yellow');
+        $this->log('   vobject convert contact.vcf contact.json');
+        $this->log('   vobject convert --format=vcard40 old.vcf new.vcf');
+        $this->log('   vobject convert --inputformat=json --format=mimedir - -');
+        $this->log('   vobject color calendar.ics');
+        $this->log('');
+        $this->log('https://github.com/fruux/sabre-vobject','purple');
+
+    }
+
+    /**
+     * Validates a VObject file
+     *
+     * @param Component $vObj
+     * @return int
+     */
+    protected function validate($vObj) {
+
+        $returnCode = 0;
+
+        switch($vObj->name) {
+            case 'VCALENDAR' :
+                $this->log("iCalendar: " . (string)$vObj->VERSION);
+                break;
+            case 'VCARD' :
+                $this->log("vCard: " . (string)$vObj->VERSION);
+                break;
+        }
+
+        $warnings = $vObj->validate();
+        if (!count($warnings)) {
+            $this->log("  No warnings!");
+        } else {
+
+            $levels = array(
+                1 => 'REPAIRED',
+                2 => 'WARNING',
+                3 => 'ERROR',
+            );
+            $returnCode = 2;
+            foreach($warnings as $warn) {
+
+                $extra = '';
+                if ($warn['node'] instanceof Property) {
+                    $extra = ' (property: "' . $warn['node']->name . '")';
+                }
+                $this->log("  [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
+
+            }
+
+        }
+
+        return $returnCode;
+
+    }
+
+    /**
+     * Repairs a VObject file
+     *
+     * @param Component $vObj
+     * @return int
+     */
+    protected function repair($vObj) {
+
+        $returnCode = 0;
+
+        switch($vObj->name) {
+            case 'VCALENDAR' :
+                $this->log("iCalendar: " . (string)$vObj->VERSION);
+                break;
+            case 'VCARD' :
+                $this->log("vCard: " . (string)$vObj->VERSION);
+                break;
+        }
+
+        $warnings = $vObj->validate(Node::REPAIR);
+        if (!count($warnings)) {
+            $this->log("  No warnings!");
+        } else {
+
+            $levels = array(
+                1 => 'REPAIRED',
+                2 => 'WARNING',
+                3 => 'ERROR',
+            );
+            $returnCode = 2;
+            foreach($warnings as $warn) {
+
+                $extra = '';
+                if ($warn['node'] instanceof Property) {
+                    $extra = ' (property: "' . $warn['node']->name . '")';
+                }
+                $this->log("  [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
+
+            }
+
+        }
+        fwrite($this->stdout, $vObj->serialize());
+
+        return $returnCode;
+
+    }
+
+    /**
+     * Converts a vObject file to a new format.
+     *
+     * @param Component $vObj
+     * @return int
+     */
+    protected function convert($vObj) {
+
+        $json = false;
+        $convertVersion = null;
+        $forceInput = null;
+
+        switch($this->format) {
+            case 'json' :
+                $json = true;
+                if ($vObj->name === 'VCARD') {
+                    $convertVersion = Document::VCARD40;
+                }
+                break;
+            case 'jcard' :
+                $json = true;
+                $forceInput = 'VCARD';
+                $convertVersion = Document::VCARD40;
+                break;
+            case 'jcal' :
+                $json = true;
+                $forceInput = 'VCALENDAR';
+                break;
+            case 'mimedir' :
+            case 'icalendar' :
+            case 'icalendar20' :
+            case 'vcard' :
+                break;
+            case 'vcard21' :
+                $convertVersion = Document::VCARD21;
+                break;
+            case 'vcard30' :
+                $convertVersion = Document::VCARD30;
+                break;
+            case 'vcard40' :
+                $convertVersion = Document::VCARD40;
+                break;
+
+        }
+
+        if ($forceInput && $vObj->name !== $forceInput) {
+            throw new \Exception('You cannot convert a ' . strtolower($vObj->name) . ' to ' . $this->format);
+        }
+        if ($convertVersion) {
+            $vObj = $vObj->convert($convertVersion);
+        }
+        if ($json) {
+            $jsonOptions = 0;
+            if ($this->pretty) {
+                $jsonOptions = JSON_PRETTY_PRINT;
+            }
+            fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions));
+        } else {
+            fwrite($this->stdout, $vObj->serialize());
+        }
+
+        return 0;
+
+    }
+
+    /**
+     * Colorizes a file
+     *
+     * @param Component $vObj
+     * @return int
+     */
+    protected function color($vObj) {
+
+        fwrite($this->stdout, $this->serializeComponent($vObj));
+
+    }
+
+    /**
+     * Returns an ansi color string for a color name.
+     *
+     * @param string $color
+     * @return string
+     */
+    protected function colorize($color, $str, $resetTo = 'default') {
+
+        $colors = array(
+            'cyan'    => '1;36',
+            'red'     => '1;31',
+            'yellow'  => '1;33',
+            'blue'    => '0;34',
+            'green'   => '0;32',
+            'default' => '0',
+            'purple'  => '0;35',
+        );
+        return "\033[" . $colors[$color] . 'm' . $str . "\033[".$colors[$resetTo]."m";
+
+    }
+
+    /**
+     * Writes out a string in specific color.
+     *
+     * @param string $color
+     * @param string $str
+     * @return void
+     */
+    protected function cWrite($color, $str) {
+
+        fwrite($this->stdout, $this->colorize($color, $str));
+
+    }
+
+    protected function serializeComponent(Component $vObj) {
+
+        $this->cWrite('cyan', 'BEGIN');
+        $this->cWrite('red', ':');
+        $this->cWrite('yellow', $vObj->name . "\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 lower in the list.
+         * To avoid score collisions, each "score category" has a reasonable
+         * space to accomodate elements. The $key is added to the $score to
+         * preserve the original relative order of elements.
+         *
+         * @param int $key
+         * @param array $array
+         * @return int
+         */
+        $sortScore = function($key, $array) {
+
+            if ($array[$key] instanceof Component) {
+
+                // We want to encode VTIMEZONE first, this is a personal
+                // preference.
+                if ($array[$key]->name === 'VTIMEZONE') {
+                    $score=300000000;
+                    return $score+$key;
+                } else {
+                    $score=400000000;
+                    return $score+$key;
+                }
+            } else {
+                // Properties get encoded first
+                // VCARD version 4.0 wants the VERSION property to appear first
+                if ($array[$key] instanceof Property) {
+                    if ($array[$key]->name === 'VERSION') {
+                        $score=100000000;
+                        return $score+$key;
+                    } else {
+                        // All other properties
+                        $score=200000000;
+                        return $score+$key;
+                    }
+                }
+            }
+
+        };
+
+        $tmp = $vObj->children;
+        uksort(
+            $vObj->children,
+            function($a, $b) use ($sortScore, $tmp) {
+
+                $sA = $sortScore($a, $tmp);
+                $sB = $sortScore($b, $tmp);
+
+                return $sA - $sB;
+
+            }
+        );
+
+        foreach($vObj->children as $child) {
+            if ($child instanceof Component) {
+                $this->serializeComponent($child);
+            } else {
+                $this->serializeProperty($child);
+            }
+        }
+
+        $this->cWrite('cyan', 'END');
+        $this->cWrite('red', ':');
+        $this->cWrite('yellow', $vObj->name . "\n");
+
+    }
+
+    /**
+     * Colorizes a property.
+     *
+     * @param Property $property
+     * @return void
+     */
+    protected function serializeProperty(Property $property) {
+
+        if ($property->group) {
+            $this->cWrite('default', $property->group);
+            $this->cWrite('red', '.');
+        }
+
+        $str = '';
+        $this->cWrite('yellow', $property->name);
+
+        foreach($property->parameters as $param) {
+
+            $this->cWrite('red',';');
+            $this->cWrite('blue', $param->serialize());
+
+        }
+        $this->cWrite('red',':');
+
+        if ($property instanceof Property\Binary) {
+
+            $this->cWrite('default', 'embedded binary stripped. (' . strlen($property->getValue()) . ' bytes)');
+
+        } else {
+
+            $parts = $property->getParts();
+            $first1 = true;
+            // Looping through property values
+            foreach($parts as $part) {
+                if ($first1) {
+                    $first1 = false;
+                } else {
+                    $this->cWrite('red', $property->delimiter);
+                }
+                $first2 = true;
+                // Looping through property sub-values
+                foreach((array)$part as $subPart) {
+                    if ($first2) {
+                        $first2 = false;
+                    } else {
+                        // The sub-value delimiter is always comma
+                        $this->cWrite('red', ',');
+                    }
+
+                    $subPart = strtr(
+                        $subPart,
+                        array(
+                            '\\' => $this->colorize('purple', '\\\\', 'green'),
+                            ';'  => $this->colorize('purple', '\;', 'green'),
+                            ','  => $this->colorize('purple', '\,', 'green'),
+                            "\n" => $this->colorize('purple', "\\n\n\t", 'green'),
+                            "\r" => "",
+                        )
+                    );
+
+                    $this->cWrite('green', $subPart);
+                }
+            }
+
+        }
+        $this->cWrite("default", "\n");
+
+    }
+
+    /**
+     * Parses the list of arguments.
+     *
+     * @param array $argv
+     * @return void
+     */
+    protected function parseArguments(array $argv) {
+
+        $positional = array();
+        $options = array();
+
+        for($ii=0; $ii < count($argv); $ii++) {
+
+            // Skipping the first argument.
+            if ($ii===0) continue;
+
+            $v = $argv[$ii];
+
+            if (substr($v,0,2)==='--') {
+                // This is a long-form option.
+                $optionName = substr($v,2);
+                $optionValue = true;
+                if (strpos($optionName,'=')) {
+                    list($optionName, $optionValue) = explode('=', $optionName);
+                }
+                $options[$optionName] = $optionValue;
+            } elseif (substr($v,0,1) === '-' && strlen($v)>1) {
+                // This is a short-form option.
+                foreach(str_split(substr($v,1)) as $option) {
+                    $options[$option] = true;
+                }
+
+            } else {
+
+                $positional[] = $v;
+
+            }
+
+        }
+
+        return array($options, $positional);
+
+    }
+
+    protected $parser;
+
+    /**
+     * Reads the input file
+     *
+     * @return Component
+     */
+    protected function readInput() {
+
+        if (!$this->parser) {
+            if ($this->inputPath!=='-') {
+                $this->stdin = fopen($this->inputPath,'r');
+            }
+
+            if ($this->inputFormat === 'mimedir') {
+                $this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving?Reader::OPTION_FORGIVING:0));
+            } else {
+                $this->parser = new Parser\Json($this->stdin, ($this->forgiving?Reader::OPTION_FORGIVING:0));
+            }
+        }
+
+        return $this->parser->parse();
+
+    }
+
+    /**
+     * Sends a message to STDERR.
+     *
+     * @param string $msg
+     * @return void
+     */
+    protected function log($msg, $color = 'default') {
+
+        if (!$this->quiet) {
+            if ($color!=='default') {
+                $msg = $this->colorize($color, $msg);
+            }
+            fwrite($this->stderr, $msg . "\n");
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,595 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Component
+ *
+ * A component represents a group of properties, such as VCALENDAR, VEVENT, or
+ * VCARD.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Component extends Node {
+
+    /**
+     * Component name.
+     *
+     * This will contain a string such as VEVENT, VTODO, VCALENDAR, VCARD.
+     *
+     * @var string
+     */
+    public $name;
+
+    /**
+     * A list of properties and/or sub-components.
+     *
+     * @var array
+     */
+    public $children = array();
+
+    /**
+     * Creates a new component.
+     *
+     * You can specify the children either in key=>value syntax, in which case
+     * properties will automatically be created, or you can just pass a list of
+     * Component and Property object.
+     *
+     * By default, a set of sensible values will be added to the component. For
+     * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
+     * ensure that this does not happen, set $defaults to false.
+     *
+     * @param Document $root
+     * @param string $name such as VCALENDAR, VEVENT.
+     * @param array $children
+     * @param bool $defaults
+     * @return void
+     */
+    function __construct(Document $root, $name, array $children = array(), $defaults = true) {
+
+        $this->name = strtoupper($name);
+        $this->root = $root;
+
+        if ($defaults) {
+            // This is a terribly convoluted way to do this, but this ensures
+            // that the order of properties as they are specified in both
+            // defaults and the childrens list, are inserted in the object in a
+            // natural way.
+            $list = $this->getDefaults();
+            $nodes = array();
+            foreach($children as $key=>$value) {
+                if ($value instanceof Node) {
+                    if (isset($list[$value->name])) {
+                        unset($list[$value->name]);
+                    }
+                    $nodes[] = $value;
+                } else {
+                    $list[$key] = $value;
+                }
+            }
+            foreach($list as $key=>$value) {
+                $this->add($key, $value);
+            }
+            foreach($nodes as $node) {
+                $this->add($node);
+            }
+        } else {
+            foreach($children as $k=>$child) {
+                if ($child instanceof Node) {
+
+                    // Component or Property
+                    $this->add($child);
+                } else {
+
+                    // Property key=>value
+                    $this->add($k, $child);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Adds a new property or component, and returns the new item.
+     *
+     * This method has 3 possible signatures:
+     *
+     * add(Component $comp) // Adds a new component
+     * add(Property $prop)  // Adds a new property
+     * add($name, $value, array $parameters = array()) // Adds a new property
+     * add($name, array $children = array()) // Adds a new component
+     * by name.
+     *
+     * @return Node
+     */
+    function add($a1, $a2 = null, $a3 = null) {
+
+        if ($a1 instanceof Node) {
+            if (!is_null($a2)) {
+                throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
+            }
+            $a1->parent = $this;
+            $this->children[] = $a1;
+
+            return $a1;
+
+        } elseif(is_string($a1)) {
+
+            $item = $this->root->create($a1, $a2, $a3);
+            $item->parent = $this;
+            $this->children[] = $item;
+
+            return $item;
+
+        } else {
+
+            throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
+
+        }
+
+    }
+
+    /**
+     * This method removes a component or property from this component.
+     *
+     * You can either specify the item by name (like DTSTART), in which case
+     * all properties/components with that name will be removed, or you can
+     * pass an instance of a property or component, in which case only that
+     * exact item will be removed.
+     *
+     * The removed item will be returned. In case there were more than 1 items
+     * removed, only the last one will be returned.
+     *
+     * @param mixed $item
+     * @return void
+     */
+    function remove($item) {
+
+        if (is_string($item)) {
+            $children = $this->select($item);
+            foreach($children as $k=>$child) {
+                unset($this->children[$k]);
+            }
+            return $child;
+        } else {
+            foreach($this->children as $k => $child) {
+                if ($child===$item) {
+                    unset($this->children[$k]);
+                    return $child;
+                }
+            }
+
+            throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component');
+
+        }
+
+    }
+
+    /**
+     * Returns an iterable list of children
+     *
+     * @return array
+     */
+    function children() {
+
+        return $this->children;
+
+    }
+
+    /**
+     * This method only returns a list of sub-components. Properties are
+     * ignored.
+     *
+     * @return array
+     */
+    function getComponents() {
+
+        $result = array();
+        foreach($this->children as $child) {
+            if ($child instanceof Component) {
+                $result[] = $child;
+            }
+        }
+
+        return $result;
+
+    }
+
+    /**
+     * 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
+     */
+    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 Property && strtoupper($child->group) === $group))
+                )
+                ||
+                (
+                    $name === '' && $child instanceof Property && strtoupper($child->group) === $group
+                )
+            ) {
+
+                $result[$key] = $child;
+
+            }
+        }
+
+        reset($result);
+        return $result;
+
+    }
+
+    /**
+     * Turns the object back into a serialized blob.
+     *
+     * @return string
+     */
+    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 lower in the list.
+         * To avoid score collisions, each "score category" has a reasonable
+         * space to accomodate elements. The $key is added to the $score to
+         * preserve the original relative order of elements.
+         *
+         * @param int $key
+         * @param array $array
+         * @return int
+         */
+        $sortScore = function($key, $array) {
+
+            if ($array[$key] instanceof Component) {
+
+                // We want to encode VTIMEZONE first, this is a personal
+                // preference.
+                if ($array[$key]->name === 'VTIMEZONE') {
+                    $score=300000000;
+                    return $score+$key;
+                } else {
+                    $score=400000000;
+                    return $score+$key;
+                }
+            } else {
+                // Properties get encoded first
+                // VCARD version 4.0 wants the VERSION property to appear first
+                if ($array[$key] instanceof Property) {
+                    if ($array[$key]->name === 'VERSION') {
+                        $score=100000000;
+                        return $score+$key;
+                    } else {
+                        // All other properties
+                        $score=200000000;
+                        return $score+$key;
+                    }
+                }
+            }
+
+        };
+
+        $tmp = $this->children;
+        uksort(
+            $this->children,
+            function($a, $b) use ($sortScore, $tmp) {
+
+                $sA = $sortScore($a, $tmp);
+                $sB = $sortScore($b, $tmp);
+
+                return $sA - $sB;
+
+            }
+        );
+
+        foreach($this->children as $child) $str.=$child->serialize();
+        $str.= "END:" . $this->name . "\r\n";
+
+        return $str;
+
+    }
+
+    /**
+     * This method returns an array, with the representation as it should be
+     * encoded in json. This is used to create jCard or jCal documents.
+     *
+     * @return array
+     */
+    function jsonSerialize() {
+
+        $components = array();
+        $properties = array();
+
+        foreach($this->children as $child) {
+            if ($child instanceof Component) {
+                $components[] = $child->jsonSerialize();
+            } else {
+                $properties[] = $child->jsonSerialize();
+            }
+        }
+
+        return array(
+            strtolower($this->name),
+            $properties,
+            $components
+        );
+
+    }
+
+    /**
+     * This method should return a list of default property values.
+     *
+     * @return array
+     */
+    protected function getDefaults() {
+
+        return array();
+
+    }
+
+    /* 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.
+     *
+     * To use this, this may look something like this:
+     *
+     * $event = $calendar->VEVENT;
+     *
+     * @param string $name
+     * @return Property
+     */
+    function __get($name) {
+
+        $matches = $this->select($name);
+        if (count($matches)===0) {
+            return null;
+        } else {
+            $firstMatch = current($matches);
+            /** @var $firstMatch Property */
+            $firstMatch->setIterator(new ElementList(array_values($matches)));
+            return $firstMatch;
+        }
+
+    }
+
+    /**
+     * This method checks if a sub-element with the specified name exists.
+     *
+     * @param string $name
+     * @return bool
+     */
+    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 Component, 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
+     */
+    function __set($name, $value) {
+
+        $matches = $this->select($name);
+        $overWrite = count($matches)?key($matches):null;
+
+        if ($value instanceof Component || $value instanceof Property) {
+            $value->parent = $this;
+            if (!is_null($overWrite)) {
+                $this->children[$overWrite] = $value;
+            } else {
+                $this->children[] = $value;
+            }
+        } else {
+            $property = $this->root->create($name,$value);
+            $property->parent = $this;
+            if (!is_null($overWrite)) {
+                $this->children[$overWrite] = $property;
+            } else {
+                $this->children[] = $property;
+            }
+        }
+    }
+
+    /**
+     * Removes all properties and components within this component with the
+     * specified name.
+     *
+     * @param string $name
+     * @return void
+     */
+    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
+     */
+    function __clone() {
+
+        foreach($this->children as $key=>$child) {
+            $this->children[$key] = clone $child;
+            $this->children[$key]->parent = $this;
+        }
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *   * ? - May appear, but not more than once.
+     *
+     * It is also possible to specify defaults and severity levels for
+     * violating the rule.
+     *
+     * See the VEVENT implementation for getValidationRules for a more complex
+     * example.
+     *
+     * @var array
+     */
+    function getValidationRules() {
+
+        return array();
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   Node::REPAIR - May attempt to automatically repair the problem.
+     *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+     *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+     *
+     * This method returns an array with detected problems.
+     * Every element has the following properties:
+     *
+     *  * level - problem level.
+     *  * message - A human-readable string describing the issue.
+     *  * node - A reference to the problematic node.
+     *
+     * The level means:
+     *   1 - The issue was repaired (only happens if REPAIR was turned on).
+     *   2 - A warning.
+     *   3 - An error.
+     *
+     * @param int $options
+     * @return array
+     */
+    function validate($options = 0) {
+
+        $rules = $this->getValidationRules();
+        $defaults = $this->getDefaults();
+
+        $propertyCounters = array();
+
+        $messages = array();
+
+        foreach($this->children as $child) {
+            $name = strtoupper($child->name);
+            if (!isset($propertyCounters[$name])) {
+                $propertyCounters[$name] = 1;
+            } else {
+                $propertyCounters[$name]++;
+            }
+            $messages = array_merge($messages, $child->validate($options));
+        }
+
+        foreach($rules as $propName => $rule) {
+
+            switch($rule) {
+                case '0' :
+                    if (isset($propertyCounters[$propName])) {
+                        $messages[] = array(
+                            'level' => 3,
+                            'message' => $propName . ' MUST NOT appear in a ' . $this->name . ' component',
+                            'node' => $this,
+                        );
+                    }
+                    break;
+                case '1' :
+                    if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName]!==1) {
+                        $repaired = false;
+                        if ($options & self::REPAIR && isset($defaults[$propName])) {
+                            $this->add($propName, $defaults[$propName]);
+                        }
+                        $messages[] = array(
+                            'level' => $repaired?1:3,
+                            'message' => $propName . ' MUST appear exactly once in a ' . $this->name . ' component',
+                            'node' => $this,
+                        );
+                    }
+                    break;
+                case '+' :
+                    if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] < 1) {
+                        $messages[] = array(
+                            'level' => 3,
+                            'message' => $propName . ' MUST appear at least once in a ' . $this->name . ' component',
+                            'node' => $this,
+                        );
+                    }
+                    break;
+                case '*' :
+                    break;
+                case '?' :
+                    if (isset($propertyCounters[$propName]) && $propertyCounters[$propName] > 1) {
+                        $messages[] = array(
+                            'level' => 3,
+                            'message' => $propName . ' MUST NOT appear more than once in a ' . $this->name . ' component',
+                            'node' => $this,
+                        );
+                    }
+                    break;
+
+            }
+
+        }
+        return $messages;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VAlarm.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,136 @@
+<?php
+
+namespace Sabre\VObject\Component;
+use Sabre\VObject;
+
+/**
+ * VAlarm component
+ *
+ * This component contains some additional functionality specific for VALARMs.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VAlarm extends VObject\Component {
+
+    /**
+     * Returns a DateTime object when this alarm is going to trigger.
+     *
+     * This ignores repeated alarm, only the first trigger is returned.
+     *
+     * @return DateTime
+     */
+    public function getEffectiveTriggerTime() {
+
+        $trigger = $this->TRIGGER;
+        if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
+            $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER);
+            $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
+
+            $parentComponent = $this->parent;
+            if ($related === 'START') {
+
+                if ($parentComponent->name === 'VTODO') {
+                    $propName = 'DUE';
+                } else {
+                    $propName = 'DTSTART';
+                }
+
+                $effectiveTrigger = clone $parentComponent->$propName->getDateTime();
+                $effectiveTrigger->add($triggerDuration);
+            } else {
+                if ($parentComponent->name === 'VTODO') {
+                    $endProp = 'DUE';
+                } elseif ($parentComponent->name === 'VEVENT') {
+                    $endProp = 'DTEND';
+                } else {
+                    throw new \LogicException('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 = 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 = 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);
+        }
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    public function getValidationRules() {
+
+        return array(
+            'ACTION' => 1,
+            'TRIGGER' => 1,
+
+            'DURATION' => '?',
+            'REPEAT' => '?',
+
+            'ATTACH' => '?',
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VCalendar.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,490 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTime;
+use DateTimeZone;
+use Sabre\VObject;
+use Sabre\VObject\Component;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Recur\NoInstancesException;
+
+/**
+ * The VCalendar component
+ *
+ * This component adds functionality to a component, specific for a VCALENDAR.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCalendar extends VObject\Document {
+
+    /**
+     * The default name for this component.
+     *
+     * This should be 'VCALENDAR' or 'VCARD'.
+     *
+     * @var string
+     */
+    static $defaultName = 'VCALENDAR';
+
+    /**
+     * This is a list of components, and which classes they should map to.
+     *
+     * @var array
+     */
+    static $componentMap = array(
+        'VALARM'    => 'Sabre\\VObject\\Component\\VAlarm',
+        'VEVENT'    => 'Sabre\\VObject\\Component\\VEvent',
+        'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy',
+        'VJOURNAL'  => 'Sabre\\VObject\\Component\\VJournal',
+        'VTIMEZONE' => 'Sabre\\VObject\\Component\\VTimeZone',
+        'VTODO'     => 'Sabre\\VObject\\Component\\VTodo',
+    );
+
+    /**
+     * List of value-types, and which classes they map to.
+     *
+     * @var array
+     */
+    static $valueMap = array(
+        'BINARY'           => 'Sabre\\VObject\\Property\\Binary',
+        'BOOLEAN'          => 'Sabre\\VObject\\Property\\Boolean',
+        'CAL-ADDRESS'      => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+        'DATE'             => 'Sabre\\VObject\\Property\\ICalendar\\Date',
+        'DATE-TIME'        => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'DURATION'         => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+        'FLOAT'            => 'Sabre\\VObject\\Property\\Float',
+        'INTEGER'          => 'Sabre\\VObject\\Property\\Integer',
+        'PERIOD'           => 'Sabre\\VObject\\Property\\ICalendar\\Period',
+        'RECUR'            => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
+        'TEXT'             => 'Sabre\\VObject\\Property\\Text',
+        'TIME'             => 'Sabre\\VObject\\Property\\Time',
+        'UNKNOWN'          => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
+        'URI'              => 'Sabre\\VObject\\Property\\Uri',
+        'UTC-OFFSET'       => 'Sabre\\VObject\\Property\\UtcOffset',
+    );
+
+    /**
+     * List of properties, and which classes they map to.
+     *
+     * @var array
+     */
+    static $propertyMap = array(
+        // Calendar properties
+        'CALSCALE'      => 'Sabre\\VObject\\Property\\FlatText',
+        'METHOD'        => 'Sabre\\VObject\\Property\\FlatText',
+        'PRODID'        => 'Sabre\\VObject\\Property\\FlatText',
+        'VERSION'       => 'Sabre\\VObject\\Property\\FlatText',
+
+        // Component properties
+        'ATTACH'            => 'Sabre\\VObject\\Property\\Uri',
+        'CATEGORIES'        => 'Sabre\\VObject\\Property\\Text',
+        'CLASS'             => 'Sabre\\VObject\\Property\\FlatText',
+        'COMMENT'           => 'Sabre\\VObject\\Property\\FlatText',
+        'DESCRIPTION'       => 'Sabre\\VObject\\Property\\FlatText',
+        'GEO'               => 'Sabre\\VObject\\Property\\Float',
+        'LOCATION'          => 'Sabre\\VObject\\Property\\FlatText',
+        'PERCENT-COMPLETE'  => 'Sabre\\VObject\\Property\\Integer',
+        'PRIORITY'          => 'Sabre\\VObject\\Property\\Integer',
+        'RESOURCES'         => 'Sabre\\VObject\\Property\\Text',
+        'STATUS'            => 'Sabre\\VObject\\Property\\FlatText',
+        'SUMMARY'           => 'Sabre\\VObject\\Property\\FlatText',
+
+        // Date and Time Component Properties
+        'COMPLETED'     => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'DTEND'         => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'DUE'           => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'DTSTART'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'DURATION'      => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+        'FREEBUSY'      => 'Sabre\\VObject\\Property\\ICalendar\\Period',
+        'TRANSP'        => 'Sabre\\VObject\\Property\\FlatText',
+
+        // Time Zone Component Properties
+        'TZID'          => 'Sabre\\VObject\\Property\\FlatText',
+        'TZNAME'        => 'Sabre\\VObject\\Property\\FlatText',
+        'TZOFFSETFROM'  => 'Sabre\\VObject\\Property\\UtcOffset',
+        'TZOFFSETTO'    => 'Sabre\\VObject\\Property\\UtcOffset',
+        'TZURL'         => 'Sabre\\VObject\\Property\\Uri',
+
+        // Relationship Component Properties
+        'ATTENDEE'      => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+        'CONTACT'       => 'Sabre\\VObject\\Property\\FlatText',
+        'ORGANIZER'     => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+        'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'RELATED-TO'    => 'Sabre\\VObject\\Property\\FlatText',
+        'URL'           => 'Sabre\\VObject\\Property\\Uri',
+        'UID'           => 'Sabre\\VObject\\Property\\FlatText',
+
+        // Recurrence Component Properties
+        'EXDATE'        => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'RDATE'         => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'RRULE'         => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
+        'EXRULE'        => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545
+
+        // Alarm Component Properties
+        'ACTION'        => 'Sabre\\VObject\\Property\\FlatText',
+        'REPEAT'        => 'Sabre\\VObject\\Property\\Integer',
+        'TRIGGER'       => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+
+        // Change Management Component Properties
+        'CREATED'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'DTSTAMP'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'SEQUENCE'      => 'Sabre\\VObject\\Property\\Integer',
+
+        // Request Status
+        'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text',
+
+        // Additions from draft-daboo-valarm-extensions-04
+        'ALARM-AGENT'    => 'Sabre\\VObject\\Property\\Text',
+        'ACKNOWLEDGED'   => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+        'PROXIMITY'      => 'Sabre\\VObject\\Property\\Text',
+        'DEFAULT-ALARM'  => 'Sabre\\VObject\\Property\\Boolean',
+
+    );
+
+    /**
+     * Returns the current document type.
+     *
+     * @return void
+     */
+    function getDocumentType() {
+
+        return self::ICALENDAR20;
+
+    }
+
+    /**
+     * Returns a list of all 'base components'. For instance, if an Event has
+     * a recurrence rule, and one instance is overridden, the overridden event
+     * will have the same UID, but will be excluded from this list.
+     *
+     * VTIMEZONE components will always be excluded.
+     *
+     * @param string $componentName filter by component name
+     * @return VObject\Component[]
+     */
+    function getBaseComponents($componentName = null) {
+
+        $components = array();
+        foreach($this->children as $component) {
+
+            if (!$component instanceof 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;
+
+    }
+
+    /**
+     * Returns the first component that is not a VTIMEZONE, and does not have
+     * an RECURRENCE-ID.
+     *
+     * If there is no such component, null will be returned.
+     *
+     * @param string $componentName filter by component name
+     * @return VObject\Component|null
+     */
+    function getBaseComponent($componentName = null) {
+
+        foreach($this->children as $component) {
+
+            if (!$component instanceof VObject\Component)
+                continue;
+
+            if (isset($component->{'RECURRENCE-ID'}))
+                continue;
+
+            if ($componentName && $component->name !== strtoupper($componentName))
+                continue;
+
+            if ($component->name === 'VTIMEZONE')
+                continue;
+
+            return $component;
+
+        }
+
+    }
+
+    /**
+     * 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
+     * @param DateTimeZone $timeZone reference timezone for floating dates and
+     *                     times.
+     * @return void
+     */
+    function expand(DateTime $start, DateTime $end, DateTimeZone $timeZone = null) {
+
+        $newEvents = array();
+
+        if (!$timeZone) {
+            $timeZone = new DateTimeZone('UTC');
+        }
+
+        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!');
+            }
+
+            try {
+                $it = new EventIterator($this, $vevent->uid, $timeZone);
+            } catch (NoInstancesException $e) {
+                // This event is recurring, but it doesn't have a single
+                // instance. We are skipping this event from the output
+                // entirely.
+                unset($this->children[$key]);
+                continue;
+            }
+            $it->fastForward($start);
+
+            while($it->valid() && $it->getDTStart() < $end) {
+
+                if ($it->getDTEnd() > $start) {
+
+                    $newEvents[] = $it->getEventObject();
+
+                }
+                $it->next();
+
+            }
+
+            unset($this->children[$key]);
+
+        }
+
+        // Setting all properties to UTC time.
+        foreach($newEvents as $newEvent) {
+
+            foreach($newEvent->children as $child) {
+                if ($child instanceof VObject\Property\ICalendar\DateTime && $child->hasTime()) {
+                    $dt = $child->getDateTimes($timeZone);
+                    // We only need to update the first timezone, because
+                    // setDateTimes will match all other timezones to the
+                    // first.
+                    $dt[0]->setTimeZone(new DateTimeZone('UTC'));
+                    $child->setDateTimes($dt);
+                }
+
+            }
+
+            $this->add($newEvent);
+
+        }
+
+        // Removing all VTIMEZONE components
+        unset($this->VTIMEZONE);
+
+    }
+
+    /**
+     * This method should return a list of default property values.
+     *
+     * @return array
+     */
+    protected function getDefaults() {
+
+        return array(
+            'VERSION' => '2.0',
+            'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
+            'CALSCALE' => 'GREGORIAN',
+        );
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    function getValidationRules() {
+
+        return array(
+            'PRODID' => 1,
+            'VERSION' => 1,
+
+            'CALSCALE' => '?',
+            'METHOD' => '?',
+        );
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   Node::REPAIR - May attempt to automatically repair the problem.
+     *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+     *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+     *
+     * This method returns an array with detected problems.
+     * Every element has the following properties:
+     *
+     *  * level - problem level.
+     *  * message - A human-readable string describing the issue.
+     *  * node - A reference to the problematic node.
+     *
+     * The level means:
+     *   1 - The issue was repaired (only happens if REPAIR was turned on).
+     *   2 - A warning.
+     *   3 - An error.
+     *
+     * @param int $options
+     * @return array
+     */
+    function validate($options = 0) {
+
+        $warnings = parent::validate($options);
+
+        if ($ver = $this->VERSION) {
+            if ((string)$ver !== '2.0') {
+                $warnings[] = array(
+                    'level' => 3,
+                    'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
+                    'node' => $this,
+                );
+            }
+
+        }
+
+        $uidList = array();
+
+        $componentsFound = 0;
+
+        $componentTypes = array();
+
+        foreach($this->children as $child) {
+            if($child instanceof Component) {
+                $componentsFound++;
+
+                if (!in_array($child->name, array('VEVENT', 'VTODO', 'VJOURNAL'))) {
+                    continue;
+                }
+                $componentTypes[] = $child->name;
+
+                $uid = (string)$child->UID;
+                $isMaster = isset($child->{'RECURRENCE-ID'})?0:1;
+                if (isset($uidList[$uid])) {
+                    $uidList[$uid]['count']++;
+                    if ($isMaster && $uidList[$uid]['hasMaster']) {
+                        $warnings[] = array(
+                            'level' => 3,
+                            'message' => 'More than one master object was found for the object with UID ' . $uid,
+                            'node' => $this,
+                        );
+                    }
+                    $uidList[$uid]['hasMaster']+=$isMaster;
+                } else {
+                    $uidList[$uid] = array(
+                        'count' => 1,
+                        'hasMaster' => $isMaster,
+                    );
+                }
+
+            }
+        }
+
+        if ($componentsFound===0) {
+            $warnings[] = array(
+                'level' => 3,
+                'message' => 'An iCalendar object must have at least 1 component.',
+                'node' => $this,
+            );
+        }
+
+        if ($options & self::PROFILE_CALDAV) {
+            if (count($uidList)>1) {
+                $warnings[] = array(
+                    'level' => 3,
+                    'message' => 'A calendar object on a CalDAV server may only have components with the same UID.',
+                    'node' => $this,
+                );
+            }
+            if (count(array_unique($componentTypes))===0) {
+                $warnings[] = array(
+                    'level' => 3,
+                    'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).',
+                    'node' => $this,
+                );
+            }
+            if (count(array_unique($componentTypes))>1) {
+                $warnings[] = array(
+                    'level' => 3,
+                    'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).',
+                    'node' => $this,
+                );
+            }
+
+            if (isset($this->METHOD)) {
+                $warnings[] = array(
+                    'level' => 3,
+                    'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.',
+                    'node' => $this,
+                );
+            }
+        }
+
+        return $warnings;
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VCard.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,451 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use
+    Sabre\VObject;
+
+/**
+ * The VCard component
+ *
+ * This component represents the BEGIN:VCARD and END:VCARD found in every
+ * vcard.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCard extends VObject\Document {
+
+    /**
+     * The default name for this component.
+     *
+     * This should be 'VCALENDAR' or 'VCARD'.
+     *
+     * @var string
+     */
+    static $defaultName = 'VCARD';
+
+    /**
+     * Caching the version number
+     *
+     * @var int
+     */
+    private $version = null;
+
+    /**
+     * List of value-types, and which classes they map to.
+     *
+     * @var array
+     */
+    static $valueMap = array(
+        'BINARY'           => 'Sabre\\VObject\\Property\\Binary',
+        'BOOLEAN'          => 'Sabre\\VObject\\Property\\Boolean',
+        'CONTENT-ID'       => 'Sabre\\VObject\\Property\\FlatText',   // vCard 2.1 only
+        'DATE'             => 'Sabre\\VObject\\Property\\VCard\\Date',
+        'DATE-TIME'        => 'Sabre\\VObject\\Property\\VCard\\DateTime',
+        'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
+        'FLOAT'            => 'Sabre\\VObject\\Property\\Float',
+        'INTEGER'          => 'Sabre\\VObject\\Property\\Integer',
+        'LANGUAGE-TAG'     => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
+        'TIMESTAMP'        => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
+        'TEXT'             => 'Sabre\\VObject\\Property\\Text',
+        'TIME'             => 'Sabre\\VObject\\Property\\Time',
+        'UNKNOWN'          => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
+        'URI'              => 'Sabre\\VObject\\Property\\Uri',
+        'URL'              => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only
+        'UTC-OFFSET'       => 'Sabre\\VObject\\Property\\UtcOffset',
+    );
+
+    /**
+     * List of properties, and which classes they map to.
+     *
+     * @var array
+     */
+    static $propertyMap = array(
+
+        // vCard 2.1 properties and up
+        'N'       => 'Sabre\\VObject\\Property\\Text',
+        'FN'      => 'Sabre\\VObject\\Property\\FlatText',
+        'PHOTO'   => 'Sabre\\VObject\\Property\\Binary', // Todo: we should add a class for Binary values.
+        'BDAY'    => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+        'ADR'     => 'Sabre\\VObject\\Property\\Text',
+        'LABEL'   => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+        'TEL'     => 'Sabre\\VObject\\Property\\FlatText',
+        'EMAIL'   => 'Sabre\\VObject\\Property\\FlatText',
+        'MAILER'  => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+        'GEO'     => 'Sabre\\VObject\\Property\\FlatText',
+        'TITLE'   => 'Sabre\\VObject\\Property\\FlatText',
+        'ROLE'    => 'Sabre\\VObject\\Property\\FlatText',
+        'LOGO'    => 'Sabre\\VObject\\Property\\Binary',
+        // 'AGENT'   => 'Sabre\\VObject\\Property\\',      // Todo: is an embedded vCard. Probably rare, so
+                                 // not supported at the moment
+        'ORG'     => 'Sabre\\VObject\\Property\\Text',
+        'NOTE'    => 'Sabre\\VObject\\Property\\FlatText',
+        'REV'     => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
+        'SOUND'   => 'Sabre\\VObject\\Property\\FlatText',
+        'URL'     => 'Sabre\\VObject\\Property\\Uri',
+        'UID'     => 'Sabre\\VObject\\Property\\FlatText',
+        'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
+        'KEY'     => 'Sabre\\VObject\\Property\\FlatText',
+        'TZ'      => 'Sabre\\VObject\\Property\\Text',
+
+        // vCard 3.0 properties
+        'CATEGORIES'  => 'Sabre\\VObject\\Property\\Text',
+        'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText',
+        'PRODID'      => 'Sabre\\VObject\\Property\\FlatText',
+        'NICKNAME'    => 'Sabre\\VObject\\Property\\Text',
+        'CLASS'       => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+
+        // rfc2739 properties
+        'FBURL'        => 'Sabre\\VObject\\Property\\Uri',
+        'CAPURI'       => 'Sabre\\VObject\\Property\\Uri',
+        'CALURI'       => 'Sabre\\VObject\\Property\\Uri',
+
+        // rfc4770 properties
+        'IMPP'         => 'Sabre\\VObject\\Property\\Uri',
+
+        // vCard 4.0 properties
+        'XML'          => 'Sabre\\VObject\\Property\\FlatText',
+        'ANNIVERSARY'  => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+        'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text',
+        'LANG'         => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
+        'GENDER'       => 'Sabre\\VObject\\Property\\Text',
+        'KIND'         => 'Sabre\\VObject\\Property\\FlatText',
+
+        // rfc6474 properties
+        'BIRTHPLACE'    => 'Sabre\\VObject\\Property\\FlatText',
+        'DEATHPLACE'    => 'Sabre\\VObject\\Property\\FlatText',
+        'DEATHDATE'     => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+
+        // rfc6715 properties
+        'EXPERTISE'     => 'Sabre\\VObject\\Property\\FlatText',
+        'HOBBY'         => 'Sabre\\VObject\\Property\\FlatText',
+        'INTEREST'      => 'Sabre\\VObject\\Property\\FlatText',
+        'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText',
+
+    );
+
+    /**
+     * Returns the current document type.
+     *
+     * @return void
+     */
+    function getDocumentType() {
+
+        if (!$this->version) {
+            $version = (string)$this->VERSION;
+            switch($version) {
+                case '2.1' :
+                    $this->version = self::VCARD21;
+                    break;
+                case '3.0' :
+                    $this->version = self::VCARD30;
+                    break;
+                case '4.0' :
+                    $this->version = self::VCARD40;
+                    break;
+                default :
+                    $this->version = self::UNKNOWN;
+                    break;
+
+            }
+        }
+
+        return $this->version;
+
+    }
+
+    /**
+     * Converts the document to a different vcard version.
+     *
+     * Use one of the VCARD constants for the target. This method will return
+     * a copy of the vcard in the new version.
+     *
+     * At the moment the only supported conversion is from 3.0 to 4.0.
+     *
+     * If input and output version are identical, a clone is returned.
+     *
+     * @param int $target
+     * @return VCard
+     */
+    function convert($target) {
+
+        $converter = new VObject\VCardConverter();
+        return $converter->convert($this, $target);
+
+    }
+
+    /**
+     * VCards with version 2.1, 3.0 and 4.0 are found.
+     *
+     * If the VCARD doesn't know its version, 2.1 is assumed.
+     */
+    const DEFAULT_VERSION = self::VCARD21;
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   Node::REPAIR - May attempt to automatically repair the problem.
+     *
+     * This method returns an array with detected problems.
+     * Every element has the following properties:
+     *
+     *  * level - problem level.
+     *  * message - A human-readable string describing the issue.
+     *  * node - A reference to the problematic node.
+     *
+     * The level means:
+     *   1 - The issue was repaired (only happens if REPAIR was turned on)
+     *   2 - An inconsequential issue
+     *   3 - A severe issue.
+     *
+     * @param int $options
+     * @return array
+     */
+    function validate($options = 0) {
+
+        $warnings = array();
+
+        $versionMap = array(
+            self::VCARD21 => '2.1',
+            self::VCARD30 => '3.0',
+            self::VCARD40 => '4.0',
+        );
+
+        $version = $this->select('VERSION');
+        if (count($version)===1) {
+            $version = (string)$this->VERSION;
+            if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') {
+                $warnings[] = array(
+                    'level' => 3,
+                    'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
+                    'node' => $this,
+                );
+                if ($options & self::REPAIR) {
+                    $this->VERSION = $versionMap[self::DEFAULT_VERSION];
+                }
+            }
+            if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) {
+                $warnings[] = array(
+                    'level' => 3,
+                    'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
+                    'node' => $this,
+                );
+            }
+
+        }
+        $uid = $this->select('UID');
+        if (count($uid) === 0) {
+            if ($options & self::PROFILE_CARDDAV) {
+                // Required for CardDAV
+                $warningLevel = 3;
+                $message = 'vCards on CardDAV servers MUST have a UID property.';
+            } else {
+                // Not required for regular vcards
+                $warningLevel = 2;
+                $message = 'Adding a UID to a vCard property is recommended.';
+            }
+            if ($options & self::REPAIR) {
+                $this->UID = VObject\UUIDUtil::getUUID();
+                $warningLevel = 1;
+            }
+            $warnings[] = array(
+                'level' => $warningLevel,
+                'message' => $message,
+                'node' => $this,
+            );
+        }
+
+        $fn = $this->select('FN');
+        if (count($fn)!==1) {
+
+            $repaired = false;
+            if (($options & self::REPAIR) && count($fn) === 0) {
+                // We're going to try to see if we can use the contents of the
+                // N property.
+                if (isset($this->N)) {
+                    $value = explode(';', (string)$this->N);
+                    if (isset($value[1]) && $value[1]) {
+                        $this->FN = $value[1] . ' ' . $value[0];
+                    } else {
+                        $this->FN = $value[0];
+                    }
+                    $repaired = true;
+
+                // Otherwise, the ORG property may work
+                } elseif (isset($this->ORG)) {
+                    $this->FN = (string)$this->ORG;
+                    $repaired = true;
+                }
+
+            }
+            $warnings[] = array(
+                'level' => $repaired?1:3,
+                'message' => 'The FN property must appear in the VCARD component exactly 1 time',
+                'node' => $this,
+            );
+        }
+
+        return array_merge(
+            parent::validate($options),
+            $warnings
+        );
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    function getValidationRules() {
+
+        return array(
+            'ADR'          => '*',
+            'ANNIVERSARY'  => '?',
+            'BDAY'         => '?',
+            'CALADRURI'    => '*',
+            'CALURI'       => '*',
+            'CATEGORIES'   => '*',
+            'CLIENTPIDMAP' => '*',
+            'EMAIL'        => '*',
+            'FBURL'        => '*',
+            'IMPP'         => '*',
+            'GENDER'       => '?',
+            'GEO'          => '*',
+            'KEY'          => '*',
+            'KIND'         => '?',
+            'LANG'         => '*',
+            'LOGO'         => '*',
+            'MEMBER'       => '*',
+            'N'            => '?',
+            'NICKNAME'     => '*',
+            'NOTE'         => '*',
+            'ORG'          => '*',
+            'PHOTO'        => '*',
+            'PRODID'       => '?',
+            'RELATED'      => '*',
+            'REV'          => '?',
+            'ROLE'         => '*',
+            'SOUND'        => '*',
+            'SOURCE'       => '*',
+            'TEL'          => '*',
+            'TITLE'        => '*',
+            'TZ'           => '*',
+            'URL'          => '*',
+            'VERSION'      => '1',
+            'XML'          => '*',
+
+            // FN is commented out, because it's already handled by the
+            // validate function, which may also try to repair it.
+            // 'FN'           => '+',
+
+            'UID'          => '?',
+        );
+
+    }
+
+    /**
+     * Returns a preferred field.
+     *
+     * VCards can indicate wether a field such as ADR, TEL or EMAIL is
+     * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x
+     * being a number between 1 and 100).
+     *
+     * If neither of those parameters are specified, the first is returned, if
+     * a field with that name does not exist, null is returned.
+     *
+     * @param string $fieldName
+     * @return VObject\Property|null
+     */
+    function preferred($propertyName) {
+
+        $preferred = null;
+        $lastPref = 101;
+        foreach($this->select($propertyName) as $field) {
+
+            $pref = 101;
+            if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
+                $pref = 1;
+            } elseif (isset($field['PREF'])) {
+                $pref = $field['PREF']->getValue();
+            }
+
+            if ($pref < $lastPref || is_null($preferred)) {
+                $preferred = $field;
+                $lastPref = $pref;
+            }
+
+        }
+        return $preferred;
+
+    }
+
+    /**
+     * This method should return a list of default property values.
+     *
+     * @return array
+     */
+    protected function getDefaults() {
+
+        return array(
+            'VERSION' => '3.0',
+            'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
+        );
+
+    }
+
+    /**
+     * This method returns an array, with the representation as it should be
+     * encoded in json. This is used to create jCard or jCal documents.
+     *
+     * @return array
+     */
+    function jsonSerialize() {
+
+        // A vcard does not have sub-components, so we're overriding this
+        // method to remove that array element.
+        $properties = array();
+
+        foreach($this->children as $child) {
+            $properties[] = $child->jsonSerialize();
+        }
+
+        return array(
+            strtolower($this->name),
+            $properties,
+        );
+
+    }
+
+    /**
+     * Returns the default class for a property name.
+     *
+     * @param string $propertyName
+     * @return string
+     */
+    function getClassNameForPropertyName($propertyName) {
+
+        $className = parent::getClassNameForPropertyName($propertyName);
+        // In vCard 4, BINARY no longer exists, and we need URI instead.
+
+        if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType()===self::VCARD40) {
+            return 'Sabre\\VObject\\Property\\Uri';
+        }
+        return $className;
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VEvent.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,140 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+use Sabre\VObject\Recur\EventIterator;
+
+/**
+ * VEvent component
+ *
+ * This component contains some additional functionality specific for VEVENT's.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VEvent extends VObject\Component {
+
+    /**
+     * 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) {
+
+        if ($this->RRULE) {
+            $it = new EventIterator($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)) {
+
+            // The DTEND property is considered non inclusive. So for a 3 day
+            // event in july, dtstart and dtend would have to be July 1st and
+            // July 4th respectively.
+            //
+            // See:
+            // http://tools.ietf.org/html/rfc5545#page-54
+            $effectiveEnd = $this->DTEND->getDateTime();
+
+        } elseif (isset($this->DURATION)) {
+            $effectiveEnd = clone $effectiveStart;
+            $effectiveEnd->add(VObject\DateTimeParser::parseDuration($this->DURATION));
+        } elseif (!$this->DTSTART->hasTime()) {
+            $effectiveEnd = clone $effectiveStart;
+            $effectiveEnd->modify('+1 day');
+        } else {
+            $effectiveEnd = clone $effectiveStart;
+        }
+        return (
+            ($start <= $effectiveEnd) && ($end > $effectiveStart)
+        );
+
+    }
+
+    /**
+     * This method should return a list of default property values.
+     *
+     * @return array
+     */
+    protected function getDefaults() {
+
+        return array(
+            'UID'     => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+            'DTSTAMP' => date('Ymd\\THis\\Z'),
+        );
+
+    }
+
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    public function getValidationRules() {
+
+        $hasMethod = isset($this->parent->METHOD);
+        return array(
+            'UID' => 1,
+            'DTSTAMP' => 1,
+            'DTSTART' => $hasMethod?'?':'1',
+            'CLASS' => '?',
+            'CREATED' => '?',
+            'DESCRIPTION' => '?',
+            'GEO' => '?',
+            'LAST-MODIFICATION' => '?',
+            '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' => '*',
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VFreeBusy.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,102 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * The VFreeBusy component
+ *
+ * This component adds functionality to a component, specific for VFREEBUSY
+ * components.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VFreeBusy extends VObject\Component {
+
+    /**
+     * Checks based on the contained FREEBUSY information, if a timeslot is
+     * available.
+     *
+     * @param DateTime $start
+     * @param Datetime $end
+     * @return bool
+     */
+    public function isFree(\DateTime $start, \Datetime $end) {
+
+        foreach($this->select('FREEBUSY') as $freebusy) {
+
+            // We are only interested in FBTYPE=BUSY (the default),
+            // FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE.
+            if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'],0,4))!=='BUSY') {
+                continue;
+            }
+
+            // The freebusy component can hold more than 1 value, separated by
+            // commas.
+            $periods = explode(',', (string)$freebusy);
+
+            foreach($periods as $period) {
+                // Every period is formatted as [start]/[end]. The start is an
+                // absolute UTC time, the end may be an absolute UTC time, or
+                // duration (relative) value.
+                list($busyStart, $busyEnd) = explode('/', $period);
+
+                $busyStart = VObject\DateTimeParser::parse($busyStart);
+                $busyEnd = VObject\DateTimeParser::parse($busyEnd);
+                if ($busyEnd instanceof \DateInterval) {
+                    $tmp = clone $busyStart;
+                    $tmp->add($busyEnd);
+                    $busyEnd = $tmp;
+                }
+
+                if($start < $busyEnd && $end > $busyStart) {
+                    return false;
+                }
+
+            }
+
+        }
+
+        return true;
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    public function getValidationRules() {
+
+        return array(
+            'UID' => 1,
+            'DTSTAMP' => 1,
+
+            'CONTACT' => '?',
+            'DTSTART' => '?',
+            'DTEND' => '?',
+            'ORGANIZER' => '?',
+            'URL' => '?',
+
+            'ATTENDEE' => '*',
+            'COMMENT' => '*',
+            'FREEBUSY' => '*',
+            'REQUEST-STATUS' => '*',
+        );
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VJournal.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,90 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * VJournal component
+ *
+ * This component contains some additional functionality specific for VJOURNALs.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VJournal extends VObject\Component {
+
+    /**
+     * 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) {
+
+        $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
+        if ($dtstart) {
+            $effectiveEnd = clone $dtstart;
+            if (!$this->DTSTART->hasTime()) {
+                $effectiveEnd->modify('+1 day');
+            }
+
+            return ($start <= $effectiveEnd && $end > $dtstart);
+
+        }
+        return false;
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    public function getValidationRules() {
+
+        return array(
+            'UID' => 1,
+            'DTSTAMP' => 1,
+
+            'CLASS' => '?',
+            'CREATED' => '?',
+            'DTSTART' => '?',
+            'LAST-MODIFICATION' => '?',
+            'ORGANIZER' => '?',
+            'RECURRENCE-ID' => '?',
+            'SEQUENCE' => '?',
+            'STATUS' => '?',
+            'SUMMARY' => '?',
+            'URL' => '?',
+
+            'RRULE' => '?',
+
+            'ATTACH' => '*',
+            'ATTENDEE' => '*',
+            'CATEGORIES' => '*',
+            'COMMENT' => '*',
+            'CONTACT' => '*',
+            'DESCRIPTION' => '*',
+            'EXDATE' => '*',
+            'RELATED-TO' => '*',
+            'RDATE' => '*',
+        );
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VTimeZone.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,67 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * The VTimeZone component
+ *
+ * This component adds functionality to a component, specific for VTIMEZONE
+ * components.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VTimeZone extends VObject\Component {
+
+    /**
+     * Returns the PHP DateTimeZone for this VTIMEZONE component.
+     *
+     * If we can't accurately determine the timezone, this method will return
+     * UTC.
+     *
+     * @return \DateTimeZone
+     */
+    function getTimeZone() {
+
+        return VObject\TimeZoneUtil::getTimeZone((string)$this->TZID, $this->root);
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    function getValidationRules() {
+
+        return array(
+            'TZID' => 1,
+
+            'LAST-MODIFICATION' => '?',
+            'TZURL' => '?',
+
+            // At least 1 STANDARD or DAYLIGHT must appear, or more. But both
+            // cannot appear in the same VTIMEZONE.
+            //
+            // The validator is not specific yet to pick this up, so these
+            // rules are too loose.
+            'STANDARD' => '*',
+            'DAYLIGHT' => '*',
+        );
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Component/VTodo.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,176 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * VTodo component
+ *
+ * This component contains some additional functionality specific for VTODOs.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VTodo extends VObject\Component {
+
+    /**
+     * 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) {
+
+        $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
+        $duration = isset($this->DURATION)?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;
+
+    }
+
+    /**
+     * A simple list of validation rules.
+     *
+     * This is simply a list of properties, and how many times they either
+     * must or must not appear.
+     *
+     * Possible values per property:
+     *   * 0 - Must not appear.
+     *   * 1 - Must appear exactly once.
+     *   * + - Must appear at least once.
+     *   * * - Can appear any number of times.
+     *
+     * @var array
+     */
+    public function getValidationRules() {
+
+        return array(
+            'UID' => 1,
+            'DTSTAMP' => 1,
+
+            'CLASS' => '?',
+            'COMPLETED' => '?',
+            'CREATED' => '?',
+            'DESCRIPTION' => '?',
+            'DTSTART' => '?',
+            'GEO' => '?',
+            'LAST-MODIFICATION' => '?',
+            'LOCATION' => '?',
+            'ORGANIZER' => '?',
+            'PERCENT' => '?',
+            'PRIORITY' => '?',
+            'RECURRENCE-ID' => '?',
+            'SEQUENCE' => '?',
+            'STATUS' => '?',
+            'SUMMARY' => '?',
+            'URL' => '?',
+
+            'RRULE' => '?',
+            'DUE' => '?',
+            'DURATION' => '?',
+
+            'ATTACH' => '*',
+            'ATTENDEE' => '*',
+            'CATEGORIES' => '*',
+            'COMMENT' => '*',
+            'CONTACT' => '*',
+            'EXDATE' => '*',
+            'REQUEST-STATUS' => '*',
+            'RELATED-TO' => '*',
+            'RESOURCES' => '*',
+            'RDATE' => '*',
+        );
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   Node::REPAIR - May attempt to automatically repair the problem.
+     *
+     * This method returns an array with detected problems.
+     * Every element has the following properties:
+     *
+     *  * level - problem level.
+     *  * message - A human-readable string describing the issue.
+     *  * node - A reference to the problematic node.
+     *
+     * The level means:
+     *   1 - The issue was repaired (only happens if REPAIR was turned on)
+     *   2 - An inconsequential issue
+     *   3 - A severe issue.
+     *
+     * @param int $options
+     * @return array
+     */
+    public function validate($options = 0) {
+
+        $result = parent::validate($options);
+        if (isset($this->DUE) && isset($this->DTSTART)) {
+
+            $due = $this->DUE;
+            $dtStart = $this->DTSTART;
+
+            if ($due->getValueType() !== $dtStart->getValueType()) {
+
+                $result[] = array(
+                    'level'   => 3,
+                    'message' => 'The value type (DATE or DATE-TIME) must be identical for DUE and DTSTART',
+                    'node' => $due,
+                );
+
+            } elseif ($due->getDateTime() < $dtStart->getDateTime()) {
+
+                $result[] = array(
+                    'level'   => 3,
+                    'message' => 'DUE must occur after DTSTART',
+                    'node' => $due,
+                );
+
+            }
+
+        }
+
+        return $result;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/DateTimeParser.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,428 @@
+<?php
+
+namespace Sabre\VObject;
+
+use DateTime;
+use DateTimeZone;
+use DateInterval;
+use InvalidArgumentException;
+use LogicException;
+
+/**
+ * DateTimeParser
+ *
+ * This class is responsible for parsing the several different date and time
+ * formats iCalendar and vCards have.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateTimeParser {
+
+    /**
+     * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
+     *
+     * Specifying a reference timezone is optional. It will only be used
+     * if the non-UTC format is used. The argument is used as a reference, the
+     * returned DateTime object will still be in the UTC timezone.
+     *
+     * @param string $dt
+     * @param DateTimeZone $tz
+     * @return DateTime
+     */
+    static public function parseDateTime($dt, DateTimeZone $tz = null) {
+
+        // Format is YYYYMMDD + "T" + hhmmss
+        $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
+
+        if (!$result) {
+            throw new LogicException('The supplied iCalendar datetime value is incorrect: ' . $dt);
+        }
+
+        if ($matches[7]==='Z' || is_null($tz)) {
+            $tz = new DateTimeZone('UTC');
+        }
+        $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
+
+        // Still resetting the timezone, to normalize everything to UTC
+        // $date->setTimeZone(new \DateTimeZone('UTC'));
+        return $date;
+
+    }
+
+    /**
+     * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object.
+     *
+     * @param string $date
+     * @param DateTimeZone $tz
+     * @return DateTime
+     */
+    static public function parseDate($date, DateTimeZone $tz = null) {
+
+        // Format is YYYYMMDD
+        $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
+
+        if (!$result) {
+            throw new LogicException('The supplied iCalendar date value is incorrect: ' . $date);
+        }
+
+        if (is_null($tz)) {
+            $tz = new DateTimeZone('UTC');
+        }
+
+        $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], $tz);
+        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<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
+        if (!$result) {
+            throw new LogicException('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, $referenceTz);
+        } else {
+            return self::parseDateTime($date, $referenceTz);
+        }
+
+    }
+
+    /**
+     * This method parses a vCard date and or time value.
+     *
+     * This can be used for the DATE, DATE-TIME, TIMESTAMP and
+     * DATE-AND-OR-TIME value.
+     *
+     * This method returns an array, not a DateTime value.
+     *
+     * The elements in the array are in the following order:
+     * year, month, date, hour, minute, second, timezone
+     *
+     * Almost any part of the string may be omitted. It's for example legal to
+     * just specify seconds, leave out the year, etc.
+     *
+     * Timezone is either returned as 'Z' or as '+08:00'
+     *
+     * For any non-specified values null is returned.
+     *
+     * List of date formats that are supported:
+     * YYYY
+     * YYYY-MM
+     * YYYYMMDD
+     * --MMDD
+     * ---DD
+     *
+     * YYYY-MM-DD
+     * --MM-DD
+     * ---DD
+     *
+     * List of supported time formats:
+     *
+     * HH
+     * HHMM
+     * HHMMSS
+     * -MMSS
+     * --SS
+     *
+     * HH
+     * HH:MM
+     * HH:MM:SS
+     * -MM:SS
+     * --SS
+     *
+     * A full basic-format date-time string looks like :
+     * 20130603T133901
+     *
+     * A full extended-format date-time string looks like :
+     * 2013-06-03T13:39:01
+     *
+     * Times may be postfixed by a timezone offset. This can be either 'Z' for
+     * UTC, or a string like -0500 or +1100.
+     *
+     * @param string $date
+     * @return array
+     */
+    static public function parseVCardDateTime($date) {
+
+        $regex = '/^
+            (?:  # date part
+                (?:
+                    (?: (?P<year> [0-9]{4}) (?: -)?| --)
+                    (?P<month> [0-9]{2})?
+                |---)
+                (?P<date> [0-9]{2})?
+            )?
+            (?:T  # time part
+                (?P<hour> [0-9]{2} | -)
+                (?P<minute> [0-9]{2} | -)?
+                (?P<second> [0-9]{2})?
+
+                (?P<timezone> # timezone offset
+
+                    Z | (?: \+|-)(?: [0-9]{4})
+
+                )?
+
+            )?
+            $/x';
+
+
+        if (!preg_match($regex, $date, $matches)) {
+
+            // Attempting to parse the extended format.
+            $regex = '/^
+                (?: # date part
+                    (?: (?P<year> [0-9]{4}) - | -- )
+                    (?P<month> [0-9]{2}) -
+                    (?P<date> [0-9]{2})
+                )?
+                (?:T # time part
+
+                    (?: (?P<hour> [0-9]{2}) : | -)
+                    (?: (?P<minute> [0-9]{2}) : | -)?
+                    (?P<second> [0-9]{2})?
+
+                    (?P<timezone> # timezone offset
+
+                        Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
+
+                    )?
+
+                )?
+                $/x';
+
+            if (!preg_match($regex, $date, $matches)) {
+                throw new InvalidArgumentException('Invalid vCard date-time string: ' . $date);
+            }
+
+        }
+        $parts = array(
+            'year',
+            'month',
+            'date',
+            'hour',
+            'minute',
+            'second',
+            'timezone'
+        );
+
+        $result = array();
+        foreach($parts as $part) {
+
+            if (empty($matches[$part])) {
+                $result[$part] = null;
+            } elseif ($matches[$part] === '-' || $matches[$part] === '--') {
+                $result[$part] = null;
+            } else {
+                $result[$part] = $matches[$part];
+            }
+
+        }
+
+        return $result;
+
+    }
+
+    /**
+     * This method parses a vCard TIME value.
+     *
+     * This method returns an array, not a DateTime value.
+     *
+     * The elements in the array are in the following order:
+     * hour, minute, second, timezone
+     *
+     * Almost any part of the string may be omitted. It's for example legal to
+     * just specify seconds, leave out the hour etc.
+     *
+     * Timezone is either returned as 'Z' or as '+08:00'
+     *
+     * For any non-specified values null is returned.
+     *
+     * List of supported time formats:
+     *
+     * HH
+     * HHMM
+     * HHMMSS
+     * -MMSS
+     * --SS
+     *
+     * HH
+     * HH:MM
+     * HH:MM:SS
+     * -MM:SS
+     * --SS
+     *
+     * A full basic-format time string looks like :
+     * 133901
+     *
+     * A full extended-format time string looks like :
+     * 13:39:01
+     *
+     * Times may be postfixed by a timezone offset. This can be either 'Z' for
+     * UTC, or a string like -0500 or +11:00.
+     *
+     * @param string $date
+     * @return array
+     */
+    static public function parseVCardTime($date) {
+
+        $regex = '/^
+            (?P<hour> [0-9]{2} | -)
+            (?P<minute> [0-9]{2} | -)?
+            (?P<second> [0-9]{2})?
+
+            (?P<timezone> # timezone offset
+
+                Z | (?: \+|-)(?: [0-9]{4})
+
+            )?
+            $/x';
+
+
+        if (!preg_match($regex, $date, $matches)) {
+
+            // Attempting to parse the extended format.
+            $regex = '/^
+                (?: (?P<hour> [0-9]{2}) : | -)
+                (?: (?P<minute> [0-9]{2}) : | -)?
+                (?P<second> [0-9]{2})?
+
+                (?P<timezone> # timezone offset
+
+                    Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
+
+                )?
+                $/x';
+
+            if (!preg_match($regex, $date, $matches)) {
+                throw new InvalidArgumentException('Invalid vCard time string: ' . $date);
+            }
+
+        }
+        $parts = array(
+            'hour',
+            'minute',
+            'second',
+            'timezone'
+        );
+
+        $result = array();
+        foreach($parts as $part) {
+
+            if (empty($matches[$part])) {
+                $result[$part] = null;
+            } elseif ($matches[$part] === '-') {
+                $result[$part] = null;
+            } else {
+                $result[$part] = $matches[$part];
+            }
+
+        }
+
+        return $result;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Document.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,261 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Document
+ *
+ * A document is just like a component, except that it's also the top level
+ * element.
+ *
+ * Both a VCALENDAR and a VCARD are considered documents.
+ *
+ * This class also provides a registry for document types.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Document extends Component {
+
+    /**
+     * Unknown document type
+     */
+    const UNKNOWN = 1;
+
+    /**
+     * vCalendar 1.0
+     */
+    const VCALENDAR10 = 2;
+
+    /**
+     * iCalendar 2.0
+     */
+    const ICALENDAR20 = 3;
+
+    /**
+     * vCard 2.1
+     */
+    const VCARD21 = 4;
+
+    /**
+     * vCard 3.0
+     */
+    const VCARD30 = 5;
+
+    /**
+     * vCard 4.0
+     */
+    const VCARD40 = 6;
+
+    /**
+     * The default name for this component.
+     *
+     * This should be 'VCALENDAR' or 'VCARD'.
+     *
+     * @var string
+     */
+    static public $defaultName;
+
+    /**
+     * List of properties, and which classes they map to.
+     *
+     * @var array
+     */
+    static public $propertyMap = array();
+
+    /**
+     * List of components, along with which classes they map to.
+     *
+     * @var array
+     */
+    static public $componentMap = array();
+
+    /**
+     * List of value-types, and which classes they map to.
+     *
+     * @var array
+     */
+    static public $valueMap = array();
+
+    /**
+     * Creates a new document.
+     *
+     * We're changing the default behavior slightly here. First, we don't want
+     * to have to specify a name (we already know it), and we want to allow
+     * children to be specified in the first argument.
+     *
+     * But, the default behavior also works.
+     *
+     * So the two sigs:
+     *
+     * new Document(array $children = array(), $defaults = true);
+     * new Document(string $name, array $children = array(), $defaults = true)
+     *
+     * @return void
+     */
+    public function __construct() {
+
+        $args = func_get_args();
+        if (count($args)===0 || is_array($args[0])) {
+            array_unshift($args, $this, static::$defaultName);
+            call_user_func_array(array('parent', '__construct'), $args);
+        } else {
+            array_unshift($args, $this);
+            call_user_func_array(array('parent', '__construct'), $args);
+        }
+
+    }
+
+    /**
+     * Returns the current document type.
+     *
+     * @return void
+     */
+    public function getDocumentType() {
+
+        return self::UNKNOWN;
+
+    }
+
+    /**
+     * Creates a new component or property.
+     *
+     * If it's a known component, we will automatically call createComponent.
+     * otherwise, we'll assume it's a property and call createProperty instead.
+     *
+     * @param string $name
+     * @param string $arg1,... Unlimited number of args
+     * @return mixed
+     */
+    public function create($name) {
+
+        if (isset(static::$componentMap[strtoupper($name)])) {
+
+            return call_user_func_array(array($this,'createComponent'), func_get_args());
+
+        } else {
+
+            return call_user_func_array(array($this,'createProperty'), func_get_args());
+
+        }
+
+    }
+
+    /**
+     * Creates a new component
+     *
+     * This method automatically searches for the correct component class, based
+     * on its name.
+     *
+     * You can specify the children either in key=>value syntax, in which case
+     * properties will automatically be created, or you can just pass a list of
+     * Component and Property object.
+     *
+     * By default, a set of sensible values will be added to the component. For
+     * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
+     * ensure that this does not happen, set $defaults to false.
+     *
+     * @param string $name
+     * @param array $children
+     * @param bool $defaults
+     * @return Component
+     */
+    public function createComponent($name, array $children = null, $defaults = true) {
+
+        $name = strtoupper($name);
+        $class = 'Sabre\\VObject\\Component';
+
+        if (isset(static::$componentMap[$name])) {
+            $class=static::$componentMap[$name];
+        }
+        if (is_null($children)) $children = array();
+        return new $class($this, $name, $children, $defaults);
+
+    }
+
+    /**
+     * Factory method for creating new properties
+     *
+     * This method automatically searches for the correct property class, based
+     * on its name.
+     *
+     * You can specify the parameters either in key=>value syntax, in which case
+     * parameters will automatically be created, or you can just pass a list of
+     * Parameter objects.
+     *
+     * @param string $name
+     * @param mixed $value
+     * @param array $parameters
+     * @param string $valueType Force a specific valuetype, such as URI or TEXT
+     * @return Property
+     */
+    public function createProperty($name, $value = null, array $parameters = null, $valueType = null) {
+
+        // If there's a . in the name, it means it's prefixed by a groupname.
+        if (($i=strpos($name,'.'))!==false) {
+            $group = substr($name, 0, $i);
+            $name = strtoupper(substr($name, $i+1));
+        } else {
+            $name = strtoupper($name);
+            $group = null;
+        }
+
+        $class = null;
+
+        if ($valueType) {
+            // The valueType argument comes first to figure out the correct
+            // class.
+            $class = $this->getClassNameForPropertyValue($valueType);
+        }
+
+        if (is_null($class) && isset($parameters['VALUE'])) {
+            // If a VALUE parameter is supplied, we should use that.
+            $class = $this->getClassNameForPropertyValue($parameters['VALUE']);
+        }
+        if (is_null($class)) {
+            $class = $this->getClassNameForPropertyName($name);
+        }
+        if (is_null($parameters)) $parameters = array();
+
+        return new $class($this, $name, $value, $parameters, $group);
+
+    }
+
+    /**
+     * This method returns a full class-name for a value parameter.
+     *
+     * For instance, DTSTART may have VALUE=DATE. In that case we will look in
+     * our valueMap table and return the appropriate class name.
+     *
+     * This method returns null if we don't have a specialized class.
+     *
+     * @param string $valueParam
+     * @return void
+     */
+    public function getClassNameForPropertyValue($valueParam) {
+
+        $valueParam = strtoupper($valueParam);
+        if (isset(static::$valueMap[$valueParam])) {
+            return static::$valueMap[$valueParam];
+        }
+
+    }
+
+    /**
+     * Returns the default class for a property name.
+     *
+     * @param string $propertyName
+     * @return string
+     */
+    public function getClassNameForPropertyName($propertyName) {
+
+        if (isset(static::$propertyMap[$propertyName])) {
+            return static::$propertyMap[$propertyName];
+        } else {
+            return 'Sabre\\VObject\\Property\\Unknown';
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/ElementList.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,172 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * VObject ElementList
+ *
+ * This class represents a list of elements. Lists are the result of queries,
+ * such as doing $vcalendar->vevent where there's multiple VEVENT objects.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class 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 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');
+
+    }
+
+    /* }}} */
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/EofException.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,15 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Exception thrown by parser when the end of the stream has been reached,
+ * before this was expected.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class EofException extends ParseException {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/FreeBusyGenerator.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,355 @@
+<?php
+
+namespace Sabre\VObject;
+
+use DateTimeZone;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Recur\EventIterator;
+
+/**
+ * This class helps with generating FREEBUSY reports based on existing sets of
+ * objects.
+ *
+ * It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and
+ * generates a single VFREEBUSY object.
+ *
+ * VFREEBUSY components are described in RFC5545, The rules for what should
+ * go in a single freebusy report is taken from RFC4791, section 7.10.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class FreeBusyGenerator {
+
+    /**
+     * Input objects
+     *
+     * @var array
+     */
+    protected $objects;
+
+    /**
+     * Start of range
+     *
+     * @var DateTime|null
+     */
+    protected $start;
+
+    /**
+     * End of range
+     *
+     * @var DateTime|null
+     */
+    protected $end;
+
+    /**
+     * VCALENDAR object
+     *
+     * @var Component
+     */
+    protected $baseObject;
+
+    /**
+     * Reference timezone.
+     *
+     * When we are calculating busy times, and we come across so-called
+     * floating times (times without a timezone), we use the reference timezone
+     * instead.
+     *
+     * This is also used for all-day events.
+     *
+     * This defaults to UTC.
+     *
+     * @var DateTimeZone
+     */
+    protected $timeZone;
+
+    /**
+     * Creates the generator.
+     *
+     * Check the setTimeRange and setObjects methods for details about the
+     * arguments.
+     *
+     * @param DateTime $start
+     * @param DateTime $end
+     * @param mixed $objects
+     * @param DateTimeZone $timeZone
+     * @return void
+     */
+    public function __construct(\DateTime $start = null, \DateTime $end = null, $objects = null, DateTimeZone $timeZone = null) {
+
+        if ($start && $end) {
+            $this->setTimeRange($start, $end);
+        }
+
+        if ($objects) {
+            $this->setObjects($objects);
+        }
+        if (is_null($timeZone)) {
+            $timeZone = new DateTimeZone('UTC');
+        }
+        $this->setTimeZone($timeZone);
+
+    }
+
+    /**
+     * Sets the VCALENDAR object.
+     *
+     * If this is set, it will not be generated for you. You are responsible
+     * for setting things like the METHOD, CALSCALE, VERSION, etc..
+     *
+     * The VFREEBUSY object will be automatically added though.
+     *
+     * @param Component $vcalendar
+     * @return void
+     */
+    public function setBaseObject(Component $vcalendar) {
+
+        $this->baseObject = $vcalendar;
+
+    }
+
+    /**
+     * Sets the input objects
+     *
+     * You must either specify a valendar object as a strong, or as the parse
+     * Component.
+     * It's also possible to specify multiple objects as an array.
+     *
+     * @param mixed $objects
+     * @return void
+     */
+    public function setObjects($objects) {
+
+        if (!is_array($objects)) {
+            $objects = array($objects);
+        }
+
+        $this->objects = array();
+        foreach($objects as $object) {
+
+            if (is_string($object)) {
+                $this->objects[] = Reader::read($object);
+            } elseif ($object instanceof 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;
+
+    }
+
+    /**
+     * Sets the reference timezone for floating times.
+     *
+     * @param DateTimeZone $timeZone
+     * @return void
+     */
+    public function setTimeZone(DateTimeZone $timeZone) {
+
+        $this->timeZone = $timeZone;
+
+    }
+
+    /**
+     * Parses the input data and returns a correct VFREEBUSY object, wrapped in
+     * a VCALENDAR.
+     *
+     * @return 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 EventIterator($object, (string)$component->uid, $this->timeZone);
+                            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($this->timeZone);
+                            if ($this->end && $startTime > $this->end) {
+                                break;
+                            }
+                            $endTime = null;
+                            if (isset($component->DTEND)) {
+                                $endTime = $component->DTEND->getDateTime($this->timeZone);
+                            } elseif (isset($component->DURATION)) {
+                                $duration = DateTimeParser::parseDuration((string)$component->DURATION);
+                                $endTime = clone $startTime;
+                                $endTime->add($duration);
+                            } elseif (!$component->DTSTART->hasTime()) {
+                                $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 = DateTimeParser::parseDateTime($startTime);
+
+                                if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') {
+                                    $duration = DateTimeParser::parseDuration($endTime);
+                                    $endTime = clone $startTime;
+                                    $endTime->add($duration);
+                                } else {
+                                    $endTime = 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 VCalendar();
+        }
+
+        $vfreebusy = $calendar->createComponent('VFREEBUSY');
+        $calendar->add($vfreebusy);
+
+        if ($this->start) {
+            $dtstart = $calendar->createProperty('DTSTART');
+            $dtstart->setDateTime($this->start);
+            $vfreebusy->add($dtstart);
+        }
+        if ($this->end) {
+            $dtend = $calendar->createProperty('DTEND');
+            $dtend->setDateTime($this->end);
+            $vfreebusy->add($dtend);
+        }
+        $dtstamp = $calendar->createProperty('DTSTAMP');
+        $dtstamp->setDateTime(new \DateTime('now', new \DateTimeZone('UTC')));
+        $vfreebusy->add($dtstamp);
+
+        foreach($busyTimes as $busyTime) {
+
+            $busyTime[0]->setTimeZone(new \DateTimeZone('UTC'));
+            $busyTime[1]->setTimeZone(new \DateTimeZone('UTC'));
+
+            $prop = $calendar->createProperty(
+                'FREEBUSY',
+                $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
+            );
+            $prop['FBTYPE'] = $busyTime[2];
+            $vfreebusy->add($prop);
+
+        }
+
+        return $calendar;
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/ITip/Broker.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,942 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Reader;
+use Sabre\VObject\Recur\EventIterator;
+
+/**
+ * The ITip\Broker class is a utility class that helps with processing
+ * so-called iTip messages.
+ *
+ * iTip is defined in rfc5546, stands for iCalendar Transport-Independent
+ * Interoperability Protocol, and describes the underlying mechanism for
+ * using iCalendar for scheduling for for example through email (also known as
+ * IMip) and CalDAV Scheduling.
+ *
+ * This class helps by:
+ *
+ * 1. Creating individual invites based on an iCalendar event for each
+ *    attendee.
+ * 2. Generating invite updates based on an iCalendar update. This may result
+ *    in new invites, updates and cancellations for attendees, if that list
+ *    changed.
+ * 3. On the receiving end, it can create a local iCalendar event based on
+ *    a received invite.
+ * 4. It can also process an invite update on a local event, ensuring that any
+ *    overridden properties from attendees are retained.
+ * 5. It can create a accepted or declined iTip reply based on an invite.
+ * 6. It can process a reply from an invite and update an events attendee
+ *     status based on a reply.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Broker {
+
+    /**
+     * This setting determines whether the rules for the SCHEDULE-AGENT
+     * parameter should be followed.
+     *
+     * This is a parameter defined on ATTENDEE properties, introduced by RFC
+     * 6638. This parameter allows a caldav client to tell the server 'Don't do
+     * any scheduling operations'.
+     *
+     * If this setting is turned on, any attendees with SCHEDULE-AGENT set to
+     * CLIENT will be ignored. This is the desired behavior for a CalDAV
+     * server, but if you're writing an iTip application that doesn't deal with
+     * CalDAV, you may want to ignore this parameter.
+     *
+     * @var bool
+     */
+    public $scheduleAgentServerRules = true;
+
+    /**
+     * The broker will try during 'parseEvent' figure out whether the change
+     * was significant.
+     *
+     * It uses a few different ways to do this. One of these ways is seeing if
+     * certain properties changed values. This list of specified here.
+     *
+     * This list is taken from:
+     * * http://tools.ietf.org/html/rfc5546#section-2.1.4
+     *
+     * @var string[]
+     */
+    public $significantChangeProperties = array(
+        'DTSTART',
+        'DTEND',
+        'DURATION',
+        'DUE',
+        'RRULE',
+        'RDATE',
+        'EXDATE',
+        'STATUS',
+    );
+
+    /**
+     * This method is used to process an incoming itip message.
+     *
+     * Examples:
+     *
+     * 1. A user is an attendee to an event. The organizer sends an updated
+     * meeting using a new iTip message with METHOD:REQUEST. This function
+     * will process the message and update the attendee's event accordingly.
+     *
+     * 2. The organizer cancelled the event using METHOD:CANCEL. We will update
+     * the users event to state STATUS:CANCELLED.
+     *
+     * 3. An attendee sent a reply to an invite using METHOD:REPLY. We can
+     * update the organizers event to update the ATTENDEE with its correct
+     * PARTSTAT.
+     *
+     * The $existingObject is updated in-place. If there is no existing object
+     * (because it's a new invite for example) a new object will be created.
+     *
+     * If an existing object does not exist, and the method was CANCEL or
+     * REPLY, the message effectively gets ignored, and no 'existingObject'
+     * will be created.
+     *
+     * The updated $existingObject is also returned from this function.
+     *
+     * If the iTip message was not supported, we will always return false.
+     *
+     * @param Message $itipMessage
+     * @param VCalendar $existingObject
+     * @return VCalendar|null
+     */
+    public function processMessage(Message $itipMessage, VCalendar $existingObject = null) {
+
+        // We only support events at the moment.
+        if ($itipMessage->component !== 'VEVENT') {
+            return false;
+        }
+
+        switch($itipMessage->method) {
+
+            case 'REQUEST' :
+                return $this->processMessageRequest($itipMessage, $existingObject);
+
+            case 'CANCEL' :
+                return $this->processMessageCancel($itipMessage, $existingObject);
+
+            case 'REPLY' :
+                return $this->processMessageReply($itipMessage, $existingObject);
+
+            default :
+                // Unsupported iTip message
+                return null;
+
+        }
+
+        return $existingObject;
+
+    }
+
+    /**
+     * This function parses a VCALENDAR object and figure out if any messages
+     * need to be sent.
+     *
+     * A VCALENDAR object will be created from the perspective of either an
+     * attendee, or an organizer. You must pass a string identifying the
+     * current user, so we can figure out who in the list of attendees or the
+     * organizer we are sending this message on behalf of.
+     *
+     * It's possible to specify the current user as an array, in case the user
+     * has more than one identifying href (such as multiple emails).
+     *
+     * It $oldCalendar is specified, it is assumed that the operation is
+     * updating an existing event, which means that we need to look at the
+     * differences between events, and potentially send old attendees
+     * cancellations, and current attendees updates.
+     *
+     * If $calendar is null, but $oldCalendar is specified, we treat the
+     * operation as if the user has deleted an event. If the user was an
+     * organizer, this means that we need to send cancellation notices to
+     * people. If the user was an attendee, we need to make sure that the
+     * organizer gets the 'declined' message.
+     *
+     * @param VCalendar|string $calendar
+     * @param string|array $userHref
+     * @param VCalendar|string $oldCalendar
+     * @return array
+     */
+    public function parseEvent($calendar = null, $userHref, $oldCalendar = null) {
+
+        if ($oldCalendar) {
+            if (is_string($oldCalendar)) {
+                $oldCalendar = Reader::read($oldCalendar);
+            }
+            if (!isset($oldCalendar->VEVENT)) {
+                // We only support events at the moment
+                return array();
+            }
+
+            $oldEventInfo = $this->parseEventInfo($oldCalendar);
+        } else {
+            $oldEventInfo = array(
+                'organizer' => null,
+                'significantChangeHash' => '',
+                'attendees' => array(),
+            );
+        }
+
+        $userHref = (array)$userHref;
+
+        if (!is_null($calendar)) {
+
+            if (is_string($calendar)) {
+                $calendar = Reader::read($calendar);
+            }
+            if (!isset($calendar->VEVENT)) {
+                // We only support events at the moment
+                return array();
+            }
+            $eventInfo = $this->parseEventInfo($calendar);
+            if (!$eventInfo['attendees'] && !$oldEventInfo['attendees']) {
+                // If there were no attendees on either side of the equation,
+                // we don't need to do anything.
+                return array();
+            }
+            if (!$eventInfo['organizer'] && !$oldEventInfo['organizer']) {
+                // There was no organizer before or after the change.
+                return array();
+            }
+
+            $baseCalendar = $calendar;
+
+            // If the new object didn't have an organizer, the organizer
+            // changed the object from a scheduling object to a non-scheduling
+            // object. We just copy the info from the old object.
+            if (!$eventInfo['organizer'] && $oldEventInfo['organizer']) {
+                $eventInfo['organizer'] = $oldEventInfo['organizer'];
+                $eventInfo['organizerName'] = $oldEventInfo['organizerName'];
+            }
+
+        } else {
+            // The calendar object got deleted, we need to process this as a
+            // cancellation / decline.
+            if (!$oldCalendar) {
+                // No old and no new calendar, there's no thing to do.
+                return array();
+            }
+
+            $eventInfo = $oldEventInfo;
+
+            if (in_array($eventInfo['organizer'], $userHref)) {
+                // This is an organizer deleting the event.
+                $eventInfo['attendees'] = array();
+                // Increasing the sequence, but only if the organizer deleted
+                // the event.
+                $eventInfo['sequence']++;
+            } else {
+                // This is an attendee deleting the event.
+                foreach($eventInfo['attendees'] as $key=>$attendee) {
+                    if (in_array($attendee['href'], $userHref)) {
+                        $eventInfo['attendees'][$key]['instances'] = array('master' =>
+                            array('id'=>'master', 'partstat' => 'DECLINED')
+                        );
+                    }
+                }
+            }
+            $baseCalendar = $oldCalendar;
+
+        }
+
+        if (in_array($eventInfo['organizer'], $userHref)) {
+            return $this->parseEventForOrganizer($baseCalendar, $eventInfo, $oldEventInfo);
+        } elseif ($oldCalendar) {
+            // We need to figure out if the user is an attendee, but we're only
+            // doing so if there's an oldCalendar, because we only want to
+            // process updates, not creation of new events.
+            foreach($eventInfo['attendees'] as $attendee) {
+                if (in_array($attendee['href'], $userHref)) {
+                    return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']);
+                }
+            }
+        }
+        return array();
+
+    }
+
+    /**
+     * Processes incoming REQUEST messages.
+     *
+     * This is message from an organizer, and is either a new event
+     * invite, or an update to an existing one.
+     *
+     *
+     * @param Message $itipMessage
+     * @param VCalendar $existingObject
+     * @return VCalendar|null
+     */
+    protected function processMessageRequest(Message $itipMessage, VCalendar $existingObject = null) {
+
+        if (!$existingObject) {
+            // This is a new invite, and we're just going to copy over
+            // all the components from the invite.
+            $existingObject = new VCalendar();
+            foreach($itipMessage->message->getComponents() as $component) {
+                $existingObject->add(clone $component);
+            }
+        } else {
+            // We need to update an existing object with all the new
+            // information. We can just remove all existing components
+            // and create new ones.
+            foreach($existingObject->getComponents() as $component) {
+                $existingObject->remove($component);
+            }
+            foreach($itipMessage->message->getComponents() as $component) {
+                $existingObject->add(clone $component);
+            }
+        }
+        return $existingObject;
+
+    }
+
+    /**
+     * Processes incoming CANCEL messages.
+     *
+     * This is a message from an organizer, and means that either an
+     * attendee got removed from an event, or an event got cancelled
+     * altogether.
+     *
+     * @param Message $itipMessage
+     * @param VCalendar $existingObject
+     * @return VCalendar|null
+     */
+    protected function processMessageCancel(Message $itipMessage, VCalendar $existingObject = null) {
+
+        if (!$existingObject) {
+            // The event didn't exist in the first place, so we're just
+            // ignoring this message.
+        } else {
+            foreach($existingObject->VEVENT as $vevent) {
+                $vevent->STATUS = 'CANCELLED';
+                $vevent->SEQUENCE = $itipMessage->sequence;
+            }
+        }
+        return $existingObject;
+
+    }
+
+    /**
+     * Processes incoming REPLY messages.
+     *
+     * The message is a reply. This is for example an attendee telling
+     * an organizer he accepted the invite, or declined it.
+     *
+     * @param Message $itipMessage
+     * @param VCalendar $existingObject
+     * @return VCalendar|null
+     */
+    protected function processMessageReply(Message $itipMessage, VCalendar $existingObject = null) {
+
+        // A reply can only be processed based on an existing object.
+        // If the object is not available, the reply is ignored.
+        if (!$existingObject) {
+            return null;
+        }
+        $instances = array();
+        $requestStatus = '2.0';
+
+        // Finding all the instances the attendee replied to.
+        foreach($itipMessage->message->VEVENT as $vevent) {
+            $recurId = isset($vevent->{'RECURRENCE-ID'})?$vevent->{'RECURRENCE-ID'}->getValue():'master';
+            $attendee = $vevent->ATTENDEE;
+            $instances[$recurId] = $attendee['PARTSTAT']->getValue();
+            if (isset($vevent->{'REQUEST-STATUS'})) {
+                $requestStatus = $vevent->{'REQUEST-STATUS'}->getValue();
+                list($requestStatus) = explode(';', $requestStatus);
+            }
+        }
+
+        // Now we need to loop through the original organizer event, to find
+        // all the instances where we have a reply for.
+        $masterObject = null;
+        foreach($existingObject->VEVENT as $vevent) {
+            $recurId = isset($vevent->{'RECURRENCE-ID'})?$vevent->{'RECURRENCE-ID'}->getValue():'master';
+            if ($recurId==='master') {
+                $masterObject = $vevent;
+            }
+            if (isset($instances[$recurId])) {
+                $attendeeFound = false;
+                if (isset($vevent->ATTENDEE)) {
+                    foreach($vevent->ATTENDEE as $attendee) {
+                        if ($attendee->getValue() === $itipMessage->sender) {
+                            $attendeeFound = true;
+                            $attendee['PARTSTAT'] = $instances[$recurId];
+                            $attendee['SCHEDULE-STATUS'] = $requestStatus;
+                            // Un-setting the RSVP status, because we now know
+                            // that the attende already replied.
+                            unset($attendee['RSVP']);
+                            break;
+                        }
+                    }
+                }
+                if (!$attendeeFound) {
+                    // Adding a new attendee. The iTip documentation calls this
+                    // a party crasher.
+                    $attendee = $vevent->add('ATTENDEE', $itipMessage->sender, array(
+                        'PARTSTAT' => $instances[$recurId]
+                    ));
+                    if ($itipMessage->senderName) $attendee['CN'] = $itipMessage->senderName;
+                }
+                unset($instances[$recurId]);
+            }
+        }
+
+        if(!$masterObject) {
+            // No master object, we can't add new instances.
+            return null;
+        }
+        // If we got replies to instances that did not exist in the
+        // original list, it means that new exceptions must be created.
+        foreach($instances as $recurId=>$partstat) {
+
+            $recurrenceIterator = new EventIterator($existingObject, $itipMessage->uid);
+            $found = false;
+            $iterations = 1000;
+            do {
+
+                $newObject = $recurrenceIterator->getEventObject();
+                $recurrenceIterator->next();
+
+                if (isset($newObject->{'RECURRENCE-ID'}) && $newObject->{'RECURRENCE-ID'}->getValue()===$recurId) {
+                    $found = true;
+                }
+                $iterations--;
+
+            } while($recurrenceIterator->valid() && !$found && $iterations);
+
+            // Invalid recurrence id. Skipping this object.
+            if (!$found) continue;
+
+            unset(
+                $newObject->RRULE,
+                $newObject->EXDATE,
+                $newObject->RDATE
+            );
+            $attendeeFound = false;
+            if (isset($newObject->ATTENDEE)) {
+                foreach($newObject->ATTENDEE as $attendee) {
+                    if ($attendee->getValue() === $itipMessage->sender) {
+                        $attendeeFound = true;
+                        $attendee['PARTSTAT'] = $partstat;
+                        break;
+                    }
+                }
+            }
+            if (!$attendeeFound) {
+                // Adding a new attendee
+                $attendee = $newObject->add('ATTENDEE', $itipMessage->sender, array(
+                    'PARTSTAT' => $partstat
+                ));
+                if ($itipMessage->senderName) {
+                    $attendee['CN'] = $itipMessage->senderName;
+                }
+            }
+            $existingObject->add($newObject);
+
+        }
+        return $existingObject;
+
+    }
+
+    /**
+     * This method is used in cases where an event got updated, and we
+     * potentially need to send emails to attendees to let them know of updates
+     * in the events.
+     *
+     * We will detect which attendees got added, which got removed and create
+     * specific messages for these situations.
+     *
+     * @param VCalendar $calendar
+     * @param array $eventInfo
+     * @param array $oldEventInfo
+     * @return array
+     */
+    protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) {
+
+        // Merging attendee lists.
+        $attendees = array();
+        foreach($oldEventInfo['attendees'] as $attendee) {
+            $attendees[$attendee['href']] = array(
+                'href' => $attendee['href'],
+                'oldInstances' => $attendee['instances'],
+                'newInstances' => array(),
+                'name' => $attendee['name'],
+                'forceSend' => null,
+            );
+        }
+        foreach($eventInfo['attendees'] as $attendee) {
+            if (isset($attendees[$attendee['href']])) {
+                $attendees[$attendee['href']]['name'] = $attendee['name'];
+                $attendees[$attendee['href']]['newInstances'] = $attendee['instances'];
+                $attendees[$attendee['href']]['forceSend'] = $attendee['forceSend'];
+            } else {
+                $attendees[$attendee['href']] = array(
+                    'href' => $attendee['href'],
+                    'oldInstances' => array(),
+                    'newInstances' => $attendee['instances'],
+                    'name' => $attendee['name'],
+                    'forceSend' => $attendee['forceSend'],
+                );
+            }
+        }
+
+        $messages = array();
+
+        foreach($attendees as $attendee) {
+
+            // An organizer can also be an attendee. We should not generate any
+            // messages for those.
+            if ($attendee['href']===$eventInfo['organizer']) {
+                continue;
+            }
+
+            $message = new Message();
+            $message->uid = $eventInfo['uid'];
+            $message->component = 'VEVENT';
+            $message->sequence = $eventInfo['sequence'];
+            $message->sender = $eventInfo['organizer'];
+            $message->senderName = $eventInfo['organizerName'];
+            $message->recipient = $attendee['href'];
+            $message->recipientName = $attendee['name'];
+
+            if (!$attendee['newInstances']) {
+
+                // If there are no instances the attendee is a part of, it
+                // means the attendee was removed and we need to send him a
+                // CANCEL.
+                $message->method = 'CANCEL';
+
+                // Creating the new iCalendar body.
+                $icalMsg = new VCalendar();
+                $icalMsg->METHOD = $message->method;
+                $event = $icalMsg->add('VEVENT', array(
+                    'UID' => $message->uid,
+                    'SEQUENCE' => $message->sequence,
+                ));
+                if (isset($calendar->VEVENT->SUMMARY)) {
+                    $event->add('SUMMARY', $calendar->VEVENT->SUMMARY->getValue());
+                }
+                $event->add(clone $calendar->VEVENT->DTSTART);
+                $org = $event->add('ORGANIZER', $eventInfo['organizer']);
+                if ($eventInfo['organizerName']) $org['CN'] = $eventInfo['organizerName'];
+                $event->add('ATTENDEE', $attendee['href'], array(
+                    'CN' => $attendee['name'],
+                ));
+                $message->significantChange = true;
+
+            } else {
+
+                // The attendee gets the updated event body
+                $message->method = 'REQUEST';
+
+                // Creating the new iCalendar body.
+                $icalMsg = new VCalendar();
+                $icalMsg->METHOD = $message->method;
+
+                foreach($calendar->select('VTIMEZONE') as $timezone) {
+                    $icalMsg->add(clone $timezone);
+                }
+
+                // We need to find out that this change is significant. If it's
+                // not, systems may opt to not send messages.
+                //
+                // We do this based on the 'significantChangeHash' which is
+                // some value that changes if there's a certain set of
+                // properties changed in the event, or simply if there's a
+                // difference in instances that the attendee is invited to.
+
+                $message->significantChange =
+                    $attendee['forceSend'] === 'REQUEST' ||
+                    array_keys($attendee['oldInstances']) != array_keys($attendee['newInstances']) ||
+                    $oldEventInfo['significantChangeHash']!==$eventInfo['significantChangeHash'];
+
+                foreach($attendee['newInstances'] as $instanceId => $instanceInfo) {
+
+                    $currentEvent = clone $eventInfo['instances'][$instanceId];
+                    if ($instanceId === 'master') {
+
+                        // We need to find a list of events that the attendee
+                        // is not a part of to add to the list of exceptions.
+                        $exceptions = array();
+                        foreach($eventInfo['instances'] as $instanceId=>$vevent) {
+                            if (!isset($attendee['newInstances'][$instanceId])) {
+                                $exceptions[] = $instanceId;
+                            }
+                        }
+
+                        // If there were exceptions, we need to add it to an
+                        // existing EXDATE property, if it exists.
+                        if ($exceptions) {
+                            if (isset($currentEvent->EXDATE)) {
+                                $currentEvent->EXDATE->setParts(array_merge(
+                                    $currentEvent->EXDATE->getParts(),
+                                    $exceptions
+                                ));
+                            } else {
+                                $currentEvent->EXDATE = $exceptions;
+                            }
+                        }
+
+                        // Cleaning up any scheduling information that
+                        // shouldn't be sent along.
+                        unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']);
+                        unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']);
+
+                        foreach($currentEvent->ATTENDEE as $attendee) {
+                            unset($attendee['SCHEDULE-FORCE-SEND']);
+                            unset($attendee['SCHEDULE-STATUS']);
+
+                            // We're adding PARTSTAT=NEEDS-ACTION to ensure that
+                            // iOS shows an "Inbox Item"
+                            if (!isset($attendee['PARTSTAT'])) {
+                                $attendee['PARTSTAT'] = 'NEEDS-ACTION';
+                            }
+
+                        }
+
+                    }
+
+                    $icalMsg->add($currentEvent);
+
+                }
+
+            }
+
+            $message->message = $icalMsg;
+            $messages[] = $message;
+
+        }
+
+        return $messages;
+
+    }
+
+    /**
+     * Parse an event update for an attendee.
+     *
+     * This function figures out if we need to send a reply to an organizer.
+     *
+     * @param VCalendar $calendar
+     * @param array $eventInfo
+     * @param array $oldEventInfo
+     * @param string $attendee
+     * @return Message[]
+     */
+    protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, array $oldEventInfo, $attendee) {
+
+        if ($this->scheduleAgentServerRules && $eventInfo['organizerScheduleAgent']==='CLIENT') {
+            return array();
+        }
+
+        // Don't bother generating messages for events that have already been
+        // cancelled.
+        if ($eventInfo['status']==='CANCELLED') {
+            return array();
+        }
+
+        $instances = array();
+        foreach($oldEventInfo['attendees'][$attendee]['instances'] as $instance) {
+
+            $instances[$instance['id']] = array(
+                'id' => $instance['id'],
+                'oldstatus' => $instance['partstat'],
+                'newstatus' => null,
+            );
+
+        }
+        foreach($eventInfo['attendees'][$attendee]['instances'] as $instance) {
+
+            if (isset($instances[$instance['id']])) {
+                $instances[$instance['id']]['newstatus'] = $instance['partstat'];
+            } else {
+                $instances[$instance['id']] = array(
+                    'id' => $instance['id'],
+                    'oldstatus' => null,
+                    'newstatus' => $instance['partstat'],
+                );
+            }
+
+        }
+
+        // We need to also look for differences in EXDATE. If there are new
+        // items in EXDATE, it means that an attendee deleted instances of an
+        // event, which means we need to send DECLINED specifically for those
+        // instances.
+        // We only need to do that though, if the master event is not declined.
+        if ($instances['master']['newstatus'] !== 'DECLINED') {
+            foreach($eventInfo['exdate'] as $exDate) {
+
+                if (!in_array($exDate, $oldEventInfo['exdate'])) {
+                    if (isset($instances[$exDate])) {
+                        $instances[$exDate]['newstatus'] = 'DECLINED';
+                    } else {
+                        $instances[$exDate] = array(
+                            'id' => $exDate,
+                            'oldstatus' => null,
+                            'newstatus' => 'DECLINED',
+                        );
+                    }
+                }
+
+            }
+        }
+
+        // Gathering a few extra properties for each instance.
+        foreach($instances as $recurId=>$instanceInfo) {
+
+            if (isset($eventInfo['instances'][$recurId])) {
+                $instances[$recurId]['dtstart'] = clone $eventInfo['instances'][$recurId]->DTSTART;
+            } else {
+                $instances[$recurId]['dtstart'] = $recurId;
+            }
+
+        }
+
+        $message = new Message();
+        $message->uid = $eventInfo['uid'];
+        $message->method = 'REPLY';
+        $message->component = 'VEVENT';
+        $message->sequence = $eventInfo['sequence'];
+        $message->sender = $attendee;
+        $message->senderName = $eventInfo['attendees'][$attendee]['name'];
+        $message->recipient = $eventInfo['organizer'];
+        $message->recipientName = $eventInfo['organizerName'];
+
+        $icalMsg = new VCalendar();
+        $icalMsg->METHOD = 'REPLY';
+
+        $hasReply = false;
+
+        foreach($instances as $instance) {
+
+            if ($instance['oldstatus']==$instance['newstatus'] && $eventInfo['organizerForceSend'] !== 'REPLY') {
+                // Skip
+                continue;
+            }
+
+            $event = $icalMsg->add('VEVENT', array(
+                'UID' => $message->uid,
+                'SEQUENCE' => $message->sequence,
+            ));
+            $summary = isset($calendar->VEVENT->SUMMARY)?$calendar->VEVENT->SUMMARY->getValue():'';
+            // Adding properties from the correct source instance
+            if (isset($eventInfo['instances'][$instance['id']])) {
+                $instanceObj = $eventInfo['instances'][$instance['id']];
+                $event->add(clone $instanceObj->DTSTART);
+                if (isset($instanceObj->SUMMARY)) {
+                    $event->add('SUMMARY', $instanceObj->SUMMARY->getValue());
+                } elseif ($summary) {
+                    $event->add('SUMMARY', $summary);
+                }
+            } else {
+                // This branch of the code is reached, when a reply is
+                // generated for an instance of a recurring event, through the
+                // fact that the instance has disappeared by showing up in
+                // EXDATE
+                $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']);
+                // Treat is as a DATE field
+                if (strlen($instance['id']) <= 8) {
+                    $recur = $event->add('DTSTART', $dt, array('VALUE' => 'DATE'));
+                } else {
+                    $recur = $event->add('DTSTART', $dt);
+                }
+                if ($summary) {
+                    $event->add('SUMMARY', $summary);
+                }
+            }
+            if ($instance['id'] !== 'master') {
+                $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']);
+                // Treat is as a DATE field
+                if (strlen($instance['id']) <= 8) {
+                    $recur = $event->add('RECURRENCE-ID', $dt, array('VALUE' => 'DATE'));
+                } else {
+                    $recur = $event->add('RECURRENCE-ID', $dt);
+                }
+            }
+            $organizer = $event->add('ORGANIZER', $message->recipient);
+            if ($message->recipientName) {
+                $organizer['CN'] = $message->recipientName;
+            }
+            $attendee = $event->add('ATTENDEE', $message->sender, array(
+                'PARTSTAT' => $instance['newstatus']
+            ));
+            if ($message->senderName) {
+                $attendee['CN'] = $message->senderName;
+            }
+            $hasReply = true;
+
+        }
+
+        if ($hasReply) {
+            $message->message = $icalMsg;
+            return array($message);
+        } else {
+            return array();
+        }
+
+    }
+
+    /**
+     * Returns attendee information and information about instances of an
+     * event.
+     *
+     * Returns an array with the following keys:
+     *
+     * 1. uid
+     * 2. organizer
+     * 3. organizerName
+     * 4. attendees
+     * 5. instances
+     *
+     * @param VCalendar $calendar
+     * @return array
+     */
+    protected function parseEventInfo(VCalendar $calendar = null) {
+
+        $uid = null;
+        $organizer = null;
+        $organizerName = null;
+        $organizerForceSend = null;
+        $sequence = null;
+        $timezone = null;
+        $status = null;
+        $organizerScheduleAgent = 'SERVER';
+
+        $significantChangeHash = '';
+
+        // Now we need to collect a list of attendees, and which instances they
+        // are a part of.
+        $attendees = array();
+
+        $instances = array();
+        $exdate = array();
+
+        foreach($calendar->VEVENT as $vevent) {
+
+            if (is_null($uid)) {
+                $uid = $vevent->UID->getValue();
+            } else {
+                if ($uid !== $vevent->UID->getValue()) {
+                    throw new ITipException('If a calendar contained more than one event, they must have the same UID.');
+                }
+            }
+
+            if (!isset($vevent->DTSTART)) {
+                throw new ITipException('An event MUST have a DTSTART property.');
+            }
+
+            if (isset($vevent->ORGANIZER)) {
+                if (is_null($organizer)) {
+                    $organizer = $vevent->ORGANIZER->getNormalizedValue();
+                    $organizerName = isset($vevent->ORGANIZER['CN'])?$vevent->ORGANIZER['CN']:null;
+                } else {
+                    if ($organizer !== $vevent->ORGANIZER->getNormalizedValue()) {
+                        throw new SameOrganizerForAllComponentsException('Every instance of the event must have the same organizer.');
+                    }
+                }
+                $organizerForceSend =
+                    isset($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) ?
+                    strtoupper($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) :
+                    null;
+                $organizerScheduleAgent =
+                    isset($vevent->ORGANIZER['SCHEDULE-AGENT']) ?
+                    strtoupper((string)$vevent->ORGANIZER['SCHEDULE-AGENT']) :
+                    'SERVER';
+            }
+            if (is_null($sequence) && isset($vevent->SEQUENCE)) {
+                $sequence = $vevent->SEQUENCE->getValue();
+            }
+            if (isset($vevent->EXDATE)) {
+                $exdate = $vevent->EXDATE->getParts();
+            }
+            if (isset($vevent->STATUS)) {
+                $status = strtoupper($vevent->STATUS->getValue());
+            }
+
+            $recurId = isset($vevent->{'RECURRENCE-ID'})?$vevent->{'RECURRENCE-ID'}->getValue():'master';
+            if ($recurId==='master') {
+                $timezone = $vevent->DTSTART->getDateTime()->getTimeZone();
+            }
+            if(isset($vevent->ATTENDEE)) {
+                foreach($vevent->ATTENDEE as $attendee) {
+
+                    if ($this->scheduleAgentServerRules &&
+                        isset($attendee['SCHEDULE-AGENT']) &&
+                        strtoupper($attendee['SCHEDULE-AGENT']->getValue()) === 'CLIENT'
+                    ) {
+                        continue;
+                    }
+                    $partStat =
+                        isset($attendee['PARTSTAT']) ?
+                        strtoupper($attendee['PARTSTAT']) :
+                        'NEEDS-ACTION';
+
+                    $forceSend =
+                        isset($attendee['SCHEDULE-FORCE-SEND']) ?
+                        strtoupper($attendee['SCHEDULE-FORCE-SEND']) :
+                        null;
+
+
+                    if (isset($attendees[$attendee->getNormalizedValue()])) {
+                        $attendees[$attendee->getNormalizedValue()]['instances'][$recurId] = array(
+                            'id' => $recurId,
+                            'partstat' => $partStat,
+                            'force-send' => $forceSend,
+                        );
+                    } else {
+                        $attendees[$attendee->getNormalizedValue()] = array(
+                            'href' => $attendee->getNormalizedValue(),
+                            'instances' => array(
+                                $recurId => array(
+                                    'id' => $recurId,
+                                    'partstat' => $partStat,
+                                ),
+                            ),
+                            'name' => isset($attendee['CN'])?(string)$attendee['CN']:null,
+                            'forceSend' => $forceSend,
+                        );
+                    }
+
+                }
+                $instances[$recurId] = $vevent;
+
+            }
+
+            foreach($this->significantChangeProperties as $prop) {
+                if (isset($vevent->$prop)) {
+                    $significantChangeHash.=$prop.':';
+                    foreach($vevent->select($prop) as $val) {
+                        $significantChangeHash.= $val->getValue().';';
+                    }
+                }
+            }
+
+        }
+        $significantChangeHash = md5($significantChangeHash);
+
+        return compact(
+            'uid',
+            'organizer',
+            'organizerName',
+            'organizerScheduleAgent',
+            'organizerForceSend',
+            'instances',
+            'attendees',
+            'sequence',
+            'exdate',
+            'timezone',
+            'significantChangeHash',
+            'status'
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/ITip/ITipException.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,15 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+use Exception;
+
+/**
+ * This message is emitted in case of serious problems with iTip messages.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ITipException extends Exception {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/ITip/Message.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,141 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+/**
+ * This class represents an iTip message
+ *
+ * A message holds all the information relevant to the message, including the
+ * object itself.
+ *
+ * It should for the most part be treated as immutable.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Message {
+
+    /**
+     * The object's UID
+     *
+     * @var string
+     */
+    public $uid;
+
+    /**
+     * The component type, such as VEVENT
+     *
+     * @var string
+     */
+    public $component;
+
+    /**
+     * Contains the ITip method, which is something like REQUEST, REPLY or
+     * CANCEL.
+     *
+     * @var string
+     */
+    public $method;
+
+    /**
+     * The current sequence number for the event.
+     *
+     * @var int
+     */
+    public $sequence;
+
+    /**
+     * The senders' email address.
+     *
+     * Note that this does not imply that this has to be used in a From: field
+     * if the message is sent by email. It may also be populated in Reply-To:
+     * or not at all.
+     *
+     * @var string
+     */
+    public $sender;
+
+    /**
+     * The name of the sender. This is often populated from a CN parameter from
+     * either the ORGANIZER or ATTENDEE, depending on the message.
+     *
+     * @var string|null
+     */
+    public $senderName;
+
+    /**
+     * The recipient's email address.
+     *
+     * @var string
+     */
+    public $recipient;
+
+    /**
+     * The name of the recipient. This is usually populated with the CN
+     * parameter from the ATTENDEE or ORGANIZER property, if it's available.
+     *
+     * @var string|null
+     */
+    public $recipientName;
+
+    /**
+     * After the message has been delivered, this should contain a string such
+     * as : 1.1;Sent or 1.2;Delivered.
+     *
+     * In case of a failure, this will hold the error status code.
+     *
+     * See:
+     * http://tools.ietf.org/html/rfc6638#section-7.3
+     *
+     * @var string
+     */
+    public $scheduleStatus;
+
+    /**
+     * The iCalendar / iTip body.
+     *
+     * @var \Sabre\VObject\Component\VCalendar
+     */
+    public $message;
+
+    /**
+     * This will be set to true, if the iTip broker considers the change
+     * 'significant'.
+     *
+     * In practice, this means that we'll only mark it true, if for instance
+     * DTSTART changed. This allows systems to only send iTip messages when
+     * significant changes happened. This is especially useful for iMip, as
+     * normally a ton of messages may be generated for normal calendar use.
+     *
+     * To see the list of properties that are considered 'significant', check
+     * out Sabre\VObject\ITip\Broker::$significantChangeProperties.
+     *
+     * @var bool
+     */
+    public $significantChange = true;
+
+    /**
+     * Returns the schedule status as a string.
+     *
+     * For example:
+     * 1.2
+     *
+     * @return mixed bool|string
+     */
+    public function getScheduleStatus() {
+
+        if(!$this->scheduleStatus) {
+
+            return false;
+
+        } else {
+
+            list($scheduleStatus) = explode(';', $this->scheduleStatus);
+            return $scheduleStatus;
+
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+/**
+ * SameOrganizerForAllComponentsException
+ *
+ * This exception is emitted when an event is encountered with more than one
+ * component (e.g.: exceptions), but the organizer is not identical in every
+ * component.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SameOrganizerForAllComponentsException extends ITipException {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Node.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,226 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * A node is the root class for every element in an iCalendar of vCard object.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable {
+
+    /**
+     * The following constants are used by the validate() method.
+     *
+     * If REPAIR is set, the validator will attempt to repair any broken data
+     * (if possible).
+     */
+    const REPAIR = 1;
+
+    /**
+     * If this option is set, the validator will operate on the vcards on the
+     * assumption that the vcards need to be valid for CardDAV.
+     *
+     * This means for example that the UID is required, whereas it is not for
+     * regular vcards.
+     */
+    const PROFILE_CARDDAV = 2;
+
+    /**
+     * If this option is set, the validator will operate on iCalendar objects
+     * on the assumption that the vcards need to be valid for CalDAV.
+     *
+     * This means for example that calendars can only contain objects with
+     * identical component types and UIDs.
+     */
+    const PROFILE_CALDAV = 4;
+
+    /**
+     * Reference to the parent object, if this is not the top object.
+     *
+     * @var Node
+     */
+    public $parent;
+
+    /**
+     * Iterator override
+     *
+     * @var ElementList
+     */
+    protected $iterator = null;
+
+    /**
+     * The root document
+     *
+     * @var Component
+     */
+    protected $root;
+
+    /**
+     * Serializes the node into a mimedir format
+     *
+     * @return string
+     */
+    abstract public function serialize();
+
+    /**
+     * This method returns an array, with the representation as it should be
+     * encoded in json. This is used to create jCard or jCal documents.
+     *
+     * @return array
+     */
+    abstract public function jsonSerialize();
+
+    /* {{{ IteratorAggregator interface */
+
+    /**
+     * Returns the iterator for this object
+     *
+     * @return ElementList
+     */
+    public function getIterator() {
+
+        if (!is_null($this->iterator))
+            return $this->iterator;
+
+        return new ElementList(array($this));
+
+    }
+
+    /**
+     * Sets the overridden iterator
+     *
+     * Note that this is not actually part of the iterator interface
+     *
+     * @param ElementList $iterator
+     * @return void
+     */
+    public function setIterator(ElementList $iterator) {
+
+        $this->iterator = $iterator;
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   Node::REPAIR - May attempt to automatically repair the problem.
+     *
+     * This method returns an array with detected problems.
+     * Every element has the following properties:
+     *
+     *  * level - problem level.
+     *  * message - A human-readable string describing the issue.
+     *  * node - A reference to the problematic node.
+     *
+     * The level means:
+     *   1 - The issue was repaired (only happens if REPAIR was turned on)
+     *   2 - An inconsequential issue
+     *   3 - A severe issue.
+     *
+     * @param int $options
+     * @return array
+     */
+    public function validate($options = 0) {
+
+        return array();
+
+    }
+
+    /* }}} */
+
+    /* {{{ 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);
+
+    // @codeCoverageIgnoreStart
+    //
+    // This method always throws an exception, so we ignore the closing
+    // brace
+    }
+    // @codeCoverageIgnoreEnd
+
+    /**
+     * 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);
+
+    // @codeCoverageIgnoreStart
+    //
+    // This method always throws an exception, so we ignore the closing
+    // brace
+    }
+    // @codeCoverageIgnoreEnd
+
+    /* }}} */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Parameter.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,373 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    ArrayObject;
+
+/**
+ * VObject Parameter
+ *
+ * This class represents a parameter. A parameter is always tied to a property.
+ * In the case of:
+ *   DTSTART;VALUE=DATE:20101108
+ * VALUE=DATE would be the parameter name and value.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Parameter extends Node {
+
+    /**
+     * Parameter name
+     *
+     * @var string
+     */
+    public $name;
+
+    /**
+     * vCard 2.1 allows parameters to be encoded without a name.
+     *
+     * We can deduce the parameter name based on it's value.
+     *
+     * @var bool
+     */
+    public $noName = false;
+
+    /**
+     * Parameter value
+     *
+     * @var string
+     */
+    protected $value;
+
+    /**
+     * Sets up the object.
+     *
+     * It's recommended to use the create:: factory method instead.
+     *
+     * @param string $name
+     * @param string $value
+     */
+    public function __construct(Document $root, $name, $value = null) {
+
+        $this->name = strtoupper($name);
+        $this->root = $root;
+        if (is_null($name)) {
+            $this->noName = true;
+            $this->name = static::guessParameterNameByValue($value);
+        }
+
+        // If guessParameterNameByValue() returns an empty string
+        // above, we're actually dealing with a parameter that has no value.
+        // In that case we have to move the value to the name.
+        if ($this->name === '') {
+            $this->noName = false;
+            $this->name = strtoupper($value);
+        } else {
+            $this->setValue($value);
+        }
+
+    }
+
+    /**
+     * Try to guess property name by value, can be used for vCard 2.1 nameless parameters.
+     *
+     * Figuring out what the name should have been. Note that a ton of
+     * these are rather silly in 2014 and would probably rarely be
+     * used, but we like to be complete.
+     *
+     * @param string $value
+     * @return string
+     */
+    public static function guessParameterNameByValue($value) {
+        switch(strtoupper($value)) {
+
+            // Encodings
+            case '7-BIT' :
+            case 'QUOTED-PRINTABLE' :
+            case 'BASE64' :
+                $name = 'ENCODING';
+                break;
+
+            // Common types
+            case 'WORK' :
+            case 'HOME' :
+            case 'PREF' :
+
+                // Delivery Label Type
+            case 'DOM' :
+            case 'INTL' :
+            case 'POSTAL' :
+            case 'PARCEL' :
+
+                // Telephone types
+            case 'VOICE' :
+            case 'FAX' :
+            case 'MSG' :
+            case 'CELL' :
+            case 'PAGER' :
+            case 'BBS' :
+            case 'MODEM' :
+            case 'CAR' :
+            case 'ISDN' :
+            case 'VIDEO' :
+
+                // EMAIL types (lol)
+            case 'AOL' :
+            case 'APPLELINK' :
+            case 'ATTMAIL' :
+            case 'CIS' :
+            case 'EWORLD' :
+            case 'INTERNET' :
+            case 'IBMMAIL' :
+            case 'MCIMAIL' :
+            case 'POWERSHARE' :
+            case 'PRODIGY' :
+            case 'TLX' :
+            case 'X400' :
+
+                // Photo / Logo format types
+            case 'GIF' :
+            case 'CGM' :
+            case 'WMF' :
+            case 'BMP' :
+            case 'DIB' :
+            case 'PICT' :
+            case 'TIFF' :
+            case 'PDF ':
+            case 'PS' :
+            case 'JPEG' :
+            case 'MPEG' :
+            case 'MPEG2' :
+            case 'AVI' :
+            case 'QTIME' :
+
+                // Sound Digital Audio Type
+            case 'WAVE' :
+            case 'PCM' :
+            case 'AIFF' :
+
+                // Key types
+            case 'X509' :
+            case 'PGP' :
+                $name = 'TYPE';
+                break;
+
+            // Value types
+            case 'INLINE' :
+            case 'URL' :
+            case 'CONTENT-ID' :
+            case 'CID' :
+                $name = 'VALUE';
+                break;
+
+            default:
+                $name = '';
+        }
+
+        return $name;
+    }
+
+    /**
+     * Updates the current value.
+     *
+     * This may be either a single, or multiple strings in an array.
+     *
+     * @param string|array $value
+     * @return void
+     */
+    public function setValue($value) {
+
+        $this->value = $value;
+
+    }
+
+    /**
+     * Returns the current value
+     *
+     * This method will always return a string, or null. If there were multiple
+     * values, it will automatically concatinate them (separated by comma).
+     *
+     * @return string|null
+     */
+    public function getValue() {
+
+        if (is_array($this->value)) {
+            return implode(',' , $this->value);
+        } else {
+            return $this->value;
+        }
+
+    }
+
+    /**
+     * Sets multiple values for this parameter.
+     *
+     * @param array $value
+     * @return void
+     */
+    public function setParts(array $value) {
+
+        $this->value = $value;
+
+    }
+
+    /**
+     * Returns all values for this parameter.
+     *
+     * If there were no values, an empty array will be returned.
+     *
+     * @return array
+     */
+    public function getParts() {
+
+        if (is_array($this->value)) {
+            return $this->value;
+        } elseif (is_null($this->value)) {
+            return array();
+        } else {
+            return array($this->value);
+        }
+
+    }
+
+    /**
+     * Adds a value to this parameter
+     *
+     * If the argument is specified as an array, all items will be added to the
+     * parameter value list.
+     *
+     * @param string|array $part
+     * @return void
+     */
+    public function addValue($part) {
+
+        if (is_null($this->value)) {
+            $this->value = $part;
+        } else {
+            $this->value = array_merge((array)$this->value, (array)$part);
+        }
+
+    }
+
+    /**
+     * Checks if this parameter contains the specified value.
+     *
+     * This is a case-insensitive match. It makes sense to call this for for
+     * instance the TYPE parameter, to see if it contains a keyword such as
+     * 'WORK' or 'FAX'.
+     *
+     * @param string $value
+     * @return bool
+     */
+    public function has($value) {
+
+        return in_array(
+            strtolower($value),
+            array_map('strtolower', (array)$this->value)
+        );
+
+    }
+
+    /**
+     * Turns the object back into a serialized blob.
+     *
+     * @return string
+     */
+    public function serialize() {
+
+        $value = $this->getParts();
+
+        if (count($value)===0) {
+            return $this->name . '=';
+        }
+
+        if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) {
+
+            return implode(';', $value);
+
+        }
+
+        return $this->name . '=' . array_reduce(
+            $value,
+            function($out, $item) {
+
+                if (!is_null($out)) $out.=',';
+
+                // If there's no special characters in the string, we'll use the simple
+                // format.
+                //
+                // The list of special characters is defined as:
+                //
+                // Any character except CONTROL, DQUOTE, ";", ":", ","
+                //
+                // by the iCalendar spec:
+                // https://tools.ietf.org/html/rfc5545#section-3.1
+                //
+                // And we add ^ to that because of:
+                // https://tools.ietf.org/html/rfc6868
+                //
+                // But we've found that iCal (7.0, shipped with OSX 10.9)
+                // severaly trips on + characters not being quoted, so we
+                // added + as well.
+                if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) {
+                    return $out.$item;
+                } else {
+                    // Enclosing in double-quotes, and using RFC6868 for encoding any
+                    // special characters
+                    $out.='"' . strtr(
+                        $item,
+                        array(
+                            '^'  => '^^',
+                            "\n" => '^n',
+                            '"'  => '^\'',
+                        )
+                    ) . '"';
+                    return $out;
+                }
+
+            }
+        );
+
+    }
+
+    /**
+     * This method returns an array, with the representation as it should be
+     * encoded in json. This is used to create jCard or jCal documents.
+     *
+     * @return array
+     */
+    public function jsonSerialize() {
+
+        return $this->value;
+
+    }
+
+    /**
+     * Called when this object is being cast to a string
+     *
+     * @return string
+     */
+    public function __toString() {
+
+        return (string)$this->getValue();
+
+    }
+
+    /**
+     * Returns the iterator for this object
+     *
+     * @return ElementList
+     */
+    public function getIterator() {
+
+        if (!is_null($this->iterator))
+            return $this->iterator;
+
+        return $this->iterator = new ArrayObject((array)$this->value);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/ParseException.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,13 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Exception thrown by Reader if an invalid object was attempted to be parsed.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ParseException extends \Exception {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Parser/Json.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,194 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+use
+    Sabre\VObject\Component\VCalendar,
+    Sabre\VObject\Component\VCard,
+    Sabre\VObject\ParseException,
+    Sabre\VObject\EofException;
+
+/**
+ * Json Parser.
+ *
+ * This parser parses both the jCal and jCard formats.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Json extends Parser {
+
+    /**
+     * The input data
+     *
+     * @var array
+     */
+    protected $input;
+
+    /**
+     * Root component
+     *
+     * @var Document
+     */
+    protected $root;
+
+    /**
+     * This method starts the parsing process.
+     *
+     * If the input was not supplied during construction, it's possible to pass
+     * it here instead.
+     *
+     * If either input or options are not supplied, the defaults will be used.
+     *
+     * @param resource|string|array|null $input
+     * @param int|null $options
+     * @return array
+     */
+    public function parse($input = null, $options = null) {
+
+        if (!is_null($input)) {
+            $this->setInput($input);
+        }
+        if (is_null($this->input)) {
+            throw new EofException('End of input stream, or no input supplied');
+        }
+
+        if (!is_null($options)) {
+            $this->options = $options;
+        }
+
+        switch($this->input[0]) {
+            case 'vcalendar' :
+                $this->root = new VCalendar(array(), false);
+                break;
+            case 'vcard' :
+                $this->root = new VCard(array(), false);
+                break;
+            default :
+                throw new ParseException('The root component must either be a vcalendar, or a vcard');
+
+        }
+        foreach($this->input[1] as $prop) {
+            $this->root->add($this->parseProperty($prop));
+        }
+        if (isset($this->input[2])) foreach($this->input[2] as $comp) {
+            $this->root->add($this->parseComponent($comp));
+        }
+
+        // Resetting the input so we can throw an feof exception the next time.
+        $this->input = null;
+
+        return $this->root;
+
+    }
+
+    /**
+     * Parses a component
+     *
+     * @param array $jComp
+     * @return \Sabre\VObject\Component
+     */
+    public function parseComponent(array $jComp) {
+
+        // We can remove $self from PHP 5.4 onward.
+        $self = $this;
+
+        $properties = array_map(
+            function($jProp) use ($self) {
+                return $self->parseProperty($jProp);
+            },
+            $jComp[1]
+        );
+
+        if (isset($jComp[2])) {
+
+            $components = array_map(
+                function($jComp) use ($self) {
+                    return $self->parseComponent($jComp);
+                },
+                $jComp[2]
+            );
+
+        } else $components = array();
+
+        return $this->root->createComponent(
+            $jComp[0],
+            array_merge($properties, $components),
+            $defaults = false
+        );
+
+    }
+
+    /**
+     * Parses properties.
+     *
+     * @param array $jProp
+     * @return \Sabre\VObject\Property
+     */
+    public function parseProperty(array $jProp) {
+
+        list(
+            $propertyName,
+            $parameters,
+            $valueType
+        ) = $jProp;
+
+        $propertyName = strtoupper($propertyName);
+
+        // This is the default class we would be using if we didn't know the
+        // value type. We're using this value later in this function.
+        $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
+
+        $parameters = (array)$parameters;
+
+        $value = array_slice($jProp, 3);
+
+        $valueType = strtoupper($valueType);
+
+        if (isset($parameters['group'])) {
+            $propertyName = $parameters['group'] . '.' . $propertyName;
+            unset($parameters['group']);
+        }
+
+        $prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
+        $prop->setJsonValue($value);
+
+        // We have to do something awkward here. FlatText as well as Text
+        // represents TEXT values. We have to normalize these here. In the
+        // future we can get rid of FlatText once we're allowed to break BC
+        // again.
+        if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') {
+            $defaultPropertyClass = 'Sabre\VObject\Property\Text';
+        }
+
+        // If the value type we received (e.g.: TEXT) was not the default value
+        // type for the given property (e.g.: BDAY), we need to add a VALUE=
+        // parameter.
+        if ($defaultPropertyClass !== get_class($prop)) {
+            $prop["VALUE"] = $valueType;
+        }
+
+        return $prop;
+
+    }
+
+    /**
+     * Sets the input data
+     *
+     * @param resource|string|array $input
+     * @return void
+     */
+    public function setInput($input) {
+
+        if (is_resource($input)) {
+            $input = stream_get_contents($input);
+        }
+        if (is_string($input)) {
+            $input = json_decode($input);
+        }
+        $this->input = $input;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Parser/MimeDir.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,628 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+use
+    Sabre\VObject\ParseException,
+    Sabre\VObject\EofException,
+    Sabre\VObject\Component,
+    Sabre\VObject\Property,
+    Sabre\VObject\Component\VCalendar,
+    Sabre\VObject\Component\VCard;
+
+/**
+ * MimeDir parser.
+ *
+ * This class parses iCalendar 2.0 and vCard 2.1, 3.0 and 4.0 files. This
+ * parser will return one of the following two objects from the parse method:
+ *
+ * Sabre\VObject\Component\VCalendar
+ * Sabre\VObject\Component\VCard
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class MimeDir extends Parser {
+
+    /**
+     * The input stream.
+     *
+     * @var resource
+     */
+    protected $input;
+
+    /**
+     * Root component
+     *
+     * @var Component
+     */
+    protected $root;
+
+    /**
+     * Parses an iCalendar or vCard file
+     *
+     * Pass a stream or a string. If null is parsed, the existing buffer is
+     * used.
+     *
+     * @param string|resource|null $input
+     * @param int|null $options
+     * @return array
+     */
+    public function parse($input = null, $options = null) {
+
+        $this->root = null;
+        if (!is_null($input)) {
+
+            $this->setInput($input);
+
+        }
+
+        if (!is_null($options)) $this->options = $options;
+
+        $this->parseDocument();
+
+        return $this->root;
+
+    }
+
+    /**
+     * Sets the input buffer. Must be a string or stream.
+     *
+     * @param resource|string $input
+     * @return void
+     */
+    public function setInput($input) {
+
+        // Resetting the parser
+        $this->lineIndex = 0;
+        $this->startLine = 0;
+
+        if (is_string($input)) {
+            // Convering to a stream.
+            $stream = fopen('php://temp', 'r+');
+            fwrite($stream, $input);
+            rewind($stream);
+            $this->input = $stream;
+        } elseif (is_resource($input)) {
+            $this->input = $input;
+        } else {
+            throw new \InvalidArgumentException('This parser can only read from strings or streams.');
+        }
+
+    }
+
+    /**
+     * Parses an entire document.
+     *
+     * @return void
+     */
+    protected function parseDocument() {
+
+        $line = $this->readLine();
+
+        // BOM is ZERO WIDTH NO-BREAK SPACE (U+FEFF).
+        // It's 0xEF 0xBB 0xBF in UTF-8 hex.
+        if (   3 <= strlen($line)
+            && ord($line[0]) === 0xef
+            && ord($line[1]) === 0xbb
+            && ord($line[2]) === 0xbf) {
+            $line = substr($line, 3);
+        }
+
+        switch(strtoupper($line)) {
+            case 'BEGIN:VCALENDAR' :
+                $class = isset(VCalendar::$componentMap['VCALENDAR'])
+                    ? VCalendar::$componentMap[$name]
+                    : 'Sabre\\VObject\\Component\\VCalendar';
+                break;
+            case 'BEGIN:VCARD' :
+                $class = isset(VCard::$componentMap['VCARD'])
+                    ? VCard::$componentMap['VCARD']
+                    : 'Sabre\\VObject\\Component\\VCard';
+                break;
+            default :
+                throw new ParseException('This parser only supports VCARD and VCALENDAR files');
+        }
+
+        $this->root = new $class(array(), false);
+
+        while(true) {
+
+            // Reading until we hit END:
+            $line = $this->readLine();
+            if (strtoupper(substr($line,0,4)) === 'END:') {
+                break;
+            }
+            $result = $this->parseLine($line);
+            if ($result) {
+                $this->root->add($result);
+            }
+
+        }
+
+        $name = strtoupper(substr($line, 4));
+        if ($name!==$this->root->name) {
+            throw new ParseException('Invalid MimeDir file. expected: "END:' . $this->root->name . '" got: "END:' . $name . '"');
+        }
+
+    }
+
+    /**
+     * Parses a line, and if it hits a component, it will also attempt to parse
+     * the entire component
+     *
+     * @param string $line Unfolded line
+     * @return Node
+     */
+    protected function parseLine($line) {
+
+        // Start of a new component
+        if (strtoupper(substr($line, 0, 6)) === 'BEGIN:') {
+
+            $component = $this->root->createComponent(substr($line,6), array(), false);
+
+            while(true) {
+
+                // Reading until we hit END:
+                $line = $this->readLine();
+                if (strtoupper(substr($line,0,4)) === 'END:') {
+                    break;
+                }
+                $result = $this->parseLine($line);
+                if ($result) {
+                    $component->add($result);
+                }
+
+            }
+
+            $name = strtoupper(substr($line, 4));
+            if ($name!==$component->name) {
+                throw new ParseException('Invalid MimeDir file. expected: "END:' . $component->name . '" got: "END:' . $name . '"');
+            }
+
+            return $component;
+
+        } else {
+
+            // Property reader
+            $property = $this->readProperty($line);
+            if (!$property) {
+                // Ignored line
+                return false;
+            }
+            return $property;
+
+        }
+
+    }
+
+    /**
+     * We need to look ahead 1 line every time to see if we need to 'unfold'
+     * the next line.
+     *
+     * If that was not the case, we store it here.
+     *
+     * @var null|string
+     */
+    protected $lineBuffer;
+
+    /**
+     * The real current line number.
+     */
+    protected $lineIndex = 0;
+
+    /**
+     * In the case of unfolded lines, this property holds the line number for
+     * the start of the line.
+     *
+     * @var int
+     */
+    protected $startLine = 0;
+
+    /**
+     * Contains a 'raw' representation of the current line.
+     *
+     * @var string
+     */
+    protected $rawLine;
+
+    /**
+     * Reads a single line from the buffer.
+     *
+     * This method strips any newlines and also takes care of unfolding.
+     *
+     * @throws \Sabre\VObject\EofException
+     * @return string
+     */
+    protected function readLine() {
+
+        if (!is_null($this->lineBuffer)) {
+            $rawLine = $this->lineBuffer;
+            $this->lineBuffer = null;
+        } else {
+            do {
+                $eof = feof($this->input);
+
+                $rawLine = fgets($this->input);
+
+                if ($eof || (feof($this->input) && $rawLine===false)) {
+                    throw new EofException('End of document reached prematurely');
+                }
+                if ($rawLine === false) {
+                    throw new ParseException('Error reading from input stream');
+                }
+                $rawLine = rtrim($rawLine, "\r\n");
+            } while ($rawLine === ''); // Skipping empty lines
+            $this->lineIndex++;
+        }
+        $line = $rawLine;
+
+        $this->startLine = $this->lineIndex;
+
+        // Looking ahead for folded lines.
+        while (true) {
+
+            $nextLine = rtrim(fgets($this->input), "\r\n");
+            $this->lineIndex++;
+            if (!$nextLine) {
+                break;
+            }
+            if ($nextLine[0] === "\t" || $nextLine[0] === " ") {
+                $line .= substr($nextLine, 1);
+                $rawLine .= "\n " . substr($nextLine, 1);
+            } else {
+                $this->lineBuffer = $nextLine;
+                break;
+            }
+
+        }
+        $this->rawLine = $rawLine;
+        return $line;
+
+    }
+
+    /**
+     * Reads a property or component from a line.
+     *
+     * @return void
+     */
+    protected function readProperty($line) {
+
+        if ($this->options & self::OPTION_FORGIVING) {
+            $propNameToken = 'A-Z0-9\-\._\\/';
+        } else {
+            $propNameToken = 'A-Z0-9\-\.';
+        }
+
+        $paramNameToken = 'A-Z0-9\-';
+        $safeChar = '^";:,';
+        $qSafeChar = '^"';
+
+        $regex = "/
+            ^(?P<name> [$propNameToken]+ ) (?=[;:])        # property name
+            |
+            (?<=:)(?P<propValue> .+)$                      # property value
+            |
+            ;(?P<paramName> [$paramNameToken]+) (?=[=;:])  # parameter name
+            |
+            (=|,)(?P<paramValue>                           # parameter value
+                (?: [$safeChar]*) |
+                \"(?: [$qSafeChar]+)\"
+            ) (?=[;:,])
+            /xi";
+
+        //echo $regex, "\n"; die();
+        preg_match_all($regex, $line, $matches,  PREG_SET_ORDER);
+
+        $property = array(
+            'name' => null,
+            'parameters' => array(),
+            'value' => null
+        );
+
+        $lastParam = null;
+
+        /**
+         * Looping through all the tokens.
+         *
+         * Note that we are looping through them in reverse order, because if a
+         * sub-pattern matched, the subsequent named patterns will not show up
+         * in the result.
+         */
+        foreach($matches as $match) {
+
+            if (isset($match['paramValue'])) {
+                if ($match['paramValue'] && $match['paramValue'][0] === '"') {
+                    $value = substr($match['paramValue'], 1, -1);
+                } else {
+                    $value = $match['paramValue'];
+                }
+
+                $value = $this->unescapeParam($value);
+
+                if (is_null($property['parameters'][$lastParam])) {
+                    $property['parameters'][$lastParam] = $value;
+                } elseif (is_array($property['parameters'][$lastParam])) {
+                    $property['parameters'][$lastParam][] = $value;
+                } else {
+                    $property['parameters'][$lastParam] = array(
+                        $property['parameters'][$lastParam],
+                        $value
+                    );
+                }
+                continue;
+            }
+            if (isset($match['paramName'])) {
+                $lastParam = strtoupper($match['paramName']);
+                if (!isset($property['parameters'][$lastParam])) {
+                    $property['parameters'][$lastParam] = null;
+                }
+                continue;
+            }
+            if (isset($match['propValue'])) {
+                $property['value'] = $match['propValue'];
+                continue;
+            }
+            if (isset($match['name']) && $match['name']) {
+                $property['name'] = strtoupper($match['name']);
+                continue;
+            }
+
+            // @codeCoverageIgnoreStart
+            throw new \LogicException('This code should not be reachable');
+            // @codeCoverageIgnoreEnd
+
+        }
+
+        if (is_null($property['value'])) {
+            $property['value'] = '';
+        }
+        if (!$property['name']) {
+            if ($this->options & self::OPTION_IGNORE_INVALID_LINES) {
+                return false;
+            }
+            throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
+        }
+
+        // vCard 2.1 states that parameters may appear without a name, and only
+        // a value. We can deduce the value based on it's name.
+        //
+        // Our parser will get those as parameters without a value instead, so
+        // we're filtering these parameters out first.
+        $namedParameters = array();
+        $namelessParameters = array();
+
+        foreach($property['parameters'] as $name=>$value) {
+            if (!is_null($value)) {
+                $namedParameters[$name] = $value;
+            } else {
+                $namelessParameters[] = $name;
+            }
+        }
+
+        $propObj = $this->root->createProperty($property['name'], null, $namedParameters);
+
+        foreach($namelessParameters as $namelessParameter) {
+            $propObj->add(null, $namelessParameter);
+        }
+
+        if (strtoupper($propObj['ENCODING']) === 'QUOTED-PRINTABLE') {
+            $propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue());
+        } else {
+            $propObj->setRawMimeDirValue($property['value']);
+        }
+
+        return $propObj;
+
+    }
+
+    /**
+     * Unescapes a property value.
+     *
+     * vCard 2.1 says:
+     *   * Semi-colons must be escaped in some property values, specifically
+     *     ADR, ORG and N.
+     *   * Semi-colons must be escaped in parameter values, because semi-colons
+     *     are also use to separate values.
+     *   * No mention of escaping backslashes with another backslash.
+     *   * newlines are not escaped either, instead QUOTED-PRINTABLE is used to
+     *     span values over more than 1 line.
+     *
+     * vCard 3.0 says:
+     *   * (rfc2425) Backslashes, newlines (\n or \N) and comma's must be
+     *     escaped, all time time.
+     *   * Comma's are used for delimeters in multiple values
+     *   * (rfc2426) Adds to to this that the semi-colon MUST also be escaped,
+     *     as in some properties semi-colon is used for separators.
+     *   * Properties using semi-colons: N, ADR, GEO, ORG
+     *   * Both ADR and N's individual parts may be broken up further with a
+     *     comma.
+     *   * Properties using commas: NICKNAME, CATEGORIES
+     *
+     * vCard 4.0 (rfc6350) says:
+     *   * Commas must be escaped.
+     *   * Semi-colons may be escaped, an unescaped semi-colon _may_ be a
+     *     delimiter, depending on the property.
+     *   * Backslashes must be escaped
+     *   * Newlines must be escaped as either \N or \n.
+     *   * Some compound properties may contain multiple parts themselves, so a
+     *     comma within a semi-colon delimited property may also be unescaped
+     *     to denote multiple parts _within_ the compound property.
+     *   * Text-properties using semi-colons: N, ADR, ORG, CLIENTPIDMAP.
+     *   * Text-properties using commas: NICKNAME, RELATED, CATEGORIES, PID.
+     *
+     * Even though the spec says that commas must always be escaped, the
+     * example for GEO in Section 6.5.2 seems to violate this.
+     *
+     * iCalendar 2.0 (rfc5545) says:
+     *   * Commas or semi-colons may be used as delimiters, depending on the
+     *     property.
+     *   * Commas, semi-colons, backslashes, newline (\N or \n) are always
+     *     escaped, unless they are delimiters.
+     *   * Colons shall not be escaped.
+     *   * Commas can be considered the 'default delimiter' and is described as
+     *     the delimiter in cases where the order of the multiple values is
+     *     insignificant.
+     *   * Semi-colons are described as the delimiter for 'structured values'.
+     *     They are specifically used in Semi-colons are used as a delimiter in
+     *     REQUEST-STATUS, RRULE, GEO and EXRULE. EXRULE is deprecated however.
+     *
+     * Now for the parameters
+     *
+     * If delimiter is not set (null) this method will just return a string.
+     * If it's a comma or a semi-colon the string will be split on those
+     * characters, and always return an array.
+     *
+     * @param string $input
+     * @param string $delimiter
+     * @return string|string[]
+     */
+    static public function unescapeValue($input, $delimiter = ';') {
+
+        $regex = '#  (?: (\\\\ (?: \\\\ | N | n | ; | , ) )';
+        if ($delimiter) {
+            $regex .= ' | (' . $delimiter . ')';
+        }
+        $regex .= ') #x';
+
+        $matches = preg_split($regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+
+        $resultArray = array();
+        $result = '';
+
+        foreach($matches as $match) {
+
+            switch ($match) {
+                case '\\\\' :
+                    $result .='\\';
+                    break;
+                case '\N' :
+                case '\n' :
+                    $result .="\n";
+                    break;
+                case '\;' :
+                    $result .=';';
+                    break;
+                case '\,' :
+                    $result .=',';
+                    break;
+                case $delimiter :
+                    $resultArray[] = $result;
+                    $result = '';
+                    break;
+                default :
+                    $result .= $match;
+                    break;
+
+            }
+
+        }
+
+        $resultArray[] = $result;
+        return $delimiter ? $resultArray : $result;
+
+    }
+
+    /**
+     * Unescapes a parameter value.
+     *
+     * vCard 2.1:
+     *   * Does not mention a mechanism for this. In addition, double quotes
+     *     are never used to wrap values.
+     *   * This means that parameters can simply not contain colons or
+     *     semi-colons.
+     *
+     * vCard 3.0 (rfc2425, rfc2426):
+     *   * Parameters _may_ be surrounded by double quotes.
+     *   * If this is not the case, semi-colon, colon and comma may simply not
+     *     occur (the comma used for multiple parameter values though).
+     *   * If it is surrounded by double-quotes, it may simply not contain
+     *     double-quotes.
+     *   * This means that a parameter can in no case encode double-quotes, or
+     *     newlines.
+     *
+     * vCard 4.0 (rfc6350)
+     *   * Behavior seems to be identical to vCard 3.0
+     *
+     * iCalendar 2.0 (rfc5545)
+     *   * Behavior seems to be identical to vCard 3.0
+     *
+     * Parameter escaping mechanism (rfc6868) :
+     *   * This rfc describes a new way to escape parameter values.
+     *   * New-line is encoded as ^n
+     *   * ^ is encoded as ^^.
+     *   * " is encoded as ^'
+     *
+     * @param string $input
+     * @return void
+     */
+    private function unescapeParam($input) {
+
+        return
+            preg_replace_callback(
+                '#(\^(\^|n|\'))#',
+                function($matches) {
+                    switch($matches[2]) {
+                        case 'n' :
+                            return "\n";
+                        case '^' :
+                            return '^';
+                        case '\'' :
+                            return '"';
+
+                    // @codeCoverageIgnoreStart
+                    }
+                    // @codeCoverageIgnoreEnd
+                },
+                $input
+            );
+    }
+
+    /**
+     * Gets the full quoted printable value.
+     *
+     * We need a special method for this, because newlines have both a meaning
+     * in vCards, and in QuotedPrintable.
+     *
+     * This method does not do any decoding.
+     *
+     * @return string
+     */
+    private function extractQuotedPrintableValue() {
+
+        // We need to parse the raw line again to get the start of the value.
+        //
+        // We are basically looking for the first colon (:), but we need to
+        // skip over the parameters first, as they may contain one.
+        $regex = '/^
+            (?: [^:])+ # Anything but a colon
+            (?: "[^"]")* # A parameter in double quotes
+            : # start of the value we really care about
+            (.*)$
+        /xs';
+
+        preg_match($regex, $this->rawLine, $matches);
+
+        $value = $matches[1];
+        // Removing the first whitespace character from every line. Kind of
+        // like unfolding, but we keep the newline.
+        $value = str_replace("\n ", "\n", $value);
+
+        // Microsoft products don't always correctly fold lines, they may be
+        // missing a whitespace. So if 'forgiving' is turned on, we will take
+        // those as well.
+        if ($this->options & self::OPTION_FORGIVING) {
+            while(substr($value,-1) === '=') {
+                // Reading the line
+                $this->readLine();
+                // Grabbing the raw form
+                $value.="\n" . $this->rawLine;
+            }
+        }
+
+        return $value;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Parser/Parser.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,77 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+/**
+ * Abstract parser.
+ *
+ * This class serves as a base-class for the different parsers.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Parser {
+
+    /**
+     * Turning on this option makes the parser more forgiving.
+     *
+     * In the case of the MimeDir parser, this means that the parser will
+     * accept slashes and underscores in property names, and it will also
+     * attempt to fix Microsoft vCard 2.1's broken line folding.
+     */
+    const OPTION_FORGIVING = 1;
+
+    /**
+     * If this option is turned on, any lines we cannot parse will be ignored
+     * by the reader.
+     */
+    const OPTION_IGNORE_INVALID_LINES = 2;
+
+    /**
+     * Bitmask of parser options
+     *
+     * @var int
+     */
+    protected $options;
+
+    /**
+     * Creates the parser.
+     *
+     * Optionally, it's possible to parse the input stream here.
+     *
+     * @param mixed $input
+     * @param int $options Any parser options (OPTION constants).
+     * @return void
+     */
+    public function __construct($input = null, $options = 0) {
+
+        if (!is_null($input)) {
+            $this->setInput($input);
+        }
+        $this->options = $options;
+    }
+
+    /**
+     * This method starts the parsing process.
+     *
+     * If the input was not supplied during construction, it's possible to pass
+     * it here instead.
+     *
+     * If either input or options are not supplied, the defaults will be used.
+     *
+     * @param mixed $input
+     * @param int|null $options
+     * @return array
+     */
+    abstract public function parse($input = null, $options = null);
+
+    /**
+     * Sets the input data
+     *
+     * @param mixed $input
+     * @return void
+     */
+    abstract public function setInput($input);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,518 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Property
+ *
+ * A property is always in a KEY:VALUE structure, and may optionally contain
+ * parameters.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Property extends Node {
+
+    /**
+     * Property name.
+     *
+     * This will contain a string such as DTSTART, SUMMARY, FN.
+     *
+     * @var string
+     */
+    public $name;
+
+    /**
+     * Property group.
+     *
+     * This is only used in vcards
+     *
+     * @var string
+     */
+    public $group;
+
+    /**
+     * List of parameters
+     *
+     * @var array
+     */
+    public $parameters = array();
+
+    /**
+     * Current value
+     *
+     * @var mixed
+     */
+    protected $value;
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = ';';
+
+    /**
+     * Creates the generic property.
+     *
+     * Parameters must be specified in key=>value syntax.
+     *
+     * @param Component $root The root document
+     * @param string $name
+     * @param string|array|null $value
+     * @param array $parameters List of parameters
+     * @param string $group The vcard property group
+     * @return void
+     */
+    public function __construct(Component $root, $name, $value = null, array $parameters = array(), $group = null) {
+
+        $this->name = $name;
+        $this->group = $group;
+
+        $this->root = $root;
+
+        foreach($parameters as $k=>$v) {
+            $this->add($k, $v);
+        }
+
+        if (!is_null($value)) {
+            $this->setValue($value);
+        }
+
+    }
+
+    /**
+     * Updates the current value.
+     *
+     * This may be either a single, or multiple strings in an array.
+     *
+     * @param string|array $value
+     * @return void
+     */
+    public function setValue($value) {
+
+        $this->value = $value;
+
+    }
+
+    /**
+     * Returns the current value.
+     *
+     * This method will always return a singular value. If this was a
+     * multi-value object, some decision will be made first on how to represent
+     * it as a string.
+     *
+     * To get the correct multi-value version, use getParts.
+     *
+     * @return string
+     */
+    public function getValue() {
+
+        if (is_array($this->value)) {
+            if (count($this->value)==0) {
+                return null;
+            } elseif (count($this->value)===1) {
+                return $this->value[0];
+            } else {
+                return $this->getRawMimeDirValue($this->value);
+            }
+        } else {
+            return $this->value;
+        }
+
+    }
+
+    /**
+     * Sets a multi-valued property.
+     *
+     * @param array $parts
+     * @return void
+     */
+    public function setParts(array $parts) {
+
+        $this->value = $parts;
+
+    }
+
+    /**
+     * Returns a multi-valued property.
+     *
+     * This method always returns an array, if there was only a single value,
+     * it will still be wrapped in an array.
+     *
+     * @return array
+     */
+    public function getParts() {
+
+        if (is_null($this->value)) {
+            return array();
+        } elseif (is_array($this->value)) {
+            return $this->value;
+        } else {
+            return array($this->value);
+        }
+
+    }
+
+    /**
+     * Adds a new parameter, and returns the new item.
+     *
+     * If a parameter with same name already existed, the values will be
+     * combined.
+     * If nameless parameter is added, we try to guess it's name.
+     *
+     * @param string $name
+     * @param string|null|array $value
+     * @return Node
+     */
+    public function add($name, $value = null) {
+        $noName = false;
+        if ($name === null) {
+            $name = Parameter::guessParameterNameByValue($value);
+            $noName = true;
+        }
+
+        if (isset($this->parameters[strtoupper($name)])) {
+            $this->parameters[strtoupper($name)]->addValue($value);
+        }
+        else {
+            $param = new Parameter($this->root, $name, $value);
+            $param->noName = $noName;
+            $this->parameters[$param->name] = $param;
+        }
+    }
+
+    /**
+     * Returns an iterable list of children
+     *
+     * @return array
+     */
+    public function parameters() {
+
+        return $this->parameters;
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    abstract public function getValueType();
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    abstract public function setRawMimeDirValue($val);
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    abstract public function getRawMimeDirValue();
+
+    /**
+     * Turns the object back into a serialized blob.
+     *
+     * @return string
+     */
+    public function serialize() {
+
+        $str = $this->name;
+        if ($this->group) $str = $this->group . '.' . $this->name;
+
+        foreach($this->parameters as $param) {
+
+            $str.=';' . $param->serialize();
+
+        }
+
+        $str.=':' . $this->getRawMimeDirValue();
+
+        $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;
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        return $this->getParts();
+
+    }
+
+    /**
+     * Sets the json value, as it would appear in a jCard or jCal object.
+     *
+     * The value must always be an array.
+     *
+     * @param array $value
+     * @return void
+     */
+    public function setJsonValue(array $value) {
+
+        if (count($value)===1) {
+            $this->setValue(reset($value));
+        } else {
+            $this->setValue($value);
+        }
+
+    }
+
+    /**
+     * This method returns an array, with the representation as it should be
+     * encoded in json. This is used to create jCard or jCal documents.
+     *
+     * @return array
+     */
+    public function jsonSerialize() {
+
+        $parameters = array();
+
+        foreach($this->parameters as $parameter) {
+            if ($parameter->name === 'VALUE') {
+                continue;
+            }
+            $parameters[strtolower($parameter->name)] = $parameter->jsonSerialize();
+        }
+        // In jCard, we need to encode the property-group as a separate 'group'
+        // parameter.
+        if ($this->group) {
+            $parameters['group'] = $this->group;
+        }
+
+        return array_merge(
+            array(
+                strtolower($this->name),
+                (object)$parameters,
+                strtolower($this->getValueType()),
+            ),
+            $this->getJsonValue()
+        );
+    }
+
+
+    /**
+     * Called when this object is being cast to a string.
+     *
+     * If the property only had a single value, you will get just that. In the
+     * case the property had multiple values, the contents will be escaped and
+     * combined with ,.
+     *
+     * @return string
+     */
+    public function __toString() {
+
+        return (string)$this->getValue();
+
+    }
+
+    /* 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.
+     *
+     * If the parameter does not exist, null is returned.
+     *
+     * @param string $name
+     * @return Node
+     */
+    public function offsetGet($name) {
+
+        if (is_int($name)) return parent::offsetGet($name);
+        $name = strtoupper($name);
+
+        if (!isset($this->parameters[$name])) {
+            return null;
+        }
+
+        return $this->parameters[$name];
+
+    }
+
+    /**
+     * 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);
+            // @codeCoverageIgnoreStart
+            // This will never be reached, because an exception is always
+            // thrown.
+            return;
+            // @codeCoverageIgnoreEnd
+        }
+
+        $param = new Parameter($this->root, $name, $value);
+        $this->parameters[$param->name] = $param;
+
+    }
+
+    /**
+     * 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);
+            // @codeCoverageIgnoreStart
+            // This will never be reached, because an exception is always
+            // thrown.
+            return;
+            // @codeCoverageIgnoreEnd
+        }
+
+        unset($this->parameters[strtoupper($name)]);
+
+    }
+    /* }}} */
+
+    /**
+     * 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;
+        }
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   - Node::REPAIR - If something is broken, and automatic repair may
+     *                    be attempted.
+     *
+     * An array is returned with warnings.
+     *
+     * Every item in the array has the following properties:
+     *    * level - (number between 1 and 3 with severity information)
+     *    * message - (human readable message)
+     *    * node - (reference to the offending node)
+     *
+     * @param int $options
+     * @return array
+     */
+    public function validate($options = 0) {
+
+        $warnings = array();
+
+        // Checking if our value is UTF-8
+        if (!StringUtil::isUTF8($this->getRawMimeDirValue())) {
+
+            $oldValue = $this->getRawMimeDirValue();
+            $level = 3;
+            if ($options & self::REPAIR) {
+                $newValue = StringUtil::convertToUTF8($oldValue);
+                if (true || StringUtil::isUTF8($newValue)) {
+                    $this->setRawMimeDirValue($newValue);
+                    $level = 1;
+                }
+
+            }
+
+
+            if (preg_match('%([\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', $oldValue, $matches)) {
+                $message = 'Property contained a control character (0x' . bin2hex($matches[1]) . ')';
+            } else {
+                $message = 'Property is not valid UTF-8! ' . $oldValue;
+            }
+
+            $warnings[] = array(
+                'level' => $level,
+                'message' => $message,
+                'node' => $this,
+            );
+        }
+
+        // Checking if the propertyname does not contain any invalid bytes.
+        if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) {
+            $warnings[] = array(
+                'level' => 1,
+                'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed',
+                'node' => $this,
+            );
+            if ($options & self::REPAIR) {
+                // Uppercasing and converting underscores to dashes.
+                $this->name = strtoupper(
+                    str_replace('_', '-', $this->name)
+                );
+                // Removing every other invalid character
+                $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name);
+
+            }
+
+        }
+
+        // Validating inner parameters
+        foreach($this->parameters as $param) {
+            $warnings = array_merge($warnings, $param->validate($options));
+        }
+
+        return $warnings;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Binary.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,127 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+    LogicException,
+    Sabre\VObject\Property;
+
+/**
+ * BINARY property
+ *
+ * This object represents BINARY values.
+ *
+ * Binary values are most commonly used by the iCalendar ATTACH property, and
+ * the vCard PHOTO property.
+ *
+ * This property will transparently encode and decode to base64.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Binary extends Property {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = null;
+
+    /**
+     * Updates the current value.
+     *
+     * This may be either a single, or multiple strings in an array.
+     *
+     * @param string|array $value
+     * @return void
+     */
+    public function setValue($value) {
+
+        if(is_array($value)) {
+
+            if(count($value) === 1) {
+                $this->value = $value[0];
+            } else {
+                throw new \InvalidArgumentException('The argument must either be a string or an array with only one child');
+            }
+
+        } else {
+
+            $this->value = $value;
+
+        }
+
+    }
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->value = base64_decode($val);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return base64_encode($this->value);
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return 'BINARY';
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        return array(base64_encode($this->getValue()));
+
+    }
+
+    /**
+     * Sets the json value, as it would appear in a jCard or jCal object.
+     *
+     * The value must always be an array.
+     *
+     * @param array $value
+     * @return void
+     */
+    public function setJsonValue(array $value) {
+
+        $value = array_map('base64_decode', $value);
+        parent::setJsonValue($value);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Boolean.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,63 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+    Sabre\VObject\Property;
+
+/**
+ * Boolean property
+ *
+ * This object represents BOOLEAN values. These are always the case-insenstive
+ * string TRUE or FALSE.
+ *
+ * Automatic conversion to PHP's true and false are done.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Boolean extends Property {
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $val = strtoupper($val)==='TRUE'?true:false;
+        $this->setValue($val);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return $this->value?'TRUE':'FALSE';
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return 'BOOLEAN';
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/FlatText.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,49 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+/**
+ * FlatText property
+ *
+ * This object represents certain TEXT values.
+ *
+ * Specifically, this property is used for text values where there is only 1
+ * part. Semi-colons and colons will be de-escaped when deserializing, but if
+ * any semi-colons or commas appear without a backslash, we will not assume
+ * that they are delimiters.
+ *
+ * vCard 2.1 specifically has a whole bunch of properties where this may
+ * happen, as it only defines a delimiter for a few properties.
+ *
+ * vCard 4.0 states something similar. An unescaped semi-colon _may_ be a
+ * delimiter, depending on the property.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class FlatText extends Text {
+
+    /**
+     * Field separator
+     *
+     * @var string
+     */
+    public $delimiter = ',';
+
+    /**
+     * Sets the value as a quoted-printable encoded string.
+     *
+     * Overriding this so we're not splitting on a ; delimiter.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setQuotedPrintableValue($val) {
+
+        $val = quoted_printable_decode($val);
+        $this->setValue($val);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Float.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,104 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+    Sabre\VObject\Property;
+
+/**
+ * Float property
+ *
+ * This object represents FLOAT values. These can be 1 or more floating-point
+ * numbers.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Float extends Property {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = ';';
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $val = explode($this->delimiter, $val);
+        foreach($val as &$item) {
+            $item = (float)$item;
+        }
+        $this->setParts($val);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return implode(
+            $this->delimiter,
+            $this->getParts()
+        );
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "FLOAT";
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        $val = array_map(
+            function($item) {
+
+                return (float)$item;
+
+            },
+            $this->getParts()
+        );
+
+        // Special-casing the GEO property.
+        //
+        // See:
+        // http://tools.ietf.org/html/draft-ietf-jcardcal-jcal-04#section-3.4.1.2
+        if ($this->name==='GEO') {
+            return array($val);
+        } else {
+            return $val;
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/CalAddress.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,61 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use
+    Sabre\VObject\Property\Text;
+
+/**
+ * CalAddress property
+ *
+ * This object encodes CAL-ADDRESS values, as defined in rfc5545
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CalAddress extends Text {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = null;
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return 'CAL-ADDRESS';
+
+    }
+
+    /**
+     * This returns a normalized form of the value.
+     *
+     * This is primarily used right now to turn mixed-cased schemes in user
+     * uris to lower-case.
+     *
+     * Evolution in particular tends to encode mailto: as MAILTO:.
+     *
+     * @return string
+     */
+    public function getNormalizedValue() {
+
+        $input = $this->getValue();
+        if (!strpos($input, ':')) {
+            return $input;
+        }
+        list($schema, $everythingElse) = explode(':', $input, 2);
+        return strtolower($schema) . ':' . $everythingElse;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Date.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+/**
+ * DateTime property
+ *
+ * This object represents DATE values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.3.5
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Date extends DateTime {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,388 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use DateTimeZone;
+use Sabre\VObject\Property;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\TimeZoneUtil;
+
+/**
+ * DateTime property
+ *
+ * This object represents DATE-TIME values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.3.4
+ *
+ * This particular object has a bit of hackish magic that it may also in some
+ * cases represent a DATE value. This is because it's a common usecase to be
+ * able to change a DATE-TIME into a DATE.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateTime extends Property {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = ',';
+
+    /**
+     * Sets a multi-valued property.
+     *
+     * You may also specify DateTime objects here.
+     *
+     * @param array $parts
+     * @return void
+     */
+    public function setParts(array $parts) {
+
+        if (isset($parts[0]) && $parts[0] instanceof \DateTime) {
+            $this->setDateTimes($parts);
+        } else {
+            parent::setParts($parts);
+        }
+
+    }
+
+    /**
+     * Updates the current value.
+     *
+     * This may be either a single, or multiple strings in an array.
+     *
+     * Instead of strings, you may also use DateTime here.
+     *
+     * @param string|array|\DateTime $value
+     * @return void
+     */
+    public function setValue($value) {
+
+        if (is_array($value) && isset($value[0]) && $value[0] instanceof \DateTime) {
+            $this->setDateTimes($value);
+        } elseif ($value instanceof \DateTime) {
+            $this->setDateTimes(array($value));
+        } else {
+            parent::setValue($value);
+        }
+
+    }
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue(explode($this->delimiter, $val));
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return implode($this->delimiter, $this->getParts());
+
+    }
+
+    /**
+     * Returns true if this is a DATE-TIME value, false if it's a DATE.
+     *
+     * @return bool
+     */
+    public function hasTime() {
+
+        return strtoupper((string)$this['VALUE']) !== 'DATE';
+
+    }
+
+    /**
+     * Returns true if this is a floating DATE or DATE-TIME.
+     *
+     * Note that DATE is always floating.
+     */
+    public function isFloating() {
+
+        return
+            !$this->hasTime() ||
+            (
+                !isset($this['TZID']) &&
+                strpos($this->getValue(),'Z')===false
+            );
+
+    }
+
+    /**
+     * Returns a date-time value.
+     *
+     * Note that if this property contained more than 1 date-time, only the
+     * first will be returned. To get an array with multiple values, call
+     * getDateTimes.
+     *
+     * If no timezone information is known, because it's either an all-day
+     * property or floating time, we will use the DateTimeZone argument to
+     * figure out the exact date.
+     *
+     * @param DateTimeZone $timeZone
+     * @return \DateTime
+     */
+    public function getDateTime(DateTimeZone $timeZone = null) {
+
+        $dt = $this->getDateTimes($timeZone);
+        if (!$dt) return null;
+
+        return $dt[0];
+
+    }
+
+    /**
+     * Returns multiple date-time values.
+     *
+     * If no timezone information is known, because it's either an all-day
+     * property or floating time, we will use the DateTimeZone argument to
+     * figure out the exact date.
+     *
+     * @param DateTimeZone $timeZone
+     * @return \DateTime[]
+     */
+    public function getDateTimes(DateTimeZone $timeZone = null) {
+
+        // Does the property have a TZID?
+        $tzid = $this['TZID'];
+
+        if ($tzid) {
+            $timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root);
+        }
+
+        $dts = array();
+        foreach($this->getParts() as $part) {
+            $dts[] = DateTimeParser::parse($part, $timeZone);
+        }
+        return $dts;
+
+    }
+
+    /**
+     * Sets the property as a DateTime object.
+     *
+     * @param \DateTime $dt
+     * @param bool isFloating If set to true, timezones will be ignored.
+     * @return void
+     */
+    public function setDateTime(\DateTime $dt, $isFloating = false) {
+
+        $this->setDateTimes(array($dt), $isFloating);
+
+    }
+
+    /**
+     * Sets the property as multiple date-time objects.
+     *
+     * The first value will be used as a reference for the timezones, and all
+     * the otehr values will be adjusted for that timezone
+     *
+     * @param \DateTime[] $dt
+     * @param bool isFloating If set to true, timezones will be ignored.
+     * @return void
+     */
+    public function setDateTimes(array $dt, $isFloating = false) {
+
+        $values = array();
+
+        if($this->hasTime()) {
+
+            $tz = null;
+            $isUtc = false;
+
+            foreach($dt as $d) {
+
+                if ($isFloating) {
+                    $values[] = $d->format('Ymd\\THis');
+                    continue;
+                }
+                if (is_null($tz)) {
+                    $tz = $d->getTimeZone();
+                    $isUtc = in_array($tz->getName() , array('UTC', 'GMT', 'Z'));
+                    if (!$isUtc) {
+                        $this->offsetSet('TZID', $tz->getName());
+                    }
+                } else {
+                    $d->setTimeZone($tz);
+                }
+
+                if ($isUtc) {
+                    $values[] = $d->format('Ymd\\THis\\Z');
+                } else {
+                    $values[] = $d->format('Ymd\\THis');
+                }
+
+            }
+            if ($isUtc || $isFloating) {
+                $this->offsetUnset('TZID');
+            }
+
+        } else {
+
+            foreach($dt as $d) {
+
+                $values[] = $d->format('Ymd');
+
+            }
+            $this->offsetUnset('TZID');
+
+        }
+
+        $this->value = $values;
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return $this->hasTime()?'DATE-TIME':'DATE';
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        $dts = $this->getDateTimes();
+        $hasTime = $this->hasTime();
+        $isFloating = $this->isFloating();
+
+        $tz = $dts[0]->getTimeZone();
+        $isUtc = $isFloating ? false : in_array($tz->getName() , array('UTC', 'GMT', 'Z'));
+
+        return array_map(
+            function($dt) use ($hasTime, $isUtc) {
+
+                if ($hasTime) {
+                    return $dt->format('Y-m-d\\TH:i:s') . ($isUtc?'Z':'');
+                } else {
+                    return $dt->format('Y-m-d');
+                }
+
+            },
+            $dts
+        );
+
+    }
+
+    /**
+     * Sets the json value, as it would appear in a jCard or jCal object.
+     *
+     * The value must always be an array.
+     *
+     * @param array $value
+     * @return void
+     */
+    public function setJsonValue(array $value) {
+
+        // dates and times in jCal have one difference to dates and times in
+        // iCalendar. In jCal date-parts are separated by dashes, and
+        // time-parts are separated by colons. It makes sense to just remove
+        // those.
+        $this->setValue(
+            array_map(
+                function($item) {
+
+                    return strtr($item, array(':'=>'', '-'=>''));
+
+                },
+                $value
+            )
+        );
+
+    }
+    /**
+     * We need to intercept offsetSet, because it may be used to alter the
+     * VALUE from DATE-TIME to DATE or vice-versa.
+     *
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     */
+    public function offsetSet($name, $value) {
+
+        parent::offsetSet($name, $value);
+        if (strtoupper($name)!=='VALUE') {
+            return;
+        }
+
+        // This will ensure that dates are correctly encoded.
+        $this->setDateTimes($this->getDateTimes());
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   Node::REPAIR - May attempt to automatically repair the problem.
+     *
+     * This method returns an array with detected problems.
+     * Every element has the following properties:
+     *
+     *  * level - problem level.
+     *  * message - A human-readable string describing the issue.
+     *  * node - A reference to the problematic node.
+     *
+     * The level means:
+     *   1 - The issue was repaired (only happens if REPAIR was turned on)
+     *   2 - An inconsequential issue
+     *   3 - A severe issue.
+     *
+     * @param int $options
+     * @return array
+     */
+    public function validate($options = 0) {
+
+        $messages = parent::validate($options);
+        $valueType = $this->getValueType();
+        $value = $this->getValue();
+        try {
+            switch($valueType) {
+                case 'DATE' :
+                    $foo = DateTimeParser::parseDate($value);
+                    break;
+                case 'DATE-TIME' :
+                    $foo = DateTimeParser::parseDateTime($value);
+                    break;
+            }
+        } catch (\LogicException $e) {
+            $messages[] = array(
+                'level' => 3,
+                'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType,
+                'node' => $this,
+            );
+        }
+        return $messages;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Duration.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,86 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use
+    Sabre\VObject\Property,
+    Sabre\VObject\Parser\MimeDir,
+    Sabre\VObject\DateTimeParser;
+
+/**
+ * Duration property
+ *
+ * This object represents DURATION values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.3.6
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Duration extends Property {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = ',';
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue(explode($this->delimiter, $val));
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return implode($this->delimiter, $this->getParts());
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return 'DURATION';
+
+    }
+
+    /**
+     * Returns a DateInterval representation of the Duration property.
+     *
+     * If the property has more than one value, only the first is returned.
+     *
+     * @return \DateInterval
+     */
+    public function getDateInterval() {
+
+        $parts = $this->getParts();
+        $value = $parts[0];
+        return DateTimeParser::parseDuration($value);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Period.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,129 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use
+    Sabre\VObject\Property,
+    Sabre\VObject\Parser\MimeDir,
+    Sabre\VObject\DateTimeParser;
+
+/**
+ * Period property
+ *
+ * This object represents PERIOD values, as defined here:
+ *
+ * http://tools.ietf.org/html/rfc5545#section-3.8.2.6
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Period extends Property {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = ',';
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue(explode($this->delimiter, $val));
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return implode($this->delimiter, $this->getParts());
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "PERIOD";
+
+    }
+
+    /**
+     * Sets the json value, as it would appear in a jCard or jCal object.
+     *
+     * The value must always be an array.
+     *
+     * @param array $value
+     * @return void
+     */
+    public function setJsonValue(array $value) {
+
+        $value = array_map(
+            function($item) {
+
+                return strtr(implode('/', $item), array(':' => '', '-' => ''));
+
+            },
+            $value
+        );
+        parent::setJsonValue($value);
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        $return = array();
+        foreach($this->getParts() as $item) {
+
+            list($start, $end) = explode('/', $item, 2);
+
+            $start = DateTimeParser::parseDateTime($start);
+
+            // This is a duration value.
+            if ($end[0]==='P') {
+                $return[] = array(
+                    $start->format('Y-m-d\\TH:i:s'),
+                    $end
+                );
+            } else {
+                $end = DateTimeParser::parseDateTime($end);
+                $return[] = array(
+                    $start->format('Y-m-d\\TH:i:s'),
+                    $end->format('Y-m-d\\TH:i:s'),
+                );
+            }
+
+        }
+
+        return $return;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,203 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use
+    Sabre\VObject\Property,
+    Sabre\VObject\Parser\MimeDir;
+
+/**
+ * Recur property
+ *
+ * This object represents RECUR properties.
+ * These values are just used for RRULE and the now deprecated EXRULE.
+ *
+ * The RRULE property may look something like this:
+ *
+ * RRULE:FREQ=MONTHLY;BYDAY=1,2,3;BYHOUR=5.
+ *
+ * This property exposes this as a key=>value array that is accessible using
+ * getParts, and may be set using setParts.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Recur extends Property {
+
+    /**
+     * Updates the current value.
+     *
+     * This may be either a single, or multiple strings in an array.
+     *
+     * @param string|array $value
+     * @return void
+     */
+    public function setValue($value) {
+
+        // If we're getting the data from json, we'll be receiving an object
+        if ($value instanceof \StdClass) {
+            $value = (array)$value;
+        }
+
+        if (is_array($value)) {
+            $newVal = array();
+            foreach($value as $k=>$v) {
+
+                if (is_string($v)) {
+                    $v = strtoupper($v);
+
+                    // The value had multiple sub-values
+                    if (strpos($v,',')!==false) {
+                        $v = explode(',', $v);
+                    }
+                } else {
+                    $v = array_map('strtoupper', $v);
+                }
+
+                $newVal[strtoupper($k)] = $v;
+            }
+            $this->value = $newVal;
+        } elseif (is_string($value)) {
+            $this->value = self::stringToArray($value);
+        } else {
+            throw new \InvalidArgumentException('You must either pass a string, or a key=>value array');
+        }
+
+    }
+
+    /**
+     * Returns the current value.
+     *
+     * This method will always return a singular value. If this was a
+     * multi-value object, some decision will be made first on how to represent
+     * it as a string.
+     *
+     * To get the correct multi-value version, use getParts.
+     *
+     * @return string
+     */
+    public function getValue() {
+
+        $out = array();
+        foreach($this->value as $key=>$value) {
+            $out[] = $key . '=' . (is_array($value)?implode(',', $value):$value);
+        }
+        return strtoupper(implode(';',$out));
+
+    }
+
+    /**
+     * Sets a multi-valued property.
+     *
+     * @param array $parts
+     * @return void
+     */
+    public function setParts(array $parts) {
+
+        $this->setValue($parts);
+
+    }
+
+    /**
+     * Returns a multi-valued property.
+     *
+     * This method always returns an array, if there was only a single value,
+     * it will still be wrapped in an array.
+     *
+     * @return array
+     */
+    public function getParts() {
+
+        return $this->value;
+
+    }
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue($val);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return $this->getValue();
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "RECUR";
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        $values = array();
+        foreach($this->getParts() as $k=>$v) {
+            $values[strtolower($k)] = $v;
+        }
+        return array($values);
+
+    }
+
+    /**
+     * Parses an RRULE value string, and turns it into a struct-ish array.
+     *
+     * @param string $value
+     * @return array
+     */
+    static function stringToArray($value) {
+
+        $value = strtoupper($value);
+        $newValue = array();
+        foreach(explode(';', $value) as $part) {
+
+            // Skipping empty parts.
+            if (empty($part)) {
+                continue;
+            }
+            list($partName, $partValue) = explode('=', $part);
+
+            // The value itself had multiple values..
+            if (strpos($partValue,',')!==false) {
+                $partValue=explode(',', $partValue);
+            }
+            $newValue[$partName] = $partValue;
+
+        }
+
+        return $newValue;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Integer.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,72 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+    Sabre\VObject\Property;
+
+/**
+ * Integer property
+ *
+ * This object represents INTEGER values. These are always a single integer.
+ * They may be preceeded by either + or -.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Integer extends Property {
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue((int)$val);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return $this->value;
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "INTEGER";
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        return array((int)$this->getValue());
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Text.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,333 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+    Sabre\VObject\Property,
+    Sabre\VObject\Component,
+    Sabre\VObject\Parser\MimeDir,
+    Sabre\VObject\Document;
+
+/**
+ * Text property
+ *
+ * This object represents TEXT values.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Text extends Property {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string
+     */
+    public $delimiter = ',';
+
+    /**
+     * List of properties that are considered 'structured'.
+     *
+     * @var array
+     */
+    protected $structuredValues = array(
+        // vCard
+        'N',
+        'ADR',
+        'ORG',
+        'GENDER',
+
+        // iCalendar
+        'REQUEST-STATUS',
+    );
+
+    /**
+     * Some text components have a minimum number of components.
+     *
+     * N must for instance be represented as 5 components, separated by ;, even
+     * if the last few components are unused.
+     *
+     * @var array
+     */
+    protected $minimumPropertyValues = array(
+        'N' => 5,
+        'ADR' => 7,
+    );
+
+    /**
+     * Creates the property.
+     *
+     * You can specify the parameters either in key=>value syntax, in which case
+     * parameters will automatically be created, or you can just pass a list of
+     * Parameter objects.
+     *
+     * @param Component $root The root document
+     * @param string $name
+     * @param string|array|null $value
+     * @param array $parameters List of parameters
+     * @param string $group The vcard property group
+     * @return void
+     */
+    public function __construct(Component $root, $name, $value = null, array $parameters = array(), $group = null) {
+
+        // There's two types of multi-valued text properties:
+        // 1. multivalue properties.
+        // 2. structured value properties
+        //
+        // The former is always separated by a comma, the latter by semi-colon.
+        if (in_array($name, $this->structuredValues)) {
+            $this->delimiter = ';';
+        }
+
+        parent::__construct($root, $name, $value, $parameters, $group);
+
+    }
+
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue(MimeDir::unescapeValue($val, $this->delimiter));
+
+    }
+
+    /**
+     * Sets the value as a quoted-printable encoded string.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setQuotedPrintableValue($val) {
+
+        $val = quoted_printable_decode($val);
+
+        // Quoted printable only appears in vCard 2.1, and the only character
+        // that may be escaped there is ;. So we are simply splitting on just
+        // that.
+        //
+        // We also don't have to unescape \\, so all we need to look for is a ;
+        // that's not preceeded with a \.
+        $regex = '# (?<!\\\\) ; #x';
+        $matches = preg_split($regex, $val);
+        $this->setValue($matches);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        $val = $this->getParts();
+
+        if (isset($this->minimumPropertyValues[$this->name])) {
+            $val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
+        }
+
+        foreach($val as &$item) {
+
+            if (!is_array($item)) {
+                $item = array($item);
+            }
+
+            foreach($item as &$subItem) {
+                $subItem = strtr(
+                    $subItem,
+                    array(
+                        '\\' => '\\\\',
+                        ';'  => '\;',
+                        ','  => '\,',
+                        "\n" => '\n',
+                        "\r" => "",
+                    )
+                );
+            }
+            $item = implode(',', $item);
+
+        }
+
+        return implode($this->delimiter, $val);
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        // Structured text values should always be returned as a single
+        // array-item. Multi-value text should be returned as multiple items in
+        // the top-array.
+        if (in_array($this->name, $this->structuredValues)) {
+            return array($this->getParts());
+        } else {
+            return $this->getParts();
+        }
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "TEXT";
+
+    }
+
+    /**
+     * Turns the object back into a serialized blob.
+     *
+     * @return string
+     */
+    public function serialize() {
+
+        // We need to kick in a special type of encoding, if it's a 2.1 vcard.
+        if ($this->root->getDocumentType() !== Document::VCARD21) {
+            return parent::serialize();
+        }
+
+        $val = $this->getParts();
+
+        if (isset($this->minimumPropertyValues[$this->name])) {
+            $val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
+        }
+
+        // Imploding multiple parts into a single value, and splitting the
+        // values with ;.
+        if (count($val)>1) {
+            foreach($val as $k=>$v) {
+                $val[$k] = str_replace(';','\;', $v);
+            }
+            $val = implode(';', $val);
+        } else {
+            $val = $val[0];
+        }
+
+        $str = $this->name;
+        if ($this->group) $str = $this->group . '.' . $this->name;
+        foreach($this->parameters as $param) {
+
+            if ($param->getValue() === 'QUOTED-PRINTABLE') {
+                continue;
+            }
+            $str.=';' . $param->serialize();
+
+        }
+
+
+
+        // If the resulting value contains a \n, we must encode it as
+        // quoted-printable.
+        if (strpos($val,"\n") !== false) {
+
+            $str.=';ENCODING=QUOTED-PRINTABLE:';
+            $lastLine=$str;
+            $out = null;
+
+            // The PHP built-in quoted-printable-encode does not correctly
+            // encode newlines for us. Specifically, the \r\n sequence must in
+            // vcards be encoded as =0D=OA and we must insert soft-newlines
+            // every 75 bytes.
+            for($ii=0;$ii<strlen($val);$ii++) {
+                $ord = ord($val[$ii]);
+                // These characters are encoded as themselves.
+                if ($ord >= 32 && $ord <=126) {
+                    $lastLine.=$val[$ii];
+                } else {
+                    $lastLine.='=' . strtoupper(bin2hex($val[$ii]));
+                }
+                if (strlen($lastLine)>=75) {
+                    // Soft line break
+                    $out.=$lastLine. "=\r\n ";
+                    $lastLine = null;
+                }
+
+            }
+            if (!is_null($lastLine)) $out.= $lastLine . "\r\n";
+            return $out;
+
+        } else {
+            $str.=':' . $val;
+            $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;
+
+
+        }
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   - Node::REPAIR - If something is broken, and automatic repair may
+     *                    be attempted.
+     *
+     * An array is returned with warnings.
+     *
+     * Every item in the array has the following properties:
+     *    * level - (number between 1 and 3 with severity information)
+     *    * message - (human readable message)
+     *    * node - (reference to the offending node)
+     *
+     * @param int $options
+     * @return array
+     */
+    public function validate($options = 0) {
+
+        $warnings = parent::validate($options);
+
+        if (isset($this->minimumPropertyValues[$this->name])) {
+
+            $minimum = $this->minimumPropertyValues[$this->name];
+            $parts = $this->getParts();
+            if (count($parts) < $minimum) {
+                $warnings[] = array(
+                    'level' => 1,
+                    'message' => 'This property must have at least ' . $minimum . ' components. It only has ' . count($parts),
+                    'node' => $this,
+                );
+                if ($options & self::REPAIR) {
+                    $parts = array_pad($parts, $minimum, '');
+                    $this->setParts($parts);
+                }
+            }
+
+        }
+        return $warnings;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Time.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,94 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\DateTimeParser;
+
+/**
+ * Time property
+ *
+ * This object encodes TIME values.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Time extends Text {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = null;
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "TIME";
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        $parts = DateTimeParser::parseVCardTime($this->getValue());
+
+        $timeStr = '';
+
+        // Hour
+        if (!is_null($parts['hour'])) {
+            $timeStr.=$parts['hour'];
+
+            if (!is_null($parts['minute'])) {
+                $timeStr.=':';
+            }
+        } else {
+            // We know either minute or second _must_ be set, so we insert a
+            // dash for an empty value.
+            $timeStr.='-';
+        }
+
+        // Minute
+        if (!is_null($parts['minute'])) {
+            $timeStr.=$parts['minute'];
+
+            if (!is_null($parts['second'])) {
+                $timeStr.=':';
+            }
+        } else {
+            if (isset($parts['second'])) {
+                // Dash for empty minute
+                $timeStr.='-';
+            }
+        }
+
+        // Second
+        if (!is_null($parts['second'])) {
+            $timeStr.=$parts['second'];
+        }
+
+        // Timezone
+        if (!is_null($parts['timezone'])) {
+            $timeStr.=$parts['timezone'];
+        }
+
+        return array($timeStr);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Unknown.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,50 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use
+    Sabre\VObject\Property,
+    Sabre\VObject\Component,
+    Sabre\VObject\Parser\MimeDir,
+    Sabre\VObject\Document;
+
+/**
+ * Unknown property
+ *
+ * This object represents any properties not recognized by the parser.
+ * This type of value has been introduced by the jCal, jCard specs.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Unknown extends Text {
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        return array($this->getRawMimeDirValue());
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "UNKNOWN";
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/Uri.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,95 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\Property;
+
+/**
+ * URI property
+ *
+ * This object encodes URI values. vCard 2.1 calls these URL.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Uri extends Text {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = null;
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "URI";
+
+    }
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        // Normally we don't need to do any type of unescaping for these
+        // properties, however.. we've noticed that Google Contacts
+        // specifically escapes the colon (:) with a blackslash. While I have
+        // no clue why they thought that was a good idea, I'm unescaping it
+        // anyway.
+        //
+        // Good thing backslashes are not allowed in urls. Makes it easy to
+        // assume that a backslash is always intended as an escape character.
+        if ($this->name === 'URL') {
+            $regex = '#  (?: (\\\\ (?: \\\\ | : ) ) ) #x';
+            $matches = preg_split($regex, $val, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+            $newVal = '';
+            foreach($matches as $match) {
+                switch($match) {
+                    case '\:' :
+                        $newVal.=':';
+                        break;
+                    default :
+                        $newVal.=$match;
+                        break;
+                }
+            }
+            $this->value = $newVal;
+        } else {
+            $this->value = $val;
+        }
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        if (is_array($this->value)) {
+            return $this->value[0];
+        } else {
+            return $this->value;
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/UtcOffset.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,37 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+/**
+ * UtcOffset property
+ *
+ * This object encodes UTC-OFFSET values.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class UtcOffset extends Text {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = null;
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "UTC-OFFSET";
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/VCard/Date.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,33 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use
+    Sabre\VObject\DateTimeParser;
+
+/**
+ * Date property
+ *
+ * This object encodes vCard DATE values.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Date extends DateAndOrTime {
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "DATE";
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/VCard/DateAndOrTime.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,317 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use
+    Sabre\VObject\DateTimeParser,
+    Sabre\VObject\Property\Text,
+    Sabre\VObject\Property,
+    DateTime;
+
+/**
+ * DateAndOrTime property
+ *
+ * This object encodes DATE-AND-OR-TIME values.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateAndOrTime extends Property {
+
+    /**
+     * Field separator
+     *
+     * @var null|string
+     */
+    public $delimiter = null;
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "DATE-AND-OR-TIME";
+
+    }
+
+    /**
+     * Sets a multi-valued property.
+     *
+     * You may also specify DateTime objects here.
+     *
+     * @param array $parts
+     * @return void
+     */
+    public function setParts(array $parts) {
+
+        if (count($parts)>1) {
+            throw new \InvalidArgumentException('Only one value allowed');
+        }
+        if (isset($parts[0]) && $parts[0] instanceof \DateTime) {
+            $this->setDateTime($parts[0]);
+        } else {
+            parent::setParts($parts);
+        }
+
+    }
+
+    /**
+     * Updates the current value.
+     *
+     * This may be either a single, or multiple strings in an array.
+     *
+     * Instead of strings, you may also use DateTime here.
+     *
+     * @param string|array|\DateTime $value
+     * @return void
+     */
+    public function setValue($value) {
+
+        if ($value instanceof \DateTime) {
+            $this->setDateTime($value);
+        } else {
+            parent::setValue($value);
+        }
+
+    }
+
+    /**
+     * Sets the property as a DateTime object.
+     *
+     * @param \DateTime $dt
+     * @return void
+     */
+    public function setDateTime(\DateTime $dt) {
+
+        $values = array();
+
+        $tz = null;
+        $isUtc = false;
+
+        $tz = $dt->getTimeZone();
+        $isUtc = in_array($tz->getName() , array('UTC', 'GMT', 'Z'));
+
+        if ($isUtc) {
+            $value = $dt->format('Ymd\\THis\\Z');
+        } else {
+            // Calculating the offset.
+            $value = $dt->format('Ymd\\THisO');
+        }
+
+        $this->value = $value;
+
+    }
+
+    /**
+     * Returns a date-time value.
+     *
+     * Note that if this property contained more than 1 date-time, only the
+     * first will be returned. To get an array with multiple values, call
+     * getDateTimes.
+     *
+     * If no time was specified, we will always use midnight (in the default
+     * timezone) as the time.
+     *
+     * If parts of the date were omitted, such as the year, we will grab the
+     * current values for those. So at the time of writing, if the year was
+     * omitted, we would have filled in 2014.
+     *
+     * @return \DateTime
+     */
+    public function getDateTime() {
+
+        $dts = array();
+        $now = new DateTime();
+
+        $tzFormat = $now->getTimezone()->getOffset($now)===0?'\\Z':'O';
+        $nowParts = DateTimeParser::parseVCardDateTime($now->format('Ymd\\This' . $tzFormat));
+
+        $value = $this->getValue();
+
+        $dateParts = DateTimeParser::parseVCardDateTime($this->getValue());
+
+        // This sets all the missing parts to the current date/time.
+        // So if the year was missing for a birthday, we're making it 'this
+        // year'.
+        foreach($dateParts as $k=>$v) {
+            if (is_null($v)) {
+                $dateParts[$k] = $nowParts[$k];
+            }
+        }
+        return new DateTime("$dateParts[year]-$dateParts[month]-$dateParts[date] $dateParts[hour]:$dateParts[minute]:$dateParts[second] $dateParts[timezone]");
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        $parts = DateTimeParser::parseVCardDateTime($this->getValue());
+
+        $dateStr = '';
+
+        // Year
+        if (!is_null($parts['year'])) {
+            $dateStr.=$parts['year'];
+
+            if (!is_null($parts['month'])) {
+                // If a year and a month is set, we need to insert a separator
+                // dash.
+                $dateStr.='-';
+            }
+
+        } else {
+
+            if (!is_null($parts['month']) || !is_null($parts['date'])) {
+                // Inserting two dashes
+                $dateStr.='--';
+            }
+
+        }
+
+        // Month
+
+        if (!is_null($parts['month'])) {
+            $dateStr.=$parts['month'];
+
+            if (isset($parts['date'])) {
+                // If month and date are set, we need the separator dash.
+                $dateStr.='-';
+            }
+        } else {
+            if (isset($parts['date'])) {
+                // If the month is empty, and a date is set, we need a 'empty
+                // dash'
+                $dateStr.='-';
+            }
+        }
+
+        // Date
+        if (!is_null($parts['date'])) {
+            $dateStr.=$parts['date'];
+        }
+
+
+        // Early exit if we don't have a time string.
+        if (is_null($parts['hour']) && is_null($parts['minute']) && is_null($parts['second'])) {
+            return array($dateStr);
+        }
+
+        $dateStr.='T';
+
+        // Hour
+        if (!is_null($parts['hour'])) {
+            $dateStr.=$parts['hour'];
+
+            if (!is_null($parts['minute'])) {
+                $dateStr.=':';
+            }
+        } else {
+            // We know either minute or second _must_ be set, so we insert a
+            // dash for an empty value.
+            $dateStr.='-';
+        }
+
+        // Minute
+        if (!is_null($parts['minute'])) {
+            $dateStr.=$parts['minute'];
+
+            if (!is_null($parts['second'])) {
+                $dateStr.=':';
+            }
+        } else {
+            if (isset($parts['second'])) {
+                // Dash for empty minute
+                $dateStr.='-';
+            }
+        }
+
+        // Second
+        if (!is_null($parts['second'])) {
+            $dateStr.=$parts['second'];
+        }
+
+        // Timezone
+        if (!is_null($parts['timezone'])) {
+            $dateStr.=$parts['timezone'];
+        }
+
+        return array($dateStr);
+
+    }
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue($val);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return implode($this->delimiter, $this->getParts());
+
+    }
+
+    /**
+     * Validates the node for correctness.
+     *
+     * The following options are supported:
+     *   Node::REPAIR - May attempt to automatically repair the problem.
+     *
+     * This method returns an array with detected problems.
+     * Every element has the following properties:
+     *
+     *  * level - problem level.
+     *  * message - A human-readable string describing the issue.
+     *  * node - A reference to the problematic node.
+     *
+     * The level means:
+     *   1 - The issue was repaired (only happens if REPAIR was turned on)
+     *   2 - An inconsequential issue
+     *   3 - A severe issue.
+     *
+     * @param int $options
+     * @return array
+     */
+    public function validate($options = 0) {
+
+        $messages = parent::validate($options);
+        $value = $this->getValue();
+        try {
+            DateTimeParser::parseVCardDateTime($value);
+        } catch (\InvalidArgumentException $e) {
+            $messages[] = array(
+                'level' => 3,
+                'message' => 'The supplied value (' . $value . ') is not a correct DATE-AND-OR-TIME property',
+                'node' => $this,
+            );
+        }
+        return $messages;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/VCard/DateTime.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,33 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use
+    Sabre\VObject\DateTimeParser;
+
+/**
+ * DateTime property
+ *
+ * This object encodes DATE-TIME values for vCards.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class DateTime extends DateAndOrTime {
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "DATE-TIME";
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/VCard/LanguageTag.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,59 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use
+    Sabre\VObject\Property;
+
+/**
+ * LanguageTag property
+ *
+ * This object represents LANGUAGE-TAG values as used in vCards.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class LanguageTag extends Property {
+
+    /**
+     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
+     *
+     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
+     * not yet done, but parameters are not included.
+     *
+     * @param string $val
+     * @return void
+     */
+    public function setRawMimeDirValue($val) {
+
+        $this->setValue($val);
+
+    }
+
+    /**
+     * Returns a raw mime-dir representation of the value.
+     *
+     * @return string
+     */
+    public function getRawMimeDirValue() {
+
+        return $this->value;
+
+    }
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "LANGUAGE-TAG";
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Property/VCard/TimeStamp.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,69 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use
+    Sabre\VObject\DateTimeParser,
+    Sabre\VObject\Property\Text;
+
+/**
+ * TimeStamp property
+ *
+ * This object encodes TIMESTAMP values.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class TimeStamp extends Text {
+
+    /**
+     * In case this is a multi-value property. This string will be used as a
+     * delimiter.
+     *
+     * @var string|null
+     */
+    public $delimiter = null;
+
+    /**
+     * Returns the type of value.
+     *
+     * This corresponds to the VALUE= parameter. Every property also has a
+     * 'default' valueType.
+     *
+     * @return string
+     */
+    public function getValueType() {
+
+        return "TIMESTAMP";
+
+    }
+
+    /**
+     * Returns the value, in the format it should be encoded for json.
+     *
+     * This method must always return an array.
+     *
+     * @return array
+     */
+    public function getJsonValue() {
+
+        $parts = DateTimeParser::parseVCardDateTime($this->getValue());
+
+        $dateStr =
+            $parts['year'] . '-' .
+            $parts['month'] . '-' .
+            $parts['date'] . 'T' .
+            $parts['hour'] . ':' .
+            $parts['minute'] . ':' .
+            $parts['second'];
+
+        // Timezone
+        if (!is_null($parts['timezone'])) {
+            $dateStr.=$parts['timezone'];
+        }
+
+        return array($dateStr);
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Reader.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,73 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * iCalendar/vCard/jCal/jCard reader object.
+ *
+ * This object provides a few (static) convenience methods to quickly access
+ * the parsers.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Reader {
+
+    /**
+     * If this option is passed to the reader, it will be less strict about the
+     * validity of the lines.
+     */
+    const OPTION_FORGIVING = 1;
+
+    /**
+     * If this option is turned on, any lines we cannot parse will be ignored
+     * by the reader.
+     */
+    const OPTION_IGNORE_INVALID_LINES = 2;
+
+    /**
+     * Parses a vCard or iCalendar object, and returns the top component.
+     *
+     * The options argument is a bitfield. Pass any of the OPTIONS constant to
+     * alter the parsers' behaviour.
+     *
+     * You can either supply a string, or a readable stream for input.
+     *
+     * @param string|resource $data
+     * @param int $options
+     * @return Document
+     */
+    static public function read($data, $options = 0) {
+
+        $parser = new Parser\MimeDir();
+        $result = $parser->parse($data, $options);
+
+        return $result;
+
+    }
+
+    /**
+     * Parses a jCard or jCal object, and returns the top component.
+     *
+     * The options argument is a bitfield. Pass any of the OPTIONS constant to
+     * alter the parsers' behaviour.
+     *
+     * You can either a string, a readable stream, or an array for it's input.
+     * Specifying the array is useful if json_decode was already called on the
+     * input.
+     *
+     * @param string|resource|array $data
+     * @param int $options
+     * @return Node
+     */
+    static public function readJson($data, $options = 0) {
+
+        $parser = new Parser\Json();
+        $result = $parser->parse($data, $options);
+
+        return $result;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Recur/EventIterator.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,497 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use InvalidArgumentException;
+use DateTime;
+use DateTimeZone;
+use Sabre\VObject\Component;
+use Sabre\VObject\Component\VEvent;
+
+/**
+ * This class is used to determine new for a recurring event, when the next
+ * events occur.
+ *
+ * This iterator may loop infinitely in the future, therefore it is important
+ * that if you use this class, you set hard limits for the amount of iterations
+ * you want to handle.
+ *
+ * Note that currently there is not full support for the entire iCalendar
+ * specification, as it's very complex and contains a lot of permutations
+ * that's not yet used very often in software.
+ *
+ * For the focus has been on features as they actually appear in Calendaring
+ * software, but this may well get expanded as needed / on demand
+ *
+ * The following RRULE properties are supported
+ *   * UNTIL
+ *   * INTERVAL
+ *   * COUNT
+ *   * FREQ=DAILY
+ *     * BYDAY
+ *     * BYHOUR
+ *     * BYMONTH
+ *   * FREQ=WEEKLY
+ *     * BYDAY
+ *     * BYHOUR
+ *     * WKST
+ *   * FREQ=MONTHLY
+ *     * BYMONTHDAY
+ *     * BYDAY
+ *     * BYSETPOS
+ *   * FREQ=YEARLY
+ *     * BYMONTH
+ *     * BYMONTHDAY (only if BYMONTH is also set)
+ *     * BYDAY (only if BYMONTH is also set)
+ *
+ * Anything beyond this is 'undefined', which means that it may get ignored, or
+ * you may get unexpected results. The effect is that in some applications the
+ * specified recurrence may look incorrect, or is missing.
+ *
+ * The recurrence iterator also does not yet support THISANDFUTURE.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class EventIterator implements \Iterator {
+
+    /**
+     * Reference timeZone for floating dates and times.
+     *
+     * @var DateTimeZone
+     */
+    protected $timeZone;
+
+    /**
+     * True if we're iterating an all-day event.
+     *
+     * @var bool
+     */
+    protected $allDay = false;
+
+    /**
+     * Creates the iterator
+     *
+     * You should pass a VCALENDAR component, as well as the UID of the event
+     * we're going to traverse.
+     *
+     * @param Component $vcal
+     * @param string|null $uid
+     * @param DateTimeZone $timeZone Reference timezone for floating dates and
+     *                               times.
+     */
+    public function __construct(Component $vcal, $uid = null, DateTimeZone $timeZone = null) {
+
+        if (is_null($this->timeZone)) {
+            $timeZone = new DateTimeZone('UTC');
+        }
+        $this->timeZone = $timeZone;
+
+        if ($vcal instanceof VEvent) {
+            // Single instance mode.
+            $events = array($vcal);
+        } else {
+            $uid = (string)$uid;
+            if (!$uid) {
+                throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
+            }
+            if (!isset($vcal->VEVENT)) {
+                throw new InvalidArgumentException('No events found in this calendar');
+            }
+            $events = array();
+            foreach($vcal->VEVENT as $event) {
+                if ($event->uid->getValue() === $uid) {
+                    $events[] = $event;
+                }
+            }
+
+        }
+
+        foreach($events as $vevent) {
+
+            if (!isset($vevent->{'RECURRENCE-ID'})) {
+
+                $this->masterEvent = $vevent;
+
+            } else {
+
+                $this->exceptions[
+                    $vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp()
+                ] = true;
+                $this->overriddenEvents[] = $vevent;
+
+            }
+
+        }
+
+        if (!$this->masterEvent) {
+            // No base event was found. CalDAV does allow cases where only
+            // overridden instances are stored.
+            //
+            // In this particular case, we're just going to grab the first
+            // event and use that instead. This may not always give the
+            // desired result.
+            if (!count($this->overriddenEvents)) {
+                throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid);
+            }
+            $this->masterEvent = array_shift($this->overriddenEvents);
+        }
+
+        $this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone);
+        $this->allDay = !$this->masterEvent->DTSTART->hasTime();
+
+        if (isset($this->masterEvent->EXDATE)) {
+
+            foreach($this->masterEvent->EXDATE as $exDate) {
+
+                foreach($exDate->getDateTimes($this->timeZone) as $dt) {
+                    $this->exceptions[$dt->getTimeStamp()] = true;
+                }
+
+            }
+
+        }
+
+        if (isset($this->masterEvent->DTEND)) {
+            $this->eventDuration =
+                $this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() -
+                $this->startDate->getTimeStamp();
+        } elseif (isset($this->masterEvent->DURATION)) {
+            $duration = $this->masterEvent->DURATION->getDateInterval();
+            $end = clone $this->startDate;
+            $end->add($duration);
+            $this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp();
+        } elseif ($this->allDay) {
+            $this->eventDuration = 3600 * 24;
+        } else {
+            $this->eventDuration = 0;
+        }
+
+        if (isset($this->masterEvent->RDATE)) {
+            $this->recurIterator = new RDateIterator(
+                $this->masterEvent->RDATE->getParts(),
+                $this->startDate
+            );
+        } elseif (isset($this->masterEvent->RRULE)) {
+            $this->recurIterator = new RRuleIterator(
+                $this->masterEvent->RRULE->getParts(),
+                $this->startDate
+            );
+        } else {
+            $this->recurIterator = new RRuleIterator(
+                array(
+                    'FREQ' => 'DAILY',
+                    'COUNT' => 1,
+                ),
+                $this->startDate
+            );
+        }
+
+        $this->rewind();
+        if (!$this->valid()) {
+            throw new NoInstancesException('This recurrence rule does not generate any valid instances');
+        }
+
+    }
+
+    /**
+     * Returns the date for the current position of the iterator.
+     *
+     * @return DateTime
+     */
+    public function current() {
+
+        if ($this->currentDate) {
+            return clone $this->currentDate;
+        }
+
+    }
+
+    /**
+     * This method returns the start date for the current iteration of the
+     * event.
+     *
+     * @return DateTime
+     */
+    public function getDtStart() {
+
+        if ($this->currentDate) {
+            return clone $this->currentDate;
+        }
+
+    }
+
+    /**
+     * This method returns the end date for the current iteration of the
+     * event.
+     *
+     * @return DateTime
+     */
+    public function getDtEnd() {
+
+        if (!$this->valid()) {
+            return null;
+        }
+        $end = clone $this->currentDate;
+        $end->modify('+' . $this->eventDuration . ' seconds');
+        return $end;
+
+    }
+
+    /**
+     * Returns a VEVENT for the current iterations of the event.
+     *
+     * This VEVENT will have a recurrence id, and it's DTSTART and DTEND
+     * altered.
+     *
+     * @return VEvent
+     */
+    public function getEventObject() {
+
+        if ($this->currentOverriddenEvent) {
+            return $this->currentOverriddenEvent;
+        }
+
+        $event = clone $this->masterEvent;
+
+        // Ignoring the following block, because PHPUnit's code coverage
+        // ignores most of these lines, and this messes with our stats.
+        //
+        // @codeCoverageIgnoreStart
+        unset(
+            $event->RRULE,
+            $event->EXDATE,
+            $event->RDATE,
+            $event->EXRULE,
+            $event->{'RECURRENCE-ID'}
+        );
+        // @codeCoverageIgnoreEnd
+
+        $event->DTSTART->setDateTime($this->getDtStart());
+        if (isset($event->DTEND)) {
+            $event->DTEND->setDateTime($this->getDtEnd());
+        }
+        // Including a RECURRENCE-ID to the object, unless this is the first
+        // object.
+        //
+        // The inner recurIterator is always one step ahead, this is why we're
+        // checking for the key being higher than 1.
+        if ($this->recurIterator->key() > 1) {
+            $recurid = clone $event->DTSTART;
+            $recurid->name = 'RECURRENCE-ID';
+            $event->add($recurid);
+        }
+        return $event;
+
+    }
+
+    /**
+     * Returns the current position of the iterator.
+     *
+     * This is for us simply a 0-based index.
+     *
+     * @return int
+     */
+    public function key() {
+
+        // The counter is always 1 ahead.
+        return $this->counter - 1;
+
+    }
+
+    /**
+     * This is called after next, to see if the iterator is still at a valid
+     * position, or if it's at the end.
+     *
+     * @return bool
+     */
+    public function valid() {
+
+        return !!$this->currentDate;
+
+    }
+
+    /**
+     * Sets the iterator back to the starting point.
+     */
+    public function rewind() {
+
+        $this->recurIterator->rewind();
+        // re-creating overridden event index.
+        $index = array();
+        foreach($this->overriddenEvents as $key=>$event) {
+            $stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp();
+            $index[$stamp] = $key;
+        }
+        krsort($index);
+        $this->counter = 0;
+        $this->overriddenEventsIndex = $index;
+        $this->currentOverriddenEvent = null;
+
+        $this->nextDate = null;
+        $this->currentDate = clone $this->startDate;
+
+        $this->next();
+
+    }
+
+    /**
+     * Advances the iterator with one step.
+     *
+     * @return void
+     */
+    public function next() {
+
+        $this->currentOverriddenEvent = null;
+        $this->counter++;
+        if ($this->nextDate) {
+            // We had a stored value.
+            $nextDate = $this->nextDate;
+            $this->nextDate = null;
+        } else {
+            // We need to ask rruleparser for the next date.
+            // We need to do this until we find a date that's not in the
+            // exception list.
+            do {
+                if (!$this->recurIterator->valid()) {
+                    $nextDate = null;
+                    break;
+                }
+                $nextDate = $this->recurIterator->current();
+                $this->recurIterator->next();
+            } while(isset($this->exceptions[$nextDate->getTimeStamp()]));
+
+        }
+
+
+        // $nextDate now contains what rrule thinks is the next one, but an
+        // overridden event may cut ahead.
+        if ($this->overriddenEventsIndex) {
+
+            $offset = end($this->overriddenEventsIndex);
+            $timestamp = key($this->overriddenEventsIndex);
+            if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) {
+                // Overridden event comes first.
+                $this->currentOverriddenEvent = $this->overriddenEvents[$offset];
+
+                // Putting the rrule next date aside.
+                $this->nextDate = $nextDate;
+                $this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone);
+
+                // Ensuring that this item will only be used once.
+                array_pop($this->overriddenEventsIndex);
+
+                // Exit point!
+                return;
+
+            }
+
+        }
+
+        $this->currentDate = $nextDate;
+
+    }
+
+    /**
+     * Quickly jump to a date in the future.
+     *
+     * @param DateTime $dateTime
+     */
+    public function fastForward(DateTime $dateTime) {
+
+        while($this->valid() && $this->getDtEnd() < $dateTime ) {
+            $this->next();
+        }
+
+    }
+
+    /**
+     * Returns true if this recurring event never ends.
+     *
+     * @return bool
+     */
+    public function isInfinite() {
+
+        return $this->recurIterator->isInfinite();
+
+    }
+
+    /**
+     * RRULE parser
+     *
+     * @var RRuleIterator
+     */
+    protected $recurIterator;
+
+    /**
+     * The duration, in seconds, of the master event.
+     *
+     * We use this to calculate the DTEND for subsequent events.
+     */
+    protected $eventDuration;
+
+    /**
+     * A reference to the main (master) event.
+     *
+     * @var VEVENT
+     */
+    protected $masterEvent;
+
+    /**
+     * List of overridden events.
+     *
+     * @var array
+     */
+    protected $overriddenEvents = array();
+
+    /**
+     * Overridden event index.
+     *
+     * Key is timestamp, value is the index of the item in the $overriddenEvent
+     * property.
+     *
+     * @var array
+     */
+    protected $overriddenEventsIndex;
+
+    /**
+     * A list of recurrence-id's that are either part of EXDATE, or are
+     * overridden.
+     *
+     * @var array
+     */
+    protected $exceptions = array();
+
+    /**
+     * Internal event counter
+     *
+     * @var int
+     */
+    protected $counter;
+
+    /**
+     * The very start of the iteration process.
+     *
+     * @var DateTime
+     */
+    protected $startDate;
+
+    /**
+     * Where we are currently in the iteration process
+     *
+     * @var DateTime
+     */
+    protected $currentDate;
+
+    /**
+     * The next date from the rrule parser.
+     *
+     * Sometimes we need to temporary store the next date, because an
+     * overridden event came before.
+     *
+     * @var DateTime
+     */
+    protected $nextDate;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Recur/NoInstancesException.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,18 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use Exception;
+
+/**
+ * This exception gets thrown when a recurrence iterator produces 0 instances.
+ *
+ * This may happen when every occurence in a rrule is also in EXDATE.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class NoInstancesException extends Exception {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Recur/RDateIterator.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,174 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use DateTime;
+use InvalidArgumentException;
+use Iterator;
+use Sabre\VObject\DateTimeParser;
+
+
+/**
+ * RRuleParser
+ *
+ * This class receives an RRULE string, and allows you to iterate to get a list
+ * of dates in that recurrence.
+ *
+ * For instance, passing: FREQ=DAILY;LIMIT=5 will cause the iterator to contain
+ * 5 items, one for each day.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class RDateIterator implements Iterator {
+
+    /**
+     * Creates the Iterator.
+     *
+     * @param string|array $rrule
+     * @param DateTime $start
+     */
+    public function __construct($rrule, DateTime $start) {
+
+        $this->startDate = $start;
+        $this->parseRDate($rrule);
+        $this->currentDate = clone $this->startDate;
+
+    }
+
+    /* Implementation of the Iterator interface {{{ */
+
+    public function current() {
+
+        if (!$this->valid()) return null;
+        return clone $this->currentDate;
+
+    }
+
+    /**
+     * Returns the current item number.
+     *
+     * @return int
+     */
+    public function key() {
+
+        return $this->counter;
+
+    }
+
+    /**
+     * Returns whether the current item is a valid item for the recurrence
+     * iterator.
+     *
+     * @return bool
+     */
+    public function valid() {
+
+        return ($this->counter <= count($this->dates));
+
+    }
+
+    /**
+     * Resets the iterator.
+     *
+     * @return void
+     */
+    public function rewind() {
+
+        $this->currentDate = clone $this->startDate;
+        $this->counter = 0;
+
+    }
+
+    /**
+     * Goes on to the next iteration.
+     *
+     * @return void
+     */
+    public function next() {
+
+        $this->counter++;
+        if (!$this->valid()) return;
+
+        $this->currentDate =
+            DateTimeParser::parse(
+                $this->dates[$this->counter-1]
+            );
+
+    }
+
+    /* End of Iterator implementation }}} */
+
+    /**
+     * Returns true if this recurring event never ends.
+     *
+     * @return bool
+     */
+    public function isInfinite() {
+
+        return false;
+
+    }
+
+    /**
+     * This method allows you to quickly go to the next occurrence after the
+     * specified date.
+     *
+     * @param DateTime $dt
+     * @return void
+     */
+    public function fastForward(\DateTime $dt) {
+
+        while($this->valid() && $this->currentDate < $dt ) {
+            $this->next();
+        }
+
+    }
+
+    /**
+     * The reference start date/time for the rrule.
+     *
+     * All calculations are based on this initial date.
+     *
+     * @var DateTime
+     */
+    protected $startDate;
+
+    /**
+     * The date of the current iteration. You can get this by calling
+     * ->current().
+     *
+     * @var DateTime
+     */
+    protected $currentDate;
+
+    /**
+     * The current item in the list.
+     *
+     * You can get this number with the key() method.
+     *
+     * @var int
+     */
+    protected $counter = 0;
+
+    /* }}} */
+
+    /**
+     * This method receives a string from an RRULE property, and populates this
+     * class with all the values.
+     *
+     * @param string|array $rrule
+     * @return void
+     */
+    protected function parseRDate($rdate) {
+
+        if (is_string($rdate)) {
+            $rdate = explode(',', $rdate);
+        }
+
+        $this->dates = $rdate;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Recur/RRuleIterator.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,901 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use DateTime;
+use InvalidArgumentException;
+use Iterator;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Property;
+
+
+/**
+ * RRuleParser
+ *
+ * This class receives an RRULE string, and allows you to iterate to get a list
+ * of dates in that recurrence.
+ *
+ * For instance, passing: FREQ=DAILY;LIMIT=5 will cause the iterator to contain
+ * 5 items, one for each day.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class RRuleIterator implements Iterator {
+
+    /**
+     * Creates the Iterator
+     *
+     * @param string|array $rrule
+     * @param DateTime $start
+     */
+    public function __construct($rrule, DateTime $start) {
+
+        $this->startDate = $start;
+        $this->parseRRule($rrule);
+        $this->currentDate = clone $this->startDate;
+
+    }
+
+    /* Implementation of the Iterator interface {{{ */
+
+    public function current() {
+
+        if (!$this->valid()) return null;
+        return clone $this->currentDate;
+
+    }
+
+    /**
+     * Returns the current item number
+     *
+     * @return int
+     */
+    public function key() {
+
+        return $this->counter;
+
+    }
+
+    /**
+     * Returns whether the current item is a valid item for the recurrence
+     * iterator. This will return false if we've gone beyond the UNTIL or COUNT
+     * statements.
+     *
+     * @return bool
+     */
+    public function valid() {
+
+        if (!is_null($this->count)) {
+            return $this->counter < $this->count;
+        }
+        return is_null($this->until) || $this->currentDate <= $this->until;
+
+    }
+
+    /**
+     * Resets the iterator
+     *
+     * @return void
+     */
+    public function rewind() {
+
+        $this->currentDate = clone $this->startDate;
+        $this->counter = 0;
+
+    }
+
+    /**
+     * Goes on to the next iteration
+     *
+     * @return void
+     */
+    public function next() {
+
+        $previousStamp = $this->currentDate->getTimeStamp();
+
+        // Otherwise, we find the next event in the normal RRULE
+        // sequence.
+        switch($this->frequency) {
+
+            case 'hourly' :
+                $this->nextHourly();
+                break;
+
+            case 'daily' :
+                $this->nextDaily();
+                break;
+
+            case 'weekly' :
+                $this->nextWeekly();
+                break;
+
+            case 'monthly' :
+                $this->nextMonthly();
+                break;
+
+            case 'yearly' :
+                $this->nextYearly();
+                break;
+
+        }
+        $this->counter++;
+
+    }
+
+    /* End of Iterator implementation }}} */
+
+    /**
+     * Returns true if this recurring event never ends.
+     *
+     * @return bool
+     */
+    public function isInfinite() {
+
+        return !$this->count && !$this->until;
+
+    }
+
+    /**
+     * This method allows you to quickly go to the next occurrence after the
+     * specified date.
+     *
+     * @param DateTime $dt
+     * @return void
+     */
+    public function fastForward(\DateTime $dt) {
+
+        while($this->valid() && $this->currentDate < $dt ) {
+            $this->next();
+        }
+
+    }
+
+    /**
+     * The reference start date/time for the rrule.
+     *
+     * All calculations are based on this initial date.
+     *
+     * @var DateTime
+     */
+    protected $startDate;
+
+    /**
+     * The date of the current iteration. You can get this by calling
+     * ->current().
+     *
+     * @var DateTime
+     */
+    protected $currentDate;
+
+    /**
+     * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly,
+     * yearly.
+     *
+     * @var string
+     */
+    protected $frequency;
+
+    /**
+     * The number of recurrences, or 'null' if infinitely recurring.
+     *
+     * @var int
+     */
+    protected $count;
+
+    /**
+     * The interval.
+     *
+     * If for example frequency is set to daily, interval = 2 would mean every
+     * 2 days.
+     *
+     * @var int
+     */
+    protected $interval = 1;
+
+    /**
+     * The last instance of this recurrence, inclusively
+     *
+     * @var \DateTime|null
+     */
+    protected $until;
+
+    /**
+     * Which seconds to recur.
+     *
+     * This is an array of integers (between 0 and 60)
+     *
+     * @var array
+     */
+    protected $bySecond;
+
+    /**
+     * Which minutes to recur
+     *
+     * This is an array of integers (between 0 and 59)
+     *
+     * @var array
+     */
+    protected $byMinute;
+
+    /**
+     * Which hours to recur
+     *
+     * This is an array of integers (between 0 and 23)
+     *
+     * @var array
+     */
+    protected $byHour;
+
+    /**
+     * The current item in the list.
+     *
+     * You can get this number with the key() method.
+     *
+     * @var int
+     */
+    protected $counter = 0;
+
+    /**
+     * Which weekdays to recur.
+     *
+     * This is an array of weekdays
+     *
+     * This may also be preceeded by a positive or negative integer. If present,
+     * this indicates the nth occurrence of a specific day within the monthly or
+     * yearly rrule. For instance, -2TU indicates the second-last tuesday of
+     * the month, or year.
+     *
+     * @var array
+     */
+    protected $byDay;
+
+    /**
+     * Which days of the month to recur
+     *
+     * This is an array of days of the months (1-31). The value can also be
+     * negative. -5 for instance means the 5th last day of the month.
+     *
+     * @var array
+     */
+    protected $byMonthDay;
+
+    /**
+     * Which days of the year to recur.
+     *
+     * This is an array with days of the year (1 to 366). The values can also
+     * be negative. For instance, -1 will always represent the last day of the
+     * year. (December 31st).
+     *
+     * @var array
+     */
+    protected $byYearDay;
+
+    /**
+     * Which week numbers to recur.
+     *
+     * This is an array of integers from 1 to 53. The values can also be
+     * negative. -1 will always refer to the last week of the year.
+     *
+     * @var array
+     */
+    protected $byWeekNo;
+
+    /**
+     * Which months to recur.
+     *
+     * This is an array of integers from 1 to 12.
+     *
+     * @var array
+     */
+    protected $byMonth;
+
+    /**
+     * Which items in an existing st to recur.
+     *
+     * These numbers work together with an existing by* rule. It specifies
+     * exactly which items of the existing by-rule to filter.
+     *
+     * Valid values are 1 to 366 and -1 to -366. As an example, this can be
+     * used to recur the last workday of the month.
+     *
+     * This would be done by setting frequency to 'monthly', byDay to
+     * 'MO,TU,WE,TH,FR' and bySetPos to -1.
+     *
+     * @var array
+     */
+    protected $bySetPos;
+
+    /**
+     * When the week starts.
+     *
+     * @var string
+     */
+    protected $weekStart = 'MO';
+
+    /* Functions that advance the iterator {{{ */
+
+    /**
+     * Does the processing for advancing the iterator for hourly frequency.
+     *
+     * @return void
+     */
+    protected function nextHourly() {
+
+        $this->currentDate->modify('+' . $this->interval . ' hours');
+
+    }
+
+    /**
+     * Does the processing for advancing the iterator for daily frequency.
+     *
+     * @return void
+     */
+    protected function nextDaily() {
+
+        if (!$this->byHour && !$this->byDay) {
+            $this->currentDate->modify('+' . $this->interval . ' days');
+            return;
+        }
+
+        if (isset($this->byHour)) {
+            $recurrenceHours = $this->getHours();
+        }
+
+        if (isset($this->byDay)) {
+            $recurrenceDays = $this->getDays();
+        }
+
+        if (isset($this->byMonth)) {
+            $recurrenceMonths = $this->getMonths();
+        }
+
+        do {
+            if ($this->byHour) {
+                if ($this->currentDate->format('G') == '23') {
+                    // to obey the interval rule
+                    $this->currentDate->modify('+' . $this->interval-1 . ' days');
+                }
+
+                $this->currentDate->modify('+1 hours');
+
+            } else {
+                $this->currentDate->modify('+' . $this->interval . ' days');
+
+            }
+
+            // Current month of the year
+            $currentMonth = $this->currentDate->format('n');
+
+            // Current day of the week
+            $currentDay = $this->currentDate->format('w');
+
+            // Current hour of the day
+            $currentHour = $this->currentDate->format('G');
+
+        } while (
+            ($this->byDay   && !in_array($currentDay, $recurrenceDays)) ||
+            ($this->byHour  && !in_array($currentHour, $recurrenceHours)) ||
+            ($this->byMonth && !in_array($currentMonth, $recurrenceMonths))
+        );
+
+    }
+
+    /**
+     * Does the processing for advancing the iterator for weekly frequency.
+     *
+     * @return void
+     */
+    protected function nextWeekly() {
+
+        if (!$this->byHour && !$this->byDay) {
+            $this->currentDate->modify('+' . $this->interval . ' weeks');
+            return;
+        }
+
+        if ($this->byHour) {
+            $recurrenceHours = $this->getHours();
+        }
+
+        if ($this->byDay) {
+            $recurrenceDays = $this->getDays();
+        }
+
+        // First day of the week:
+        $firstDay = $this->dayMap[$this->weekStart];
+
+        do {
+
+            if ($this->byHour) {
+                $this->currentDate->modify('+1 hours');
+            } else {
+                $this->currentDate->modify('+1 days');
+            }
+
+            // Current day of the week
+            $currentDay = (int) $this->currentDate->format('w');
+
+            // Current hour of the day
+            $currentHour = (int) $this->currentDate->format('G');
+
+            // We need to roll over to the next week
+            if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) {
+                $this->currentDate->modify('+' . $this->interval-1 . ' 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]]);
+                }
+            }
+
+            // We have a match
+        } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours)));
+    }
+
+    /**
+     * 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 line does not currently work in hhvm. Temporary workaround
+            // follows:
+            // $this->currentDate->modify('first day of this month');
+            $this->currentDate = new \DateTime($this->currentDate->format('Y-m-1 H:i:s'), $this->currentDate->getTimezone());
+            // end of workaround
+            $this->currentDate->modify('+ ' . $this->interval . ' months');
+
+            // This goes to 0 because we need to start counting at the
+            // 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() {
+
+        $currentMonth = $this->currentDate->format('n');
+        $currentYear = $this->currentDate->format('Y');
+        $currentDayOfMonth = $this->currentDate->format('j');
+
+        // No sub-rules, so we just advance by year
+        if (!$this->byMonth) {
+
+            // Unless it was a leap day!
+            if ($currentMonth==2 && $currentDayOfMonth==29) {
+
+                $counter = 0;
+                do {
+                    $counter++;
+                    // Here we increase the year count by the interval, until
+                    // we hit a date that's also in a leap year.
+                    //
+                    // We could just find the next interval that's dividable by
+                    // 4, but that would ignore the rule that there's no leap
+                    // year every year that's dividable by a 100, but not by
+                    // 400. (1800, 1900, 2100). So we just rely on the datetime
+                    // functions instead.
+                    $nextDate = clone $this->currentDate;
+                    $nextDate->modify('+ ' . ($this->interval*$counter) . ' years');
+                } while ($nextDate->format('n')!=2);
+                $this->currentDate = $nextDate;
+
+                return;
+
+            }
+
+            // The easiest form
+            $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 {
+
+            // These are the 'byMonth' rules, if there are no byDay or
+            // byMonthDay sub-rules.
+            do {
+
+                $currentMonth++;
+                if ($currentMonth>12) {
+                    $currentYear+=$this->interval;
+                    $currentMonth = 1;
+                }
+            } while (!in_array($currentMonth, $this->byMonth));
+            $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth);
+
+            return;
+
+        }
+
+    }
+
+    /* }}} */
+
+    /**
+     * This method receives a string from an RRULE property, and populates this
+     * class with all the values.
+     *
+     * @param string|array $rrule
+     * @return void
+     */
+    protected function parseRRule($rrule) {
+
+        if (is_string($rrule)) {
+            $rrule = Property\ICalendar\Recur::stringToArray($rrule);
+        }
+
+        foreach($rrule as $key=>$value) {
+
+            $key = strtoupper($key);
+            switch($key) {
+
+                case 'FREQ' :
+                    $value = strtolower($value);
+                    if (!in_array(
+                        $value,
+                        array('secondly','minutely','hourly','daily','weekly','monthly','yearly')
+                    )) {
+                        throw new InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value));
+                    }
+                    $this->frequency = $value;
+                    break;
+
+                case 'UNTIL' :
+                    $this->until = DateTimeParser::parse($value, $this->startDate->getTimezone());
+
+                    // In some cases events are generated with an UNTIL=
+                    // parameter before the actual start of the event.
+                    //
+                    // Not sure why this is happening. We assume that the
+                    // intention was that the event only recurs once.
+                    //
+                    // So we are modifying the parameter so our code doesn't
+                    // break.
+                    if($this->until < $this->startDate) {
+                        $this->until = $this->startDate;
+                    }
+                    break;
+
+                case 'INTERVAL' :
+                    // No break
+
+                case 'COUNT' :
+                    $val = (int)$value;
+                    if ($val < 1) {
+                        throw new \InvalidArgumentException(strtoupper($key) . ' in RRULE must be a positive integer!');
+                    }
+                    $key = strtolower($key);
+                    $this->$key = $val;
+                    break;
+
+                case 'BYSECOND' :
+                    $this->bySecond = (array)$value;
+                    break;
+
+                case 'BYMINUTE' :
+                    $this->byMinute = (array)$value;
+                    break;
+
+                case 'BYHOUR' :
+                    $this->byHour = (array)$value;
+                    break;
+
+                case 'BYDAY' :
+                    $value = (array)$value;
+                    foreach($value as $part) {
+                        if (!preg_match('#^  (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) {
+                            throw new \InvalidArgumentException('Invalid part in BYDAY clause: ' . $part);
+                        }
+                    }
+                    $this->byDay = $value;
+                    break;
+
+                case 'BYMONTHDAY' :
+                    $this->byMonthDay = (array)$value;
+                    break;
+
+                case 'BYYEARDAY' :
+                    $this->byYearDay = (array)$value;
+                    break;
+
+                case 'BYWEEKNO' :
+                    $this->byWeekNo = (array)$value;
+                    break;
+
+                case 'BYMONTH' :
+                    $this->byMonth = (array)$value;
+                    break;
+
+                case 'BYSETPOS' :
+                    $this->bySetPos = (array)$value;
+                    break;
+
+                case 'WKST' :
+                    $this->weekStart = strtoupper($value);
+                    break;
+
+                default:
+                    throw new \InvalidArgumentException('Not supported: ' . strtoupper($key));
+
+            }
+
+        }
+
+    }
+
+    /**
+     * Mappings between the day number and english day name.
+     *
+     * @var array
+     */
+    protected $dayNames = array(
+        0 => 'Sunday',
+        1 => 'Monday',
+        2 => 'Tuesday',
+        3 => 'Wednesday',
+        4 => 'Thursday',
+        5 => 'Friday',
+        6 => 'Saturday',
+    );
+
+    /**
+     * 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();
+
+            // workaround for missing 'first day of the month' support in hhvm
+            $checkDate = new \DateTime($startDate->format('Y-m-1'));
+            // workaround modify always advancing the date even if the current day is a $dayName in hhvm
+            if ($checkDate->format('l') !== $dayName) {
+                $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;
+
+    }
+
+    /**
+     * Simple mapping from iCalendar day names to day numbers
+     *
+     * @var array
+     */
+    protected $dayMap = array(
+        'SU' => 0,
+        'MO' => 1,
+        'TU' => 2,
+        'WE' => 3,
+        'TH' => 4,
+        'FR' => 5,
+        'SA' => 6,
+    );
+
+    protected function getHours()
+    {
+        $recurrenceHours = array();
+        foreach($this->byHour as $byHour) {
+            $recurrenceHours[] = $byHour;
+        }
+
+        return $recurrenceHours;
+    }
+
+    protected function getDays() {
+
+        $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)];
+
+        }
+
+        return $recurrenceDays;
+    }
+
+    protected function getMonths() {
+
+        $recurrenceMonths = array();
+        foreach($this->byMonth as $byMonth) {
+            $recurrenceMonths[] = $byMonth;
+        }
+
+        return $recurrenceMonths;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/RecurrenceIterator.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,21 @@
+<?php
+
+namespace Sabre\VObject;
+
+use Sabre\VObject\Recur\EventIterator;
+
+/**
+ * RecurrenceIterator
+ *
+ * This class is deprecated. Use Sabre\VObject\Recur\EventIterator instead.
+ * This class will be removed from a future version.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @deprecated
+ * @license http://sabre.io/license Modified BSD License
+ */
+class RecurrenceIterator extends EventIterator {
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Splitter/ICalendar.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,116 @@
+<?php
+
+namespace Sabre\VObject\Splitter;
+
+use
+    Sabre\VObject,
+    Sabre\VObject\Component\VCalendar;
+
+/**
+ * Splitter
+ *
+ * This class is responsible for splitting up iCalendar objects.
+ *
+ * This class expects a single VCALENDAR object with one or more
+ * calendar-objects inside. Objects with identical UID's will be combined into
+ * a single object.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Dominik Tobschall
+ * @author Armin Hackmann
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ICalendar implements SplitterInterface {
+
+    /**
+     * Timezones
+     *
+     * @var array
+     */
+    protected $vtimezones = array();
+
+    /**
+     * iCalendar objects
+     *
+     * @var array
+     */
+    protected $objects = array();
+
+    /**
+     * Constructor
+     *
+     * The splitter should receive an readable file stream as it's input.
+     *
+     * @param resource $input
+     * @param int $options Parser options, see the OPTIONS constants.
+     */
+    public function __construct($input, $options = 0) {
+
+        $data = VObject\Reader::read($input, $options);
+        $vtimezones = array();
+        $components = array();
+
+        if (!$data instanceof VObject\Component\VCalendar) {
+            throw new VObject\ParseException('Supplied input could not be parsed as VCALENDAR.');
+        }
+
+        foreach($data->children() as $component) {
+            if (!$component instanceof VObject\Component) {
+                continue;
+            }
+
+            // Get all timezones
+            if ($component->name === 'VTIMEZONE') {
+                $this->vtimezones[(string)$component->TZID] = $component;
+                continue;
+            }
+
+            // Get component UID for recurring Events search
+            if(!$component->UID) {
+                $component->UID = sha1(microtime()) . '-vobjectimport';
+            }
+            $uid = (string)$component->UID;
+
+            // Take care of recurring events
+            if (!array_key_exists($uid, $this->objects)) {
+                $this->objects[$uid] = new VCalendar();
+            }
+
+            $this->objects[$uid]->add(clone $component);
+        }
+
+    }
+
+    /**
+     * Every time getNext() is called, a new object will be parsed, until we
+     * hit the end of the stream.
+     *
+     * When the end is reached, null will be returned.
+     *
+     * @return Sabre\VObject\Component|null
+     */
+    public function getNext() {
+
+        if($object=array_shift($this->objects)) {
+
+            // create our baseobject
+            $object->version = '2.0';
+            $object->prodid = '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN';
+            $object->calscale = 'GREGORIAN';
+
+            // add vtimezone information to obj (if we have it)
+            foreach ($this->vtimezones as $vtimezone) {
+                $object->add($vtimezone);
+            }
+
+            return $object;
+
+        } else {
+
+            return null;
+
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Splitter/SplitterInterface.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,39 @@
+<?php
+
+namespace Sabre\VObject\Splitter;
+
+/**
+ * VObject splitter
+ *
+ * The splitter is responsible for reading a large vCard or iCalendar object,
+ * and splitting it into multiple objects.
+ *
+ * This is for example for Card and CalDAV, which require every event and vcard
+ * to exist in their own objects, instead of one large one.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Dominik Tobschall
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface SplitterInterface {
+
+    /**
+     * Constructor
+     *
+     * The splitter should receive an readable file stream as it's input.
+     *
+     * @param resource $input
+     */
+    public function __construct($input);
+
+    /**
+     * Every time getNext() is called, a new object will be parsed, until we
+     * hit the end of the stream.
+     *
+     * When the end is reached, null will be returned.
+     *
+     * @return Sabre\VObject\Component|null
+     */
+    public function getNext();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Splitter/VCard.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,79 @@
+<?php
+
+namespace Sabre\VObject\Splitter;
+
+use
+    Sabre\VObject,
+    Sabre\VObject\Parser\MimeDir;
+
+/**
+ * Splitter
+ *
+ * This class is responsible for splitting up VCard objects.
+ *
+ * It is assumed that the input stream contains 1 or more VCARD objects. This
+ * class checks for BEGIN:VCARD and END:VCARD and parses each encountered
+ * component individually.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Dominik Tobschall
+ * @author Armin Hackmann
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCard implements SplitterInterface {
+
+    /**
+     * File handle
+     *
+     * @var resource
+     */
+    protected $input;
+
+    /**
+     * Persistent parser
+     *
+     * @var MimeDir
+     */
+    protected $parser;
+
+    /**
+     * Constructor
+     *
+     * The splitter should receive an readable file stream as it's input.
+     *
+     * @param resource $input
+     * @param int $options Parser options, see the OPTIONS constants.
+     */
+    public function __construct($input, $options = 0) {
+
+        $this->input = $input;
+        $this->parser = new MimeDir($input, $options);
+
+    }
+
+    /**
+     * Every time getNext() is called, a new object will be parsed, until we
+     * hit the end of the stream.
+     *
+     * When the end is reached, null will be returned.
+     *
+     * @return Sabre\VObject\Component|null
+     */
+    public function getNext() {
+
+        try {
+            $object = $this->parser->parse();
+
+            if (!$object instanceof VObject\Component\VCard) {
+                throw new VObject\ParseException('The supplied input contained non-VCARD data.');
+            }
+
+        } catch (VObject\EofException $e) {
+            return null;
+        }
+
+        return $object;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/StringUtil.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,65 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Useful utilities for working with various strings.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class StringUtil {
+
+    /**
+     * Returns true or false depending on if a string is valid UTF-8
+     *
+     * @param string $str
+     * @return bool
+     */
+    static public function isUTF8($str) {
+
+        // Control characters
+        if (preg_match('%[\x00-\x08\x0B-\x0C\x0E\x0F]%', $str)) {
+            return false;
+        }
+
+        return (bool)preg_match('%%u', $str);
+
+    }
+
+    /**
+     * This method tries its best to convert the input string to UTF-8.
+     *
+     * Currently only ISO-5991-1 input and UTF-8 input is supported, but this
+     * may be expanded upon if we receive other examples.
+     *
+     * @param string $str
+     * @return string
+     */
+    static public function convertToUTF8($str) {
+
+        $encoding = mb_detect_encoding($str , array('UTF-8','ISO-8859-1', 'WINDOWS-1252'), true);
+
+        switch($encoding) {
+            case 'ISO-8859-1' :
+                $newStr = utf8_encode($str);
+                break;
+            /* Unreachable code. Not sure yet how we can improve this
+             * situation.
+            case 'WINDOWS-1252' :
+                $newStr = iconv('cp1252', 'UTF-8', $str);
+                break;
+             */
+            default :
+                 $newStr = $str;
+
+        }
+
+        // Removing any control characters
+        return (preg_replace('%(?:[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', '', $newStr));
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/TimeZoneUtil.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,265 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Time zone name translation
+ *
+ * This file translates well-known time zone names into "Olson database" time zone names.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Frank Edelhaeuser (fedel@users.sourceforge.net)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class TimeZoneUtil {
+
+    public static $map = null;
+
+    /**
+     * List of microsoft exchange timezone ids.
+     *
+     * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
+     */
+    public static $microsoftExchangeMap = array(
+        0  => 'UTC',
+        31 => 'Africa/Casablanca',
+
+        // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
+        // I'm not even kidding.. We handle this special case in the
+        // getTimeZone method.
+        2  => 'Europe/Lisbon',
+        1  => 'Europe/London',
+        4  => 'Europe/Berlin',
+        6  => 'Europe/Prague',
+        3  => 'Europe/Paris',
+        69 => 'Africa/Luanda', // This was a best guess
+        7  => 'Europe/Athens',
+        5  => 'Europe/Bucharest',
+        49 => 'Africa/Cairo',
+        50 => 'Africa/Harare',
+        59 => 'Europe/Helsinki',
+        27 => 'Asia/Jerusalem',
+        26 => 'Asia/Baghdad',
+        74 => 'Asia/Kuwait',
+        51 => 'Europe/Moscow',
+        56 => 'Africa/Nairobi',
+        25 => 'Asia/Tehran',
+        24 => 'Asia/Muscat', // Best guess
+        54 => 'Asia/Baku',
+        48 => 'Asia/Kabul',
+        58 => 'Asia/Yekaterinburg',
+        47 => 'Asia/Karachi',
+        23 => 'Asia/Calcutta',
+        62 => 'Asia/Kathmandu',
+        46 => 'Asia/Almaty',
+        71 => 'Asia/Dhaka',
+        66 => 'Asia/Colombo',
+        61 => 'Asia/Rangoon',
+        22 => 'Asia/Bangkok',
+        64 => 'Asia/Krasnoyarsk',
+        45 => 'Asia/Shanghai',
+        63 => 'Asia/Irkutsk',
+        21 => 'Asia/Singapore',
+        73 => 'Australia/Perth',
+        75 => 'Asia/Taipei',
+        20 => 'Asia/Tokyo',
+        72 => 'Asia/Seoul',
+        70 => 'Asia/Yakutsk',
+        19 => 'Australia/Adelaide',
+        44 => 'Australia/Darwin',
+        18 => 'Australia/Brisbane',
+        76 => 'Australia/Sydney',
+        43 => 'Pacific/Guam',
+        42 => 'Australia/Hobart',
+        68 => 'Asia/Vladivostok',
+        41 => 'Asia/Magadan',
+        17 => 'Pacific/Auckland',
+        40 => 'Pacific/Fiji',
+        67 => 'Pacific/Tongatapu',
+        29 => 'Atlantic/Azores',
+        53 => 'Atlantic/Cape_Verde',
+        30 => 'America/Noronha',
+         8 => 'America/Sao_Paulo', // Best guess
+        32 => 'America/Argentina/Buenos_Aires',
+        60 => 'America/Godthab',
+        28 => 'America/St_Johns',
+         9 => 'America/Halifax',
+        33 => 'America/Caracas',
+        65 => 'America/Santiago',
+        35 => 'America/Bogota',
+        10 => 'America/New_York',
+        34 => 'America/Indiana/Indianapolis',
+        55 => 'America/Guatemala',
+        11 => 'America/Chicago',
+        37 => 'America/Mexico_City',
+        36 => 'America/Edmonton',
+        38 => 'America/Phoenix',
+        12 => 'America/Denver', // Best guess
+        13 => 'America/Los_Angeles', // Best guess
+        14 => 'America/Anchorage',
+        15 => 'Pacific/Honolulu',
+        16 => 'Pacific/Midway',
+        39 => 'Pacific/Kwajalein',
+    );
+
+    /**
+     * This method will try to find out the correct timezone for an iCalendar
+     * date-time value.
+     *
+     * You must pass the contents of the TZID parameter, as well as the full
+     * calendar.
+     *
+     * If the lookup fails, this method will return the default PHP timezone
+     * (as configured using date_default_timezone_set, or the date.timezone ini
+     * setting).
+     *
+     * Alternatively, if $failIfUncertain is set to true, it will throw an
+     * exception if we cannot accurately determine the timezone.
+     *
+     * @param string $tzid
+     * @param Sabre\VObject\Component $vcalendar
+     * @return DateTimeZone
+     */
+    static public function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) {
+
+        // First we will just see if the tzid is a support timezone identifier.
+        //
+        // The only exception is if the timezone starts with (. This is to
+        // handle cases where certain microsoft products generate timezone
+        // identifiers that for instance look like:
+        //
+        // (GMT+01.00) Sarajevo/Warsaw/Zagreb
+        //
+        // Since PHP 5.5.10, the first bit will be used as the timezone and
+        // this method will return just GMT+01:00. This is wrong, because it
+        // doesn't take DST into account.
+        if ($tzid[0]!=='(') {
+
+            // PHP has a bug that logs PHP warnings even it shouldn't:
+            // https://bugs.php.net/bug.php?id=67881
+            //
+            // That's why we're checking if we'll be able to successfull instantiate
+            // \DateTimeZone() before doing so. Otherwise we could simply instantiate
+            // and catch the exception.
+            $tzIdentifiers = \DateTimeZone::listIdentifiers();
+
+            try {
+                if (
+                    (in_array($tzid, $tzIdentifiers)) ||
+                    (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) ||
+                    (in_array($tzid, self::getIdentifiersBC()))
+                ) {
+                    return new \DateTimeZone($tzid);
+                }
+            } catch(\Exception $e) {
+            }
+
+        }
+
+        self::loadTzMaps();
+
+        // Next, we check if the tzid is somewhere in our tzid map.
+        if (isset(self::$map[$tzid])) {
+            return new \DateTimeZone(self::$map[$tzid]);
+        }
+
+        // Maybe the author was hyper-lazy and just included an offset. We
+        // support it, but we aren't happy about it.
+        if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
+
+            // Note that the path in the source will never be taken from PHP 5.5.10
+            // onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
+            // already gets returned early in this function. Once we drop support
+            // for versions under PHP 5.5.10, this bit can be taken out of the
+            // source.
+            // @codeCoverageIgnoreStart
+            return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2],0,2),'0'));
+            // @codeCoverageIgnoreEnd
+        }
+
+        if ($vcalendar) {
+
+            // If that didn't work, we will scan VTIMEZONE objects
+            foreach($vcalendar->select('VTIMEZONE') as $vtimezone) {
+
+                if ((string)$vtimezone->TZID === $tzid) {
+
+                    // Some clients add 'X-LIC-LOCATION' with the olson name.
+                    if (isset($vtimezone->{'X-LIC-LOCATION'})) {
+
+                        $lic = (string)$vtimezone->{'X-LIC-LOCATION'};
+
+                        // Libical generators may specify strings like
+                        // "SystemV/EST5EDT". For those we must remove the
+                        // SystemV part.
+                        if (substr($lic,0,8)==='SystemV/') {
+                            $lic = substr($lic,8);
+                        }
+
+                        return self::getTimeZone($lic, null, $failIfUncertain);
+
+                    }
+                    // Microsoft may add a magic number, which we also have an
+                    // answer for.
+                    if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
+                        $cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();
+
+                        // 2 can mean both Europe/Lisbon and Europe/Sarajevo.
+                        if ($cdoId===2 && strpos((string)$vtimezone->TZID, 'Sarajevo')!==false) {
+                            return new \DateTimeZone('Europe/Sarajevo');
+                        }
+
+                        if (isset(self::$microsoftExchangeMap[$cdoId])) {
+                            return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
+                        }
+                    }
+
+                }
+
+            }
+
+        }
+
+        if ($failIfUncertain) {
+            throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid);
+        }
+
+        // If we got all the way here, we default to UTC.
+        return new \DateTimeZone(date_default_timezone_get());
+
+    }
+
+    /**
+     * This method will load in all the tz mapping information, if it's not yet
+     * done.
+     */
+    static public function loadTzMaps() {
+
+        if (!is_null(self::$map)) return;
+
+        self::$map = array_merge(
+            include __DIR__ .  '/timezonedata/windowszones.php',
+            include __DIR__ .  '/timezonedata/lotuszones.php',
+            include __DIR__ .  '/timezonedata/exchangezones.php',
+            include __DIR__ .  '/timezonedata/php-workaround.php'
+        );
+
+    }
+
+    /**
+     * This method returns an array of timezone identifiers, that are supported
+     * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers()
+     *
+     * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
+     * - It's not supported by some PHP versions as well as HHVM.
+     * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
+     * (See timezonedata/php-bc.php and timezonedata php-workaround.php)
+     *
+     * @return array
+     */
+    static public function getIdentifiersBC() {
+        return include __DIR__ .  '/timezonedata/php-bc.php';
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/UUIDUtil.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,67 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * UUID Utility
+ *
+ * This class has static methods to generate and validate UUID's.
+ * UUIDs are used a decent amount within various *DAV standards, so it made
+ * sense to include it.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class UUIDUtil {
+
+    /**
+     * Returns a pseudo-random v4 UUID
+     *
+     * This function is based on a comment by Andrew Moore on php.net
+     *
+     * @see http://www.php.net/manual/en/function.uniqid.php#94959
+     * @return string
+     */
+    static public function getUUID() {
+
+        return sprintf(
+
+            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+
+            // 32 bits for "time_low"
+            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
+
+            // 16 bits for "time_mid"
+            mt_rand( 0, 0xffff ),
+
+            // 16 bits for "time_hi_and_version",
+            // four most significant bits holds version number 4
+            mt_rand( 0, 0x0fff ) | 0x4000,
+
+            // 16 bits, 8 bits for "clk_seq_hi_res",
+            // 8 bits for "clk_seq_low",
+            // two most significant bits holds zero and one for variant DCE1.1
+            mt_rand( 0, 0x3fff ) | 0x8000,
+
+            // 48 bits for "node"
+            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
+        );
+    }
+
+    /**
+     * Checks if a string is a valid UUID.
+     *
+     * @param string $uuid
+     * @return bool
+     */
+    static public function validateUUID($uuid) {
+
+        return preg_match(
+            '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i',
+            $uuid
+        ) == true;
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/VCardConverter.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,459 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This utility converts vcards from one version to another.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCardConverter {
+
+    /**
+     * Converts a vCard object to a new version.
+     *
+     * targetVersion must be one of:
+     *   Document::VCARD21
+     *   Document::VCARD30
+     *   Document::VCARD40
+     *
+     * Currently only 3.0 and 4.0 as input and output versions.
+     *
+     * 2.1 has some minor support for the input version, it's incomplete at the
+     * moment though.
+     *
+     * If input and output version are identical, a clone is returned.
+     *
+     * @param Component\VCard $input
+     * @param int $targetVersion
+     */
+    public function convert(Component\VCard $input, $targetVersion) {
+
+        $inputVersion = $input->getDocumentType();
+        if ($inputVersion===$targetVersion) {
+            return clone $input;
+        }
+
+        if (!in_array($inputVersion, array(Document::VCARD21, Document::VCARD30, Document::VCARD40))) {
+            throw new \InvalidArgumentException('Only vCard 2.1, 3.0 and 4.0 are supported for the input data');
+        }
+        if (!in_array($targetVersion, array(Document::VCARD30, Document::VCARD40))) {
+            throw new \InvalidArgumentException('You can only use vCard 3.0 or 4.0 for the target version');
+        }
+
+        $newVersion = $targetVersion===Document::VCARD40?'4.0':'3.0';
+
+        $output = new Component\VCard(array(
+            'VERSION' => $newVersion,
+        ));
+
+        foreach($input->children as $property) {
+
+            $this->convertProperty($input, $output, $property, $targetVersion);
+
+        }
+
+        return $output;
+
+    }
+
+    /**
+     * Handles conversion of a single property.
+     *
+     * @param Component\VCard $input
+     * @param Component\VCard $output
+     * @param Property $property
+     * @param int $targetVersion
+     * @return void
+     */
+    protected function convertProperty(Component\VCard $input, Component\VCard $output, Property $property, $targetVersion) {
+
+        // Skipping these, those are automatically added.
+        if (in_array($property->name, array('VERSION', 'PRODID'))) {
+            return;
+        }
+
+        $parameters = $property->parameters();
+        $valueType = null;
+        if (isset($parameters['VALUE'])) {
+            $valueType = $parameters['VALUE']->getValue();
+            unset($parameters['VALUE']);
+        }
+        if (!$valueType) {
+            $valueType = $property->getValueType();
+        }
+        $newProperty = $output->createProperty(
+            $property->name,
+            $property->getParts(),
+            array(), // parameters will get added a bit later.
+            $valueType
+        );
+
+
+        if ($targetVersion===Document::VCARD30) {
+
+            if ($property instanceof Property\Uri && in_array($property->name, array('PHOTO','LOGO','SOUND'))) {
+
+                $newProperty = $this->convertUriToBinary($output, $newProperty);
+
+            } elseif ($property instanceof Property\VCard\DateAndOrTime) {
+
+                // In vCard 4, the birth year may be optional. This is not the
+                // case for vCard 3. Apple has a workaround for this that
+                // allows applications that support Apple's extension still
+                // omit birthyears in vCard 3, but applications that do not
+                // support this, will just use a random birthyear. We're
+                // choosing 1604 for the birthyear, because that's what apple
+                // uses.
+                $parts = DateTimeParser::parseVCardDateTime($property->getValue());
+                if (is_null($parts['year'])) {
+                    $newValue = '1604-' . $parts['month'] . '-' . $parts['date'];
+                    $newProperty->setValue($newValue);
+                    $newProperty['X-APPLE-OMIT-YEAR'] = '1604';
+                }
+
+                if ($newProperty->name == 'ANNIVERSARY') {
+                    // Microsoft non-standard anniversary
+                    $newProperty->name = 'X-ANNIVERSARY';
+
+                    // We also need to add a new apple property for the same
+                    // purpose. This apple property needs a 'label' in the same
+                    // group, so we first need to find a groupname that doesn't
+                    // exist yet.
+                    $x = 1;
+                    while($output->select('ITEM' . $x . '.')) {
+                        $x++;
+                    }
+                    $output->add('ITEM' . $x . '.X-ABDATE', $newProperty->getValue(), array('VALUE' => 'DATE-AND-OR-TIME'));
+                    $output->add('ITEM' . $x . '.X-ABLABEL', '_$!<Anniversary>!$_');
+                }
+
+            } elseif ($property->name === 'KIND') {
+
+                switch(strtolower($property->getValue())) {
+                    case 'org' :
+                        // vCard 3.0 does not have an equivalent to KIND:ORG,
+                        // but apple has an extension that means the same
+                        // thing.
+                        $newProperty = $output->createProperty('X-ABSHOWAS','COMPANY');
+                        break;
+
+                    case 'individual' :
+                        // Individual is implicit, so we skip it.
+                        return;
+
+                    case 'group' :
+                        // OS X addressbook property
+                        $newProperty = $output->createProperty('X-ADDRESSBOOKSERVER-KIND','GROUP');
+                        break;
+                }
+
+
+            }
+
+        } elseif ($targetVersion===Document::VCARD40) {
+
+            // These properties were removed in vCard 4.0
+            if (in_array($property->name, array('NAME', 'MAILER', 'LABEL', 'CLASS'))) {
+                return;
+            }
+
+            if ($property instanceof Property\Binary) {
+
+                $newProperty = $this->convertBinaryToUri($output, $newProperty, $parameters);
+
+            } elseif ($property instanceof Property\VCard\DateAndOrTime && isset($parameters['X-APPLE-OMIT-YEAR'])) {
+
+                // If a property such as BDAY contained 'X-APPLE-OMIT-YEAR',
+                // then we're stripping the year from the vcard 4 value.
+                $parts = DateTimeParser::parseVCardDateTime($property->getValue());
+                if ($parts['year']===$property['X-APPLE-OMIT-YEAR']->getValue()) {
+                    $newValue = '--' . $parts['month'] . '-' . $parts['date'];
+                    $newProperty->setValue($newValue);
+                }
+
+                // Regardless if the year matched or not, we do need to strip
+                // X-APPLE-OMIT-YEAR.
+                unset($parameters['X-APPLE-OMIT-YEAR']);
+
+            }
+            switch($property->name) {
+                case 'X-ABSHOWAS' :
+                    if (strtoupper($property->getValue()) === 'COMPANY') {
+                        $newProperty = $output->createProperty('KIND','ORG');
+                    }
+                    break;
+                case 'X-ADDRESSBOOKSERVER-KIND' :
+                    if (strtoupper($property->getValue()) === 'GROUP') {
+                        $newProperty = $output->createProperty('KIND','GROUP');
+                    }
+                    break;
+                case 'X-ANNIVERSARY' :
+                    $newProperty->name = 'ANNIVERSARY';
+                    // If we already have an anniversary property with the same
+                    // value, ignore.
+                    foreach ($output->select('ANNIVERSARY') as $anniversary) {
+                        if ($anniversary->getValue() === $newProperty->getValue()) {
+                            return;
+                        }
+                    }
+                    break;
+                case 'X-ABDATE' :
+                    // Find out what the label was, if it exists.
+                    if (!$property->group) {
+                        break;
+                    }
+                    $label = $input->{$property->group . '.X-ABLABEL'};
+
+                    // We only support converting anniversaries.
+                    if (!$label || $label->getValue()!=='_$!<Anniversary>!$_') {
+                        break;
+                    }
+
+                    // If we already have an anniversary property with the same
+                    // value, ignore.
+                    foreach ($output->select('ANNIVERSARY') as $anniversary) {
+                        if ($anniversary->getValue() === $newProperty->getValue()) {
+                            return;
+                        }
+                    }
+                    $newProperty->name = 'ANNIVERSARY';
+                    break;
+                // Apple's per-property label system.
+                case 'X-ABLABEL' :
+                    if($newProperty->getValue() === '_$!<Anniversary>!$_') {
+                        // We can safely remove these, as they are converted to
+                        // ANNIVERSARY properties.
+                        return;
+                    }
+                    break;
+
+            }
+
+        }
+
+        // set property group
+        $newProperty->group = $property->group;
+
+        if ($targetVersion===Document::VCARD40) {
+            $this->convertParameters40($newProperty, $parameters);
+        } else {
+            $this->convertParameters30($newProperty, $parameters);
+        }
+
+        // Lastly, we need to see if there's a need for a VALUE parameter.
+        //
+        // We can do that by instantating a empty property with that name, and
+        // seeing if the default valueType is identical to the current one.
+        $tempProperty = $output->createProperty($newProperty->name);
+        if ($tempProperty->getValueType() !== $newProperty->getValueType()) {
+            $newProperty['VALUE'] = $newProperty->getValueType();
+        }
+
+        $output->add($newProperty);
+
+
+    }
+
+    /**
+     * Converts a BINARY property to a URI property.
+     *
+     * vCard 4.0 no longer supports BINARY properties.
+     *
+     * @param Component\VCard $output
+     * @param Property\Uri $property The input property.
+     * @param $parameters List of parameters that will eventually be added to
+     *                    the new property.
+     * @return Property\Uri
+     */
+    protected function convertBinaryToUri(Component\VCard $output, Property\Binary $newProperty, array &$parameters) {
+
+        $value = $newProperty->getValue();
+        $newProperty = $output->createProperty(
+            $newProperty->name,
+            null, // no value
+            array(), // no parameters yet
+            'URI' // Forcing the BINARY type
+        );
+
+        $mimeType = 'application/octet-stream';
+
+        // See if we can find a better mimetype.
+        if (isset($parameters['TYPE'])) {
+
+            $newTypes = array();
+            foreach($parameters['TYPE']->getParts() as $typePart) {
+                if (in_array(
+                    strtoupper($typePart),
+                    array('JPEG','PNG','GIF')
+                )) {
+                    $mimeType = 'image/' . strtolower($typePart);
+                } else {
+                    $newTypes[] = $typePart;
+                }
+            }
+
+            // If there were any parameters we're not converting to a
+            // mime-type, we need to keep them.
+            if ($newTypes) {
+                $parameters['TYPE']->setParts($newTypes);
+            } else {
+                unset($parameters['TYPE']);
+            }
+
+        }
+
+        $newProperty->setValue('data:' . $mimeType . ';base64,' . base64_encode($value));
+        return $newProperty;
+
+    }
+
+    /**
+     * Converts a URI property to a BINARY property.
+     *
+     * In vCard 4.0 attachments are encoded as data: uri. Even though these may
+     * be valid in vCard 3.0 as well, we should convert those to BINARY if
+     * possible, to improve compatibility.
+     *
+     * @param Component\VCard $output
+     * @param Property\Uri $property The input property.
+     * @return Property\Binary|null
+     */
+    protected function convertUriToBinary(Component\VCard $output, Property\Uri $newProperty) {
+
+        $value = $newProperty->getValue();
+
+        // Only converting data: uris
+        if (substr($value, 0, 5)!=='data:') {
+            return $newProperty;
+        }
+
+        $newProperty = $output->createProperty(
+            $newProperty->name,
+            null, // no value
+            array(), // no parameters yet
+            'BINARY'
+        );
+
+        $mimeType = substr($value, 5, strpos($value, ',')-5);
+        if (strpos($mimeType, ';')) {
+            $mimeType = substr($mimeType,0,strpos($mimeType, ';'));
+            $newProperty->setValue(base64_decode(substr($value, strpos($value,',')+1)));
+        } else {
+            $newProperty->setValue(substr($value, strpos($value,',')+1));
+        }
+        unset($value);
+
+        $newProperty['ENCODING'] = 'b';
+        switch($mimeType) {
+
+            case 'image/jpeg' :
+                $newProperty['TYPE'] = 'JPEG';
+                break;
+            case 'image/png' :
+                $newProperty['TYPE'] = 'PNG';
+                break;
+            case 'image/gif' :
+                $newProperty['TYPE'] = 'GIF';
+                break;
+
+        }
+
+
+        return $newProperty;
+
+    }
+
+    /**
+     * Adds parameters to a new property for vCard 4.0
+     *
+     * @param Property $newProperty
+     * @param array $parameters
+     * @return void
+     */
+    protected function convertParameters40(Property $newProperty, array $parameters) {
+
+        // Adding all parameters.
+        foreach($parameters as $param) {
+
+            // vCard 2.1 allowed parameters with no name
+            if ($param->noName) $param->noName = false;
+
+            switch($param->name) {
+
+                // We need to see if there's any TYPE=PREF, because in vCard 4
+                // that's now PREF=1.
+                case 'TYPE' :
+                    foreach($param->getParts() as $paramPart) {
+
+                        if (strtoupper($paramPart)==='PREF') {
+                            $newProperty->add('PREF','1');
+                        } else {
+                            $newProperty->add($param->name, $paramPart);
+                        }
+
+                    }
+                    break;
+                // These no longer exist in vCard 4
+                case 'ENCODING' :
+                case 'CHARSET' :
+                    break;
+
+                default :
+                    $newProperty->add($param->name, $param->getParts());
+                    break;
+
+            }
+
+        }
+
+    }
+
+    /**
+     * Adds parameters to a new property for vCard 3.0
+     *
+     * @param Property $newProperty
+     * @param array $parameters
+     * @return void
+     */
+    protected function convertParameters30(Property $newProperty, array $parameters) {
+
+        // Adding all parameters.
+        foreach($parameters as $param) {
+
+            // vCard 2.1 allowed parameters with no name
+            if ($param->noName) $param->noName = false;
+
+            switch($param->name) {
+
+                case 'ENCODING' :
+                    // This value only existed in vCard 2.1, and should be
+                    // removed for anything else.
+                    if (strtoupper($param->getValue())!=='QUOTED-PRINTABLE') {
+                        $newProperty->add($param->name, $param->getParts());
+                    }
+                    break;
+
+                /*
+                 * Converting PREF=1 to TYPE=PREF.
+                 *
+                 * Any other PREF numbers we'll drop.
+                 */
+                case 'PREF' :
+                    if ($param->getValue()=='1') {
+                        $newProperty->add('TYPE','PREF');
+                    }
+                    break;
+
+                default :
+                    $newProperty->add($param->name, $param->getParts());
+                    break;
+
+            }
+
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/Version.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This class contains the version number for the VObject package
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '3.3.5';
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/timezonedata/exchangezones.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Microsoft exchange timezones
+ * Source:
+ * http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx
+ *
+ * Correct timezones deduced with help from:
+ * http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return array(
+    'Universal Coordinated Time' => 'UTC',
+    'Casablanca, Monrovia' => 'Africa/Casablanca',
+    'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon',
+    'Greenwich Mean Time; Dublin, Edinburgh, London' =>  'Europe/London',
+    'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
+    'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague',
+    'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
+    'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris',
+    'Prague, Central Europe' => 'Europe/Prague',
+    'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo',
+    'West Central Africa' => 'Africa/Luanda', // This was a best guess
+    'Athens, Istanbul, Minsk' => 'Europe/Athens',
+    'Bucharest' => 'Europe/Bucharest',
+    'Cairo' => 'Africa/Cairo',
+    'Harare, Pretoria' => 'Africa/Harare',
+    'Helsinki, Riga, Tallinn' => 'Europe/Helsinki',
+    'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem',
+    'Baghdad' => 'Asia/Baghdad',
+    'Arab, Kuwait, Riyadh' => 'Asia/Kuwait',
+    'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
+    'East Africa, Nairobi' => 'Africa/Nairobi',
+    'Tehran' => 'Asia/Tehran',
+    'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess
+    'Baku, Tbilisi, Yerevan' => 'Asia/Baku',
+    'Kabul' => 'Asia/Kabul',
+    'Ekaterinburg' => 'Asia/Yekaterinburg',
+    'Islamabad, Karachi, Tashkent' => 'Asia/Karachi',
+    'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta',
+    'Kathmandu, Nepal' => 'Asia/Kathmandu',
+    'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty',
+    'Astana, Dhaka' => 'Asia/Dhaka',
+    'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo',
+    'Rangoon' => 'Asia/Rangoon',
+    'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
+    'Krasnoyarsk' => 'Asia/Krasnoyarsk',
+    'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai',
+    'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk',
+    'Kuala Lumpur, Singapore' => 'Asia/Singapore',
+    'Perth, Western Australia' => 'Australia/Perth',
+    'Taipei' => 'Asia/Taipei',
+    'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
+    'Seoul, Korea Standard time' => 'Asia/Seoul',
+    'Yakutsk' => 'Asia/Yakutsk',
+    'Adelaide, Central Australia' => 'Australia/Adelaide',
+    'Darwin' => 'Australia/Darwin',
+    'Brisbane, East Australia' => 'Australia/Brisbane',
+    'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney',
+    'Guam, Port Moresby' => 'Pacific/Guam',
+    'Hobart, Tasmania' => 'Australia/Hobart',
+    'Vladivostok' => 'Asia/Vladivostok',
+    'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan',
+    'Auckland, Wellington' => 'Pacific/Auckland',
+    'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji',
+    'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu',
+    'Azores' => 'Atlantic/Azores',
+    'Cape Verde Is.' => 'Atlantic/Cape_Verde',
+    'Mid-Atlantic' => 'America/Noronha',
+    'Brasilia' => 'America/Sao_Paulo', // Best guess
+    'Buenos Aires' => 'America/Argentina/Buenos_Aires',
+    'Greenland' => 'America/Godthab',
+    'Newfoundland' => 'America/St_Johns',
+    'Atlantic Time (Canada)' => 'America/Halifax',
+    'Caracas, La Paz' => 'America/Caracas',
+    'Santiago' => 'America/Santiago',
+    'Bogota, Lima, Quito' => 'America/Bogota',
+    'Eastern Time (US & Canada)' => 'America/New_York',
+    'Indiana (East)' => 'America/Indiana/Indianapolis',
+    'Central America' => 'America/Guatemala',
+    'Central Time (US & Canada)' => 'America/Chicago',
+    'Mexico City, Tegucigalpa' => 'America/Mexico_City',
+    'Saskatchewan' => 'America/Edmonton',
+    'Arizona' => 'America/Phoenix',
+    'Mountain Time (US & Canada)' => 'America/Denver', // Best guess
+    'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess
+    'Alaska' => 'America/Anchorage',
+    'Hawaii' => 'Pacific/Honolulu',
+    'Midway Island, Samoa' => 'Pacific/Midway',
+    'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein',
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/timezonedata/lotuszones.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * The following list are timezone names that could be generated by
+ * Lotus / Domino
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return array(
+    'Dateline'               => 'Etc/GMT-12',
+    'Samoa'                  => 'Pacific/Apia',
+    'Hawaiian'               => 'Pacific/Honolulu',
+    'Alaskan'                => 'America/Anchorage',
+    'Pacific'                => 'America/Los_Angeles',
+    'Pacific Standard Time'  => 'America/Los_Angeles',
+    'Mexico Standard Time 2' => 'America/Chihuahua',
+    'Mountain'               => 'America/Denver',
+    // 'Mountain Standard Time' => 'America/Chihuahua', // conflict with windows timezones.
+    'US Mountain'            => 'America/Phoenix',
+    'Canada Central'         => 'America/Edmonton',
+    'Central America'        => 'America/Guatemala',
+    'Central'                => 'America/Chicago',
+    // 'Central Standard Time'  => 'America/Mexico_City', // conflict with windows timezones.
+    'Mexico'                 => 'America/Mexico_City',
+    'Eastern'                => 'America/New_York',
+    'SA Pacific'             => 'America/Bogota',
+    'US Eastern'             => 'America/Indiana/Indianapolis',
+    'Venezuela'              => 'America/Caracas',
+    'Atlantic'               => 'America/Halifax',
+    'Central Brazilian'      => 'America/Manaus',
+    'Pacific SA'             => 'America/Santiago',
+    'SA Western'             => 'America/La_Paz',
+    'Newfoundland'           => 'America/St_Johns',
+    'Argentina'              => 'America/Argentina/Buenos_Aires',
+    'E. South America'       => 'America/Belem',
+    'Greenland'              => 'America/Godthab',
+    'Montevideo'             => 'America/Montevideo',
+    'SA Eastern'             => 'America/Belem',
+    // 'Mid-Atlantic'           => 'Etc/GMT-2', // conflict with windows timezones.
+    'Azores'                 => 'Atlantic/Azores',
+    'Cape Verde'             => 'Atlantic/Cape_Verde',
+    'Greenwich'              => 'Atlantic/Reykjavik', // No I'm serious.. Greenwich is not GMT.
+    'Morocco'                => 'Africa/Casablanca',
+    'Central Europe'         => 'Europe/Prague',
+    'Central European'       => 'Europe/Sarajevo',
+    'Romance'                => 'Europe/Paris',
+    'W. Central Africa'      => 'Africa/Lagos', // Best guess
+    'W. Europe'              => 'Europe/Amsterdam',
+    'E. Europe'              => 'Europe/Minsk',
+    'Egypt'                  => 'Africa/Cairo',
+    'FLE'                    => 'Europe/Helsinki',
+    'GTB'                    => 'Europe/Athens',
+    'Israel'                 => 'Asia/Jerusalem',
+    'Jordan'                 => 'Asia/Amman',
+    'Middle East'            => 'Asia/Beirut',
+    'Namibia'                => 'Africa/Windhoek',
+    'South Africa'           => 'Africa/Harare',
+    'Arab'                   => 'Asia/Kuwait',
+    'Arabic'                 => 'Asia/Baghdad',
+    'E. Africa'              => 'Africa/Nairobi',
+    'Georgian'               => 'Asia/Tbilisi',
+    'Russian'                => 'Europe/Moscow',
+    'Iran'                   => 'Asia/Tehran',
+    'Arabian'                => 'Asia/Muscat',
+    'Armenian'               => 'Asia/Yerevan',
+    'Azerbijan'              => 'Asia/Baku',
+    'Caucasus'               => 'Asia/Yerevan',
+    'Mauritius'              => 'Indian/Mauritius',
+    'Afghanistan'            => 'Asia/Kabul',
+    'Ekaterinburg'           => 'Asia/Yekaterinburg',
+    'Pakistan'               => 'Asia/Karachi',
+    'West Asia'              => 'Asia/Tashkent',
+    'India'                  => 'Asia/Calcutta',
+    'Sri Lanka'              => 'Asia/Colombo',
+    'Nepal'                  => 'Asia/Kathmandu',
+    'Central Asia'           => 'Asia/Dhaka',
+    'N. Central Asia'        => 'Asia/Almaty',
+    'Myanmar'                => 'Asia/Rangoon',
+    'North Asia'             => 'Asia/Krasnoyarsk',
+    'SE Asia'                => 'Asia/Bangkok',
+    'China'                  => 'Asia/Shanghai',
+    'North Asia East'        => 'Asia/Irkutsk',
+    'Singapore'              => 'Asia/Singapore',
+    'Taipei'                 => 'Asia/Taipei',
+    'W. Australia'           => 'Australia/Perth',
+    'Korea'                  => 'Asia/Seoul',
+    'Tokyo'                  => 'Asia/Tokyo',
+    'Yakutsk'                => 'Asia/Yakutsk',
+    'AUS Central'            => 'Australia/Darwin',
+    'Cen. Australia'         => 'Australia/Adelaide',
+    'AUS Eastern'            => 'Australia/Sydney',
+    'E. Australia'           => 'Australia/Brisbane',
+    'Tasmania'               => 'Australia/Hobart',
+    'Vladivostok'            => 'Asia/Vladivostok',
+    'West Pacific'           => 'Pacific/Guam',
+    'Central Pacific'        => 'Asia/Magadan',
+    'Fiji'                   => 'Pacific/Fiji',
+    'New Zealand'            => 'Pacific/Auckland',
+    'Tonga'                  => 'Pacific/Tongatapu',
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/timezonedata/php-bc.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,153 @@
+<?php
+/**
+ * A list of additional PHP timezones that are returned by
+ * DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC)
+ * valid for new DateTimeZone()
+ *
+ * This list does not include those timezone identifiers that we have to map to
+ * a different identifier for some PHP versions (see php-workaround.php).
+ *
+ * Instead of using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC)
+ * directly, we use this file because DateTimeZone::ALL_WITH_BC is not properly
+ * supported by all PHP version and HHVM.
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return array(
+    'Africa/Asmera',
+    'Africa/Timbuktu',
+    'America/Argentina/ComodRivadavia',
+    'America/Atka',
+    'America/Buenos_Aires',
+    'America/Catamarca',
+    'America/Coral_Harbour',
+    'America/Cordoba',
+    'America/Ensenada',
+    'America/Fort_Wayne',
+    'America/Indianapolis',
+    'America/Jujuy',
+    'America/Knox_IN',
+    'America/Louisville',
+    'America/Mendoza',
+    'America/Montreal',
+    'America/Porto_Acre',
+    'America/Rosario',
+    'America/Shiprock',
+    'America/Virgin',
+    'Antarctica/South_Pole',
+    'Asia/Ashkhabad',
+    'Asia/Calcutta',
+    'Asia/Chungking',
+    'Asia/Dacca',
+    'Asia/Istanbul',
+    'Asia/Katmandu',
+    'Asia/Macao',
+    'Asia/Saigon',
+    'Asia/Tel_Aviv',
+    'Asia/Thimbu',
+    'Asia/Ujung_Pandang',
+    'Asia/Ulan_Bator',
+    'Atlantic/Faeroe',
+    'Atlantic/Jan_Mayen',
+    'Australia/ACT',
+    'Australia/Canberra',
+    'Australia/LHI',
+    'Australia/North',
+    'Australia/NSW',
+    'Australia/Queensland',
+    'Australia/South',
+    'Australia/Tasmania',
+    'Australia/Victoria',
+    'Australia/West',
+    'Australia/Yancowinna',
+    'Brazil/Acre',
+    'Brazil/DeNoronha',
+    'Brazil/East',
+    'Brazil/West',
+    'Canada/Atlantic',
+    'Canada/Central',
+    'Canada/East-Saskatchewan',
+    'Canada/Eastern',
+    'Canada/Mountain',
+    'Canada/Newfoundland',
+    'Canada/Pacific',
+    'Canada/Saskatchewan',
+    'Canada/Yukon',
+    'CET',
+    'Chile/Continental',
+    'Chile/EasterIsland',
+    'EET',
+    'EST',
+    'Etc/GMT',
+    'Etc/GMT+0',
+    'Etc/GMT+1',
+    'Etc/GMT+10',
+    'Etc/GMT+11',
+    'Etc/GMT+12',
+    'Etc/GMT+2',
+    'Etc/GMT+3',
+    'Etc/GMT+4',
+    'Etc/GMT+5',
+    'Etc/GMT+6',
+    'Etc/GMT+7',
+    'Etc/GMT+8',
+    'Etc/GMT+9',
+    'Etc/GMT-0',
+    'Etc/GMT-1',
+    'Etc/GMT-10',
+    'Etc/GMT-11',
+    'Etc/GMT-12',
+    'Etc/GMT-13',
+    'Etc/GMT-14',
+    'Etc/GMT-2',
+    'Etc/GMT-3',
+    'Etc/GMT-4',
+    'Etc/GMT-5',
+    'Etc/GMT-6',
+    'Etc/GMT-7',
+    'Etc/GMT-8',
+    'Etc/GMT-9',
+    'Etc/GMT0',
+    'Etc/Greenwich',
+    'Etc/UCT',
+    'Etc/Universal',
+    'Etc/UTC',
+    'Etc/Zulu',
+    'Europe/Belfast',
+    'Europe/Nicosia',
+    'Europe/Tiraspol',
+    'GB',
+    'GMT',
+    'GMT+0',
+    'GMT-0',
+    'HST',
+    'MET',
+    'Mexico/BajaNorte',
+    'Mexico/BajaSur',
+    'Mexico/General',
+    'MST',
+    'NZ',
+    'Pacific/Ponape',
+    'Pacific/Samoa',
+    'Pacific/Truk',
+    'Pacific/Yap',
+    'PRC',
+    'ROC',
+    'ROK',
+    'UCT',
+    'US/Alaska',
+    'US/Aleutian',
+    'US/Arizona',
+    'US/Central',
+    'US/East-Indiana',
+    'US/Eastern',
+    'US/Hawaii',
+    'US/Indiana-Starke',
+    'US/Michigan',
+    'US/Mountain',
+    'US/Pacific',
+    'US/Pacific-New',
+    'US/Samoa',
+    'WET',
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/timezonedata/php-workaround.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,45 @@
+<?php
+/**
+ * A list of PHP timezones that were supported until 5.5.9, removed in
+ * PHP 5.5.10 and re-introduced in PHP 5.5.17
+ *
+ * DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) returns them,
+ * but they are invalid for new DateTimeZone(). Fixed in PHP 5.5.17.
+ * https://bugs.php.net/bug.php?id=66985
+ *
+ * Some more info here:
+ * http://evertpot.com/php-5-5-10-timezone-changes/
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+return array(
+    'CST6CDT'   => 'America/Chicago',
+    'Cuba'      => 'America/Havana',
+    'Egypt'     => 'Africa/Cairo',
+    'Eire'      => 'Europe/Dublin',
+    'EST5EDT'   => 'America/New_York',
+    'Factory'   => 'UTC',
+    'GB-Eire'   => 'Europe/London',
+    'GMT0'      => 'UTC',
+    'Greenwich' => 'UTC',
+    'Hongkong'  => 'Asia/Hong_Kong',
+    'Iceland'   => 'Atlantic/Reykjavik',
+    'Iran'      => 'Asia/Tehran',
+    'Israel'    => 'Asia/Jerusalem',
+    'Jamaica'   => 'America/Jamaica',
+    'Japan'     => 'Asia/Tokyo',
+    'Kwajalein' => 'Pacific/Kwajalein',
+    'Libya'     => 'Africa/Tripoli',
+    'MST7MDT'   => 'America/Denver',
+    'Navajo'    => 'America/Denver',
+    'NZ-CHAT'   => 'Pacific/Chatham',
+    'Poland'    => 'Europe/Warsaw',
+    'Portugal'  => 'Europe/Lisbon',
+    'PST8PDT'   => 'America/Los_Angeles',
+    'Singapore' => 'Asia/Singapore',
+    'Turkey'    => 'Europe/Istanbul',
+    'Universal' => 'UTC',
+    'W-SU'      => 'Europe/Moscow',
+    'Zulu'      => 'UTC',
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/lib/timezonedata/windowszones.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * Automatically generated timezone file
+ *
+ * Last update: 2014-10-03T07:58:31-04:00
+ * Source: http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml
+ *
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+
+return array (
+  'AUS Central Standard Time' => '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',
+  'Atlantic Standard Time' => 'America/Halifax',
+  'Azerbaijan Standard Time' => 'Asia/Baku',
+  'Azores Standard Time' => 'Atlantic/Azores',
+  'Bahia Standard Time' => 'America/Bahia',
+  '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. 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/Bucharest',
+  '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',
+  'Kaliningrad Standard Time' => 'Europe/Kaliningrad',
+  'Korea Standard Time' => 'Asia/Seoul',
+  'Libya Standard Time' => 'Africa/Tripoli',
+  'Line Islands Standard Time' => 'Pacific/Kiritimati',
+  'Magadan Standard Time' => 'Asia/Magadan',
+  'Mauritius Standard Time' => 'Indian/Mauritius',
+  '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',
+  'Turkey Standard Time' => 'Europe/Istanbul',
+  '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',
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/AttachIssueTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,22 @@
+<?php
+
+namespace Sabre\VObject;
+
+class AttachIssueTest extends \PHPUnit_Framework_TestCase {
+
+    function testRead() {
+
+        $event = <<<ICS
+BEGIN:VCALENDAR\r
+BEGIN:VEVENT\r
+ATTACH;FMTTYPE=;ENCODING=:Zm9v\r
+END:VEVENT\r
+END:VCALENDAR\r
+
+ICS;
+        $obj = Reader::read($event);
+        $this->assertEquals($event, $obj->serialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/CliTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,650 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Tests the cli.
+ *
+ * Warning: these tests are very rudimentary.
+ */
+class CliTest extends \PHPUnit_Framework_TestCase {
+
+    public function setUp() {
+
+        $this->cli = new CliMock();
+        $this->cli->stderr = fopen('php://memory','r+');
+        $this->cli->stdout = fopen('php://memory','r+');
+
+    }
+
+    public function testInvalidArg() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', '--hi'))
+        );
+        rewind($this->cli->stderr);
+        $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100);
+
+    }
+
+    public function testQuiet() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', '-q'))
+        );
+        $this->assertTrue($this->cli->quiet);
+
+        rewind($this->cli->stderr);
+        $this->assertEquals(0, strlen(stream_get_contents($this->cli->stderr)));
+
+    }
+
+    public function testHelp() {
+
+        $this->assertEquals(
+            0,
+            $this->cli->main(array('vobject', '-h'))
+        );
+        rewind($this->cli->stderr);
+        $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100);
+
+    }
+
+    public function testFormat() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', '--format=jcard'))
+        );
+
+        rewind($this->cli->stderr);
+        $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100);
+
+        $this->assertEquals('jcard', $this->cli->format);
+
+    }
+
+    public function testFormatInvalid() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', '--format=foo'))
+        );
+
+        rewind($this->cli->stderr);
+        $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100);
+
+        $this->assertNull($this->cli->format);
+
+    }
+
+    public function testInputFormatInvalid() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', '--inputformat=foo'))
+        );
+
+        rewind($this->cli->stderr);
+        $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100);
+
+        $this->assertNull($this->cli->format);
+
+    }
+
+
+    public function testNoInputFile() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', 'color'))
+        );
+
+        rewind($this->cli->stderr);
+        $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100);
+
+    }
+
+    public function testTooManyArgs() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', 'color', 'a', 'b', 'c'))
+        );
+
+    }
+
+    public function testUnknownCommand() {
+
+        $this->assertEquals(
+            1,
+            $this->cli->main(array('vobject', 'foo', '-'))
+        );
+
+    }
+
+    public function testConvertJson() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<ICS
+BEGIN:VCARD
+VERSION:3.0
+FN:Cowboy Henk
+END:VCARD
+ICS
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+
+        $this->assertEquals(
+            0,
+            $this->cli->main(array('vobject', 'convert','--format=json', '-'))
+        );
+
+        rewind($this->cli->stdout);
+        $version = Version::VERSION;
+        $this->assertEquals(
+            '["vcard",[["version",{},"text","4.0"],["prodid",{},"text","-\/\/Sabre\/\/Sabre VObject '. $version .'\/\/EN"],["fn",{},"text","Cowboy Henk"]]]',
+            stream_get_contents($this->cli->stdout)
+        );
+
+    }
+
+    public function testConvertJCardPretty() {
+
+        if (version_compare(PHP_VERSION, '5.4.0') < 0) {
+            $this->markTestSkipped('This test required PHP 5.4.0');
+        }
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<ICS
+BEGIN:VCARD
+VERSION:3.0
+FN:Cowboy Henk
+END:VCARD
+ICS
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+
+        $this->assertEquals(
+            0,
+            $this->cli->main(array('vobject', 'convert','--format=jcard', '--pretty', '-'))
+        );
+
+        rewind($this->cli->stdout);
+        $version = Version::VERSION;
+
+        // PHP 5.5.12 changed the output
+
+        $expected = <<<JCARD
+[
+    "vcard",
+    [
+        [
+            "versi
+JCARD;
+
+          $this->assertStringStartsWith(
+            $expected,
+            stream_get_contents($this->cli->stdout)
+        );
+
+    }
+
+    public function testConvertJCalFail() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<ICS
+BEGIN:VCARD
+VERSION:3.0
+FN:Cowboy Henk
+END:VCARD
+ICS
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+
+        $this->assertEquals(
+            2,
+            $this->cli->main(array('vobject', 'convert','--format=jcal', '--inputformat=mimedir', '-'))
+        );
+
+    }
+
+    public function testConvertMimeDir() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<JCARD
+[
+    "vcard",
+    [
+        [
+            "version",
+            {
+
+            },
+            "text",
+            "4.0"
+        ],
+        [
+            "prodid",
+            {
+
+            },
+            "text",
+            "-\/\/Sabre\/\/Sabre VObject 3.1.0\/\/EN"
+        ],
+        [
+            "fn",
+            {
+
+            },
+            "text",
+            "Cowboy Henk"
+        ]
+    ]
+]
+JCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+
+        $this->assertEquals(
+            0,
+            $this->cli->main(array('vobject', 'convert','--format=mimedir', '--inputformat=json', '--pretty', '-'))
+        );
+
+        rewind($this->cli->stdout);
+        $expected = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject 3.1.0//EN
+FN:Cowboy Henk
+END:VCARD
+
+VCF;
+
+          $this->assertEquals(
+            strtr($expected, array("\n"=>"\r\n")),
+            stream_get_contents($this->cli->stdout)
+        );
+
+    }
+
+    public function testConvertDefaultFormats() {
+
+        $inputStream = fopen('php://memory','r+');
+        $outputFile = SABRE_TEMPDIR . 'bar.json';
+
+        $this->assertEquals(
+            2,
+            $this->cli->main(array('vobject', 'convert','foo.json',$outputFile))
+        );
+
+        $this->assertEquals('json', $this->cli->inputFormat);
+        $this->assertEquals('json', $this->cli->format);
+
+    }
+
+    public function testConvertDefaultFormats2() {
+
+        $outputFile = SABRE_TEMPDIR . 'bar.ics';
+
+        $this->assertEquals(
+            2,
+            $this->cli->main(array('vobject', 'convert','foo.ics',$outputFile))
+        );
+
+        $this->assertEquals('mimedir', $this->cli->inputFormat);
+        $this->assertEquals('mimedir', $this->cli->format);
+
+    }
+
+    public function testVCard3040() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 3.1.0//EN
+FN:Cowboy Henk
+END:VCARD
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+
+        $this->assertEquals(
+            0,
+            $this->cli->main(array('vobject', 'convert','--format=vcard40', '--pretty', '-'))
+        );
+
+        rewind($this->cli->stdout);
+
+        $version = Version::VERSION;
+        $expected = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+FN:Cowboy Henk
+END:VCARD
+
+VCF;
+
+          $this->assertEquals(
+            strtr($expected, array("\n"=>"\r\n")),
+            stream_get_contents($this->cli->stdout)
+        );
+
+    }
+
+    public function testVCard4030() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject 3.1.0//EN
+FN:Cowboy Henk
+END:VCARD
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+
+        $this->assertEquals(
+            0,
+            $this->cli->main(array('vobject', 'convert','--format=vcard30', '--pretty', '-'))
+        );
+
+        $version = Version::VERSION;
+
+        rewind($this->cli->stdout);
+        $expected = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+FN:Cowboy Henk
+END:VCARD
+
+VCF;
+
+          $this->assertEquals(
+            strtr($expected, array("\n"=>"\r\n")),
+            stream_get_contents($this->cli->stdout)
+        );
+
+    }
+
+    public function testVCard4021() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject 3.1.0//EN
+FN:Cowboy Henk
+END:VCARD
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+
+        // vCard 2.1 is not supported yet, so this returns a failure.
+        $this->assertEquals(
+            2,
+            $this->cli->main(array('vobject', 'convert','--format=vcard21', '--pretty', '-'))
+        );
+
+    }
+
+    function testValidate() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject 3.1.0//EN
+UID:foo
+FN:Cowboy Henk
+END:VCARD
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+        $result = $this->cli->main(array('vobject', 'validate', '-'));
+
+        $this->assertEquals(
+            0,
+            $result
+        );
+
+    }
+
+    function testValidateFail() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCALENDAR
+VERSION:2.0
+END:VCARD
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+        // vCard 2.1 is not supported yet, so this returns a failure.
+        $this->assertEquals(
+            2,
+            $this->cli->main(array('vobject', 'validate', '-'))
+        );
+
+    }
+
+    function testValidateFail2() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCALENDAR
+VERSION:5.0
+END:VCALENDAR
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+        // vCard 2.1 is not supported yet, so this returns a failure.
+        $this->assertEquals(
+            2,
+            $this->cli->main(array('vobject', 'validate', '-'))
+        );
+
+    }
+
+    function testRepair() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCARD
+VERSION:5.0
+END:VCARD
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+        // vCard 2.1 is not supported yet, so this returns a failure.
+        $this->assertEquals(
+            2,
+            $this->cli->main(array('vobject', 'repair', '-'))
+        );
+
+        rewind($this->cli->stdout);
+        $this->assertRegExp("/^BEGIN:VCARD\r\nVERSION:2.1\r\nUID:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\r\nEND:VCARD\r\n$/", stream_get_contents($this->cli->stdout));
+    }
+
+    function testRepairNothing() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 3.1.0//EN
+BEGIN:VEVENT
+UID:foo
+DTSTAMP:20140122T233226Z
+DTSTART:20140101T120000Z
+END:VEVENT
+END:VCALENDAR
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+        // vCard 2.1 is not supported yet, so this returns a failure.
+
+        $result = $this->cli->main(array('vobject', 'repair', '-'));
+
+        rewind($this->cli->stderr);
+        $error = stream_get_contents($this->cli->stderr);
+
+        $this->assertEquals(
+            0,
+            $result,
+            "This should have been error free. stderr output:\n" . $error
+        );
+
+    }
+
+    /**
+     * Note: this is a very shallow test, doesn't dig into the actual output,
+     * but just makes sure there's no errors thrown.
+     *
+     * The colorizer is not a critical component, it's mostly a debugging tool.
+     */
+    function testColorCalendar() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        $version = Version::VERSION;
+
+        /**
+         * This object is not valid, but it's designed to hit every part of the
+         * colorizer source.
+         */
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject {$version}//EN
+BEGIN:VTIMEZONE
+END:VTIMEZONE
+BEGIN:VEVENT
+ATTENDEE;RSVP=TRUE:mailto:foo@example.org
+REQUEST-STATUS:5;foo
+ATTACH:blabla
+END:VEVENT
+END:VCALENDAR
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+        // vCard 2.1 is not supported yet, so this returns a failure.
+
+        $result = $this->cli->main(array('vobject', 'color', '-'));
+
+        rewind($this->cli->stderr);
+        $error = stream_get_contents($this->cli->stderr);
+
+        $this->assertEquals(
+            0,
+            $result,
+            "This should have been error free. stderr output:\n" . $error
+        );
+
+    }
+
+    /**
+     * Note: this is a very shallow test, doesn't dig into the actual output,
+     * but just makes sure there's no errors thrown.
+     *
+     * The colorizer is not a critical component, it's mostly a debugging tool.
+     */
+    function testColorVCard() {
+
+        $inputStream = fopen('php://memory','r+');
+
+        $version = Version::VERSION;
+
+        /**
+         * This object is not valid, but it's designed to hit every part of the
+         * colorizer source.
+         */
+        fwrite($inputStream, <<<VCARD
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject {$version}//EN
+ADR:1;2;3;4a,4b;5;6
+group.TEL:123454768
+END:VCARD
+
+VCARD
+    );
+        rewind($inputStream);
+        $this->cli->stdin = $inputStream;
+        // vCard 2.1 is not supported yet, so this returns a failure.
+
+        $result = $this->cli->main(array('vobject', 'color', '-'));
+
+        rewind($this->cli->stderr);
+        $error = stream_get_contents($this->cli->stderr);
+
+        $this->assertEquals(
+            0,
+            $result,
+            "This should have been error free. stderr output:\n" . $error
+        );
+
+    }
+}
+
+class CliMock extends Cli {
+
+    public $log = array();
+
+    public $quiet = false;
+
+    public $format;
+
+    public $pretty;
+
+    public $stdin;
+
+    public $stdout;
+
+    public $stderr;
+
+    public $inputFormat;
+
+    public $outputFormat;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VAlarmTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,179 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject\Component;
+use DateTime;
+use Sabre\VObject\Reader;
+
+class VAlarmTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider timeRangeTestData
+     */
+    public function testInTimeRange(VAlarm $valarm,$start,$end,$outcome) {
+
+        $this->assertEquals($outcome, $valarm->isInTimeRange($start, $end));
+
+    }
+
+    public function timeRangeTestData() {
+
+        $tests = array();
+
+        $calendar = new VCalendar();
+
+        // Hard date and time        
+        $valarm1 = $calendar->createComponent('VALARM');
+        $valarm1->add(
+            $calendar->createProperty('TRIGGER', '20120312T130000Z', array('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 = $calendar->createComponent('VALARM');
+        $valarm2->add(
+            $calendar->createProperty('TRIGGER', '-P1D', array('VALUE' => 'DURATION'))
+        );
+
+        $vevent2 = $calendar->createComponent('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 = $calendar->createComponent('VALARM');
+        $valarm3->add( $calendar->createProperty('TRIGGER', '-P1D', array('VALUE'=>'DURATION', 'RELATED' => 'END')) );
+
+        $vevent3 = $calendar->createComponent('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 = $calendar->createComponent('VALARM');
+        $valarm4->TRIGGER = '-P1D';
+        $valarm4->TRIGGER['VALUE'] = 'DURATION';
+        $valarm4->TRIGGER['RELATED']= 'END';
+
+        $vtodo4 = $calendar->createComponent('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 = $calendar->createComponent('VALARM');
+        $valarm5->TRIGGER = '-P1D';
+        $valarm5->TRIGGER['VALUE'] = 'DURATION';
+        $valarm5->REPEAT = 10;
+        $valarm5->DURATION = 'P1D';
+
+        $vevent5 = $calendar->createComponent('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 = $calendar->createComponent('VALARM');
+        $valarm6->TRIGGER = '-P1D';
+        $valarm6->TRIGGER['VALUE'] = 'DURATION';
+        $valarm6->DURATION = 'P1D';
+
+        $vevent6 = $calendar->createComponent('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 = $calendar->createComponent('VALARM');
+        $valarm7->TRIGGER = '-P1D';
+        $valarm7->TRIGGER['VALUE'] = 'DURATION';
+        $valarm7->TRIGGER['RELATED']= 'END';
+
+        $vevent7 = $calendar->createComponent('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 = $calendar->createComponent('VALARM');
+        $valarm7->TRIGGER = '-P1D';
+        $valarm7->TRIGGER['VALUE'] = 'DURATION';
+        $valarm7->TRIGGER['RELATED']= 'END';
+
+        $vevent7 = $calendar->createComponent('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 LogicException
+     */
+    public function testInTimeRangeInvalidComponent() {
+
+        $calendar = new VCalendar();
+        $valarm = $calendar->createComponent('VALARM');
+        $valarm->TRIGGER = '-P1D';
+        $valarm->TRIGGER['RELATED'] = 'END';
+
+        $vjournal = $calendar->createComponent('VJOURNAL');
+        $vjournal->add($valarm);
+
+        $valarm->isInTimeRange(new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'));
+
+    }
+
+    /**
+     * This bug was found and reported on the mailing list.
+     */
+    public function testInTimeRangeBuggy() {
+
+$input = <<<BLA
+BEGIN:VCALENDAR
+BEGIN:VTODO
+DTSTAMP:20121003T064931Z
+UID:b848cb9a7bb16e464a06c222ca1f8102@examle.com
+STATUS:NEEDS-ACTION
+DUE:20121005T000000Z
+SUMMARY:Task 1
+CATEGORIES:AlarmCategory
+BEGIN:VALARM
+TRIGGER:-PT10M
+ACTION:DISPLAY
+DESCRIPTION:Task 1
+END:VALARM
+END:VTODO
+END:VCALENDAR
+BLA;
+
+        $vobj = Reader::read($input);
+
+        $this->assertTrue($vobj->VTODO->VALARM->isInTimeRange(new \DateTime('2012-10-01 00:00:00'), new \DateTime('2012-11-01 00:00:00')));
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VCalendarTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,696 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeZone;
+use Sabre\VObject;
+
+class VCalendarTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider expandData
+     */
+    public function testExpand($input, $output, $timeZone = 'UTC', $start = '2011-12-01', $end = '2011-12-31') {
+
+        $vcal = VObject\Reader::read($input);
+
+        $timeZone = new DateTimeZone($timeZone);
+
+        $vcal->expand(
+            new \DateTime($start),
+            new \DateTime($end),
+            $timeZone
+        );
+
+        // This will normalize the output
+        $output = 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: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:20111202T120000Z
+DTEND:20111202T130000Z
+RECURRENCE-ID:20111202T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bla6
+SUMMARY:Testing RRule
+DTSTART:20111209T120000Z
+DTEND:20111209T130000Z
+RECURRENCE-ID:20111209T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bla6
+SUMMARY:Testing RRule
+DTSTART:20111216T120000Z
+DTEND:20111216T130000Z
+RECURRENCE-ID:20111216T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bla6
+SUMMARY:Testing RRule
+DTSTART:20111223T120000Z
+DTEND:20111223T130000Z
+RECURRENCE-ID:20111223T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bla6
+SUMMARY:Testing RRule
+DTSTART:20111230T120000Z
+DTEND:20111230T130000Z
+RECURRENCE-ID:20111230T120000Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $tests[] = array($input, $output);
+
+        // 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:20111202T120000Z
+DTEND: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:20111216T120000Z
+DTEND:20111216T130000Z
+RECURRENCE-ID:20111216T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bla6
+SUMMARY:Testing RRule2
+DTSTART:20111223T120000Z
+DTEND:20111223T130000Z
+RECURRENCE-ID:20111223T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bla6
+SUMMARY:Testing RRule2
+DTSTART:20111230T120000Z
+DTEND:20111230T130000Z
+RECURRENCE-ID:20111230T120000Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $tests[] = array($input, $output);
+
+        // Floating dates and times.
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:bla1
+DTSTART:20141112T195000
+END:VEVENT
+BEGIN:VEVENT
+UID:bla2
+DTSTART;VALUE=DATE:20141112
+END:VEVENT
+BEGIN:VEVENT
+UID:bla3
+DTSTART;VALUE=DATE:20141112
+RRULE:FREQ=DAILY;COUNT=2
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $output = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:bla1
+DTSTART:20141112T225000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bla2
+DTSTART;VALUE=DATE:20141112
+END:VEVENT
+BEGIN:VEVENT
+UID:bla3
+DTSTART;VALUE=DATE:20141112
+END:VEVENT
+BEGIN:VEVENT
+UID:bla3
+DTSTART;VALUE=DATE:20141113
+RECURRENCE-ID;VALUE=DATE:20141113
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array($input, $output, 'America/Argentina/Buenos_Aires', '2014-01-01', '2015-01-01');
+
+        // Recurrence rule with no valid instances
+        $input = 'BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+VERSION:2.0
+BEGIN:VEVENT
+UID:bla6
+SUMMARY:Testing RRule3
+DTSTART:20111125T120000Z
+DTEND:20111125T130000Z
+RRULE:FREQ=WEEKLY;COUNT=1
+EXDATE:20111125T120000Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $output = 'BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+VERSION:2.0
+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 = VObject\Reader::read($input);
+        $vcal->expand(
+            new \DateTime('2011-12-01'),
+            new \DateTime('2011-12-31')
+        );
+
+    }
+
+    function testGetDocumentType() {
+
+        $vcard = new VCalendar();
+        $vcard->VERSION = '2.0';
+        $this->assertEquals(VCalendar::ICALENDAR20, $vcard->getDocumentType());
+
+    }
+
+    function testValidateCorrect() {
+
+        $input = 'BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+VERSION:2.0
+PRODID:foo
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+DTSTAMP:20140122T233226Z
+UID:foo
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(array(), $vcal->validate(), 'Got an error');
+
+    }
+
+    function testValidateNoVersion() {
+
+        $input = 'BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:foo
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(1, count($vcal->validate()));
+
+    }
+
+    function testValidateWrongVersion() {
+
+        $input = 'BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+VERSION:3.0
+PRODID:foo
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(1, count($vcal->validate()));
+
+    }
+
+    function testValidateNoProdId() {
+
+        $input = 'BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(1, count($vcal->validate()));
+
+    }
+
+    function testValidateDoubleCalScale() {
+
+        $input = 'BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:foo
+CALSCALE:GREGORIAN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(1, count($vcal->validate()));
+
+    }
+
+    function testValidateDoubleMethod() {
+
+        $input = 'BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:foo
+METHOD:REQUEST
+METHOD:REQUEST
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(1, count($vcal->validate()));
+
+    }
+
+    function testValidateTwoMasterEvents() {
+
+        $input = 'BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:foo
+METHOD:REQUEST
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(1, count($vcal->validate()));
+
+    }
+
+    function testValidateOneMasterEvent() {
+
+        $input = 'BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:foo
+METHOD:REQUEST
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+RECURRENCE-ID;VALUE=DATE:20111202
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+        $this->assertEquals(0, count($vcal->validate()));
+
+    }
+
+    function testGetBaseComponent() {
+
+        $input = 'BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:foo
+METHOD:REQUEST
+BEGIN:VEVENT
+SUMMARY:test
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+RECURRENCE-ID;VALUE=DATE:20111202
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+
+        $result = $vcal->getBaseComponent();
+        $this->assertEquals('test', $result->SUMMARY->getValue());
+
+    }
+
+    function testGetBaseComponentNoResult() {
+
+        $input = 'BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:foo
+METHOD:REQUEST
+BEGIN:VEVENT
+SUMMARY:test
+RECURRENCE-ID;VALUE=DATE:20111202
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+END:VEVENT
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20111202
+UID:foo
+DTSTAMP:20140122T234434Z
+RECURRENCE-ID;VALUE=DATE:20111202
+END:VEVENT
+END:VCALENDAR
+';
+
+        $vcal = VObject\Reader::read($input);
+
+        $result = $vcal->getBaseComponent();
+        $this->assertNull($result);
+
+    }
+
+    function testNoComponents() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+END:VCALENDAR
+ICS;
+
+        $this->assertValidate(
+            $input,
+            0,
+            3,
+           "An iCalendar object must have at least 1 component."
+        );
+
+    }
+
+    function testCalDAVNoComponents() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+BEGIN:VTIMEZONE
+TZID:America/Toronto
+END:VTIMEZONE
+END:VCALENDAR
+ICS;
+
+        $this->assertValidate(
+            $input,
+            VCalendar::PROFILE_CALDAV,
+            3,
+           "A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL)."
+        );
+
+    }
+
+    function testCalDAVMultiUID() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+BEGIN:VEVENT
+UID:foo
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bar
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $this->assertValidate(
+            $input,
+            VCalendar::PROFILE_CALDAV,
+            3,
+           "A calendar object on a CalDAV server may only have components with the same UID."
+        );
+
+    }
+
+    function testCalDAVMultiComponent() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+BEGIN:VEVENT
+UID:foo
+RECURRENCE-ID:20150109T185200Z
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+BEGIN:VTODO
+UID:foo
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VTODO
+END:VCALENDAR
+ICS;
+
+        $this->assertValidate(
+            $input,
+            VCalendar::PROFILE_CALDAV,
+            3,
+           "A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL)."
+        );
+
+    }
+
+    function testCalDAVMETHOD() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+PRODID:vobject
+BEGIN:VEVENT
+UID:foo
+RECURRENCE-ID:20150109T185200Z
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $this->assertValidate(
+            $input,
+            VCalendar::PROFILE_CALDAV,
+            3,
+           "A calendar object on a CalDAV server MUST NOT have a METHOD property."
+        );
+
+    }
+
+    function assertValidate($ics, $options, $expectedLevel, $expectedMessage = null) {
+
+        $vcal = VObject\Reader::read($ics);
+        $result = $vcal->validate($options);
+
+        $this->assertValidateResult($result, $expectedLevel, $expectedMessage);
+
+    }
+
+    function assertValidateResult($input, $expectedLevel, $expectedMessage = null) {
+
+        $messages = array();
+        foreach($input as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        if ($expectedLevel === 0) {
+            $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages));
+        } else {
+            $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages));
+
+            $this->assertEquals($expectedMessage, $input[0]['message']);
+            $this->assertEquals($expectedLevel, $input[0]['level']);
+        }
+
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VCardTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,288 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+class VCardTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider validateData
+     */
+    function testValidate($input, $expectedWarnings, $expectedRepairedOutput) {
+
+        $vcard = VObject\Reader::read($input);
+
+        $warnings = $vcard->validate();
+
+        $warnMsg = array();
+        foreach($warnings as $warning) {
+            $warnMsg[] = $warning['message'];
+        }
+
+        $this->assertEquals($expectedWarnings, $warnMsg);
+
+        $vcard->validate(VObject\Component::REPAIR);
+
+        $this->assertEquals(
+            $expectedRepairedOutput,
+            $vcard->serialize()
+        );
+
+    }
+
+    public function validateData() {
+
+        $tests = array();
+
+        // Correct
+        $tests[] = array(
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
+            array(),
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
+        );
+
+        // No VERSION
+        $tests[] = array(
+            "BEGIN:VCARD\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
+            array(
+                'VERSION MUST appear exactly once in a VCARD component',
+            ),
+            "BEGIN:VCARD\r\nVERSION:3.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
+        );
+
+        // Unknown version
+        $tests[] = array(
+            "BEGIN:VCARD\r\nVERSION:2.2\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
+            array(
+                'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
+            ),
+            "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
+        );
+
+        // No FN
+        $tests[] = array(
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n",
+            array(
+                'The FN property must appear in the VCARD component exactly 1 time',
+            ),
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n",
+        );
+        // No FN, N fallback
+        $tests[] = array(
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nEND:VCARD\r\n",
+            array(
+                'The FN property must appear in the VCARD component exactly 1 time',
+            ),
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nFN:John Doe\r\nEND:VCARD\r\n",
+        );
+        // No FN, N fallback, no first name
+        $tests[] = array(
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nEND:VCARD\r\n",
+            array(
+                'The FN property must appear in the VCARD component exactly 1 time',
+            ),
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nFN:Doe\r\nEND:VCARD\r\n",
+        );
+
+        // No FN, ORG fallback
+        $tests[] = array(
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nEND:VCARD\r\n",
+            array(
+                'The FN property must appear in the VCARD component exactly 1 time',
+            ),
+            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nFN:Acme Co.\r\nEND:VCARD\r\n",
+        );
+        return $tests;
+
+    }
+
+    function testGetDocumentType() {
+
+        $vcard = new VCard(array(), false);
+        $vcard->VERSION = '2.1';
+        $this->assertEquals(VCard::VCARD21, $vcard->getDocumentType());
+
+        $vcard = new VCard(array(), false);
+        $vcard->VERSION = '3.0';
+        $this->assertEquals(VCard::VCARD30, $vcard->getDocumentType());
+
+        $vcard = new VCard(array(), false);
+        $vcard->VERSION = '4.0';
+        $this->assertEquals(VCard::VCARD40, $vcard->getDocumentType());
+
+        $vcard = new VCard(array(), false);
+        $this->assertEquals(VCard::UNKNOWN, $vcard->getDocumentType());
+    }
+
+    function testPreferredNoPref() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+EMAIL:1@example.org
+EMAIL:2@example.org
+END:VCARD
+VCF;
+
+        $vcard = VObject\Reader::read($vcard);
+        $this->assertEquals('1@example.org', $vcard->preferred('EMAIL')->getValue());
+
+    }
+
+    function testPreferredWithPref() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+EMAIL:1@example.org
+EMAIL;TYPE=PREF:2@example.org
+END:VCARD
+VCF;
+
+        $vcard = VObject\Reader::read($vcard);
+        $this->assertEquals('2@example.org', $vcard->preferred('EMAIL')->getValue());
+
+    }
+
+    function testPreferredWith40Pref() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+EMAIL:1@example.org
+EMAIL;PREF=3:2@example.org
+EMAIL;PREF=2:3@example.org
+END:VCARD
+VCF;
+
+        $vcard = VObject\Reader::read($vcard);
+        $this->assertEquals('3@example.org', $vcard->preferred('EMAIL')->getValue());
+
+    }
+
+    function testPreferredNotFound() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+END:VCARD
+VCF;
+
+        $vcard = VObject\Reader::read($vcard);
+        $this->assertNull($vcard->preferred('EMAIL'));
+
+    }
+
+    function testNoUIDCardDAV() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+FN:John Doe
+END:VCARD
+VCF;
+        $this->assertValidate(
+            $vcard,
+            VCARD::PROFILE_CARDDAV,
+            3,
+            'vCards on CardDAV servers MUST have a UID property.'
+        );
+
+    }
+
+    function testNoUIDNoCardDAV() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+FN:John Doe
+END:VCARD
+VCF;
+        $this->assertValidate(
+            $vcard,
+            0,
+            2,
+            'Adding a UID to a vCard property is recommended.'
+        );
+
+    }
+    function testNoUIDNoCardDAVRepair() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+FN:John Doe
+END:VCARD
+VCF;
+        $this->assertValidate(
+            $vcard,
+            VCARD::REPAIR,
+            1,
+            'Adding a UID to a vCard property is recommended.'
+        );
+
+    }
+
+    function testVCard21CardDAV() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+FN:John Doe
+UID:foo
+END:VCARD
+VCF;
+        $this->assertValidate(
+            $vcard,
+            VCARD::PROFILE_CARDDAV,
+            3,
+            'CardDAV servers are not allowed to accept vCard 2.1.'
+        );
+
+    }
+
+    function testVCard21NoCardDAV() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+FN:John Doe
+UID:foo
+END:VCARD
+VCF;
+        $this->assertValidate(
+            $vcard,
+            0,
+            0
+        );
+
+    }
+
+    function assertValidate($vcf, $options, $expectedLevel, $expectedMessage = null) {
+
+        $vcal = VObject\Reader::read($vcf);
+        $result = $vcal->validate($options);
+
+        $this->assertValidateResult($result, $expectedLevel, $expectedMessage);
+
+    }
+
+    function assertValidateResult($input, $expectedLevel, $expectedMessage = null) {
+
+        $messages = array();
+        foreach($input as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        if ($expectedLevel === 0) {
+            $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages));
+        } else {
+            $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages));
+
+            $this->assertEquals($expectedMessage, $input[0]['message']);
+            $this->assertEquals($expectedLevel, $input[0]['level']);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VEventTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,76 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+class VEventTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider timeRangeTestData
+     */
+    public function testInTimeRange(VEvent $vevent,$start,$end,$outcome) {
+
+        $this->assertEquals($outcome, $vevent->isInTimeRange($start, $end));
+
+    }
+
+    public function timeRangeTestData() {
+
+        $tests = array();
+
+        $calendar = new VCalendar();
+
+        $vevent = $calendar->createComponent('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);
+
+        // Added this test to ensure that recurrence rules with no DTEND also 
+        // get checked for the entire day.
+        $vevent7 = clone $vevent;
+        $vevent7->DTSTART = '20120101';
+        $vevent7->DTSTART['VALUE'] = 'DATE';
+        $vevent7->RRULE = 'FREQ=MONTHLY';
+        $tests[] = array($vevent7, new \DateTime('2012-02-01 15:00:00'), new \DateTime('2012-02-02'), true);
+        return $tests;
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VFreeBusyTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,66 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+use Sabre\VObject\Reader;
+
+class VFreeBusyTest extends \PHPUnit_Framework_TestCase {
+
+    function testIsFree() {
+
+        $input = <<<BLA
+BEGIN:VCALENDAR
+BEGIN:VFREEBUSY
+FREEBUSY;FBTYPE=FREE:20120912T000500Z/PT1H
+FREEBUSY;FBTYPE=BUSY:20120912T010000Z/20120912T020000Z
+FREEBUSY;FBTYPE=BUSY-TENTATIVE:20120912T020000Z/20120912T030000Z
+FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20120912T030000Z/20120912T040000Z
+FREEBUSY;FBTYPE=BUSY:20120912T050000Z/20120912T060000Z,20120912T080000Z/20120912T090000Z
+FREEBUSY;FBTYPE=BUSY:20120912T100000Z/PT1H
+END:VFREEBUSY
+END:VCALENDAR
+BLA;
+
+        $obj = VObject\Reader::read($input);
+        $vfb = $obj->VFREEBUSY;
+
+        $tz = new \DateTimeZone('UTC');
+
+        $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 01:15:00', $tz), new \DateTime('2012-09-12 01:45:00', $tz)));
+        $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 08:05:00', $tz), new \DateTime('2012-09-12 08:10:00', $tz)));
+        $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 10:15:00', $tz), new \DateTime('2012-09-12 10:45:00', $tz)));
+
+        // Checking whether the end time is treated as non-inclusive
+        $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:00:00', $tz), new \DateTime('2012-09-12 09:15:00', $tz)));
+        $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:45:00', $tz), new \DateTime('2012-09-12 10:00:00', $tz)));
+        $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 11:00:00', $tz), new \DateTime('2012-09-12 12:00:00', $tz)));
+
+    }
+
+    public function testValidate() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VFREEBUSY
+UID:some-random-id
+DTSTAMP:20140402T180200Z
+END:VFREEBUSY
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(array(), $messages);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VJournalTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,101 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject\Component;
+use Sabre\VObject\Reader;
+
+class VJournalTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider timeRangeTestData
+     */
+    public function testInTimeRange(VJournal $vtodo,$start,$end,$outcome) {
+
+        $this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end));
+
+    }
+
+    public function testValidate() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VJOURNAL
+UID:12345678
+DTSTAMP:20140402T174100Z
+END:VJOURNAL
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(array(), $messages);
+
+    }
+
+    public function testValidateBroken() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VJOURNAL
+UID:12345678
+DTSTAMP:20140402T174100Z
+URL:http://example.org/
+URL:http://example.com/
+END:VJOURNAL
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(
+            array("URL MUST NOT appear more than once in a VJOURNAL component"),
+            $messages
+        );
+
+    }
+
+    public function timeRangeTestData() {
+
+        $calendar = new VCalendar();
+
+        $tests = array();
+
+        $vjournal = $calendar->createComponent('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 = $calendar->createComponent('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 = $calendar->createComponent('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;
+    }
+
+
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VTimeZoneTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,57 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+use Sabre\VObject\Reader;
+
+class VTimeZoneTest extends \PHPUnit_Framework_TestCase {
+
+    function testValidate() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VTIMEZONE
+TZID:America/Toronto
+END:VTIMEZONE
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(array(), $messages);
+
+    }
+
+    function testGetTimeZone() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VTIMEZONE
+TZID:America/Toronto
+END:VTIMEZONE
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $tz = new \DateTimeZone('America/Toronto');
+
+        $this->assertEquals(
+            $tz,
+            $obj->VTIMEZONE->getTimeZone()
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Component/VTodoTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,180 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use
+    Sabre\VObject\Component,
+    Sabre\VObject\Reader;
+
+class VTodoTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider timeRangeTestData
+     */
+    public function testInTimeRange(VTodo $vtodo,$start,$end,$outcome) {
+
+        $this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end));
+
+    }
+
+    public function timeRangeTestData() {
+
+        $tests = array();
+
+        $calendar = new VCalendar();
+
+        $vtodo = $calendar->createComponent('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 = $calendar->createComponent('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 = $calendar->createComponent('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 = $calendar->createComponent('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 = $calendar->createComponent('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 = $calendar->createComponent('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;
+
+    }
+
+    public function testValidate() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VTODO
+UID:1234-21355-123156
+DTSTAMP:20140402T183400Z
+END:VTODO
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(array(), $messages);
+
+    }
+
+    public function testValidateInvalid() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VTODO
+END:VTODO
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(array(
+            "UID MUST appear exactly once in a VTODO component",
+            "DTSTAMP MUST appear exactly once in a VTODO component",
+        ), $messages);
+
+    }
+
+    public function testValidateDUEDTSTARTMisMatch() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VTODO
+UID:FOO
+DTSTART;VALUE=DATE-TIME:20140520T131600Z
+DUE;VALUE=DATE:20140520
+DTSTAMP;VALUE=DATE-TIME:20140520T131600Z
+END:VTODO
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(array(
+            "The value type (DATE or DATE-TIME) must be identical for DUE and DTSTART",
+        ), $messages);
+
+    }
+
+    public function testValidateDUEbeforeDTSTART() {
+
+        $input = <<<HI
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:YoYo
+BEGIN:VTODO
+UID:FOO
+DTSTART;VALUE=DATE:20140520
+DUE;VALUE=DATE:20140518
+DTSTAMP;VALUE=DATE-TIME:20140520T131600Z
+END:VTODO
+END:VCALENDAR
+HI;
+
+        $obj = Reader::read($input);
+
+        $warnings = $obj->validate();
+        $messages = array();
+        foreach($warnings as $warning) {
+            $messages[] = $warning['message'];
+        }
+
+        $this->assertEquals(array(
+            "DUE must occur after DTSTART",
+        ), $messages);
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ComponentTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,528 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    Sabre\VObject\Component\VCalendar,
+    Sabre\VObject\Component\VCard;
+
+class ComponentTest extends \PHPUnit_Framework_TestCase {
+
+    function testIterate() {
+
+        $comp = new VCalendar(array(), false);
+
+        $sub = $comp->createComponent('VEVENT');
+        $comp->add($sub);
+
+        $sub = $comp->createComponent('VTODO');
+        $comp->add($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 VCalendar(array(), false);
+
+        $sub = $comp->createComponent('VEVENT');
+        $comp->add($sub);
+
+        $sub = $comp->createComponent('VTODO');
+        $comp->add($sub);
+
+        $event = $comp->vevent;
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $event);
+        $this->assertEquals('VEVENT', $event->name);
+
+        $this->assertInternalType('null', $comp->vjournal);
+
+    }
+
+    function testMagicGetGroups() {
+
+        $comp = new VCard();
+
+        $sub = $comp->createProperty('GROUP1.EMAIL','1@1.com');
+        $comp->add($sub);
+
+        $sub = $comp->createProperty('GROUP2.EMAIL','2@2.com');
+        $comp->add($sub);
+
+        $sub = $comp->createProperty('EMAIL','3@3.com');
+        $comp->add($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 VCalendar();
+
+        $sub = $comp->createComponent('VEVENT');
+        $comp->add($sub);
+
+        $sub = $comp->createComponent('VTODO');
+        $comp->add($sub);
+
+        $this->assertTrue(isset($comp->vevent));
+        $this->assertTrue(isset($comp->vtodo));
+        $this->assertFalse(isset($comp->vjournal));
+
+    }
+
+    function testMagicSetScalar() {
+
+        $comp = new VCalendar();
+        $comp->myProp = 'myValue';
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property',$comp->MYPROP);
+        $this->assertEquals('myValue',(string)$comp->MYPROP);
+
+
+    }
+
+    function testMagicSetScalarTwice() {
+
+        $comp = new VCalendar(array(), false);
+        $comp->myProp = 'myValue';
+        $comp->myProp = 'myValue';
+
+        $this->assertEquals(1,count($comp->children()));
+        $this->assertInstanceOf('Sabre\\VObject\\Property',$comp->MYPROP);
+        $this->assertEquals('myValue',(string)$comp->MYPROP);
+
+    }
+
+    function testMagicSetArray() {
+
+        $comp = new VCalendar();
+        $comp->ORG = array('Acme Inc', 'Section 9');
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property',$comp->ORG);
+        $this->assertEquals(array('Acme Inc', 'Section 9'),$comp->ORG->getParts());
+
+    }
+
+    function testMagicSetComponent() {
+
+        $comp = new VCalendar();
+
+        // Note that 'myProp' is ignored here.
+        $comp->myProp = $comp->createComponent('VEVENT');
+
+        $this->assertEquals(1, count($comp));
+
+        $this->assertEquals('VEVENT',$comp->VEVENT->name);
+
+    }
+
+    function testMagicSetTwice() {
+
+        $comp = new VCalendar(array(), false);
+
+        $comp->VEVENT = $comp->createComponent('VEVENT');
+        $comp->VEVENT = $comp->createComponent('VEVENT');
+
+        $this->assertEquals(1, count($comp->children()));
+
+        $this->assertEquals('VEVENT',$comp->VEVENT->name);
+
+    }
+
+    function testArrayAccessGet() {
+
+        $comp = new VCalendar(array(), false);
+
+        $event = $comp->createComponent('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 Component);
+        $this->assertEquals('Event 2', (string)$comp->vevent[1]->summary);
+
+    }
+
+    function testArrayAccessExists() {
+
+        $comp = new VCalendar();
+
+        $event = $comp->createComponent('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 VCalendar();
+        $comp['hey'] = 'hi there';
+
+    }
+    /**
+     * @expectedException LogicException
+     */
+    function testArrayAccessUnset() {
+
+        $comp = new VCalendar();
+        unset($comp[0]);
+
+    }
+
+    function testAddScalar() {
+
+        $comp = new VCalendar(array(), false);
+
+        $comp->add('myprop','value');
+
+        $this->assertEquals(1, count($comp->children()));
+
+        $bla = $comp->children[0];
+
+        $this->assertTrue($bla instanceof Property);
+        $this->assertEquals('MYPROP',$bla->name);
+        $this->assertEquals('value',(string)$bla);
+
+    }
+
+    function testAddScalarParams() {
+
+        $comp = new VCalendar(array(), false);
+
+        $comp->add('myprop','value',array('param1'=>'value1'));
+
+        $this->assertEquals(1, count($comp->children()));
+
+        $bla = $comp->children[0];
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $bla);
+        $this->assertEquals('MYPROP',$bla->name);
+        $this->assertEquals('value', (string)$bla);
+
+        $this->assertEquals(1, count($bla->parameters()));
+
+        $this->assertEquals('PARAM1',$bla->parameters['PARAM1']->name);
+        $this->assertEquals('value1',$bla->parameters['PARAM1']->getValue());
+
+    }
+
+
+    function testAddComponent() {
+
+        $comp = new VCalendar(array(), false);
+
+        $comp->add($comp->createComponent('VEVENT'));
+
+        $this->assertEquals(1, count($comp->children()));
+
+        $this->assertEquals('VEVENT',$comp->VEVENT->name);
+
+    }
+
+    function testAddComponentTwice() {
+
+        $comp = new VCalendar(array(), false);
+
+        $comp->add($comp->createComponent('VEVENT'));
+        $comp->add($comp->createComponent('VEVENT'));
+
+        $this->assertEquals(2, count($comp->children()));
+
+        $this->assertEquals('VEVENT',$comp->VEVENT->name);
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testAddArgFail() {
+
+        $comp = new VCalendar();
+        $comp->add($comp->createComponent('VEVENT'),'hello');
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testAddArgFail2() {
+
+        $comp = new VCalendar();
+        $comp->add(array());
+
+    }
+
+    function testMagicUnset() {
+
+        $comp = new VCalendar(array(), false);
+        $comp->add($comp->createComponent('VEVENT'));
+
+        unset($comp->vevent);
+
+        $this->assertEquals(0, count($comp->children()));
+
+    }
+
+
+    function testCount() {
+
+        $comp = new VCalendar();
+        $this->assertEquals(1,$comp->count());
+
+    }
+
+    function testChildren() {
+
+        $comp = new VCalendar(array(), false);
+
+        // Note that 'myProp' is ignored here.
+        $comp->add($comp->createComponent('VEVENT'));
+        $comp->add($comp->createComponent('VTODO'));
+
+        $r = $comp->children();
+        $this->assertInternalType('array', $r);
+        $this->assertEquals(2,count($r));
+    }
+
+    function testGetComponents() {
+
+        $comp = new VCalendar();
+
+        $comp->add($comp->createProperty('FOO','BAR'));
+        $comp->add($comp->createComponent('VTODO'));
+
+        $r = $comp->getComponents();
+        $this->assertInternalType('array', $r);
+        $this->assertEquals(1, count($r));
+        $this->assertEquals('VTODO', $r[0]->name);
+    }
+
+    function testSerialize() {
+
+        $comp = new VCalendar(array(), false);
+        $this->assertEquals("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n", $comp->serialize());
+
+    }
+
+    function testSerializeChildren() {
+
+        $comp = new VCalendar(array(), false);
+        $event = $comp->add($comp->createComponent('VEVENT'));
+        unset($event->DTSTAMP, $event->UID);
+        $comp->add($comp->createComponent('VTODO'));
+
+        $str = $comp->serialize();
+
+        $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", $str);
+
+    }
+
+    function testSerializeOrderCompAndProp() {
+
+        $comp = new VCalendar(array(), false);
+        $comp->add($event = $comp->createComponent('VEVENT'));
+        $comp->add('PROP1','BLABLA');
+        $comp->add('VERSION','2.0');
+        $comp->add($comp->createComponent('VTIMEZONE'));
+
+        unset($event->DTSTAMP, $event->UID);
+        $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);
+
+    }
+
+    function testAnotherSerializeOrderProp() {
+
+        $prop4s=array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10');
+
+        $comp = new VCard(array(), false);
+
+        $comp->__set('SOMEPROP','FOO');
+        $comp->__set('ANOTHERPROP','FOO');
+        $comp->__set('THIRDPROP','FOO');
+        foreach ($prop4s as $prop4) {
+            $comp->add('PROP4', 'FOO '.$prop4);
+        }
+        $comp->__set('PROPNUMBERFIVE', 'FOO');
+        $comp->__set('PROPNUMBERSIX', 'FOO');
+        $comp->__set('PROPNUMBERSEVEN', 'FOO');
+        $comp->__set('PROPNUMBEREIGHT', 'FOO');
+        $comp->__set('PROPNUMBERNINE', 'FOO');
+        $comp->__set('PROPNUMBERTEN', 'FOO');
+        $comp->__set('VERSION','2.0');
+        $comp->__set('UID', 'FOO');
+
+        $str = $comp->serialize();
+
+        $this->assertEquals("BEGIN:VCARD\r\nVERSION:2.0\r\nSOMEPROP:FOO\r\nANOTHERPROP:FOO\r\nTHIRDPROP:FOO\r\nPROP4:FOO 1\r\nPROP4:FOO 2\r\nPROP4:FOO 3\r\nPROP4:FOO 4\r\nPROP4:FOO 5\r\nPROP4:FOO 6\r\nPROP4:FOO 7\r\nPROP4:FOO 8\r\nPROP4:FOO 9\r\nPROP4:FOO 10\r\nPROPNUMBERFIVE:FOO\r\nPROPNUMBERSIX:FOO\r\nPROPNUMBERSEVEN:FOO\r\nPROPNUMBEREIGHT:FOO\r\nPROPNUMBERNINE:FOO\r\nPROPNUMBERTEN:FOO\r\nUID:FOO\r\nEND:VCARD\r\n", $str);
+
+    }
+
+    function testInstantiateWithChildren() {
+
+        $comp = new VCard(array(
+            'ORG' => array('Acme Inc.', 'Section 9'),
+            'FN' => 'Finn The Human',
+        ));
+
+        $this->assertEquals(array('Acme Inc.', 'Section 9'), $comp->ORG->getParts());
+        $this->assertEquals('Finn The Human', $comp->FN->getValue());
+
+    }
+
+    function testInstantiateSubComponent() {
+
+        $comp = new VCalendar();
+        $event = $comp->createComponent('VEVENT', array(
+            $comp->createProperty('UID', '12345'),
+        ));
+        $comp->add($event);
+
+        $this->assertEquals('12345', $comp->VEVENT->UID->getValue());
+
+    }
+
+    function testRemoveByName() {
+
+        $comp = new VCalendar(array(), false);
+        $comp->add('prop1','val1');
+        $comp->add('prop2','val2');
+        $comp->add('prop2','val2');
+
+        $comp->remove('prop2');
+        $this->assertFalse(isset($comp->prop2));
+        $this->assertTrue(isset($comp->prop1));
+
+    }
+
+    function testRemoveByObj() {
+
+        $comp = new VCalendar(array(), false);
+        $comp->add('prop1','val1');
+        $prop = $comp->add('prop2','val2');
+
+        $comp->remove($prop);
+        $this->assertFalse(isset($comp->prop2));
+        $this->assertTrue(isset($comp->prop1));
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testRemoveNotFound() {
+
+        $comp = new VCalendar(array(), false);
+        $prop = $comp->createProperty('A','B');
+        $comp->remove($prop);
+
+    }
+
+    /**
+     * @dataProvider ruleData
+     */
+    function testValidateRules($componentList, $errorCount) {
+
+        $vcard = new Component\VCard();
+
+        $component = new FakeComponent($vcard,'Hi', array(), $defaults = false );
+        foreach($componentList as $v) {
+            $component->add($v,'Hello.');
+        }
+
+        $this->assertEquals($errorCount, count($component->validate()));
+
+    }
+
+    function testValidateRepair() {
+
+        $vcard = new Component\VCard();
+
+        $component = new FakeComponent($vcard,'Hi', array(), $defaults = false );
+        $component->validate(Component::REPAIR);
+        $this->assertEquals('yow', $component->BAR->getValue());
+
+    }
+
+    function ruleData() {
+
+        return array(
+
+            array(array(), 2),
+            array(array('FOO'), 3),
+            array(array('BAR'), 1),
+            array(array('BAZ'), 1),
+            array(array('BAR','BAZ'), 0),
+            array(array('BAR','BAZ','ZIM',), 0),
+            array(array('BAR','BAZ','ZIM','GIR'), 0),
+            array(array('BAR','BAZ','ZIM','GIR','GIR'), 1),
+
+        );
+
+    }
+
+}
+
+class FakeComponent extends Component {
+
+    public function getValidationRules() {
+
+        return array(
+            'FOO' => '0',
+            'BAR' => '1',
+            'BAZ' => '+',
+            'ZIM' => '*',
+            'GIR' => '?',
+        );
+
+    }
+
+    public function getDefaults() {
+
+        return array(
+            'BAR' => 'yow',
+        );
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/DateTimeParserTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,389 @@
+<?php
+
+namespace Sabre\VObject;
+
+use DateTime;
+use DateTimeZone;
+use DateInterval;
+
+class DateTimeParserTest extends \PHPUnit_Framework_TestCase {
+
+    function testParseICalendarDuration() {
+
+        $this->assertEquals('+1 weeks', DateTimeParser::parseDuration('P1W',true));
+        $this->assertEquals('+5 days',  DateTimeParser::parseDuration('P5D',true));
+        $this->assertEquals('+5 days 3 hours 50 minutes 12 seconds', DateTimeParser::parseDuration('P5DT3H50M12S',true));
+        $this->assertEquals('-1 weeks 50 minutes', DateTimeParser::parseDuration('-P1WT50M',true));
+        $this->assertEquals('+50 days 3 hours 2 seconds', DateTimeParser::parseDuration('+P50DT3H2S',true));
+        $this->assertEquals('+0 seconds', DateTimeParser::parseDuration('+PT0S',true));
+        $this->assertEquals(new DateInterval('PT0S'), DateTimeParser::parseDuration('PT0S'));
+
+    }
+
+    function testParseICalendarDurationDateInterval() {
+
+        $expected = new DateInterval('P7D');
+        $this->assertEquals($expected, DateTimeParser::parseDuration('P1W'));
+        $this->assertEquals($expected, DateTimeParser::parse('P1W'));
+
+        $expected = new DateInterval('PT3M');
+        $expected->invert = true;
+        $this->assertEquals($expected, DateTimeParser::parseDuration('-PT3M'));
+
+    }
+
+    /**
+     * @expectedException LogicException
+     */
+    function testParseICalendarDurationFail() {
+
+        DateTimeParser::parseDuration('P1X',true);
+
+    }
+
+    function testParseICalendarDateTime() {
+
+        $dateTime = DateTimeParser::parseDateTime('20100316T141405');
+
+        $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC'));
+
+        $this->assertEquals($compare, $dateTime);
+
+    }
+
+    /**
+     * @depends testParseICalendarDateTime
+     * @expectedException LogicException
+     */
+    function testParseICalendarDateTimeBadFormat() {
+
+        $dateTime = DateTimeParser::parseDateTime('20100316T141405 ');
+
+    }
+
+    /**
+     * @depends testParseICalendarDateTime
+     */
+    function testParseICalendarDateTimeUTC() {
+
+        $dateTime = DateTimeParser::parseDateTime('20100316T141405Z');
+
+        $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('UTC'));
+        $this->assertEquals($compare, $dateTime);
+
+    }
+
+    /**
+     * @depends testParseICalendarDateTime
+     */
+    function testParseICalendarDateTimeUTC2() {
+
+        $dateTime = DateTimeParser::parseDateTime('20101211T160000Z');
+
+        $compare = new DateTime('2010-12-11 16:00:00',new DateTimeZone('UTC'));
+        $this->assertEquals($compare, $dateTime);
+
+    }
+
+    /**
+     * @depends testParseICalendarDateTime
+     */
+    function testParseICalendarDateTimeCustomTimeZone() {
+
+        $dateTime = DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam'));
+
+        $compare = new DateTime('2010-03-16 14:14:05',new DateTimeZone('Europe/Amsterdam'));
+        $this->assertEquals($compare, $dateTime);
+
+    }
+
+    function testParseICalendarDate() {
+
+        $dateTime = DateTimeParser::parseDate('20100316');
+
+        $expected = new DateTime('2010-03-16 00:00:00',new DateTimeZone('UTC'));
+
+        $this->assertEquals($expected, $dateTime);
+
+        $dateTime = DateTimeParser::parse('20100316');
+        $this->assertEquals($expected, $dateTime);
+
+    }
+
+    /**
+     * TCheck if a date with year > 4000 will not throw an exception. iOS seems to use 45001231 in yearly recurring events
+     */
+    function testParseICalendarDateGreaterThan4000() {
+
+        $dateTime = DateTimeParser::parseDate('45001231');
+
+        $expected = new DateTime('4500-12-31 00:00:00',new DateTimeZone('UTC'));
+
+        $this->assertEquals($expected, $dateTime);
+
+        $dateTime = DateTimeParser::parse('45001231');
+        $this->assertEquals($expected, $dateTime);
+
+    }
+
+    /**
+     * Check if a datetime with year > 4000 will not throw an exception. iOS seems to use 45001231T235959 in yearly recurring events
+     */
+    function testParseICalendarDateTimeGreaterThan4000() {
+
+        $dateTime = DateTimeParser::parseDateTime('45001231T235959');
+
+        $expected = new DateTime('4500-12-31 23:59:59',new DateTimeZone('UTC'));
+
+        $this->assertEquals($expected, $dateTime);
+
+        $dateTime = DateTimeParser::parse('45001231T235959');
+        $this->assertEquals($expected, $dateTime);
+
+    }
+
+    /**
+     * @depends testParseICalendarDate
+     * @expectedException LogicException
+     */
+    function testParseICalendarDateBadFormat() {
+
+        $dateTime = DateTimeParser::parseDate('20100316T141405');
+
+    }
+
+    /**
+     * @dataProvider vcardDates
+     */
+    function testVCardDate($input, $output) {
+
+        $this->assertEquals(
+            $output,
+            DateTimeParser::parseVCardDateTime($input)
+        );
+
+    }
+
+    /**
+     * @dataProvider vcardDates
+     * @expectedException \InvalidArgumentException
+     */
+    function testBadVCardDate() {
+
+        DateTimeParser::parseVCardDateTime('1985---01');
+
+    }
+
+    /**
+     * @dataProvider vcardDates
+     * @expectedException \InvalidArgumentException
+     */
+    function testBadVCardTime() {
+
+        DateTimeParser::parseVCardTime('23:12:166');
+
+    }
+
+    function vcardDates() {
+
+        return array(
+            array(
+                "19961022T140000",
+                array(
+                    "year" => 1996,
+                    "month" => 10,
+                    "date" => 22,
+                    "hour" => 14,
+                    "minute" => 00,
+                    "second" => 00,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "--1022T1400",
+                array(
+                    "year" => null,
+                    "month" => 10,
+                    "date" => 22,
+                    "hour" => 14,
+                    "minute" => 00,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "---22T14",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => 22,
+                    "hour" => 14,
+                    "minute" => null,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "19850412",
+                array(
+                    "year" => 1985,
+                    "month" => 4,
+                    "date" => 12,
+                    "hour" => null,
+                    "minute" => null,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "1985-04",
+                array(
+                    "year" => 1985,
+                    "month" => 04,
+                    "date" => null,
+                    "hour" => null,
+                    "minute" => null,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "1985",
+                array(
+                    "year" => 1985,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => null,
+                    "minute" => null,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "--0412",
+                array(
+                    "year" => null,
+                    "month" => 4,
+                    "date" => 12,
+                    "hour" => null,
+                    "minute" => null,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "---12",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => 12,
+                    "hour" => null,
+                    "minute" => null,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "T102200",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => 10,
+                    "minute" => 22,
+                    "second" => 0,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "T1022",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => 10,
+                    "minute" => 22,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "T10",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => 10,
+                    "minute" => null,
+                    "second" => null,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "T-2200",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => null,
+                    "minute" => 22,
+                    "second" => 00,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "T--00",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => null,
+                    "minute" => null,
+                    "second" => 00,
+                    "timezone" => null
+                ),
+            ),
+            array(
+                "T102200Z",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => 10,
+                    "minute" => 22,
+                    "second" => 00,
+                    "timezone" => 'Z'
+                ),
+            ),
+            array(
+                "T102200-0800",
+                array(
+                    "year" => null,
+                    "month" => null,
+                    "date" => null,
+                    "hour" => 10,
+                    "minute" => 22,
+                    "second" => 00,
+                    "timezone" => '-0800'
+                ),
+            ),
+
+            // extended format
+            array(
+                "2012-11-29T15:10:53Z",
+                array(
+                    "year" => 2012,
+                    "month" => 11,
+                    "date" => 29,
+                    "hour" => 15,
+                    "minute" => 10,
+                    "second" => 53,
+                    "timezone" => 'Z'
+                ),
+            ),
+        );
+
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/DocumentTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,70 @@
+<?php
+
+namespace Sabre\VObject;
+
+class DocumentTest extends \PHPUnit_Framework_TestCase {
+
+    function testGetDocumentType() {
+
+        $doc = new MockDocument();
+        $this->assertEquals(Document::UNKNOWN, $doc->getDocumentType());
+
+    }
+
+    function testConstruct() {
+
+        $doc = new MockDocument('VLIST');
+        $this->assertEquals('VLIST', $doc->name);
+
+    }
+
+    function testCreateComponent() {
+
+        $vcal = new Component\VCalendar(array(), false);
+
+        $event = $vcal->createComponent('VEVENT');
+
+        $this->assertInstanceOf('Sabre\VObject\Component\VEvent', $event);
+        $vcal->add($event);
+
+        $prop = $vcal->createProperty('X-PROP','1234256',array('X-PARAM' => '3'));
+        $this->assertInstanceOf('Sabre\VObject\Property', $prop);
+
+        $event->add($prop);
+
+        unset(
+            $event->DTSTAMP,
+            $event->UID
+        );
+
+        $out = $vcal->serialize();
+        $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nX-PROP;X-PARAM=3:1234256\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $out);
+
+    }
+
+    function testCreate() {
+
+        $vcal = new Component\VCalendar(array(), false);
+
+        $event = $vcal->create('VEVENT');
+        $this->assertInstanceOf('Sabre\VObject\Component\VEvent', $event);
+
+        $event = $vcal->create('CALSCALE');
+        $this->assertInstanceOf('Sabre\VObject\Property\Text', $event);
+
+    }
+
+    function testGetClassNameForPropertyValue() {
+
+        $vcal = new Component\VCalendar(array(), false);
+        $this->assertEquals('Sabre\\VObject\\Property\\Text', $vcal->getClassNameForPropertyValue('TEXT'));
+        $this->assertNull($vcal->getClassNameForPropertyValue('FOO'));
+
+    }
+
+}
+
+
+class MockDocument extends Document {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ElementListTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,33 @@
+<?php
+
+namespace Sabre\VObject;
+
+class ElementListTest extends \PHPUnit_Framework_TestCase {
+
+    function testIterate() {
+
+        $cal = new Component\VCalendar();
+        $sub = $cal->createComponent('VEVENT');
+
+        $elems = array(
+            $sub,
+            clone $sub,
+            clone $sub
+        );
+
+        $elemList = new ElementList($elems);
+
+        $count = 0;
+        foreach($elemList as $key=>$subcomponent) {
+
+           $count++;
+           $this->assertInstanceOf('Sabre\\VObject\\Component',$subcomponent);
+
+        }
+        $this->assertEquals(3,$count);
+        $this->assertEquals(2,$key);
+
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/EmClientTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,55 @@
+<?php
+
+namespace Sabre\VObject;
+
+class EmClientTest extends \PHPUnit_Framework_TestCase {
+
+    function testParseTz() {
+
+        $str = 'BEGIN:VCALENDAR
+X-WR-CALNAME:Blackhawks Schedule 2011-12
+X-APPLE-CALENDAR-COLOR:#E51717
+X-WR-TIMEZONE:America/Chicago
+CALSCALE:GREGORIAN
+PRODID:-//eM Client/4.0.13961.0
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:America/Chicago
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0600
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+DTSTART:20070311T020000
+TZNAME:CDT
+TZOFFSETTO:-0500
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0500
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+DTSTART:20071104T020000
+TZNAME:CST
+TZOFFSETTO:-0600
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20110624T181236Z
+UID:be3bbfff-96e8-4c66-9908-ab791a62231d
+DTEND;TZID="America/Chicago":20111008T223000
+TRANSP:OPAQUE
+SUMMARY:Stars @ Blackhawks (Home Opener)
+DTSTART;TZID="America/Chicago":20111008T193000
+DTSTAMP:20120330T013232Z
+SEQUENCE:2
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY
+LAST-MODIFIED:20120330T013237Z
+CLASS:PUBLIC
+END:VEVENT
+END:VCALENDAR';
+
+        $vObject = Reader::read($str);
+        $dt = $vObject->VEVENT->DTSTART->getDateTime();
+        $this->assertEquals(new \DateTime('2011-10-08 19:30:00', new \DateTimeZone('America/Chicago')), $dt);
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/EmptyParameterTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,69 @@
+<?php
+
+namespace Sabre\VObject;
+
+class IssueEmptyParameterTest extends \PHPUnit_Framework_TestCase {
+
+    function testRead() {
+
+        $input = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+N:Doe;Jon;;;
+FN:Jon Doe
+EMAIL;X-INTERN:foo@example.org
+UID:foo
+END:VCARD
+VCF;
+
+        $vcard = Reader::read($input);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCard', $vcard);
+        $vcard = $vcard->convert(\Sabre\VObject\Document::VCARD30);
+        $vcard = $vcard->serialize();
+
+        $converted = Reader::read($vcard);
+        $converted->validate();
+
+        $this->assertTrue(isset($converted->EMAIL['X-INTERN']));
+
+        $version = Version::VERSION;
+
+        $expected = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+N:Doe;Jon;;;
+FN:Jon Doe
+EMAIL;X-INTERN=:foo@example.org
+UID:foo
+END:VCARD
+
+VCF;
+
+        $this->assertEquals($expected, str_replace("\r","", $vcard));
+
+    }
+
+    function testVCard21Parameter() {
+
+        $vcard = new Component\VCard(array(), false);
+        $vcard->VERSION = '2.1';
+        $vcard->PHOTO = 'random_stuff';
+        $vcard->PHOTO->add(null,'BASE64');
+        $vcard->UID = 'foo-bar';
+
+        $result = $vcard->serialize();
+        $expected = array(
+            "BEGIN:VCARD",
+            "VERSION:2.1",
+            "PHOTO;BASE64:" . base64_encode('random_stuff'),
+            "UID:foo-bar",
+            "END:VCARD",
+            "",
+        );
+
+        $this->assertEquals(implode("\r\n", $expected), $result);
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/EmptyValueIssueTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,31 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This test is written for Issue 68:
+ *
+ * https://github.com/fruux/sabre-vobject/issues/68 
+ */
+class EmptyValueIssueTest extends \PHPUnit_Framework_TestCase {
+
+    function testDecodeValue() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+DESCRIPTION:This is a descpription\\nwith a linebreak and a \\; \\, and :
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vobj = Reader::read($input);
+
+        // Before this bug was fixed, getValue() would return nothing.
+        $this->assertEquals("This is a descpription\nwith a linebreak and a ; , and :", $vobj->VEVENT->DESCRIPTION->getValue());
+
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/FreeBusyGeneratorTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,376 @@
+<?php
+
+namespace Sabre\VObject;
+
+class FreeBusyGeneratorTest extends \PHPUnit_Framework_TestCase {
+
+    function getInput() {
+
+        $tests = array();
+
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20110101T120000Z
+DTEND:20110101T130000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            "20110101T120000Z/20110101T130000Z"
+        );
+
+        // opaque, shows up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar2
+TRANSP:OPAQUE
+DTSTART:20110101T130000Z
+DTEND:20110101T140000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            "20110101T130000Z/20110101T140000Z"
+        );
+
+        // transparent, hidden
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar3
+TRANSP:TRANSPARENT
+DTSTART:20110101T140000Z
+DTEND:20110101T150000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            null,
+        );
+
+        // cancelled, hidden
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar4
+STATUS:CANCELLED
+DTSTART:20110101T160000Z
+DTEND:20110101T170000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            null,
+        );
+
+        // tentative, shows up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar5
+STATUS:TENTATIVE
+DTSTART:20110101T180000Z
+DTEND:20110101T190000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            '20110101T180000Z/20110101T190000Z',
+        );
+
+        // outside of time-range, hidden
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar6
+DTSTART:20110101T090000Z
+DTEND:20110101T100000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            null,
+        );
+
+        // outside of time-range, hidden
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar7
+DTSTART:20110104T090000Z
+DTEND:20110104T100000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            null,
+        );
+
+        // using duration, shows up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar8
+DTSTART:20110101T190000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            '20110101T190000Z/20110101T200000Z',
+        );
+
+        // Day-long event, shows up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar9
+DTSTART;VALUE=DATE:20110102
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            '20110102T000000Z/20110103T000000Z',
+        );
+
+
+        // No duration, does not show up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar10
+DTSTART:20110101T200000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            null,
+        );
+
+        // encoded as object, shows up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar11
+DTSTART:20110101T210000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            Reader::read($blob),
+            '20110101T210000Z/20110101T220000Z',
+        );
+
+        // Freebusy. Some parts show up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VFREEBUSY
+FREEBUSY:20110103T010000Z/20110103T020000Z
+FREEBUSY;FBTYPE=FREE:20110103T020000Z/20110103T030000Z
+FREEBUSY:20110103T030000Z/20110103T040000Z,20110103T040000Z/20110103T050000Z
+FREEBUSY:20120101T000000Z/20120101T010000Z
+FREEBUSY:20110103T050000Z/PT1H
+END:VFREEBUSY
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            Reader::read($blob),
+            array(
+                '20110103T010000Z/20110103T020000Z',
+                '20110103T030000Z/20110103T040000Z',
+                '20110103T040000Z/20110103T050000Z',
+                '20110103T050000Z/20110103T060000Z',
+            )
+        );
+
+
+        // Yearly recurrence rule, shows up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar13
+DTSTART:20100101T220000Z
+DTEND:20100101T230000Z
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            Reader::read($blob),
+            '20110101T220000Z/20110101T230000Z',
+        );
+
+
+        // Yearly recurrence rule + duration, shows up
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar14
+DTSTART:20100101T230000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            Reader::read($blob),
+            '20110101T230000Z/20110102T000000Z',
+        );
+
+        // Floating time, no timezone
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20110101T120000
+DTEND:20110101T130000
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            "20110101T120000Z/20110101T130000Z"
+        );
+
+        // Floating time + reference timezone
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20110101T120000
+DTEND:20110101T130000
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            "20110101T170000Z/20110101T180000Z",
+            new \DateTimeZone('America/Toronto')
+        );
+
+        // All-day event
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART;VALUE=DATE:20110101
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            "20110101T000000Z/20110102T000000Z"
+        );
+
+        // All-day event + reference timezone
+        $blob = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART;VALUE=DATE:20110101
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $tests[] = array(
+            $blob,
+            "20110101T050000Z/20110102T050000Z",
+            new \DateTimeZone('America/Toronto')
+        );
+        return $tests;
+
+    }
+
+    /**
+     * @dataProvider getInput
+     */
+    function testGenerator($input, $expected, $timeZone = null) {
+
+        $gen = new FreeBusyGenerator(
+            new \DateTime('20110101T110000Z', new \DateTimeZone('UTC')),
+            new \DateTime('20110103T110000Z', new \DateTimeZone('UTC')),
+            $input,
+            $timeZone
+        );
+
+        $result = $gen->getResult();
+
+        $expected = (array)$expected;
+
+        $freebusy = $result->VFREEBUSY->select('FREEBUSY');
+
+        foreach($freebusy as $fb) {
+
+            $this->assertContains((string)$fb, $expected, "$fb did not appear in our list of expected freebusy strings. This is concerning!");
+
+            $k = array_search((string)$fb, $expected);
+            unset($expected[$k]);
+
+        }
+        $this->assertTrue(
+            count($expected) === 0,
+            '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 Component\VCalendar();
+        $obj->METHOD = 'PUBLISH';
+
+        $gen = new FreeBusyGenerator();
+        $gen->setObjects(array());
+        $gen->setBaseObject($obj);
+
+        $result = $gen->getResult();
+
+        $this->assertEquals('PUBLISH', $result->METHOD->getValue());
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testInvalidArg() {
+
+        $gen = new FreeBusyGenerator(
+            new \DateTime('2012-01-01'),
+            new \DateTime('2012-12-31'),
+            new \StdClass()
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/GoogleColonEscapingTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,31 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Google produces vcards with a weird escaping of urls.
+ *
+ * VObject will provide a workaround for this, so end-user still get expected
+ * values.
+ */
+class GoogleColonEscaping extends \PHPUnit_Framework_TestCase {
+
+    function testDecode() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+FN:Evert Pot
+N:Pot;Evert;;;
+EMAIL;TYPE=INTERNET;TYPE=WORK:evert@fruux.com
+BDAY:1985-04-07
+item7.URL:http\://www.rooftopsolutions.nl/
+END:VCARD
+VCF;
+
+        $vobj = Reader::read($vcard);
+        $this->assertEquals('http://www.rooftopsolutions.nl/', $vobj->URL->getValue());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ICalendar/AttachParseTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,31 @@
+<?php
+
+namespace Sabre\VObject\ICalendar;
+
+use Sabre\VObject\Reader;
+
+class AttachParseTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * See issue #128 for more info.
+     */
+    function testParseAttach() {
+
+        $vcal = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+ATTACH;FMTTYPE=application/postscript:ftp://example.com/pub/reports/r-960812.ps
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($vcal);
+        $prop = $vcal->VEVENT->ATTACH;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property\\URI', $prop);
+        $this->assertEquals('ftp://example.com/pub/reports/r-960812.ps', $prop->getValue());
+
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/BrokerAttendeeReplyTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,962 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class BrokerAttendeeReplyTest extends BrokerTester {
+
+    function testAccepted() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SUMMARY:B-day party
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SUMMARY:B-day party
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => 'One',
+                'recipient' => 'mailto:strunk@example.org',
+                'recipientName' => 'Strunk',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+SUMMARY:B-day party
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testRecurringReply() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140724T120000Z
+SUMMARY:Daily sprint
+RRULE;FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org
+DTSTART:20140724T120000Z
+SUMMARY:Daily sprint
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+DTSTART:20140726T120000Z
+RECURRENCE-ID:20140726T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+DTSTART:20140724T120000Z
+RECURRENCE-ID:20140724T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org
+DTSTART:20140728T120000Z
+RECURRENCE-ID:20140728T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+DTSTART:20140729T120000Z
+RECURRENCE-ID:20140729T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+DTSTART:20140725T120000Z
+RECURRENCE-ID:20140725T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => 'One',
+                'recipient' => 'mailto:strunk@example.org',
+                'recipientName' => 'Strunk',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140726T120000Z
+SUMMARY:Daily sprint
+RECURRENCE-ID:20140726T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140724T120000Z
+SUMMARY:Daily sprint
+RECURRENCE-ID:20140724T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140728T120000Z
+SUMMARY:Daily sprint
+RECURRENCE-ID:20140728T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140729T120000Z
+SUMMARY:Daily sprint
+RECURRENCE-ID:20140729T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140725T120000Z
+SUMMARY:Daily sprint
+RECURRENCE-ID:20140725T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testRecurringAllDay() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140724
+RRULE;FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+    $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140724
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140726
+RECURRENCE-ID;VALUE=DATE:20140726
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140724
+RECURRENCE-ID;VALUE=DATE:20140724
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140728
+RECURRENCE-ID;VALUE=DATE:20140728
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140729
+RECURRENCE-ID;VALUE=DATE:20140729
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140725
+RECURRENCE-ID;VALUE=DATE:20140725
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+    $version = \Sabre\VObject\Version::VERSION;
+
+    $expected = array(
+        array(
+            'uid' => 'foobar',
+            'method' => 'REPLY',
+            'component' => 'VEVENT',
+            'sender' => 'mailto:one@example.org',
+            'senderName' => 'One',
+            'recipient' => 'mailto:strunk@example.org',
+            'recipientName' => 'Strunk',
+            'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140726
+RECURRENCE-ID;VALUE=DATE:20140726
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140724
+RECURRENCE-ID;VALUE=DATE:20140724
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140728
+RECURRENCE-ID;VALUE=DATE:20140728
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140729
+RECURRENCE-ID;VALUE=DATE:20140729
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140725
+RECURRENCE-ID;VALUE=DATE:20140725
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testNoChange() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $expected = array();
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testNoChangeForceSend() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;SCHEDULE-FORCE-SEND=REPLY;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => 'One',
+                'recipient' => 'mailto:strunk@example.org',
+                'recipientName' => 'Strunk',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+            )
+
+        );
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testNoRelevantAttendee() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = array();
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    /**
+     * In this test, an event exists in an attendees calendar. The event
+     * is recurring, and the attendee deletes 1 instance of the event.
+     * This instance shows up in EXDATE
+     *
+     * This should automatically generate a DECLINED message for that
+     * specific instance.
+     */
+    function testCreateReplyByException() {
+
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140811T200000Z
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140811T200000Z
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+EXDATE:20140818T200000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => null,
+                'recipient' => 'mailto:organizer@example.org',
+                'recipientName' => null,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140818T200000Z
+RECURRENCE-ID:20140818T200000Z
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    /**
+     * This test is identical to the last, but now we're working with
+     * timezones.
+     *
+     * @depends testCreateReplyByException
+     */
+    function testCreateReplyByExceptionTz() {
+
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;TZID=America/Toronto:20140811T200000
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;TZID=America/Toronto:20140811T200000
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+EXDATE;TZID=America/Toronto:20140818T200000
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => null,
+                'recipient' => 'mailto:organizer@example.org',
+                'recipientName' => null,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;TZID=America/Toronto:20140818T200000
+RECURRENCE-ID;TZID=America/Toronto:20140818T200000
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    /**
+     * @depends testCreateReplyByException
+     */
+    function testCreateReplyByExceptionAllDay() {
+
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SUMMARY:Weekly meeting
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140811
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SUMMARY:Weekly meeting
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140811
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+EXDATE;VALUE=DATE:20140818
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => null,
+                'recipient' => 'mailto:organizer@example.org',
+                'recipientName' => null,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140818
+SUMMARY:Weekly meeting
+RECURRENCE-ID;VALUE=DATE:20140818
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testDeclined() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => 'One',
+                'recipient' => 'mailto:strunk@example.org',
+                'recipientName' => 'Strunk',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testDeclinedCancelledEvent() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+STATUS:CANCELLED
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+STATUS:CANCELLED
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array();
+
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    /**
+     * In this test, a new exception is created by an attendee as well.
+     *
+     * Except in this case, there was already an overridden event, and the
+     * overridden event was marked as cancelled by the attendee.
+     *
+     * For any other attendence status, the new status would have been
+     * declined, but for this, no message should we sent.
+     */
+    function testDontCreateReplyWhenEventWasDeclined() {
+
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140811T200000Z
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+END:VEVENT
+BEGIN:VEVENT
+RECURRENCE-ID:20140818T200000Z
+UID:foobar
+SEQUENCE:1
+DTSTART:20140818T200000Z
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140811T200000Z
+RRULE:FREQ=WEEKLY
+ORGANIZER:mailto:organizer@example.org
+ATTENDEE:mailto:one@example.org
+EXDATE:20140818T200000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $expected = array();
+
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testScheduleAgentOnOrganizer() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;SCHEDULE-AGENT=CLIENT;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array();
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+    function testAcceptedAllDay() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140716
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+DTSTART;VALUE=DATE:20140716
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => 'One',
+                'recipient' => 'mailto:strunk@example.org',
+                'recipientName' => 'Strunk',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART;VALUE=DATE:20140716
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/BrokerDeleteEventTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,193 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class BrokerDeleteEventTest extends BrokerTester {
+
+    function testOrganizerDelete() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = null;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'CANCEL',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:CANCEL
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+            ),
+
+            array(
+                'uid' => 'foobar',
+                'method' => 'CANCEL',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:two@example.org',
+                'recipientName' => 'Two',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:CANCEL
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testAttendeeDelete() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = null;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REPLY',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:one@example.org',
+                'senderName' => 'One',
+                'recipient' => 'mailto:strunk@example.org',
+                'recipientName' => 'Strunk',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+            ),
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected, 'mailto:one@example.org');
+
+
+    }
+
+    function testAttendeeDeleteCancelledEvent() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+STATUS:CANCELLED
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = null;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array();
+
+        $result = $this->parse($oldMessage, $newMessage, $expected, 'mailto:one@example.org');
+
+
+    }
+
+    function testNoCalendar() {
+
+        $this->parse(null, null, array(), 'mailto:one@example.org');
+
+    }
+
+    function testVTodo() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VTODO
+UID:foobar
+SEQUENCE:1
+END:VTODO
+END:VCALENDAR
+ICS;
+        $this->parse($oldMessage, null, array(), 'mailto:one@example.org');
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/BrokerNewEventTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,507 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class BrokerNewEventTest extends \PHPUnit_Framework_TestCase {
+
+    function testNoAttendee() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140811T220000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->parse($message);
+
+    }
+
+    function testVTODO() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VTODO
+UID:foobar
+END:VTODO
+END:VCALENDAR
+ICS;
+
+        $result = $this->parse($message);
+
+    }
+
+    function testSimpleInvite() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140811T220000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=White:mailto:white@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $expectedMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140811T220000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=White;PARTSTAT=NEEDS-ACTION:mailto:white@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:white@example.org',
+                'recipientName' => 'White',
+                'message' => $expectedMessage,
+            ),
+        );
+
+        $result = $this->parse($message, $expected);
+
+    }
+
+    /**
+     * @expectedException \Sabre\VObject\ITip\ITipException
+     */
+    function testBrokenEventUIDMisMatch() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=White:mailto:white@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=White:mailto:white@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = array();
+        $this->parse($message, array());
+
+    }
+    /**
+     * @expectedException \Sabre\VObject\ITip\ITipException
+     */
+    function testBrokenEventOrganizerMisMatch() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=White:mailto:white@example.org
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER:mailto:foo@example.org
+ATTENDEE;CN=White:mailto:white@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = array();
+        $this->parse($message, array());
+
+    }
+
+    function testRecurrenceInvite() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+EXDATE:20140717T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+EXDATE:20140717T120000Z,20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:two@example.org',
+                'recipientName' => 'Two',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+EXDATE:20140717T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:three@example.org',
+                'recipientName' => 'Three',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+
+        $result = $this->parse($message, $expected);
+
+    }
+
+    function testRecurrenceInvite2() {
+
+        // This method tests a nearly identical path, but in this case the
+        // master event does not have an EXDATE.
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+EXDATE:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:two@example.org',
+                'recipientName' => 'Two',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:three@example.org',
+                'recipientName' => 'Three',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+
+        $result = $this->parse($message, $expected);
+
+    }
+
+    function testScheduleAgentClient() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140811T220000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=White;SCHEDULE-AGENT=CLIENT:mailto:white@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array();
+        $result = $this->parse($message, $expected);
+
+    }
+
+    /**
+     * @expectedException Sabre\VObject\ITip\ITipException
+     */
+    function testMultipleUID() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar2
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $result = $this->parse($message, array());
+
+    }
+
+    /**
+     * @expectedException Sabre\VObject\ITip\SameOrganizerForAllComponentsException
+     *
+     */
+    function testChangingOrganizers() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:ew@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+        $result = $this->parse($message, array());
+
+    }
+    function testNoOrganizerHasAttendee() {
+
+        $message = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140811T220000Z
+ATTENDEE;CN=Two:mailto:two@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $this->parse($message, array());
+
+    }
+
+    function parse($message, $expected = array()) {
+
+        $broker = new Broker();
+        $result = $broker->parseEvent($message, 'mailto:strunk@example.org');
+
+        $this->assertEquals(count($expected), count($result));
+
+        foreach($expected as $index=>$ex) {
+
+            $message = $result[$index];
+
+            foreach($ex as $key=>$val) {
+
+                if ($key==='message') {
+                    $this->assertEquals(
+                        str_replace("\n", "\r\n", $val),
+                        rtrim($message->message->serialize(), "\r\n")
+                    );
+                } else {
+                    $this->assertEquals($val, $message->$key);
+                }
+
+            }
+
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/BrokerProcessMessageTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,168 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class BrokerProcessMessageTest extends BrokerTester {
+
+    function testRequestNew() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+BEGIN:VEVENT
+SEQUENCE:1
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+%foo%
+BEGIN:VEVENT
+SEQUENCE:1
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, null, $expected);
+
+    }
+
+    function testRequestUpdate() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REQUEST
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+%foo%
+BEGIN:VEVENT
+SEQUENCE:1
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+%foo%
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testCancel() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+%foo%
+BEGIN:VEVENT
+SEQUENCE:1
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+%foo%
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+STATUS:CANCELLED
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testCancelNoExistingEvent() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:CANCEL
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = null;
+        $expected = null;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testUnsupportedComponent() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VTODO
+SEQUENCE:2
+UID:foobar
+END:VTODO
+END:VCALENDAR
+ICS;
+
+        $old = null;
+        $expected = null;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testUnsupportedMethod() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = null;
+        $expected = null;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/BrokerProcessReplyTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,425 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class BrokerProcessReplyTest extends BrokerTester {
+
+    function testReplyNoOriginal() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = null;
+        $expected = null;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testReplyAccept() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testReplyRequestStatus() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+UID:foobar
+REQUEST-STATUS:2.3;foo-bar!
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.3:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+
+    function testReplyPartyCrasher() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:crasher@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:crasher@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testReplyNewException() {
+
+        // This is a reply to 1 instance of a recurring event. This should
+        // automatically create an exception.
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+RECURRENCE-ID:20140725T000000Z
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+RRULE:FREQ=DAILY
+DTSTART:20140724T000000Z
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+RRULE:FREQ=DAILY
+DTSTART:20140724T000000Z
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+DTSTART:20140725T000000Z
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+RECURRENCE-ID:20140725T000000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testReplyNewExceptionTz() {
+
+        // This is a reply to 1 instance of a recurring event. This should
+        // automatically create an exception.
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+RECURRENCE-ID;TZID=America/Toronto:20140725T000000
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+RRULE:FREQ=DAILY
+DTSTART;TZID=America/Toronto:20140724T000000
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+RRULE:FREQ=DAILY
+DTSTART;TZID=America/Toronto:20140724T000000
+ATTENDEE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+DTSTART;TZID=America/Toronto:20140725T000000
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+RECURRENCE-ID;TZID=America/Toronto:20140725T000000
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testReplyPartyCrashCreateExcepton() {
+
+        // IN this test there's a recurring event that has an exception. The
+        // exception is missing the attendee.
+        //
+        // The attendee party crashes the instance, so it should show up in the
+        // resulting object.
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Crasher!:mailto:crasher@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+RECURRENCE-ID:20140725T000000Z
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+RRULE:FREQ=DAILY
+DTSTART:20140724T000000Z
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+RRULE:FREQ=DAILY
+DTSTART:20140724T000000Z
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+DTSTART:20140725T000000Z
+ORGANIZER:mailto:bar@example.org
+RECURRENCE-ID:20140725T000000Z
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Crasher!:mailto:crasher@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    function testReplyNewExceptionNoMasterEvent() {
+
+        /**
+         * This iTip message would normally create a new exception, but the
+         * server is not able to create this new instance, because there's no
+         * master event to clone from.
+         *
+         * This test checks if the message is ignored.
+         */
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+ATTENDEE;PARTSTAT=ACCEPTED;CN=Crasher!:mailto:crasher@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+RECURRENCE-ID:20140725T000000Z
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+RRULE:FREQ=DAILY
+DTSTART:20140724T000000Z
+RECURRENCE-ID:20140724T000000Z
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = null;
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+    /**
+     * @depends testReplyAccept
+     */
+    function testReplyAcceptUpdateRSVP() {
+
+        $itip = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+BEGIN:VEVENT
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+SEQUENCE:2
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+ATTENDEE;RSVP=TRUE:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:2
+UID:foobar
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:foo@example.org
+ORGANIZER:mailto:bar@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $result = $this->process($itip, $old, $expected);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/BrokerTester.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,103 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+use Sabre\VObject\Reader;
+
+/**
+ * Utilities for testing the broker
+ * 
+ * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
+ * @author Evert Pot (http://evertpot.com/) 
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class BrokerTester extends \PHPUnit_Framework_TestCase {
+
+    function parse($oldMessage, $newMessage, $expected = array(), $currentUser = 'mailto:one@example.org') {
+
+        $broker = new Broker();
+        $result = $broker->parseEvent($newMessage, $currentUser, $oldMessage);
+
+        $this->assertEquals(count($expected), count($result));
+
+        foreach($expected as $index=>$ex) {
+
+            $message = $result[$index];
+
+            foreach($ex as $key=>$val) {
+
+                if ($key==='message') {
+                    $this->assertEquals(
+                        str_replace("\n", "\r\n", $val),
+                        rtrim($message->message->serialize(), "\r\n")
+                    );
+                } else {
+                    $this->assertEquals($val, $message->$key);
+                }
+
+            }
+
+        }
+
+    }
+
+    function process($input, $existingObject = null, $expected = false) {
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $vcal = Reader::read($input);
+
+        foreach($vcal->getComponents() as $mainComponent) {
+            break;
+        }
+
+        $message = new Message();
+        $message->message = $vcal;
+        $message->method = isset($vcal->METHOD)?$vcal->METHOD->getValue():null;
+        $message->component = $mainComponent->name;
+        $message->uid = $mainComponent->uid->getValue();
+        $message->sequence = isset($vcal->VEVENT[0])?(string)$vcal->VEVENT[0]->SEQUENCE:null;
+
+        if ($message->method === 'REPLY') {
+
+            $message->sender = $mainComponent->ATTENDEE->getValue();
+            $message->senderName = isset($mainComponent->ATTENDEE['CN'])?$mainComponent->ATTENDEE['CN']->getValue():null;
+            $message->recipient = $mainComponent->ORGANIZER->getValue();
+            $message->recipientName = isset($mainComponent->ORGANIZER['CN'])?$mainComponent->ORGANIZER['CN']:null;
+
+        }
+
+        $broker = new Broker();
+
+        if (is_string($existingObject)) {
+            $existingObject = str_replace(
+                '%foo%',
+                "VERSION:2.0\nPRODID:-//Sabre//Sabre VObject $version//EN\nCALSCALE:GREGORIAN",
+                $existingObject
+            );
+            $existingObject = Reader::read($existingObject);
+        }
+
+        $result = $broker->processMessage($message, $existingObject);
+
+        if (is_string($expected)) {
+            $expected = str_replace(
+                '%foo%',
+                "VERSION:2.0\nPRODID:-//Sabre//Sabre VObject $version//EN\nCALSCALE:GREGORIAN",
+                $expected
+            );
+            $expected = str_replace("\n", "\r\n", $expected);
+
+        }
+        if ($result instanceof \Sabre\VObject\Component\VCalendar) {
+            $result = $result->serialize();
+            $result = rtrim($result,"\r\n");
+        }
+
+        $this->assertEquals(
+            $expected,
+            $result
+        );
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/BrokerUpdateEventTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,719 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class BrokerUpdateTest extends BrokerTester {
+
+    function testInviteChange() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'CANCEL',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:CANCEL
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:two@example.org',
+                'recipientName' => 'Two',
+                'significantChange' => false,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:three@example.org',
+                'recipientName' => 'Three',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testInviteChangeFromNonSchedulingToSchedulingObject() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testInviteChangeFromSchedulingToNonSchedulingObject() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'CANCEL',
+                'component' => 'VEVENT',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:CANCEL
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testNoAttendees() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array();
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testRemoveInstance() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART;TZID=America/Toronto:20140716T120000
+RRULE:FREQ=WEEKLY
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART;TZID=America/Toronto:20140716T120000
+RRULE:FREQ=WEEKLY
+EXDATE;TZID=America/Toronto:20140724T120000
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+DTSTART;TZID=America/Toronto:20140716T120000
+RRULE:FREQ=WEEKLY
+EXDATE;TZID=America/Toronto:20140724T120000
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    /**
+     * This test is identical to the first test, except this time we change the
+     * DURATION property.
+     *
+     * This should ensure that the message is significant for every attendee,
+     */
+    function testInviteChangeSignificantChange() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+DURATION:PT1H
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+DURATION:PT2H
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+ATTENDEE;CN=Three:mailto:three@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'CANCEL',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:CANCEL
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:two@example.org',
+                'recipientName' => 'Two',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+DURATION:PT2H
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:three@example.org',
+                'recipientName' => 'Three',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+DURATION:PT2H
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org
+ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testInviteNoChange() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'significantChange' => false,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testInviteNoChangeForceSend() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;SCHEDULE-FORCE-SEND=REQUEST;CN=One:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'REQUEST',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org
+ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+
+        );
+
+        $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+
+    function testInviteRemoveAttendees() {
+
+        $oldMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:1
+SUMMARY:foo
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+
+        $newMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+DTSTART:20140716T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION;
+
+        $expected = array(
+            array(
+                'uid' => 'foobar',
+                'method' => 'CANCEL',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:one@example.org',
+                'recipientName' => 'One',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:CANCEL
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=One:mailto:one@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+            array(
+                'uid' => 'foobar',
+                'method' => 'CANCEL',
+                'component' => 'VEVENT',
+                'sender' => 'mailto:strunk@example.org',
+                'senderName' => 'Strunk',
+                'recipient' => 'mailto:two@example.org',
+                'recipientName' => 'Two',
+                'significantChange' => true,
+                'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:CANCEL
+BEGIN:VEVENT
+UID:foobar
+SEQUENCE:2
+SUMMARY:foo
+DTSTART:20140716T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk@example.org
+ATTENDEE;CN=Two:mailto:two@example.org
+END:VEVENT
+END:VCALENDAR
+ICS
+
+            ),
+        );
+
+        $result = $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org');
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/EvolutionTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,2653 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class EvolutionTest extends BrokerTester {
+
+    /**
+     * Evolution does things as usual a little bit differently.
+     *
+     * We're adding a seprate test just for it.
+     */
+    function testNewEvolutionEvent() {
+
+        $ics = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Ximian//NONSGML Evolution Calendar//EN
+BEGIN:VTIMEZONE
+TZID:/freeassociation.sourceforge.net/Tzfile/America/Toronto
+X-LIC-LOCATION:America/Toronto
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19691026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19700426T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19701025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19710425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19711031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19720430T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19721029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19730429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19731028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19740428T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19741027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19750427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19751026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19760425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19761031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19770424T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19771030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19780430T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19781029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19790429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19791028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19800427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19801026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19810426T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19811025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19820425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19821031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19830424T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19831030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19840429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19841028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19850428T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19851027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19860427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19861026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19870405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19871025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19880403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19881030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19890402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19891029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19900401T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19901028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19910407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19911027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19920405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19921025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19930404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19931031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19940403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19941030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19950402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19951029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19960407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19961027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19970406T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19971026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19980405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19981025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19990404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19991031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20000402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20001029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20010401T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20011028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20020407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20021027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20030406T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20031026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20040404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20041031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20050403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20051030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20060402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20061029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20070311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20071104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20080309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20081102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20090308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20091101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20100314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20101107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20110313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20111106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20120311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20121104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20130310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20131103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20140309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20141102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20150308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20151101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20160313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20161106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20170312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20171105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20180311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20181104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20190310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20191103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20200308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20201101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20210314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20211107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20220313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20221106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20230312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20231105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20240310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20241103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20250309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20251102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20260308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20261101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20270314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20271107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20280312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20281105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20290311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20291104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20300310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20301103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20310309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20311102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20320314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20321107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20330313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20331106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20340312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20341105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20350311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20351104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20360309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20361102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20370308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20371101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:20140813T153116Z-12176-1000-1065-6@johnny-lubuntu
+DTSTAMP:20140813T142829Z
+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:201408
+ 15T110000
+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:20140815
+ T113000
+TRANSP:OPAQUE
+SEQUENCE:2
+SUMMARY:Evo makes a Meeting (fruux HQ) (fruux HQ)
+LOCATION:fruux HQ
+CLASS:PUBLIC
+ORGANIZER;SENT-BY="MAILTO:martin+johnny@fruux.com":MAILTO:martin@fruux.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE
+ ;SENT-BY="MAILTO:martin+johnny@fruux.com";LANGUAGE=en:MAILTO:martin@fruux.
+ com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=
+ TRUE;LANGUAGE=en:MAILTO:dominik@fruux.com
+CREATED:20140813T153211Z
+LAST-MODIFIED:20140813T155353Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $version = \Sabre\VObject\Version::VERSION; 
+        $expectedICS = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VTIMEZONE
+TZID:/freeassociation.sourceforge.net/Tzfile/America/Toronto
+X-LIC-LOCATION:America/Toronto
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19691026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19700426T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19701025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19710425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19711031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19720430T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19721029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19730429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19731028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19740428T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19741027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19750427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19751026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19760425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19761031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19770424T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19771030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19780430T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19781029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19790429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19791028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19800427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19801026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19810426T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19811025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19820425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19821031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19830424T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19831030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19840429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19841028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19850428T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19851027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19860427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19861026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19870405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19871025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19880403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19881030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19890402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19891029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19900401T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19901028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19910407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19911027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19920405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19921025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19930404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19931031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19940403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19941030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19950402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19951029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19960407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19961027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19970406T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19971026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19980405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19981025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19990404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19991031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20000402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20001029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20010401T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20011028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20020407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20021027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20030406T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20031026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20040404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20041031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20050403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20051030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20060402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20061029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20070311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20071104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20080309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20081102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20090308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20091101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20100314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20101107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20110313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20111106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20120311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20121104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20130310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20131103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20140309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20141102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20150308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20151101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20160313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20161106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20170312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20171105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20180311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20181104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20190310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20191103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20200308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20201101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20210314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20211107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20220313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20221106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20230312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20231105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20240310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20241103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20250309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20251102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20260308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20261101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20270314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20271107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20280312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20281105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20290311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20291104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20300310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20301103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20310309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20311102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20320314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20321107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20330313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20331106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20340312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20341105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20350311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20351104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20360309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20361102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20370308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20371101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:20140813T153116Z-12176-1000-1065-6@johnny-lubuntu
+DTSTAMP:20140813T142829Z
+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:201408
+ 15T110000
+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:20140815
+ T113000
+TRANSP:OPAQUE
+SEQUENCE:2
+SUMMARY:Evo makes a Meeting (fruux HQ) (fruux HQ)
+LOCATION:fruux HQ
+CLASS:PUBLIC
+ORGANIZER;SENT-BY="MAILTO:martin+johnny@fruux.com":MAILTO:martin@fruux.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE
+ ;SENT-BY="MAILTO:martin+johnny@fruux.com";LANGUAGE=en:MAILTO:martin@fruux.
+ com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=
+ TRUE;LANGUAGE=en:MAILTO:dominik@fruux.com
+CREATED:20140813T153211Z
+LAST-MODIFIED:20140813T155353Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $expected = array(
+            array(
+                'uid' => '20140813T153116Z-12176-1000-1065-6@johnny-lubuntu',
+                'method' => 'REQUEST',
+                'sender' => 'mailto:martin@fruux.com',
+                'senderName' => null,
+                'recipient' => 'mailto:dominik@fruux.com',
+                'recipientName' => null,
+                'message' => $expectedICS,
+            )
+        ); 
+        $this->parse(null, $ics, $expected, 'mailto:martin@fruux.com');
+
+    }
+
+    /**
+     * This is an event originally from evolution, then parsed by sabredav and
+     * again mangled by iCal. This triggered a few bugs related to email
+     * address scheme casing.
+     */
+    public function testAttendeeModify() {
+
+        $old = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject 3.3.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:/freeassociation.sourceforge.net/Tzfile/America/Toronto
+X-LIC-LOCATION:America/Toronto
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19691026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19700426T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19701025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19710425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19711031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19720430T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19721029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19730429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19731028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19740428T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19741027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19750427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19751026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19760425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19761031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19770424T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19771030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19780430T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19781029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19790429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19791028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19800427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19801026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19810426T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19811025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19820425T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19821031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19830424T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19831030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19840429T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19841028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19850428T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19851027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19860427T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19861026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19870405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19871025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19880403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19881030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19890402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19891029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19900401T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19901028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19910407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19911027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19920405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19921025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19930404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19931031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19940403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19941030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19950402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19951029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19960407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19961027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19970406T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19971026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19980405T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19981025T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19990404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19991031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20000402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20001029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20010401T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20011028T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20020407T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20021027T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20030406T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20031026T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20040404T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20041031T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20050403T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20051030T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20060402T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20061029T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20070311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20071104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20080309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20081102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20090308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20091101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20100314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20101107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20110313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20111106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20120311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20121104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20130310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20131103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20140309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20141102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20150308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20151101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20160313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20161106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20170312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20171105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20180311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20181104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20190310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20191103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20200308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20201101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20210314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20211107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20220313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20221106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20230312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20231105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20240310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20241103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20250309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20251102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20260308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20261101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20270314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20271107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20280312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20281105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20290311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20291104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20300310T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20301103T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20310309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20311102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20320314T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20321107T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20330313T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20331106T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20340312T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20341105T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20350311T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20351104T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20360309T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20361102T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:20370308T020000
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:20371101T020000
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:20140813T212317Z-6646-1000-1221-23@evert-ubuntu
+DTSTAMP:20140813T212221Z
+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:201408
+ 13T180000
+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:20140813
+ T200000
+TRANSP:OPAQUE
+SEQUENCE:4
+SUMMARY:Testing evolution
+LOCATION:Online
+CLASS:PUBLIC
+ORGANIZER:MAILTO:o@example.org
+CREATED:20140813T212510Z
+LAST-MODIFIED:20140813T212541Z
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE;LANGUAGE=en:MAILTO:o@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;LANGUAGE=en:MAILTO:a1@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;LANGUAGE=en:MAILTO:a2@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;LANGUAGE=en:MAILTO:a3@example.org
+STATUS:CANCELLED
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $new = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//Mac OS X 10.9.4//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:America/Toronto
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:EDT
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:EST
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+TRANSP:OPAQUE
+DTEND;TZID=America/Toronto:20140813T200000
+ORGANIZER:MAILTO:o@example.org
+UID:20140813T212317Z-6646-1000-1221-23@evert-ubuntu
+DTSTAMP:20140813T212221Z
+LOCATION:Online
+STATUS:CANCELLED
+SEQUENCE:4
+CLASS:PUBLIC
+SUMMARY:Testing evolution
+LAST-MODIFIED:20140813T212541Z
+DTSTART;TZID=America/Toronto:20140813T180000
+CREATED:20140813T212510Z
+ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:a2@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:o@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:a1@example.org
+ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:a3@example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $this->parse($old, $new, array(), 'mailto:a1@example.org');
+
+
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ITip/MessageTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,32 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class MessageTest extends \PHPUnit_Framework_TestCase {
+
+    public function testNoScheduleStatus() {
+
+        $message = new Message();
+        $this->assertFalse($message->getScheduleStatus());
+
+    }
+
+    public function testScheduleStatus() {
+
+        $message = new Message();
+        $message->scheduleStatus = '1.2;Delivered';
+
+        $this->assertEquals('1.2', $message->getScheduleStatus());
+
+    }
+
+    public function testUnexpectedScheduleStatus() {
+
+        $message = new Message();
+        $message->scheduleStatus = '9.9.9';
+
+        $this->assertEquals('9.9.9', $message->getScheduleStatus());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Issue153Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,14 @@
+<?php
+
+namespace Sabre\VObject;
+
+class Issue153Test extends \PHPUnit_Framework_TestCase {
+
+    function testRead() {
+
+        $obj = Reader::read(file_get_contents(dirname(__FILE__) . '/issue153.vcf'));
+        $this->assertEquals('Test Benutzer', (string)$obj->fn);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Issue26Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,36 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    DateTime,
+    DateTimeZone;
+
+class Issue26Test extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    function testExpand() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:bae5d57a98
+RRULE:FREQ=MONTHLY;BYDAY=0MO,0TU,0WE,0TH,0FR;INTERVAL=1
+DTSTART;VALUE=DATE:20130401
+DTEND;VALUE=DATE:20130402
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
+
+        $it = new Recur\EventIterator($vcal, 'bae5d57a98');
+        iterator_to_array($it);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Issue36WorkAroundTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,39 @@
+<?php
+
+namespace Sabre\VObject;
+
+class Issue36WorkAroundTest extends \PHPUnit_Framework_TestCase {
+
+    function testWorkaround() {
+
+        // See https://github.com/fruux/sabre-vobject/issues/36
+        $event = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SUMMARY:Titel
+SEQUENCE:1
+TRANSP:TRANSPARENT
+RRULE:FREQ=YEARLY
+LAST-MODIFIED:20130323T225737Z
+DTSTAMP:20130323T225737Z
+UID:1833bd44-188b-405c-9f85-1a12105318aa
+CATEGORIES:Jubiläum
+X-MOZ-GENERATION:3
+RECURRENCE-ID;RANGE=THISANDFUTURE;VALUE=DATE:20131013
+DTSTART;VALUE=DATE:20131013
+CREATED:20100721T121914Z
+DURATION:P1D
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $obj = Reader::read($event);
+
+        // If this does not throw an exception, it's all good.
+        $it = new Recur\EventIterator($obj,'1833bd44-188b-405c-9f85-1a12105318aa');
+        $this->assertInstanceOf('Sabre\\VObject\\Recur\EventIterator', $it);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Issue40Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,30 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This test is created to handle the issues brought forward by issue 40.
+ *
+ * https://github.com/fruux/sabre-vobject/issues/40
+ */
+class Issue40Test extends \PHPUnit_Framework_TestCase {
+
+    function testEncode() {
+
+        $card = new Component\VCard();
+        $card->add('N', array('van der Harten', array('Rene','J.'), "", 'Sir','R.D.O.N.'), array('SORT-AS' => array('Harten','Rene')));
+
+        $expected = implode("\r\n", array(
+            "BEGIN:VCARD",
+            "VERSION:3.0",
+            "PRODID:-//Sabre//Sabre VObject " . Version::VERSION . '//EN',
+            "N;SORT-AS=Harten,Rene:van der Harten;Rene,J.;;Sir;R.D.O.N.",
+            "END:VCARD",
+            ""
+        ));
+
+        $this->assertEquals($expected, $card->serialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Issue64Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\VObject;
+
+class Issue64Test extends \PHPUnit_Framework_TestCase {
+
+    function testRead() {
+
+        $vcard = Reader::read(file_get_contents(dirname(__FILE__) . '/issue64.vcf'));
+        $vcard = $vcard->convert(\Sabre\VObject\Document::VCARD30);
+        $vcard = $vcard->serialize();
+
+        $converted = Reader::read($vcard);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCard', $converted);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Issue96Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,24 @@
+<?php
+
+namespace Sabre\VObject;
+
+class Issue96Test extends \PHPUnit_Framework_TestCase {
+
+    function testRead() {
+
+        $input = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+SOURCE:Yahoo Contacts (http://contacts.yahoo.com)
+URL;CHARSET=utf-8;ENCODING=QUOTED-PRINTABLE:=
+http://www.example.org
+END:VCARD
+VCF;
+
+        $vcard = Reader::read($input, Reader::OPTION_FORGIVING);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCard', $vcard);
+        $this->assertEquals("http://www.example.org", $vcard->url->getValue());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/JCalTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,150 @@
+<?php
+
+namespace Sabre\VObject;
+
+class JCalTest extends \PHPUnit_Framework_TestCase {
+
+    function testToJCal() {
+
+        $cal = new Component\VCalendar();
+
+        $event = $cal->add('VEVENT', array(
+            "UID" => "foo",
+            "DTSTART" => new \DateTime("2013-05-26 18:10:00Z"),
+            "DURATION" => "P1D",
+            "CATEGORIES" => array('home', 'testing'),
+            "CREATED" => new \DateTime("2013-05-26 18:10:00Z"),
+
+            "ATTENDEE" => "mailto:armin@example.org",
+            "GEO" => array(51.96668, 7.61876),
+            "SEQUENCE" => 5,
+            "FREEBUSY" => array("20130526T210213Z/PT1H", "20130626T120000Z/20130626T130000Z"),
+            "URL" => "http://example.org/",
+            "TZOFFSETFROM" => "+05:00",
+            "RRULE" => array('FREQ' => 'WEEKLY', 'BYDAY' => array('MO','TU')),
+        ));
+
+        // Modifying DTSTART to be a date-only.
+        $event->dtstart['VALUE'] = 'DATE';
+        $event->add("X-BOOL", true, array('VALUE' => 'BOOLEAN'));
+        $event->add("X-TIME", "08:00:00", array('VALUE' => 'TIME'));
+        $event->add("ATTACH", "attachment", array('VALUE' => 'BINARY'));
+        $event->add("ATTENDEE", "mailto:dominik@example.org", array("CN" => "Dominik", "PARTSTAT" => "DECLINED"));
+
+        $event->add('REQUEST-STATUS', array("2.0", "Success"));
+        $event->add('REQUEST-STATUS', array("3.7", "Invalid Calendar User", "ATTENDEE:mailto:jsmith@example.org"));
+
+        $event->add('DTEND', '20150108T133000');
+
+        $expected = array(
+            "vcalendar",
+            array(
+                array(
+                    "version",
+                    new \StdClass(),
+                    "text",
+                    "2.0"
+                ),
+                array(
+                    "prodid",
+                    new \StdClass(),
+                    "text",
+                    "-//Sabre//Sabre VObject " . Version::VERSION . "//EN",
+                ),
+                array(
+                    "calscale",
+                    new \StdClass(),
+                    "text",
+                    "GREGORIAN"
+                ),
+            ),
+            array(
+                array("vevent",
+                    array(
+                        array(
+                            "uid", new \StdClass(), "text", "foo",
+                        ),
+                        array(
+                            "dtstart", new \StdClass(), "date", "2013-05-26",
+                        ),
+                        array(
+                            "duration", new \StdClass(), "duration", "P1D",
+                        ),
+                        array(
+                            "categories", new \StdClass(), "text", "home", "testing",
+                        ),
+                        array(
+                            "created", new \StdClass(), "date-time", "2013-05-26T18:10:00Z",
+                        ),
+
+                        array(
+                            "attendee", new \StdClass(), "cal-address", "mailto:armin@example.org",
+                        ),
+                        array(
+                            "geo", new \StdClass(), "float", array(51.96668, 7.61876),
+                        ),
+                        array(
+                            "sequence", new \StdClass(), "integer", 5
+                        ),
+                        array(
+                            "freebusy", new \StdClass(), "period",  array("2013-05-26T21:02:13", "PT1H"), array("2013-06-26T12:00:00", "2013-06-26T13:00:00"),
+                        ),
+                        array(
+                            "url", new \StdClass(), "uri", "http://example.org/",
+                        ),
+                        array(
+                            "tzoffsetfrom", new \StdClass(), "utc-offset", "+05:00",
+                        ),
+                        array(
+                            "rrule", new \StdClass(), "recur", array(
+                                'freq' => 'WEEKLY',
+                                'byday' => array('MO', 'TU'),
+                            ),
+                        ),
+                        array(
+                            "x-bool", new \StdClass(), "boolean", true
+                        ),
+                        array(
+                            "x-time", new \StdClass(), "time", "08:00:00",
+                        ),
+                        array(
+                            "attach", new \StdClass(), "binary", base64_encode('attachment')
+                        ),
+                        array(
+                            "attendee",
+                            (object)array(
+                                "cn" => "Dominik",
+                                "partstat" => "DECLINED",
+                            ),
+                            "cal-address",
+                            "mailto:dominik@example.org"
+                        ),
+                        array(
+                            "request-status",
+                            new \StdClass(),
+                            "text",
+                            array("2.0", "Success"),
+                        ),
+                        array(
+                            "request-status",
+                            new \StdClass(),
+                            "text",
+                            array("3.7", "Invalid Calendar User", "ATTENDEE:mailto:jsmith@example.org"),
+                        ),
+                        array(
+                            'dtend',
+                            new \StdClass(),
+                            "date-time",
+                            "2015-01-08T13:30:00",
+                        ),
+                    ),
+                    array(),
+                )
+            ),
+        );
+
+        $this->assertEquals($expected, $cal->jsonSerialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/JCardTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,195 @@
+<?php
+
+namespace Sabre\VObject;
+
+class JCardTest extends \PHPUnit_Framework_TestCase {
+
+    function testToJCard() {
+
+        $card = new Component\VCard(array(
+            "VERSION" => "4.0",
+            "UID" => "foo",
+            "BDAY" => "19850407",
+            "REV"  => "19951031T222710Z",
+            "LANG" => "nl",
+            "N" => array("Last", "First", "Middle", "", ""),
+            "item1.TEL" => "+1 555 123456",
+            "item1.X-AB-LABEL" => "Walkie Talkie",
+            "ADR" => array(
+                "",
+                "",
+                array("My Street", "Left Side", "Second Shack"),
+                "Hometown",
+                "PA",
+                "18252",
+                "U.S.A",
+            ),
+        ));
+
+        $card->add('BDAY', '1979-12-25', array('VALUE' => 'DATE', 'X-PARAM' => array(1,2)));
+        $card->add('BDAY', '1979-12-25T02:00:00', array('VALUE' => 'DATE-TIME'));
+
+
+        $card->add('X-TRUNCATED', '--1225', array('VALUE' => 'DATE'));
+        $card->add('X-TIME-LOCAL', '123000', array('VALUE' => 'TIME'));
+        $card->add('X-TIME-UTC', '12:30:00Z', array('VALUE' => 'TIME'));
+        $card->add('X-TIME-OFFSET', '12:30:00-08:00', array('VALUE' => 'TIME'));
+        $card->add('X-TIME-REDUCED', '23', array('VALUE' => 'TIME'));
+        $card->add('X-TIME-TRUNCATED', '--30', array('VALUE' => 'TIME'));
+
+        $card->add('X-KARMA-POINTS', '42', array('VALUE' => 'INTEGER'));
+        $card->add('X-GRADE', '1.3', array('VALUE' => 'FLOAT'));
+
+        $card->add('TZ', '-05:00', array('VALUE' => 'UTC-OFFSET'));
+
+        $expected = array(
+            "vcard",
+            array(
+                array(
+                    "version",
+                    new \StdClass(),
+                    "text",
+                    "4.0"
+                ),
+                array(
+                    "prodid",
+                    new \StdClass(),
+                    "text",
+                    "-//Sabre//Sabre VObject " . Version::VERSION . "//EN",
+                ),
+                array(
+                    "uid",
+                    new \StdClass(),
+                    "text",
+                    "foo",
+                ),
+                array(
+                    "bday",
+                    new \StdClass(),
+                    "date-and-or-time",
+                    "1985-04-07",
+                ),
+                array(
+                    "rev",
+                    new \StdClass(),
+                    "timestamp",
+                    "1995-10-31T22:27:10Z",
+                ),
+                array(
+                    "lang",
+                    new \StdClass(),
+                    "language-tag",
+                    "nl",
+                ),
+                array(
+                    "n",
+                    new \StdClass(),
+                    "text",
+                    array("Last", "First", "Middle", "", ""),
+                ),
+                array(
+                    "tel",
+                    (object)array(
+                        "group" => "item1",
+                    ),
+                    "text",
+                    "+1 555 123456",
+                ),
+                array(
+                    "x-ab-label",
+                    (object)array(
+                        "group" => "item1",
+                    ),
+                    "unknown",
+                    "Walkie Talkie",
+                ),
+                array(
+                    "adr",
+                    new \StdClass(),
+                    "text",
+                        array(
+                            "",
+                            "",
+                            array("My Street", "Left Side", "Second Shack"),
+                            "Hometown",
+                            "PA",
+                            "18252",
+                            "U.S.A",
+                        ),
+                ),
+                array(
+                    "bday",
+                    (object)array(
+                        'x-param' => array(1,2),
+                    ),
+                    "date",
+                    "1979-12-25",
+                ),
+                array(
+                    "bday",
+                    new \StdClass(),
+                    "date-time",
+                    "1979-12-25T02:00:00",
+                ),
+                array(
+                    "x-truncated",
+                    new \StdClass(),
+                    "date",
+                    "--12-25",
+                ),
+                array(
+                    "x-time-local",
+                    new \StdClass(),
+                    "time",
+                    "12:30:00"
+                ),
+                array(
+                    "x-time-utc",
+                    new \StdClass(),
+                    "time",
+                    "12:30:00Z"
+                ),
+                array(
+                    "x-time-offset",
+                    new \StdClass(),
+                    "time",
+                    "12:30:00-08:00"
+                ),
+                array(
+                    "x-time-reduced",
+                    new \StdClass(),
+                    "time",
+                    "23"
+                ),
+                array(
+                    "x-time-truncated",
+                    new \StdClass(),
+                    "time",
+                    "--30"
+                ),
+                array(
+                    "x-karma-points",
+                    new \StdClass(),
+                    "integer",
+                    42
+                ),
+                array(
+                    "x-grade",
+                    new \StdClass(),
+                    "float",
+                    1.3
+                ),
+                array(
+                    "tz",
+                    new \StdClass(),
+                    "utc-offset",
+                    "-05:00",
+                ),
+            ),
+        );
+
+        $this->assertEquals($expected, $card->jsonSerialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/LineFoldingIssueTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,23 @@
+<?php
+
+namespace Sabre\VObject;
+
+class LineFoldingIssueTest extends \PHPUnit_Framework_TestCase {
+
+    function testRead() {
+
+        $event = <<<ICS
+BEGIN:VCALENDAR\r
+BEGIN:VEVENT\r
+DESCRIPTION:TEST\\n\\n \\n\\nTEST\\n\\n \\n\\nTEST\\n\\n \\n\\nTEST\\n\\nTEST\\nTEST, TEST\r
+END:VEVENT\r
+END:VCALENDAR\r
+
+ICS;
+
+        $obj = Reader::read($event);
+        $this->assertEquals($event, $obj->serialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ParameterTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,135 @@
+<?php
+
+namespace Sabre\VObject;
+
+class ParameterTest extends \PHPUnit_Framework_TestCase {
+
+    function testSetup() {
+
+        $cal = new Component\VCalendar();
+
+        $param = new Parameter($cal, 'name','value');
+        $this->assertEquals('NAME',$param->name);
+        $this->assertEquals('value',$param->getValue());
+
+    }
+
+    function testSetupNameLess() {
+
+        $card = new Component\VCard();
+
+        $param = new Parameter($card, null,'URL');
+        $this->assertEquals('VALUE',$param->name);
+        $this->assertEquals('URL',$param->getValue());
+        $this->assertTrue($param->noName);
+
+    }
+
+    function testModify() {
+
+        $cal = new Component\VCalendar();
+
+        $param = new Parameter($cal, 'name', null);
+        $param->addValue(1);
+        $this->assertEquals(array(1), $param->getParts());
+
+        $param->setParts(array(1,2));
+        $this->assertEquals(array(1,2), $param->getParts());
+
+        $param->addValue(3);
+        $this->assertEquals(array(1,2,3), $param->getParts());
+
+        $param->setValue(4);
+        $param->addValue(5);
+        $this->assertEquals(array(4,5), $param->getParts());
+
+    }
+
+    function testCastToString() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'name', 'value');
+        $this->assertEquals('value',$param->__toString());
+        $this->assertEquals('value',(string)$param);
+
+    }
+
+    function testCastNullToString() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'name', null);
+        $this->assertEquals('',$param->__toString());
+        $this->assertEquals('',(string)$param);
+
+    }
+
+    function testSerialize() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'name', 'value');
+        $this->assertEquals('NAME=value',$param->serialize());
+
+    }
+
+    function testSerializeEmpty() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'name', null);
+        $this->assertEquals('NAME=',$param->serialize());
+
+    }
+
+    function testSerializeComplex() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'name',array("val1", "val2;", "val3^", "val4\n", "val5\""));
+        $this->assertEquals('NAME=val1,"val2;","val3^^","val4^n","val5^\'"',$param->serialize());
+
+    }
+
+    /**
+     * iCal 7.0 (OSX 10.9) has major issues with the EMAIL property, when the
+     * value contains a plus sign, and it's not quoted.
+     *
+     * So we specifically added support for that.
+     */
+    function testSerializePlusSign() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'EMAIL',"user+something@example.org");
+        $this->assertEquals('EMAIL="user+something@example.org"',$param->serialize());
+
+    }
+
+    function testIterate() {
+
+        $cal = new Component\VCalendar();
+
+        $param = new Parameter($cal, 'name', array(1,2,3,4));
+        $result = array();
+
+        foreach($param as $value) {
+            $result[] = $value;
+        }
+
+        $this->assertEquals(array(1,2,3,4), $result);
+
+    }
+
+    function testSerializeColon() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'name','va:lue');
+        $this->assertEquals('NAME="va:lue"',$param->serialize());
+
+    }
+
+    function testSerializeSemiColon() {
+
+        $cal = new Component\VCalendar();
+        $param = new Parameter($cal, 'name','va;lue');
+        $this->assertEquals('NAME="va;lue"',$param->serialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Parser/JsonTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,395 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+use
+    Sabre\VObject;
+
+class JsonTest extends \PHPUnit_Framework_TestCase {
+
+    function testRoundTripJCard() {
+
+        $input = array(
+            "vcard",
+            array(
+                array(
+                    "version",
+                    new \StdClass(),
+                    "text",
+                    "4.0"
+                ),
+                array(
+                    "prodid",
+                    new \StdClass(),
+                    "text",
+                    "-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN",
+                ),
+                array(
+                    "uid",
+                    new \StdClass(),
+                    "text",
+                    "foo",
+                ),
+                array(
+                    "bday",
+                    new \StdClass(),
+                    "date-and-or-time",
+                    "1985-04-07",
+                ),
+                array(
+                    "rev",
+                    new \StdClass(),
+                    "timestamp",
+                    "1995-10-31T22:27:10Z",
+                ),
+                array(
+                    "lang",
+                    new \StdClass(),
+                    "language-tag",
+                    "nl",
+                ),
+                array(
+                    "n",
+                    new \StdClass(),
+                    "text",
+                    array("Last", "First", "Middle", "", ""),
+                ),
+                array(
+                    "tel",
+                    (object)array(
+                        "group" => "item1",
+                    ),
+                    "text",
+                    "+1 555 123456",
+                ),
+                array(
+                    "x-ab-label",
+                    (object)array(
+                        "group" => "item1",
+                    ),
+                    "unknown",
+                    "Walkie Talkie",
+                ),
+                array(
+                    "adr",
+                    new \StdClass(),
+                    "text",
+                        array(
+                            "",
+                            "",
+                            array("My Street", "Left Side", "Second Shack"),
+                            "Hometown",
+                            "PA",
+                            "18252",
+                            "U.S.A",
+                        ),
+                ),
+                array(
+                    "bday",
+                    (object)array(
+                        'x-param' => array(1,2),
+                    ),
+                    "date",
+                    "1979-12-25",
+                ),
+                array(
+                    "bday",
+                    new \StdClass(),
+                    "date-time",
+                    "1979-12-25T02:00:00",
+                ),
+                array(
+                    "x-truncated",
+                    new \StdClass(),
+                    "date",
+                    "--12-25",
+                ),
+                array(
+                    "x-time-local",
+                    new \StdClass(),
+                    "time",
+                    "12:30:00"
+                ),
+                array(
+                    "x-time-utc",
+                    new \StdClass(),
+                    "time",
+                    "12:30:00Z"
+                ),
+                array(
+                    "x-time-offset",
+                    new \StdClass(),
+                    "time",
+                    "12:30:00-08:00"
+                ),
+                array(
+                    "x-time-reduced",
+                    new \StdClass(),
+                    "time",
+                    "23"
+                ),
+                array(
+                    "x-time-truncated",
+                    new \StdClass(),
+                    "time",
+                    "--30"
+                ),
+                array(
+                    "x-karma-points",
+                    new \StdClass(),
+                    "integer",
+                    42
+                ),
+                array(
+                    "x-grade",
+                    new \StdClass(),
+                    "float",
+                    1.3
+                ),
+                array(
+                    "tz",
+                    new \StdClass(),
+                    "utc-offset",
+                    "-05:00",
+                ),
+            ),
+        );
+
+        $parser = new Json(json_encode($input));
+        $vobj = $parser->parse();        
+
+        $version = VObject\Version::VERSION;
+
+
+        $result = $vobj->serialize();
+        $expected = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+UID:foo
+BDAY:1985-04-07
+REV:1995-10-31T22:27:10Z
+LANG:nl
+N:Last;First;Middle;;
+item1.TEL:+1 555 123456
+item1.X-AB-LABEL:Walkie Talkie
+ADR:;;My Street,Left Side,Second Shack;Hometown;PA;18252;U.S.A
+BDAY;X-PARAM=1,2;VALUE=DATE:1979-12-25
+BDAY;VALUE=DATE-TIME:1979-12-25T02:00:00
+X-TRUNCATED;VALUE=DATE:--12-25
+X-TIME-LOCAL;VALUE=TIME:12:30:00
+X-TIME-UTC;VALUE=TIME:12:30:00Z
+X-TIME-OFFSET;VALUE=TIME:12:30:00-08:00
+X-TIME-REDUCED;VALUE=TIME:23
+X-TIME-TRUNCATED;VALUE=TIME:--30
+X-KARMA-POINTS;VALUE=INTEGER:42
+X-GRADE;VALUE=FLOAT:1.3
+TZ;VALUE=UTC-OFFSET:-05:00
+END:VCARD
+
+VCF;
+        $this->assertEquals($expected, str_replace("\r", "", $result));
+
+        $this->assertEquals(
+            $input,
+            $vobj->jsonSerialize()
+        );
+
+    }
+
+    function testRoundTripJCal() {
+
+        $input = array(
+            "vcalendar",
+            array(
+                array(
+                    "version",
+                    new \StdClass(),
+                    "text",
+                    "2.0"
+                ),
+                array(
+                    "prodid",
+                    new \StdClass(),
+                    "text",
+                    "-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN",
+                ),
+                array(
+                    "calscale",
+                    new \StdClass(),
+                    "text",
+                    "GREGORIAN"
+                ),
+            ),
+            array(
+                array("vevent",
+                    array(
+                        array(
+                            "uid", new \StdClass(), "text", "foo",
+                        ),
+                        array(
+                            "dtstart", new \StdClass(), "date", "2013-05-26",
+                        ),
+                        array(
+                            "duration", new \StdClass(), "duration", "P1D",
+                        ),
+                        array(
+                            "categories", new \StdClass(), "text", "home", "testing",
+                        ),
+                        array(
+                            "created", new \StdClass(), "date-time", "2013-05-26T18:10:00Z",
+                        ),
+                        array(
+                            "attach", new \StdClass(), "binary", base64_encode('attachment')
+                        ),
+                        array(
+                            "attendee", new \StdClass(), "cal-address", "mailto:armin@example.org",
+                        ),
+                        array(
+                            "geo", new \StdClass(), "float", array(51.96668, 7.61876),
+                        ),
+                        array(
+                            "sequence", new \StdClass(), "integer", 5
+                        ),
+                        array(
+                            "freebusy", new \StdClass(), "period",  array("2013-05-26T21:02:13", "PT1H"), array("2013-06-26T12:00:00", "2013-06-26T13:00:00"),
+                        ),
+                        array(
+                            "url", new \StdClass(), "uri", "http://example.org/",
+                        ),
+                        array(
+                            "tzoffsetfrom", new \StdClass(), "utc-offset", "+05:00",
+                        ),
+                        array(
+                            "rrule", new \StdClass(), "recur", array(
+                                'freq' => 'WEEKLY',
+                                'byday' => array('MO', 'TU'),
+                            ),
+                        ),
+                        array(
+                            "x-bool", new \StdClass(), "boolean", true
+                        ),
+                        array(
+                            "x-time", new \StdClass(), "time", "08:00:00",
+                        ),
+                        array(
+                            "attendee",
+                            (object)array(
+                                "cn" => "Dominik",
+                                "partstat" => "DECLINED",
+                            ),
+                            "cal-address",
+                            "mailto:dominik@example.org"
+                        ),
+                        array(
+                            "request-status",
+                            new \StdClass(),
+                            "text",
+                            array("2.0", "Success"),
+                        ),
+                        array(
+                            "request-status",
+                            new \StdClass(),
+                            "text",
+                            array("3.7", "Invalid Calendar User", "ATTENDEE:mailto:jsmith@example.org"),
+                        ),
+                    ),
+                    array(
+                        array("valarm",
+                            array(
+                                array(
+                                    "action", new \StdClass(), "text", "DISPLAY",
+                                ),
+                            ),
+                            array(),
+                        ),
+                    ),
+                )
+            ),
+        );
+
+        $parser = new Json(json_encode($input));
+        $vobj = $parser->parse();        
+        $result = $vobj->serialize();
+
+        $version = VObject\Version::VERSION;
+
+        $expected = <<<VCF
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+UID:foo
+DTSTART;VALUE=DATE:20130526
+DURATION:P1D
+CATEGORIES:home,testing
+CREATED:20130526T181000Z
+ATTACH;VALUE=BINARY:YXR0YWNobWVudA==
+ATTENDEE:mailto:armin@example.org
+GEO:51.96668;7.61876
+SEQUENCE:5
+FREEBUSY:20130526T210213/PT1H,20130626T120000/20130626T130000
+URL:http://example.org/
+TZOFFSETFROM:+05:00
+RRULE:FREQ=WEEKLY;BYDAY=MO,TU
+X-BOOL;VALUE=BOOLEAN:TRUE
+X-TIME;VALUE=TIME:08:00:00
+ATTENDEE;CN=Dominik;PARTSTAT=DECLINED:mailto:dominik@example.org
+REQUEST-STATUS:2.0;Success
+REQUEST-STATUS:3.7;Invalid Calendar User;ATTENDEE:mailto:jsmith@example.org
+BEGIN:VALARM
+ACTION:DISPLAY
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+
+VCF;
+        $this->assertEquals($expected, str_replace("\r", "", $result));
+
+        $this->assertEquals(
+            $input,
+            $vobj->jsonSerialize()
+        );
+
+    }
+
+    function testParseStreamArg() {
+
+        $input = array(
+            "vcard",
+            array(
+                array(
+                    "FN", new \StdClass(), 'text', "foo",
+                ),
+            ),
+        );
+
+        $stream = fopen('php://memory','r+');
+        fwrite($stream, json_encode($input));
+        rewind($stream);
+
+        $result = VObject\Reader::readJson($stream,0);
+        $this->assertEquals('foo', $result->FN->getValue());
+
+    }
+
+    /**
+     * @expectedException \Sabre\VObject\ParseException
+     */
+    function testParseInvalidData() {
+
+        $json = new Json();
+        $input = array(
+            "vlist",
+            array(
+                array(
+                    "FN", new \StdClass(), 'text', "foo",
+                ),
+            ),
+        );
+
+        $json->parse(json_encode($input), 0);
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Parser/MimeDirTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,21 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+/**
+ * Note that most MimeDir related tests can actually be found in the ReaderTest
+ * class one level up.
+ */
+class MimeDirTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @expectedException \Sabre\VObject\ParseException
+     */
+    function testParseError() {
+
+        $mimeDir = new MimeDir();
+        $mimeDir->parse(fopen(__FILE__,'a'));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Parser/QuotedPrintableTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,108 @@
+<?php
+
+namespace Sabre\VObject\Parser;
+
+use
+    Sabre\VObject\Reader;
+
+class QuotedPrintableTest extends \PHPUnit_Framework_TestCase {
+
+    function testReadQuotedPrintableSimple() {
+
+        $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aach=65n\r\nEND:VCARD";
+
+        $result = Reader::read($data);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
+        $this->assertEquals('VCARD', $result->name);
+        $this->assertEquals(1, count($result->children()));
+        $this->assertEquals("Aachen", $this->getPropertyValue($result->label));
+
+    }
+
+    function testReadQuotedPrintableNewlineSoft() {
+
+        $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aa=\r\n ch=\r\n en\r\nEND:VCARD";
+        $result = Reader::read($data);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
+        $this->assertEquals('VCARD', $result->name);
+        $this->assertEquals(1, count($result->children()));
+        $this->assertEquals("Aachen", $this->getPropertyValue($result->label));
+
+    }
+
+    function testReadQuotedPrintableNewlineHard() {
+
+        $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aachen=0D=0A=\r\n Germany\r\nEND:VCARD";
+        $result = Reader::read($data);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
+        $this->assertEquals('VCARD', $result->name);
+        $this->assertEquals(1, count($result->children()));
+        $this->assertEquals("Aachen\r\nGermany", $this->getPropertyValue($result->label));
+
+
+    }
+
+    function testReadQuotedPrintableCompatibilityMS() {
+
+        $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aachen=0D=0A=\r\nDeutschland:okay\r\nEND:VCARD";
+        $result = Reader::read($data, Reader::OPTION_FORGIVING);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
+        $this->assertEquals('VCARD', $result->name);
+        $this->assertEquals(1, count($result->children()));
+        $this->assertEquals("Aachen\r\nDeutschland:okay", $this->getPropertyValue($result->label));
+
+    }
+
+    function testReadQuotesPrintableCompoundValues() {
+
+        $data = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+N:Doe;John;;;
+FN:John Doe
+ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;M=C3=BCnster =
+Str. 1;M=C3=BCnster;;48143;Deutschland
+END:VCARD
+VCF;
+
+        $result = Reader::read($data, Reader::OPTION_FORGIVING);
+        $this->assertEquals(array(
+            '','','Münster Str. 1','Münster','','48143','Deutschland'
+        ), $result->ADR->getParts());
+
+
+    }
+
+    private function getPropertyValue(\Sabre\VObject\Property $property) {
+
+        return (string)$property;
+
+        /*
+        $param = $property['encoding'];
+        if ($param !== null) {
+            $encoding = strtoupper((string)$param);
+            if ($encoding === 'QUOTED-PRINTABLE') {
+                $value = quoted_printable_decode($value);
+            } else {
+                throw new Exception();
+            }
+        }
+
+        $param = $property['charset'];
+        if ($param !== null) {
+            $charset = strtoupper((string)$param);
+            if ($charset !== 'UTF-8') {
+                $value = mb_convert_encoding($value, 'UTF-8', $charset);
+            }
+        } else {
+            $value = StringUtil::convertToUTF8($value);
+        }
+
+        return $value;
+         */
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/BinaryTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject;
+
+class BinaryTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    function testMimeDir() {
+
+        $vcard = new VObject\Component\VCard();
+        $vcard->add('PHOTO', array('a','b'));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/BooleanTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,22 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject;
+
+class BooleanTest extends \PHPUnit_Framework_TestCase {
+
+    function testMimeDir() {
+
+        $input = "BEGIN:VCARD\r\nX-AWESOME;VALUE=BOOLEAN:TRUE\r\nX-SUCKS;VALUE=BOOLEAN:FALSE\r\nEND:VCARD\r\n";
+
+        $vcard = VObject\Reader::read($input);
+        $this->assertTrue($vcard->{'X-AWESOME'}->getValue());
+        $this->assertFalse($vcard->{'X-SUCKS'}->getValue());
+
+        $this->assertEquals('BOOLEAN', $vcard->{'X-AWESOME'}->getValueType());
+        $this->assertEquals($input, $vcard->serialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/CompoundTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,50 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\Component\VCard;
+
+class CompoundTest extends \PHPUnit_Framework_TestCase {
+
+    function testSetParts() {
+
+        $arr = array(
+            'ABC, Inc.',
+            'North American Division',
+            'Marketing;Sales',
+        );
+
+        $vcard = new VCard();
+        $elem = $vcard->createProperty('ORG');
+        $elem->setParts($arr);
+
+        $this->assertEquals('ABC\, Inc.;North American Division;Marketing\;Sales', $elem->getValue());
+        $this->assertEquals(3, count($elem->getParts()));
+        $parts = $elem->getParts();
+        $this->assertEquals('Marketing;Sales', $parts[2]);
+
+    }
+
+    function testGetParts() {
+
+        $str = 'ABC\, Inc.;North American Division;Marketing\;Sales';
+
+        $vcard = new VCard();
+        $elem = $vcard->createProperty('ORG');
+        $elem->setRawMimeDirValue($str);
+
+        $this->assertEquals(3, count($elem->getParts()));
+        $parts = $elem->getParts();
+        $this->assertEquals('Marketing;Sales', $parts[2]);
+    }
+
+    function testGetPartsNull() {
+
+        $vcard = new VCard();
+        $elem = $vcard->createProperty('ORG', null);
+
+        $this->assertEquals(0, count($elem->getParts()));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/FloatTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,30 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject;
+
+class FloatTest extends \PHPUnit_Framework_TestCase {
+
+    function testMimeDir() {
+
+        $input = "BEGIN:VCARD\r\nVERSION:4.0\r\nX-FLOAT;VALUE=FLOAT:0.234;1.245\r\nEND:VCARD\r\n";
+        $mimeDir = new VObject\Parser\MimeDir($input);
+
+        $result = $mimeDir->parse($input);
+
+        $this->assertInstanceOf('Sabre\VObject\Property\Float', $result->{'X-FLOAT'});
+
+        $this->assertEquals(array(
+            0.234,
+            1.245,
+        ), $result->{'X-FLOAT'}->getParts());
+
+        $this->assertEquals(
+            $input,
+            $result->serialize()
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/ICalendar/CalAddressTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,32 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+class CalAddressTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider values
+     */
+    function testGetNormalizedValue($expected, $input) {
+
+        $vobj = new \Sabre\VObject\Component\VCalendar();
+        $property = $vobj->add('ATTENDEE', $input);
+
+        $this->assertEquals(
+            $expected,
+            $property->getNormalizedValue()
+        );
+
+    }
+
+    function values() {
+
+        return array(
+            array('mailto:a@b.com', 'mailto:a@b.com'),
+            array('mailto:a@b.com', 'MAILTO:a@b.com'),
+            array('/foo/bar', '/foo/bar'),
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/ICalendar/DateTimeTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,359 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use Sabre\VObject\Component;
+use Sabre\VObject\Component\VCalendar;
+
+
+class DateTimeTest extends \PHPUnit_Framework_TestCase {
+
+    protected $vcal;
+
+    function setUp() {
+
+        $this->vcal = new VCalendar();
+
+    }
+
+    function testSetDateTime() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setDateTime($dt);
+
+        $this->assertEquals('19850704T013000', (string)$elem);
+        $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
+        $this->assertNull($elem['VALUE']);
+
+        $this->assertTrue($elem->hasTime());
+
+    }
+
+    function testSetDateTimeLOCAL() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setDateTime($dt, $isFloating = true);
+
+        $this->assertEquals('19850704T013000', (string)$elem);
+        $this->assertNull($elem['TZID']);
+
+        $this->assertTrue($elem->hasTime());
+    }
+
+    function testSetDateTimeUTC() {
+
+        $tz = new \DateTimeZone('GMT');
+        $dt = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setDateTime($dt);
+
+        $this->assertEquals('19850704T013000Z', (string)$elem);
+        $this->assertNull($elem['TZID']);
+
+        $this->assertTrue($elem->hasTime());
+    }
+
+    function testSetDateTimeLOCALTZ() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setDateTime($dt);
+
+        $this->assertEquals('19850704T013000', (string)$elem);
+        $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
+
+        $this->assertTrue($elem->hasTime());
+    }
+
+    function testSetDateTimeDATE() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem['VALUE'] = 'DATE';
+        $elem->setDateTime($dt);
+
+        $this->assertEquals('19850704', (string)$elem);
+        $this->assertNull($elem['TZID']);
+        $this->assertEquals('DATE', (string)$elem['VALUE']);
+
+        $this->assertFalse($elem->hasTime());
+    }
+
+    function testSetValue() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setValue($dt);
+
+        $this->assertEquals('19850704T013000', (string)$elem);
+        $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
+        $this->assertNull($elem['VALUE']);
+
+        $this->assertTrue($elem->hasTime());
+
+    }
+
+    function testSetValueArray() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt2 = new \DateTime('1985-07-04 02:30:00', $tz);
+        $dt1->setTimeZone($tz);
+        $dt2->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setValue(array($dt1, $dt2));
+
+        $this->assertEquals('19850704T013000,19850704T023000', (string)$elem);
+        $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
+        $this->assertNull($elem['VALUE']);
+
+        $this->assertTrue($elem->hasTime());
+
+    }
+
+    function testSetParts() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt1 = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt2 = new \DateTime('1985-07-04 02:30:00', $tz);
+        $dt1->setTimeZone($tz);
+        $dt2->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setParts(array($dt1, $dt2));
+
+        $this->assertEquals('19850704T013000,19850704T023000', (string)$elem);
+        $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']);
+        $this->assertNull($elem['VALUE']);
+
+        $this->assertTrue($elem->hasTime());
+
+    }
+    function testSetPartsStrings() {
+
+        $dt1 = '19850704T013000Z';
+        $dt2 = '19850704T023000Z';
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setParts(array($dt1, $dt2));
+
+        $this->assertEquals('19850704T013000Z,19850704T023000Z', (string)$elem);
+        $this->assertNull($elem['VALUE']);
+
+        $this->assertTrue($elem->hasTime());
+
+    }
+
+
+    function testGetDateTimeCached() {
+
+        $tz = new \DateTimeZone('Europe/Amsterdam');
+        $dt = new \DateTime('1985-07-04 01:30:00', $tz);
+        $dt->setTimeZone($tz);
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $elem->setDateTime($dt);
+
+        $this->assertEquals($elem->getDateTime(), $dt);
+
+    }
+
+    function testGetDateTimeDateNULL() {
+
+        $elem = $this->vcal->createProperty('DTSTART');
+        $dt = $elem->getDateTime();
+
+        $this->assertNull($dt);
+
+    }
+
+    function testGetDateTimeDateDATE() {
+
+        $elem = $this->vcal->createProperty('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'));
+
+    }
+
+    function testGetDateTimeDateDATEReferenceTimeZone() {
+
+        $elem = $this->vcal->createProperty('DTSTART','19850704');
+
+        $tz = new \DateTimeZone('America/Toronto');
+        $dt = $elem->getDateTime($tz);
+        $dt->setTimeZone(new \DateTimeZone('UTC'));
+
+        $this->assertInstanceOf('DateTime', $dt);
+        $this->assertEquals('1985-07-04 04:00:00', $dt->format('Y-m-d H:i:s'));
+
+    }
+
+    function testGetDateTimeDateFloating() {
+
+        $elem = $this->vcal->createProperty('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'));
+
+    }
+
+    function testGetDateTimeDateFloatingReferenceTimeZone() {
+
+        $elem = $this->vcal->createProperty('DTSTART','19850704T013000');
+
+        $tz = new \DateTimeZone('America/Toronto');
+        $dt = $elem->getDateTime($tz);
+        $dt->setTimeZone(new \DateTimeZone('UTC'));
+
+        $this->assertInstanceOf('DateTime', $dt);
+        $this->assertEquals('1985-07-04 05:30:00', $dt->format('Y-m-d H:i:s'));
+
+    }
+
+    function testGetDateTimeDateUTC() {
+
+        $elem = $this->vcal->createProperty('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());
+
+    }
+
+    function testGetDateTimeDateLOCALTZ() {
+
+        $elem = $this->vcal->createProperty('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());
+
+    }
+
+    /**
+     * @expectedException LogicException
+     */
+    function testGetDateTimeDateInvalid() {
+
+        $elem = $this->vcal->createProperty('DTSTART','bla');
+        $dt = $elem->getDateTime();
+
+    }
+
+    function testGetDateTimeWeirdTZ() {
+
+        $elem = $this->vcal->createProperty('DTSTART','19850704T013000');
+        $elem['TZID'] = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam';
+
+
+        $event = $this->vcal->createComponent('VEVENT');
+        $event->add($elem);
+
+        $timezone = $this->vcal->createComponent('VTIMEZONE');
+        $timezone->TZID = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam';
+        $timezone->{'X-LIC-LOCATION'} = 'Europe/Amsterdam';
+
+        $this->vcal->add($event);
+        $this->vcal->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());
+
+    }
+
+    function testGetDateTimeBadTimeZone() {
+
+        $default = date_default_timezone_get();
+        date_default_timezone_set('Canada/Eastern');
+
+        $elem = $this->vcal->createProperty('DTSTART','19850704T013000');
+        $elem['TZID'] = 'Moon';
+
+
+        $event = $this->vcal->createComponent('VEVENT');
+        $event->add($elem);
+
+        $timezone = $this->vcal->createComponent('VTIMEZONE');
+        $timezone->TZID = 'Moon';
+        $timezone->{'X-LIC-LOCATION'} = 'Moon';
+
+
+        $this->vcal->add($event);
+        $this->vcal->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());
+        date_default_timezone_set($default);
+
+    }
+
+    function testUpdateValueParameter() {
+
+        $dtStart = $this->vcal->createProperty('DTSTART', new \DateTime('2013-06-07 15:05:00'));
+        $dtStart['VALUE'] = 'DATE';
+
+        $this->assertEquals("DTSTART;VALUE=DATE:20130607\r\n", $dtStart->serialize());
+
+    }
+
+    function testValidate() {
+
+        $exDate = $this->vcal->createProperty('EXDATE', '-00011130T143000Z');
+        $messages = $exDate->validate();
+        $this->assertEquals(1, count($messages));
+        $this->assertEquals(3, $messages[0]['level']);
+
+    }
+
+    /**
+     * This issue was discovered on the sabredav mailing list.
+     */
+    function testCreateDatePropertyThroughAdd() {
+
+        $vcal = new VCalendar();
+        $vevent = $vcal->add('VEVENT');
+
+        $dtstart = $vevent->add(
+            'DTSTART',
+            new \DateTime('2014-03-07'),
+            array('VALUE' => 'DATE')
+        );
+
+        $this->assertEquals("DTSTART;VALUE=DATE:20140307\r\n", $dtstart->serialize());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/ICalendar/DurationTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,20 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VEvent;
+
+class DurationTest extends \PHPUnit_Framework_TestCase {
+
+    function testGetDateInterval() {
+
+        $vcal = new VCalendar();
+        $event = $vcal->add('VEVENT', array('DURATION' => array('PT1H')));
+
+        $this->assertEquals(
+            new \DateInterval('PT1H'),
+            $event->{'DURATION'}->getDateInterval()
+        );
+    }
+} 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/ICalendar/RecurTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,46 @@
+<?php
+
+namespace Sabre\VObject\Property\ICalendar;
+
+use Sabre\VObject\Component\VCalendar;
+
+class RecurTest extends \PHPUnit_Framework_TestCase {
+
+    function testParts() {
+
+        $vcal = new VCalendar();
+        $recur = $vcal->add('RRULE', 'FREQ=Daily');
+
+        $this->assertInstanceOf('Sabre\VObject\Property\ICalendar\Recur', $recur);
+
+        $this->assertEquals(array('FREQ'=>'DAILY'), $recur->getParts());
+        $recur->setParts(array('freq'=>'MONTHLY'));
+
+        $this->assertEquals(array('FREQ'=>'MONTHLY'), $recur->getParts());
+
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    function testSetValueBadVal() {
+
+        $vcal = new VCalendar();
+        $recur = $vcal->add('RRULE', 'FREQ=Daily');
+        $recur->setValue(new \Exception());
+
+    }
+
+    function testSetSubParts() {
+
+        $vcal = new VCalendar();
+        $recur = $vcal->add('RRULE', array('FREQ'=>'DAILY', 'BYDAY'=>'mo,tu', 'BYMONTH' => array(0,1)));
+
+        $this->assertEquals(array(
+            'FREQ'=>'DAILY',
+            'BYDAY' => array('MO','TU'),
+            'BYMONTH' => array(0,1),
+        ), $recur->getParts());
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/TextTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,96 @@
+<?php
+
+namespace Sabre\VObject\Property;
+
+use Sabre\VObject\Component\VCard;
+
+class TextTest extends \PHPUnit_Framework_TestCase {
+
+    function assertVCard21serialization($propValue, $expected) {
+
+        $doc = new VCard(array(
+            'VERSION'=>'2.1',
+            'PROP' => $propValue
+        ), false);
+
+        // Adding quoted-printable, because we're testing if it gets removed
+        // automatically.
+        $doc->PROP['ENCODING'] = 'QUOTED-PRINTABLE';
+        $doc->PROP['P1'] = 'V1';
+
+
+        $output = $doc->serialize();
+
+
+        $this->assertEquals("BEGIN:VCARD\r\nVERSION:2.1\r\n$expected\r\nEND:VCARD\r\n", $output);
+
+    }
+
+    function testSerializeVCard21() {
+
+        $this->assertVCard21Serialization(
+            'f;oo',
+            'PROP;P1=V1:f;oo'
+        );
+
+    }
+
+    function testSerializeVCard21Array() {
+
+        $this->assertVCard21Serialization(
+            array('f;oo','bar'),
+            'PROP;P1=V1:f\;oo;bar'
+        );
+
+    }
+    function testSerializeVCard21Fold() {
+
+        $this->assertVCard21Serialization(
+            str_repeat('x',80),
+            'PROP;P1=V1:' . str_repeat('x',64) . "\r\n " . str_repeat('x',16)
+        );
+
+    }
+
+
+
+    function testSerializeQuotedPrintable() {
+
+        $this->assertVCard21Serialization(
+            "foo\r\nbar",
+            'PROP;P1=V1;ENCODING=QUOTED-PRINTABLE:foo=0D=0Abar'
+        );
+    }
+
+    function testSerializeQuotedPrintableFold() {
+
+        $this->assertVCard21Serialization(
+            "foo\r\nbarxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+            "PROP;P1=V1;ENCODING=QUOTED-PRINTABLE:foo=0D=0Abarxxxxxxxxxxxxxxxxxxxxxxxxxx=\r\n xxx"
+        );
+
+    }
+
+    function testValidateMinimumPropValue() {
+
+        $vcard = <<<IN
+BEGIN:VCARD
+VERSION:4.0
+UID:foo
+FN:Hi!
+N:A
+END:VCARD
+IN;
+
+        $vcard = \Sabre\VObject\Reader::read($vcard);
+        $this->assertEquals(1, count($vcard->validate()));
+
+        $this->assertEquals(1, count($vcard->N->getParts()));
+
+        $vcard->validate(\Sabre\VObject\Node::REPAIR);
+
+        $this->assertEquals(5, count($vcard->N->getParts()));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/VCard/DateAndOrTimeTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,233 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use
+    Sabre\VObject,
+    Sabre\VObject\Reader;
+
+class DateAndOrTimeTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @dataProvider dates
+     */
+    function testGetJsonValue($input, $output) {
+
+        $vcard = new VObject\Component\VCard();
+        $prop = $vcard->createProperty('BDAY', $input);
+
+        $this->assertEquals(array($output), $prop->getJsonValue());
+
+    }
+
+    function dates() {
+
+        return array(
+            array(
+                "19961022T140000",
+                "1996-10-22T14:00:00",
+            ),
+            array(
+                "--1022T1400",
+                "--10-22T14:00",
+            ),
+            array(
+                "---22T14",
+                "---22T14",
+            ),
+            array(
+                "19850412",
+                "1985-04-12",
+            ),
+            array(
+                "1985-04",
+                "1985-04",
+            ),
+            array(
+                "1985",
+                "1985",
+            ),
+            array(
+                "--0412",
+                "--04-12",
+            ),
+            array(
+                "T102200",
+                "T10:22:00",
+            ),
+            array(
+                "T1022",
+                "T10:22",
+            ),
+            array(
+                "T10",
+                "T10",
+            ),
+            array(
+                "T-2200",
+                "T-22:00",
+            ),
+            array(
+                "T102200Z",
+                "T10:22:00Z",
+            ),
+            array(
+                "T102200-0800",
+                "T10:22:00-0800",
+            ),
+            array(
+                "T--00",
+                "T--00",
+            ),
+        );
+
+    }
+
+    public function testSetParts() {
+
+        $vcard = new VObject\Component\VCard();
+
+        $prop = $vcard->createProperty('BDAY');
+        $prop->setParts(array(
+            new \DateTime('2014-04-02 18:37:00')
+        ));
+
+        $this->assertEquals('20140402T183700Z', $prop->getValue());
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    public function testSetPartsTooMany() {
+
+        $vcard = new VObject\Component\VCard();
+
+        $prop = $vcard->createProperty('BDAY');
+        $prop->setParts(array(
+            1,
+            2
+        ));
+
+    }
+
+    public function testSetPartsString() {
+
+        $vcard = new VObject\Component\VCard();
+
+        $prop = $vcard->createProperty('BDAY');
+        $prop->setParts(array(
+            "20140402T183700Z"
+        ));
+
+        $this->assertEquals('20140402T183700Z', $prop->getValue());
+
+    }
+
+    public function testSetValueDateTime() {
+
+        $vcard = new VObject\Component\VCard();
+
+        $prop = $vcard->createProperty('BDAY');
+        $prop->setValue(
+            new \DateTime('2014-04-02 18:37:00')
+        );
+
+        $this->assertEquals('20140402T183700Z', $prop->getValue());
+
+    }
+
+    public function testSetDateTimeOffset() {
+
+        $vcard = new VObject\Component\VCard();
+
+        $prop = $vcard->createProperty('BDAY');
+        $prop->setValue(
+            new \DateTime('2014-04-02 18:37:00', new \DateTimeZone('America/Toronto'))
+        );
+
+        $this->assertEquals('20140402T183700-0400', $prop->getValue());
+
+    }
+
+    public function testGetDateTime() {
+
+        $datetime = new \DateTime('2014-04-02 18:37:00', new \DateTimeZone('America/Toronto'));
+
+        $vcard = new VObject\Component\VCard();
+        $prop = $vcard->createProperty('BDAY', $datetime);
+
+        $dt = $prop->getDateTime();
+        $this->assertEquals('2014-04-02T18:37:00-04:00', $dt->format('c'), "For some reason this one failed. Current default timezone is: " . date_default_timezone_get());
+
+    }
+
+    public function testGetDateIncomplete() {
+
+        $datetime = '--0407';
+
+        $vcard = new VObject\Component\VCard();
+        $prop = $vcard->add('BDAY', $datetime);
+
+        $dt = $prop->getDateTime();
+        // Note: if the year changes between the last line and the next line of
+        // code, this test may fail.
+        //
+        // If that happens, head outside and have a drink.
+        $current = new \DateTime('now');
+        $year = $current->format('Y');
+
+        $this->assertEquals($year . '0407', $dt->format('Ymd'));
+
+    }
+
+    public function testGetDateIncompleteFromVCard() {
+
+        $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+BDAY:--0407
+END:VCARD
+VCF;
+        $vcard = Reader::read($vcard);
+        $prop = $vcard->BDAY;
+
+        $dt = $prop->getDateTime();
+        // Note: if the year changes between the last line and the next line of
+        // code, this test may fail.
+        //
+        // If that happens, head outside and have a drink.
+        $current = new \DateTime('now');
+        $year = $current->format('Y');
+
+        $this->assertEquals($year . '0407', $dt->format('Ymd'));
+
+    }
+
+    public function testValidate() {
+
+        $datetime = '--0407';
+
+        $vcard = new VObject\Component\VCard();
+        $prop = $vcard->add('BDAY', $datetime);
+
+        $this->assertEquals(array(), $prop->validate());
+
+    }
+
+    public function testValidateBroken() {
+
+        $datetime = '123';
+
+        $vcard = new VObject\Component\VCard();
+        $prop = $vcard->add('BDAY', $datetime);
+
+        $this->assertEquals(array(array(
+            'level' => 3,
+            'message' => 'The supplied value (123) is not a correct DATE-AND-OR-TIME property',
+            'node' => $prop,
+        )), $prop->validate());
+
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Property/VCard/LanguageTagTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,27 @@
+<?php
+
+namespace Sabre\VObject\Property\VCard;
+
+use Sabre\VObject;
+
+class LanguageTagTest extends \PHPUnit_Framework_TestCase {
+
+    function testMimeDir() {
+
+        $input = "BEGIN:VCARD\r\nVERSION:4.0\r\nLANG:nl\r\nEND:VCARD\r\n";
+        $mimeDir = new VObject\Parser\MimeDir($input);
+
+        $result = $mimeDir->parse($input);
+
+        $this->assertInstanceOf('Sabre\VObject\Property\VCard\LanguageTag', $result->LANG);
+
+        $this->assertEquals('nl', $result->LANG->getValue());
+
+        $this->assertEquals(
+            $input,
+            $result->serialize()
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/PropertyTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,360 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    Sabre\VObject\Component\VCalendar;
+
+class PropertyTest extends \PHPUnit_Framework_TestCase {
+
+    public function testToString() {
+
+        $cal = new VCalendar();
+
+        $property = $cal->createProperty('propname','propvalue');
+        $this->assertEquals('PROPNAME', $property->name);
+        $this->assertEquals('propvalue', $property->__toString());
+        $this->assertEquals('propvalue', (string)$property);
+        $this->assertEquals('propvalue', $property->getValue());
+
+    }
+
+    public function testCreate() {
+
+        $cal = new VCalendar();
+
+        $params = array(
+            'param1' => 'value1',
+            'param2' => 'value2',
+        );
+
+        $property = $cal->createProperty('propname','propvalue', $params);
+
+        $this->assertEquals('value1', $property['param1']->getValue());
+        $this->assertEquals('value2', $property['param2']->getValue());
+
+    }
+
+    public function testSetValue() {
+
+        $cal = new VCalendar();
+
+        $property = $cal->createProperty('propname','propvalue');
+        $property->setValue('value2');
+
+        $this->assertEquals('PROPNAME', $property->name);
+        $this->assertEquals('value2', $property->__toString());
+
+    }
+
+    public function testParameterExists() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+        $property['paramname'] = 'paramvalue';
+
+        $this->assertTrue(isset($property['PARAMNAME']));
+        $this->assertTrue(isset($property['paramname']));
+        $this->assertFalse(isset($property['foo']));
+
+    }
+
+    public function testParameterGet() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+        $property['paramname'] = 'paramvalue';
+
+        $this->assertInstanceOf('Sabre\\VObject\\Parameter',$property['paramname']);
+
+    }
+
+    public function testParameterNotExists() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+        $property['paramname'] = 'paramvalue';
+
+        $this->assertInternalType('null',$property['foo']);
+
+    }
+
+    public function testParameterMultiple() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+        $property['paramname'] = 'paramvalue';
+        $property->add('paramname', 'paramvalue');
+
+        $this->assertInstanceOf('Sabre\\VObject\\Parameter',$property['paramname']);
+        $this->assertEquals(2,count($property['paramname']->getParts()));
+
+    }
+
+    public function testSetParameterAsString() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+        $property['paramname'] = 'paramvalue';
+
+        $this->assertEquals(1,count($property->parameters()));
+        $this->assertInstanceOf('Sabre\\VObject\\Parameter', $property->parameters['PARAMNAME']);
+        $this->assertEquals('PARAMNAME',$property->parameters['PARAMNAME']->name);
+        $this->assertEquals('paramvalue',$property->parameters['PARAMNAME']->getValue());
+
+    }
+
+    public function testUnsetParameter() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+        $property['paramname'] = 'paramvalue';
+
+        unset($property['PARAMNAME']);
+        $this->assertEquals(0,count($property->parameters()));
+
+    }
+
+    public function testSerialize() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+
+        $this->assertEquals("PROPNAME:propvalue\r\n",$property->serialize());
+
+    }
+
+    public function testSerializeParam() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue', array(
+            'paramname' => 'paramvalue',
+            'paramname2' => 'paramvalue2',
+        ));
+
+        $this->assertEquals("PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propvalue\r\n",$property->serialize());
+
+    }
+
+    public function testSerializeNewLine() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('SUMMARY',"line1\nline2");
+
+        $this->assertEquals("SUMMARY:line1\\nline2\r\n",$property->serialize());
+
+    }
+
+    public function testSerializeLongLine() {
+
+        $cal = new VCalendar();
+        $value = str_repeat('!',200);
+        $property = $cal->createProperty('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() {
+
+        $cal = new VCalendar();
+        $value = str_repeat('!',65) . "\xc3\xa4bla"; // inserted umlaut-a
+        $property = $cal->createProperty('propname', $value);
+        $expected = "PROPNAME:" . str_repeat('!',65) . "\r\n \xc3\xa4bla\r\n";
+        $this->assertEquals($expected, $property->serialize());
+
+    }
+
+    public function testGetIterator() {
+
+        $cal = new VCalendar();
+        $it = new ElementList(array());
+        $property = $cal->createProperty('propname','propvalue');
+        $property->setIterator($it);
+        $this->assertEquals($it,$property->getIterator());
+
+    }
+
+
+    public function testGetIteratorDefault() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('propname','propvalue');
+        $it = $property->getIterator();
+        $this->assertTrue($it instanceof ElementList);
+        $this->assertEquals(1,count($it));
+
+    }
+
+    function testAddScalar() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('EMAIL');
+
+        $property->add('myparam','value');
+
+        $this->assertEquals(1, count($property->parameters()));
+
+        $this->assertTrue($property->parameters['MYPARAM'] instanceof Parameter);
+        $this->assertEquals('MYPARAM',$property->parameters['MYPARAM']->name);
+        $this->assertEquals('value',$property->parameters['MYPARAM']->getValue());
+
+    }
+
+    function testAddParameter() {
+
+        $cal = new VCalendar();
+        $prop = $cal->createProperty('EMAIL');
+
+        $prop->add('MYPARAM','value');
+
+        $this->assertEquals(1, count($prop->parameters()));
+        $this->assertEquals('MYPARAM',$prop['myparam']->name);
+
+    }
+
+    function testAddParameterTwice() {
+
+        $cal = new VCalendar();
+        $prop = $cal->createProperty('EMAIL');
+
+        $prop->add('MYPARAM', 'value1');
+        $prop->add('MYPARAM', 'value2');
+
+        $this->assertEquals(1, count($prop->parameters));
+        $this->assertEquals(2, count($prop->parameters['MYPARAM']->getParts()));
+
+        $this->assertEquals('MYPARAM',$prop['MYPARAM']->name);
+
+    }
+
+
+    function testClone() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('EMAIL','value');
+        $property['FOO'] = 'BAR';
+
+        $property2 = clone $property;
+
+        $property['FOO'] = 'BAZ';
+        $this->assertEquals('BAR', (string)$property2['FOO']);
+
+    }
+
+    function testCreateParams() {
+
+        $cal = new VCalendar();
+        $property = $cal->createProperty('X-PROP','value', array(
+            'param1' => 'value1',
+            'param2' => array('value2', 'value3')
+        ));
+
+        $this->assertEquals(1, count($property['PARAM1']->getParts()));
+        $this->assertEquals(2, count($property['PARAM2']->getParts()));
+
+    }
+
+    function testValidateNonUTF8() {
+
+        $calendar = new VCalendar();
+        $property = $calendar->createProperty('X-PROP', "Bla\x00");
+        $result = $property->validate(Property::REPAIR);
+
+        $this->assertEquals('Property contained a control character (0x00)', $result[0]['message']);
+        $this->assertEquals('Bla', $property->getValue());
+
+    }
+
+    function testValidateControlChars() {
+
+        $s = "chars[";
+        foreach (array(
+            0x7F, 0x5E, 0x5C, 0x3B, 0x3A, 0x2C, 0x22, 0x20,
+            0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
+            0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+            0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
+            0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+          ) as $c) {
+            $s .= sprintf('%02X(%c)', $c, $c);
+        }
+        $s .= "]end";
+
+        $calendar = new VCalendar();
+        $property = $calendar->createProperty('X-PROP', $s);
+        $result = $property->validate(Property::REPAIR);
+
+        $this->assertEquals('Property contained a control character (0x7f)', $result[0]['message']);
+        $this->assertEquals("chars[7F()5E(^)5C(\\\\)3B(\\;)3A(:)2C(\\,)22(\")20( )1F()1E()1D()1C()1B()1A()19()18()17()16()15()14()13()12()11()10()0F()0E()0D()0C()0B()0A(\\n)09(	)08()07()06()05()04()03()02()01()00()]end", $property->getRawMimeDirValue());
+
+    }
+
+
+    function testValidateBadPropertyName() {
+
+        $calendar = new VCalendar();
+        $property = $calendar->createProperty("X_*&PROP*", "Bla");
+        $result = $property->validate(Property::REPAIR);
+
+        $this->assertEquals($result[0]['message'], 'The propertyname: X_*&PROP* contains invalid characters. Only A-Z, 0-9 and - are allowed');
+        $this->assertEquals('X-PROP', $property->name);
+
+    }
+
+    function testGetValue() {
+
+        $calendar = new VCalendar();
+        $property = $calendar->createProperty("SUMMARY", null);
+        $this->assertEquals(array(), $property->getParts());
+        $this->assertNull($property->getValue());
+
+        $property->setValue(array());
+        $this->assertEquals(array(), $property->getParts());
+        $this->assertNull($property->getValue());
+
+        $property->setValue(array(1));
+        $this->assertEquals(array(1), $property->getParts());
+        $this->assertEquals(1, $property->getValue());
+
+        $property->setValue(array(1,2));
+        $this->assertEquals(array(1,2), $property->getParts());
+        $this->assertEquals('1,2', $property->getValue());
+
+        $property->setValue('str');
+        $this->assertEquals(array('str'), $property->getParts());
+        $this->assertEquals('str', $property->getValue());
+    }
+
+    /**
+     * ElementList should reject this.
+     *
+     * @expectedException \LogicException
+     */
+    public function testArrayAccessSetInt() {
+
+        $calendar = new VCalendar();
+        $property = $calendar->createProperty("X-PROP", null);
+
+        $calendar->add($property);
+        $calendar->{'X-PROP'}[0] = 'Something!';
+
+    }
+
+    /**
+     * ElementList should reject this.
+     *
+     * @expectedException \LogicException
+     */
+    public function testArrayAccessUnsetInt() {
+
+        $calendar = new VCalendar();
+        $property = $calendar->createProperty("X-PROP", null);
+
+        $calendar->add($property);
+        unset($calendar->{'X-PROP'}[0]);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/ReaderTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,449 @@
+<?php
+
+namespace Sabre\VObject;
+
+class ReaderTest extends \PHPUnit_Framework_TestCase {
+
+    function testReadComponent() {
+
+        $data = "BEGIN:VCALENDAR\r\nEND:VCALENDAR";
+
+        $result = Reader::read($data);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
+        $this->assertEquals('VCALENDAR', $result->name);
+        $this->assertEquals(0, count($result->children));
+
+    }
+    function testReadStream() {
+
+        $data = "BEGIN:VCALENDAR\r\nEND:VCALENDAR";
+
+        $stream = fopen('php://memory', 'r+');
+        fwrite($stream, $data);
+        rewind($stream);
+
+        $result = Reader::read($stream);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
+        $this->assertEquals('VCALENDAR', $result->name);
+        $this->assertEquals(0, count($result->children));
+
+    }
+
+    function testReadComponentUnixNewLine() {
+
+        $data = "BEGIN:VCALENDAR\nEND:VCALENDAR";
+
+        $result = 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 = 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 = Reader::read($data);
+
+    }
+
+    /**
+     * @expectedException Sabre\VObject\ParseException
+     */
+    function testReadCorruptSubComponent() {
+
+        $data = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:FOO\r\nEND:VCALENDAR";
+
+        $result = Reader::read($data);
+
+    }
+
+    function testReadProperty() {
+
+        $data = "BEGIN:VCALENDAR\r\nSUMMARY:propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->SUMMARY;
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('SUMMARY', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+
+    }
+
+    function testReadPropertyWithNewLine() {
+
+        $data = "BEGIN:VCALENDAR\r\nSUMMARY:Line1\\nLine2\\NLine3\\\\Not the 4th line!\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->SUMMARY;
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('SUMMARY', $result->name);
+        $this->assertEquals("Line1\nLine2\nLine3\\Not the 4th line!", $result->getValue());
+
+    }
+
+    function testReadMappedProperty() {
+
+        $data = "BEGIN:VCALENDAR\r\nDTSTART:20110529\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->DTSTART;
+        $this->assertInstanceOf('Sabre\\VObject\\Property\\ICalendar\\DateTime', $result);
+        $this->assertEquals('DTSTART', $result->name);
+        $this->assertEquals('20110529', $result->getValue());
+
+    }
+
+    function testReadMappedPropertyGrouped() {
+
+        $data = "BEGIN:VCALENDAR\r\nfoo.DTSTART:20110529\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->DTSTART;
+        $this->assertInstanceOf('Sabre\\VObject\\Property\\ICalendar\\DateTime', $result);
+        $this->assertEquals('DTSTART', $result->name);
+        $this->assertEquals('20110529', $result->getValue());
+
+    }
+
+    /**
+     * @expectedException Sabre\VObject\ParseException
+     */
+    function testReadBrokenLine() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;propValue";
+        $result = Reader::read($data);
+
+    }
+
+    function testReadPropertyInComponent() {
+
+        $data = array(
+            "BEGIN:VCALENDAR",
+            "PROPNAME:propValue",
+            "END:VCALENDAR"
+        );
+
+        $result = 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]->getValue());
+
+    }
+
+    function testReadNestedComponent() {
+
+        $data = array(
+            "BEGIN:VCALENDAR",
+            "BEGIN:VTIMEZONE",
+            "BEGIN:DAYLIGHT",
+            "END:DAYLIGHT",
+            "END:VTIMEZONE",
+            "END:VCALENDAR"
+        );
+
+        $result = 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 = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue:propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->PROPNAME;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name);
+        $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue());
+
+    }
+
+    function testReadPropertyRepeatingParameter() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;N=1;N=2;N=3,4;N=\"5\",6;N=\"7,8\";N=9,10;N=^'11^':propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->PROPNAME;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('N', $result->parameters['N']->name);
+        $this->assertEquals('1,2,3,4,5,6,7,8,9,10,"11"', $result->parameters['N']->getValue());
+        $this->assertEquals(array(1,2,3,4,5,6,"7,8",9,10,'"11"'), $result->parameters['N']->getParts());
+
+    }
+
+    function testReadPropertyRepeatingNamelessGuessedParameter() {
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;WORK;VOICE;PREF:propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->PROPNAME;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('TYPE', $result->parameters['TYPE']->name);
+        $this->assertEquals('WORK,VOICE,PREF', $result->parameters['TYPE']->getValue());
+        $this->assertEquals(array('WORK', 'VOICE', 'PREF'), $result->parameters['TYPE']->getParts());
+
+    }
+
+    function testReadPropertyNoName() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;PRODIGY:propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->PROPNAME;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('TYPE', $result->parameters['TYPE']->name);
+        $this->assertTrue($result->parameters['TYPE']->noName);
+        $this->assertEquals('PRODIGY', $result->parameters['TYPE']);
+
+    }
+
+    function testReadPropertyParameterExtraColon() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue:propValue:anotherrandomstring\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->PROPNAME;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue:anotherrandomstring', $result->getValue());
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name);
+        $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue());
+
+    }
+
+    function testReadProperty2Parameters() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->PROPNAME;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+        $this->assertEquals(2, count($result->parameters()));
+        $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name);
+        $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue());
+        $this->assertEquals('PARAMNAME2', $result->parameters['PARAMNAME2']->name);
+        $this->assertEquals('paramvalue2', $result->parameters['PARAMNAME2']->getValue());
+
+    }
+
+    function testReadPropertyParameterQuoted() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=\"paramvalue\":propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->PROPNAME;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name);
+        $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue());
+
+    }
+
+    function testReadPropertyParameterNewLines() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue1^nvalue2^^nvalue3:propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $result = $result->propname;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name);
+        $this->assertEquals("paramvalue1\nvalue2^nvalue3", $result->parameters['PARAMNAME']->getValue());
+
+    }
+
+    function testReadPropertyParameterQuotedColon() {
+
+        $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=\"param:value\":propValue\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+        $result = $result->propname;
+
+        $this->assertInstanceOf('Sabre\\VObject\\Property', $result);
+        $this->assertEquals('PROPNAME', $result->name);
+        $this->assertEquals('propValue', $result->getValue());
+        $this->assertEquals(1, count($result->parameters()));
+        $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name);
+        $this->assertEquals('param:value', $result->parameters['PARAMNAME']->getValue());
+
+    }
+
+    function testReadForgiving() {
+
+        $data = array(
+            "BEGIN:VCALENDAR",
+            "X_PROP:propValue",
+            "END:VCALENDAR"
+        );
+
+        $caught = false;
+        try {
+            $result = Reader::read(implode("\r\n",$data));
+        } catch (ParseException $e) {
+            $caught = true;
+        }
+
+        $this->assertEquals(true, $caught);
+
+        $result = Reader::read(implode("\r\n",$data), Reader::OPTION_FORGIVING);
+
+        $expected = implode("\r\n", array(
+            "BEGIN:VCALENDAR",
+            "X_PROP:propValue",
+            "END:VCALENDAR",
+            ""
+        ));
+
+        $this->assertEquals($expected, $result->serialize());
+
+    }
+
+    function testReadWithInvalidLine() {
+
+        $data = array(
+            "BEGIN:VCALENDAR",
+            "DESCRIPTION:propValue",
+            "Yes, we've actually seen a file with non-idented property values on multiple lines",
+            "END:VCALENDAR"
+        );
+
+        $caught = false;
+        try {
+            $result = Reader::read(implode("\r\n",$data));
+        } catch (ParseException $e) {
+            $caught = true;
+        }
+
+        $this->assertEquals(true, $caught);
+
+        $result = Reader::read(implode("\r\n",$data), Reader::OPTION_IGNORE_INVALID_LINES);
+
+        $expected = implode("\r\n", array(
+            "BEGIN:VCALENDAR",
+            "DESCRIPTION:propValue",
+            "END:VCALENDAR",
+            ""
+        ));
+
+        $this->assertEquals($expected, $result->serialize());
+
+    }
+
+    /**
+     * Reported as Issue 32.
+     *
+     * @expectedException \Sabre\VObject\ParseException
+     */
+    public function testReadIncompleteFile() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:1.0
+BEGIN:VEVENT
+X-FUNAMBOL-FOLDER:DEFAULT_FOLDER
+X-FUNAMBOL-ALLDAY:0
+DTSTART:20111017T110000Z
+DTEND:20111017T123000Z
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY
+CATEGORIES:
+LOCATION;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:Netviewer Meeting
+PRIORITY:1
+STATUS:3
+X-MICROSOFT-CDO-REPLYTIME:20111017T064200Z
+SUMMARY;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:Kopieren: test
+CLASS:PUBLIC
+AALARM:
+RRULE:
+X-FUNAMBOL-BILLINGINFO:
+X-FUNAMBOL-COMPANIES:
+X-FUNAMBOL-MILEAGE:
+X-FUNAMBOL-NOAGING:0
+ATTENDEE;STATUS=NEEDS ACTION;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:'Heino' heino@test.com
+ATTENDEE;STATUS=NEEDS ACTION;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:'Markus' test@test.com
+ATTENDEE;STATUS=NEEDS AC
+ICS;
+
+        Reader::read($input);
+
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testReadBrokenInput() {
+
+        Reader::read(false);
+
+    }
+
+    public function testReadBOM() {
+
+        $data = chr(0xef) . chr(0xbb) . chr(0xbf) . "BEGIN:VCALENDAR\r\nEND:VCALENDAR";
+        $result = Reader::read($data);
+
+        $this->assertInstanceOf('Sabre\\VObject\\Component', $result);
+        $this->assertEquals('VCALENDAR', $result->name);
+        $this->assertEquals(0, count($result->children));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/ByMonthInDailyTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,58 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    DateTime;
+
+class RecurrenceIteratorByMonthInDailyTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * This tests the expansion of dates with DAILY frequency in RRULE with BYMONTH restrictions
+     */
+    function testExpand() {
+
+        $ics = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.4//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+TRANSP:OPAQUE
+DTEND:20070925T183000Z
+UID:uuid
+DTSTAMP:19700101T000000Z
+LOCATION:
+DESCRIPTION:
+STATUS:CONFIRMED
+SEQUENCE:18
+SUMMARY:Stuff
+DTSTART:20070925T160000Z
+CREATED:20071004T144642Z
+RRULE:FREQ=DAILY;BYMONTH=9,10;BYDAY=SU
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($ics);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
+
+        $vcal->expand(new DateTime('2013-09-28'), new DateTime('2014-09-11'));
+
+        foreach ($vcal->VEVENT as $event) {
+            $dates[] = $event->DTSTART->getValue();
+        }
+
+        $expectedDates = array(
+            "20130929T160000Z",
+            "20131006T160000Z",
+            "20131013T160000Z",
+            "20131020T160000Z",
+            "20131027T160000Z",
+            "20140907T160000Z"
+        );
+
+        $this->assertEquals($expectedDates, $dates, 'Recursed dates are restricted by month');
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/FifthTuesdayProblemTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,54 @@
+<?php
+
+namespace Sabre\VObject\Recur\EventIterator;
+
+use Sabre\VObject\Recur;
+use Sabre\VObject\Reader;
+
+class FifthTuesdayProblemTest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * A pretty slow test. Had to be marked as 'medium' for phpunit to not die
+     * after 1 second. Would be good to optimize later.
+     *
+     * @medium
+     */
+    function testGetDTEnd() {
+
+        $ics = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.4//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+TRANSP:OPAQUE
+DTEND;TZID=America/New_York:20070925T170000
+UID:uuid
+DTSTAMP:19700101T000000Z
+LOCATION:
+DESCRIPTION:
+STATUS:CONFIRMED
+SEQUENCE:18
+SUMMARY:Stuff
+DTSTART;TZID=America/New_York:20070925T160000
+CREATED:20071004T144642Z
+RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=20071030T035959Z;BYDAY=5TU
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vObject = Reader::read($ics);
+        $it = new Recur\EventIterator($vObject, (string)$vObject->VEVENT->UID);
+
+        while($it->valid()) {
+            $it->next();
+        }
+
+        // If we got here, it means we were successful. The bug that was in the
+        // system before would fail on the 5th tuesday of the month, if the 5th
+        // tuesday did not exist.
+        $this->assertTrue(true);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/IncorrectExpandTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,62 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    DateTime,
+    DateTimeZone;
+
+/**
+ * This is a unittest for Issue #53.
+ */
+class RecurrenceIteratorIncorrectExpandTest extends \PHPUnit_Framework_TestCase {
+
+    function testExpand() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foo
+DTSTART:20130711T050000Z
+DTEND:20130711T053000Z
+RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2
+END:VEVENT
+BEGIN:VEVENT
+UID:foo
+DTSTART:20130719T050000Z
+DTEND:20130719T053000Z
+RECURRENCE-ID:20130712T050000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
+
+        $vcal->expand(new DateTime('2011-01-01'), new DateTime('2014-01-01'));
+
+        $result = $vcal->serialize();
+
+        $output = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foo
+DTSTART:20130711T050000Z
+DTEND:20130711T053000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foo
+DTSTART:20130719T050000Z
+DTEND:20130719T053000Z
+RECURRENCE-ID:20130712T050000Z
+END:VEVENT
+END:VCALENDAR
+
+ICS;
+        $this->assertEquals($output, str_replace("\r", "", $result));
+    
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,99 @@
+<?php
+
+namespace Sabre\VObject\Recur\EventIterator;
+
+use
+    DateTime,
+    DateTimeZone,
+    Sabre\VObject\Component\VCalendar,
+    Sabre\VObject\Recur;
+
+class EventIteratorInfiniteLoopProblemTest extends \PHPUnit_Framework_TestCase {
+
+    public function setUp() {
+
+        $this->vcal = new VCalendar();
+
+    }
+
+    /**
+     * This bug came from a Fruux customer. This would result in a never-ending
+     * request.
+     */
+    function testFastForwardTooFar() {
+
+        $ev = $this->vcal->createComponent('VEVENT');
+        $ev->UID = 'foobar';
+        $ev->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 = $this->vcal->createComponent('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.
+        $this->vcal->add($ev);
+
+        $it = new Recur\EventIterator($this->vcal,'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
+        );
+
+    }
+
+    /**
+     * Something, somewhere produced an ics with an interval set to 0. Because
+     * this means we increase the current day (or week, month) by 0, this also
+     * results in an infinite loop.
+     *
+     * @expectedException InvalidArgumentException
+     * @return void
+     */
+    function testZeroInterval() {
+
+        $ev = $this->vcal->createComponent('VEVENT');
+        $ev->UID = 'uuid';
+        $ev->DTSTART = '20120824T145700Z';
+        $ev->RRULE = 'FREQ=YEARLY;INTERVAL=0';
+        $this->vcal->add($ev);
+
+        $it = new Recur\EventIterator($this->vcal,'uuid');
+        $it->fastForward(new DateTime('2013-01-01 23:00:00', new DateTimeZone('UTC')));
+
+        // if we got this far.. it means we are no longer infinitely looping
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/Issue48Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,49 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    DateTime,
+    DateTimeZone;
+
+class Issue48Test extends \PHPUnit_Framework_TestCase {
+
+    function testExpand() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foo
+DTEND;TZID=Europe/Moscow:20130710T120000
+DTSTART;TZID=Europe/Moscow:20130710T110000
+RRULE:FREQ=DAILY;UNTIL=20130712T195959Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foo
+DTEND;TZID=Europe/Moscow:20130713T120000
+DTSTART;TZID=Europe/Moscow:20130713T110000
+RECURRENCE-ID;TZID=Europe/Moscow:20130711T110000
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
+
+        $it = new Recur\EventIterator($vcal, 'foo');
+
+        $result = iterator_to_array($it);
+
+        $tz = new DateTimeZone('Europe/Moscow');
+
+        $expected = array(
+            new DateTime('2013-07-10 11:00:00', $tz),
+            new DateTime('2013-07-12 11:00:00', $tz),
+            new DateTime('2013-07-13 11:00:00', $tz),
+        );
+
+        $this->assertEquals($expected, $result);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/Issue50Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,128 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    DateTime,
+    DateTimeZone;
+
+class Issue50Test extends \PHPUnit_Framework_TestCase {
+
+    function testExpand() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
+BEGIN:VTIMEZONE
+TZID:Europe/Brussels
+X-LIC-LOCATION:Europe/Brussels
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20130705T142510Z
+LAST-MODIFIED:20130715T132556Z
+DTSTAMP:20130715T132556Z
+UID:1aef0b27-3d92-4581-829a-11999dd36724
+SUMMARY:Werken
+RRULE:FREQ=DAILY;COUNT=5
+DTSTART;TZID=Europe/Brussels:20130715T090000
+DTEND;TZID=Europe/Brussels:20130715T170000
+LOCATION:Job
+DESCRIPTION:Vrij
+X-MOZ-GENERATION:9
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20130715T081654Z
+LAST-MODIFIED:20130715T110931Z
+DTSTAMP:20130715T110931Z
+UID:1aef0b27-3d92-4581-829a-11999dd36724
+SUMMARY:Werken
+RECURRENCE-ID;TZID=Europe/Brussels:20130719T090000
+DTSTART;TZID=Europe/Brussels:20130719T070000
+DTEND;TZID=Europe/Brussels:20130719T150000
+SEQUENCE:1
+LOCATION:Job
+DESCRIPTION:Vrij
+X-MOZ-GENERATION:1
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20130715T111654Z
+LAST-MODIFIED:20130715T132556Z
+DTSTAMP:20130715T132556Z
+UID:1aef0b27-3d92-4581-829a-11999dd36724
+SUMMARY:Werken
+RECURRENCE-ID;TZID=Europe/Brussels:20130716T090000
+DTSTART;TZID=Europe/Brussels:20130716T070000
+DTEND;TZID=Europe/Brussels:20130716T150000
+SEQUENCE:1
+LOCATION:Job
+X-MOZ-GENERATION:2
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20130715T125942Z
+LAST-MODIFIED:20130715T130023Z
+DTSTAMP:20130715T130023Z
+UID:1aef0b27-3d92-4581-829a-11999dd36724
+SUMMARY:Werken
+RECURRENCE-ID;TZID=Europe/Brussels:20130717T090000
+DTSTART;TZID=Europe/Brussels:20130717T070000
+DTEND;TZID=Europe/Brussels:20130717T150000
+SEQUENCE:1
+LOCATION:Job
+X-MOZ-GENERATION:3
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20130715T130024Z
+LAST-MODIFIED:20130715T130034Z
+DTSTAMP:20130715T130034Z
+UID:1aef0b27-3d92-4581-829a-11999dd36724
+SUMMARY:Werken
+RECURRENCE-ID;TZID=Europe/Brussels:20130718T090000
+DTSTART;TZID=Europe/Brussels:20130718T090000
+DTEND;TZID=Europe/Brussels:20130718T170000
+LOCATION:Job
+X-MOZ-GENERATION:5
+DESCRIPTION:Vrij
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
+
+        $it = new Recur\EventIterator($vcal, '1aef0b27-3d92-4581-829a-11999dd36724');
+
+        $result = array();
+        foreach($it as $instance) {
+
+            $result[] = $instance;
+
+        }
+
+        $tz = new DateTimeZone('Europe/Brussels');
+
+        $this->assertEquals(array(
+            new DateTime('2013-07-15 09:00:00', $tz),
+            new DateTime('2013-07-16 07:00:00', $tz),
+            new DateTime('2013-07-17 07:00:00', $tz),
+            new DateTime('2013-07-18 09:00:00', $tz),
+            new DateTime('2013-07-19 07:00:00', $tz),
+        ), $result);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/MainTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,1427 @@
+<?php
+
+namespace Sabre\VObject\EventIterator;
+
+use DateTime;
+use DateTimeZone;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Component\VCalendar;
+
+class MainTest extends \PHPUnit_Framework_TestCase {
+
+    function testValues() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=DAILY;BYHOUR=10;BYMINUTE=5;BYSECOND=16;BYWEEKNO=32;BYYEARDAY=100,200';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07'));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $this->assertTrue($it->isInfinite());
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     * @depends testValues
+     */
+    function testInvalidFreq() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+        $ev->RRULE = 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z';
+        $ev->UID = 'foo';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testVCalendarNoUID() {
+
+        $vcal = new VCalendar();
+        $it = new EventIterator($vcal);
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testVCalendarInvalidUID() {
+
+        $vcal = new VCalendar();
+        $it = new EventIterator($vcal,'foo');
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testHourly() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=HOURLY;INTERVAL=3;UNTIL=20111025T000000Z';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07 12:00:00', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,$ev->uid);
+
+        // 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 12:00:00', $tz),
+                new DateTime('2011-10-07 15:00:00', $tz),
+                new DateTime('2011-10-07 18:00:00', $tz),
+                new DateTime('2011-10-07 21:00:00', $tz),
+                new DateTime('2011-10-08 00:00:00', $tz),
+                new DateTime('2011-10-08 03:00:00', $tz),
+                new DateTime('2011-10-08 06:00:00', $tz),
+                new DateTime('2011-10-08 09:00:00', $tz),
+                new DateTime('2011-10-08 12:00:00', $tz),
+                new DateTime('2011-10-08 15:00:00', $tz),
+                new DateTime('2011-10-08 18:00:00', $tz),
+                new DateTime('2011-10-08 21:00:00', $tz),
+            ),
+            $result
+        );
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testDaily() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,$ev->uid);
+
+        // 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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,$ev->uid);
+
+        // 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 testDailyByDayByHour() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-08 06:00:00', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        // 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-08 06:00:00', $tz),
+                new datetime('2011-10-08 07:00:00', $tz),
+                new datetime('2011-10-09 06:00:00', $tz),
+                new datetime('2011-10-09 07:00:00', $tz),
+                new datetime('2011-10-15 06:00:00', $tz),
+                new datetime('2011-10-15 07:00:00', $tz),
+                new datetime('2011-10-16 06:00:00', $tz),
+                new datetime('2011-10-16 07:00:00', $tz),
+                new datetime('2011-10-22 06:00:00', $tz),
+                new datetime('2011-10-22 07:00:00', $tz),
+                new datetime('2011-10-23 06:00:00', $tz),
+                new datetime('2011-10-23 07:00:00', $tz),
+            ),
+            $result
+        );
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testDailyByHour() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2012-10-11 12:00:00', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        // 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('2012-10-11 12:00:00', $tz),
+                new datetime('2012-10-11 13:00:00', $tz),
+                new datetime('2012-10-11 14:00:00', $tz),
+                new datetime('2012-10-11 15:00:00', $tz),
+                new datetime('2012-10-13 10:00:00', $tz),
+                new datetime('2012-10-13 11:00:00', $tz),
+                new datetime('2012-10-13 12:00:00', $tz),
+                new datetime('2012-10-13 13:00:00', $tz),
+                new datetime('2012-10-13 14:00:00', $tz),
+                new datetime('2012-10-13 15:00:00', $tz),
+                new datetime('2012-10-15 10:00:00', $tz),
+                new datetime('2012-10-15 11:00:00', $tz),
+            ),
+            $result
+        );
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testDailyByDay() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        // 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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;COUNT=10';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        // 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 testWeeklyByDayByHour() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07 08:00:00', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        // Grabbing the next 12 items
+        $max = 15;
+        $result = array();
+        foreach($it as $item) {
+
+            $result[] = $item;
+            $max--;
+
+            if (!$max) break;
+
+        }
+
+        $tz = new DateTimeZone('UTC');
+
+        $this->assertEquals(
+            array(
+                new DateTime('2011-10-07 08:00:00', $tz),
+                new DateTime('2011-10-07 09:00:00', $tz),
+                new DateTime('2011-10-07 10:00:00', $tz),
+                new DateTime('2011-10-18 08:00:00', $tz),
+                new DateTime('2011-10-18 09:00:00', $tz),
+                new DateTime('2011-10-18 10:00:00', $tz),
+                new DateTime('2011-10-19 08:00:00', $tz),
+                new DateTime('2011-10-19 09:00:00', $tz),
+                new DateTime('2011-10-19 10:00:00', $tz),
+                new DateTime('2011-10-21 08:00:00', $tz),
+                new DateTime('2011-10-21 09:00:00', $tz),
+                new DateTime('2011-10-21 10:00:00', $tz),
+                new DateTime('2011-11-01 08:00:00', $tz),
+                new DateTime('2011-11-01 09:00:00', $tz),
+                new DateTime('2011-11-01 10:00:00', $tz),
+            ),
+            $result
+        );
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testWeeklyByDaySpecificHour() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07 18:00:00', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        // 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 18:00:00', $tz),
+                new DateTime('2011-10-18 18:00:00', $tz),
+                new DateTime('2011-10-19 18:00:00', $tz),
+                new DateTime('2011-10-21 18:00:00', $tz),
+                new DateTime('2011-11-01 18:00:00', $tz),
+                new DateTime('2011-11-02 18:00:00', $tz),
+                new DateTime('2011-11-04 18:00:00', $tz),
+                new DateTime('2011-11-15 18:00:00', $tz),
+                new DateTime('2011-11-16 18:00:00', $tz),
+                new DateTime('2011-11-18 18:00:00', $tz),
+                new DateTime('2011-11-29 18:00:00', $tz),
+                new DateTime('2011-11-30 18:00:00', $tz),
+            ),
+            $result
+        );
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testWeeklyByDay() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        // 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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=3;COUNT=5';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-12-05', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=12';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-12-31', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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
+        );
+
+    }
+
+    /**
+     * A pretty slow test. Had to be marked as 'medium' for phpunit to not die
+     * after 1 second. Would be good to optimize later.
+     *
+     * @depends testValues
+     * @medium
+     */
+    function testMonthlyByDay() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-08-01', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-01-03', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=YEARLY;COUNT=10;INTERVAL=3';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-01-01', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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 testYearlyLeapYear() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=YEARLY;COUNT=3';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2012-02-29', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $max = 20;
+        $result = array();
+        foreach($it as $k=>$item) {
+
+            $result[] = $item;
+            $max--;
+
+            if (!$max) break;
+
+        }
+
+        $tz = new DateTimeZone('UTC');
+
+        $this->assertEquals(
+            array(
+                new DateTime('2012-02-29', $tz),
+                new DateTime('2016-02-29', $tz),
+                new DateTime('2020-02-29', $tz),
+            ),
+            $result
+        );
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testYearlyByMonth() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-04-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU';
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-04-04', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($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', new DateTimeZone('UTC')));
+
+        $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() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RRULE = 'FREQ=YEARLY;COUNT=10';
+        $dtStart = $vcal->createProperty('DTSTART');
+
+        $tz = new DateTimeZone('Canada/Eastern');
+        $dtStart->setDateTime(new DateTime('2011-01-01 13:50:20', $tz));
+
+        $exDate1 = $vcal->createProperty('EXDATE');
+        $exDate1->setDateTimes(array(new DateTime('2012-01-01 13:50:20', $tz), new DateTime('2014-01-01 13:50:20', $tz)));
+        $exDate2 = $vcal->createProperty('EXDATE');
+        $exDate2->setDateTimes(array(new DateTime('2016-01-01 13:50:20', $tz)));
+
+        $ev->add($dtStart);
+        $ev->add($exDate1);
+        $ev->add($exDate2);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,(string)$ev->uid);
+
+        $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 = new VCalendar();
+
+        $ev1 = $vcal->createComponent('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 = $vcal->createComponent('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 = $vcal->createComponent('VEVENT');
+        $ev3->UID = 'overridden';
+        $ev3->{'RECURRENCE-ID'} = '20120113T120000Z';
+        $ev3->DTSTART = '20120115T140000Z';
+        $ev3->SUMMARY = 'Event 3';
+
+        $vcal->add($ev3);
+
+        $it = new EventIterator($vcal,'overridden');
+
+        $dates = array();
+        $summaries = array();
+        while($it->valid()) {
+
+            $dates[] = $it->getDTStart();
+            $summaries[] = (string)$it->getEventObject()->SUMMARY;
+            $it->next();
+
+        }
+
+        $tz = new DateTimeZone('UTC');
+        $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 = new VCalendar();
+
+        $ev1 = $vcal->createComponent('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 = $vcal->createComponent('VEVENT');
+        $ev2->UID = 'overridden';
+        $ev2->{'RECURRENCE-ID'} = '20120119T120000Z';
+        $ev2->DTSTART = '20120113T120000Z';
+        $ev2->SUMMARY = 'Override!';
+
+        $vcal->add($ev2);
+
+        $it = new EventIterator($vcal,'overridden');
+
+        $dates = array();
+        $summaries = array();
+        while($it->valid()) {
+
+            $dates[] = $it->getDTStart();
+            $summaries[] = (string)$it->getEventObject()->SUMMARY;
+            $it->next();
+
+        }
+
+        $tz = new DateTimeZone('UTC');
+        $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 = new VCalendar();
+        $ev1 = $vcal->createComponent('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 = $vcal->createComponent('VEVENT');
+        $ev2->UID = 'overridden';
+        $ev2->{'RECURRENCE-ID'} = '20120131T120000Z';
+        $ev2->DTSTART = '20120125T120000Z';
+        $ev2->SUMMARY = 'Override!';
+
+        $vcal->add($ev2);
+
+        $it = new EventIterator($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);
+
+    }
+
+    /**
+     * @depends testValues
+     */
+    function testRDATE() {
+
+        $vcal = new VCalendar();
+        $ev = $vcal->createComponent('VEVENT');
+
+        $ev->UID = 'bla';
+        $ev->RDATE = array(
+            new DateTime('2014-08-07', new DateTimeZone('UTC')),
+            new DateTime('2014-08-08', new DateTimeZone('UTC')),
+        );
+        $dtStart = $vcal->createProperty('DTSTART');
+        $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
+
+        $ev->add($dtStart);
+
+        $vcal->add($ev);
+
+        $it = new EventIterator($vcal,$ev->uid);
+
+        // 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('2014-08-07', $tz),
+                new DateTime('2014-08-08', $tz),
+            ),
+            $result
+        );
+
+    }
+
+    /**
+     * @depends testValues
+     * @expectedException \InvalidArgumentException
+     */
+    function testNoMasterBadUID() {
+
+        $vcal = new VCalendar();
+        // ev2 overrides an event, and puts it on 2pm instead.
+        $ev2 = $vcal->createComponent('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 = $vcal->createComponent('VEVENT');
+        $ev3->UID = 'overridden';
+        $ev3->{'RECURRENCE-ID'} = '20120113T120000Z';
+        $ev3->DTSTART = '20120115T140000Z';
+        $ev3->SUMMARY = 'Event 3';
+
+        $vcal->add($ev3);
+
+        $it = new EventIterator($vcal,'broken');
+
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/MissingOverriddenTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,63 @@
+<?php
+
+namespace Sabre\VObject;
+
+use
+    DateTime,
+    DateTimeZone;
+
+class RecurrenceIteratorMissingOverriddenTest extends \PHPUnit_Framework_TestCase {
+
+    function testExpand() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foo
+DTSTART:20130727T120000Z
+DURATION:PT1H
+RRULE:FREQ=DAILY;COUNT=2
+SUMMARY:A
+END:VEVENT
+BEGIN:VEVENT
+RECURRENCE-ID:20130728T120000Z
+UID:foo
+DTSTART:20140101T120000Z
+DURATION:PT1H
+SUMMARY:B
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
+
+        $vcal->expand(new DateTime('2011-01-01'), new DateTime('2015-01-01'));
+
+        $result = $vcal->serialize();
+
+        $output = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foo
+DTSTART:20130727T120000Z
+DURATION:PT1H
+SUMMARY:A
+END:VEVENT
+BEGIN:VEVENT
+RECURRENCE-ID:20130728T120000Z
+UID:foo
+DTSTART:20140101T120000Z
+DURATION:PT1H
+SUMMARY:B
+END:VEVENT
+END:VCALENDAR
+
+ICS;
+        $this->assertEquals($output, str_replace("\r","",$result));
+    
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/NoInstancesTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,40 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use
+    Sabre\VObject\Reader;
+
+class IssueEXDATETest extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * @expectedException \Sabre\VObject\Recur\NoInstancesException
+     */
+    function testRecurrence() {
+
+        $input = <<<ICS
+BEGIN:VCALENDAR
+PRODID:-//Google Inc//Google Calendar 70.9054//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTART;TZID=Europe/Berlin:20130329T140000
+DTEND;TZID=Europe/Berlin:20130329T153000
+RRULE:FREQ=WEEKLY;BYDAY=FR;UNTIL=20130412T115959Z
+EXDATE;TZID=Europe/Berlin:20130405T140000
+EXDATE;TZID=Europe/Berlin:20130329T140000
+DTSTAMP:20140916T201215Z
+UID:foo
+SEQUENCE:1
+SUMMARY:foo
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $this->assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal);
+
+        $it = new EventIterator($vcal, 'foo');
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/EventIterator/OverrideFirstEventTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,122 @@
+<?php
+
+namespace Sabre\VObject\RecurrenceIterator;
+
+use Sabre\VObject\Reader;
+use DateTime;
+
+class OverrideFirstEventTest extends \PHPUnit_Framework_TestCase {
+
+    function testOverrideFirstEvent() {
+
+        $input =  <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140803T120000Z
+RRULE:FREQ=WEEKLY
+SUMMARY:Original
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140803T120000Z
+DTSTART:20140803T120000Z
+SUMMARY:Overridden
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $vcal->expand(new DateTime('2014-08-01'), new DateTime('2014-09-01'));
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140803T120000Z
+DTSTART:20140803T120000Z
+SUMMARY:Overridden
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140810T120000Z
+SUMMARY:Original
+RECURRENCE-ID:20140810T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140817T120000Z
+SUMMARY:Original
+RECURRENCE-ID:20140817T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140824T120000Z
+SUMMARY:Original
+RECURRENCE-ID:20140824T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140831T120000Z
+SUMMARY:Original
+RECURRENCE-ID:20140831T120000Z
+END:VEVENT
+END:VCALENDAR
+
+ICS;
+
+        $newIcs = $vcal->serialize();
+        $newIcs = str_replace("\r\n","\n", $newIcs);
+        $this->assertEquals(
+            $expected,
+            $newIcs
+        );
+
+
+    }
+
+    function testRemoveFirstEvent() {
+
+        $input =  <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140803T120000Z
+RRULE:FREQ=WEEKLY
+EXDATE:20140803T120000Z
+SUMMARY:Original
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+        $vcal = Reader::read($input);
+        $vcal->expand(new DateTime('2014-08-01'), new DateTime('2014-08-19'));
+
+        $expected = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140810T120000Z
+SUMMARY:Original
+RECURRENCE-ID:20140810T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+DTSTART:20140817T120000Z
+SUMMARY:Original
+RECURRENCE-ID:20140817T120000Z
+END:VEVENT
+END:VCALENDAR
+
+ICS;
+
+        $newIcs = $vcal->serialize();
+        $newIcs = str_replace("\r\n","\n", $newIcs);
+        $this->assertEquals(
+            $expected,
+            $newIcs
+        );
+
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/RDateIteratorTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,56 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use DateTime;
+use DateTimeZone;
+
+class RDateIteratorTest extends \PHPUnit_Framework_TestCase {
+
+    function testSimple() {
+
+        $utc = new DateTimeZone('UTC');
+        $it = new RDateIterator('20140901T000000Z,20141001T000000Z', new DateTime('2014-08-01 00:00:00', $utc));
+
+        $expected = array(
+            new DateTime('2014-08-01 00:00:00', $utc),
+            new DateTime('2014-09-01 00:00:00', $utc),
+            new DateTime('2014-10-01 00:00:00', $utc),
+        );
+
+        $this->assertEquals(
+            $expected,
+            iterator_to_array($it)
+        );
+
+        $this->assertFalse($it->isInfinite());
+
+    }
+
+    function testFastForward() {
+
+        $utc = new DateTimeZone('UTC');
+        $it = new RDateIterator('20140901T000000Z,20141001T000000Z', new DateTime('2014-08-01 00:00:00', $utc));
+
+        $it->fastForward(new DateTime('2014-08-15 00:00:00'));
+
+        $result = array();
+        while($it->valid()) {
+            $result[] = $it->current();
+            $it->next();
+        }
+
+        $expected = array(
+            new DateTime('2014-09-01 00:00:00', $utc),
+            new DateTime('2014-10-01 00:00:00', $utc),
+        );
+
+        $this->assertEquals(
+            $expected,
+            $result
+        );
+
+        $this->assertFalse($it->isInfinite());
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Recur/RRuleIteratorTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,698 @@
+<?php
+
+namespace Sabre\VObject\Recur;
+
+use DateTime;
+use DateTimeZone;
+
+class RRuleIteratorTest extends \PHPUnit_Framework_TestCase {
+
+    function testHourly() {
+
+        $this->parse(
+            'FREQ=HOURLY;INTERVAL=3;COUNT=12',
+            '2011-10-07 12:00:00',
+            array(
+                '2011-10-07 12:00:00',
+                '2011-10-07 15:00:00',
+                '2011-10-07 18:00:00',
+                '2011-10-07 21:00:00',
+                '2011-10-08 00:00:00',
+                '2011-10-08 03:00:00',
+                '2011-10-08 06:00:00',
+                '2011-10-08 09:00:00',
+                '2011-10-08 12:00:00',
+                '2011-10-08 15:00:00',
+                '2011-10-08 18:00:00',
+                '2011-10-08 21:00:00',
+            )
+        );
+
+    }
+
+    function testDaily() {
+
+        $this->parse(
+            'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z',
+            '2011-10-07',
+            array(
+                '2011-10-07 00:00:00',
+                '2011-10-10 00:00:00',
+                '2011-10-13 00:00:00',
+                '2011-10-16 00:00:00',
+                '2011-10-19 00:00:00',
+                '2011-10-22 00:00:00',
+                '2011-10-25 00:00:00',
+            )
+        );
+
+    }
+
+    function testDailyByDayByHour() {
+
+        $this->parse(
+            'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7',
+            '2011-10-08 06:00:00',
+            array(
+                '2011-10-08 06:00:00',
+                '2011-10-08 07:00:00',
+                '2011-10-09 06:00:00',
+                '2011-10-09 07:00:00',
+                '2011-10-15 06:00:00',
+                '2011-10-15 07:00:00',
+                '2011-10-16 06:00:00',
+                '2011-10-16 07:00:00',
+                '2011-10-22 06:00:00',
+                '2011-10-22 07:00:00',
+                '2011-10-23 06:00:00',
+                '2011-10-23 07:00:00',
+            )
+        );
+
+    }
+
+    function testDailyByHour() {
+
+        $this->parse(
+            'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15',
+            '2012-10-11 12:00:00',
+            array(
+                '2012-10-11 12:00:00',
+                '2012-10-11 13:00:00',
+                '2012-10-11 14:00:00',
+                '2012-10-11 15:00:00',
+                '2012-10-13 10:00:00',
+                '2012-10-13 11:00:00',
+                '2012-10-13 12:00:00',
+                '2012-10-13 13:00:00',
+                '2012-10-13 14:00:00',
+                '2012-10-13 15:00:00',
+                '2012-10-15 10:00:00',
+                '2012-10-15 11:00:00',
+            )
+        );
+
+    }
+
+    function testDailyByDay() {
+
+        $this->parse(
+            'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR',
+            '2011-10-07 12:00:00',
+            array(
+                '2011-10-07 12:00:00',
+                '2011-10-11 12:00:00',
+                '2011-10-19 12:00:00',
+                '2011-10-21 12:00:00',
+                '2011-10-25 12:00:00',
+                '2011-11-02 12:00:00',
+                '2011-11-04 12:00:00',
+                '2011-11-08 12:00:00',
+                '2011-11-16 12:00:00',
+                '2011-11-18 12:00:00',
+                '2011-11-22 12:00:00',
+                '2011-11-30 12:00:00',
+            )
+        );
+
+    }
+
+    function testDailyCount() {
+
+        $this->parse(
+            'FREQ=DAILY;COUNT=5',
+            '2014-08-01 18:03:00',
+            array(
+                '2014-08-01 18:03:00',
+                '2014-08-02 18:03:00',
+                '2014-08-03 18:03:00',
+                '2014-08-04 18:03:00',
+                '2014-08-05 18:03:00',
+            )
+        );
+
+    }
+
+    function testDailyByMonth() {
+
+        $this->parse(
+            'FREQ=DAILY;BYMONTH=9,10;BYDAY=SU',
+            '2007-10-04 16:00:00',
+            array(
+                "2013-09-29 16:00:00",
+                "2013-10-06 16:00:00",
+                "2013-10-13 16:00:00",
+                "2013-10-20 16:00:00",
+                "2013-10-27 16:00:00",
+                "2014-09-07 16:00:00"
+            ),
+            '2013-09-28'
+        );
+
+    }
+
+    function testWeekly() {
+
+        $this->parse(
+            'FREQ=WEEKLY;INTERVAL=2;COUNT=10',
+            '2011-10-07 00:00:00',
+            array(
+                '2011-10-07 00:00:00',
+                '2011-10-21 00:00:00',
+                '2011-11-04 00:00:00',
+                '2011-11-18 00:00:00',
+                '2011-12-02 00:00:00',
+                '2011-12-16 00:00:00',
+                '2011-12-30 00:00:00',
+                '2012-01-13 00:00:00',
+                '2012-01-27 00:00:00',
+                '2012-02-10 00:00:00',
+            )
+        );
+
+    }
+
+    function testWeeklyByDay() {
+
+        $this->parse(
+            'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=MO;WKST=SA',
+            '2014-08-01 00:00:00',
+            array(
+                '2014-08-01 00:00:00',
+                '2014-08-04 00:00:00',
+                '2014-08-11 00:00:00',
+                '2014-08-18 00:00:00',
+            )
+        );
+
+    }
+
+    function testWeeklyByDay2() {
+
+        $this->parse(
+            'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU',
+            '2011-10-07 00:00:00',
+            array(
+                '2011-10-07 00:00:00',
+                '2011-10-18 00:00:00',
+                '2011-10-19 00:00:00',
+                '2011-10-21 00:00:00',
+                '2011-11-01 00:00:00',
+                '2011-11-02 00:00:00',
+                '2011-11-04 00:00:00',
+                '2011-11-15 00:00:00',
+                '2011-11-16 00:00:00',
+                '2011-11-18 00:00:00',
+                '2011-11-29 00:00:00',
+                '2011-11-30 00:00:00',
+            )
+        );
+
+    }
+
+    function testWeeklyByDayByHour() {
+
+        $this->parse(
+            'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10',
+            '2011-10-07 08:00:00',
+            array(
+                '2011-10-07 08:00:00',
+                '2011-10-07 09:00:00',
+                '2011-10-07 10:00:00',
+                '2011-10-18 08:00:00',
+                '2011-10-18 09:00:00',
+                '2011-10-18 10:00:00',
+                '2011-10-19 08:00:00',
+                '2011-10-19 09:00:00',
+                '2011-10-19 10:00:00',
+                '2011-10-21 08:00:00',
+                '2011-10-21 09:00:00',
+                '2011-10-21 10:00:00',
+                '2011-11-01 08:00:00',
+                '2011-11-01 09:00:00',
+                '2011-11-01 10:00:00',
+            )
+        );
+
+    }
+
+    function testWeeklyByDaySpecificHour() {
+
+        $this->parse(
+            'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU',
+            '2011-10-07 18:00:00',
+            array(
+                '2011-10-07 18:00:00',
+                '2011-10-18 18:00:00',
+                '2011-10-19 18:00:00',
+                '2011-10-21 18:00:00',
+                '2011-11-01 18:00:00',
+                '2011-11-02 18:00:00',
+                '2011-11-04 18:00:00',
+                '2011-11-15 18:00:00',
+                '2011-11-16 18:00:00',
+                '2011-11-18 18:00:00',
+                '2011-11-29 18:00:00',
+                '2011-11-30 18:00:00',
+            )
+        );
+
+    }
+
+    function testMonthly() {
+
+        $this->parse(
+            'FREQ=MONTHLY;INTERVAL=3;COUNT=5',
+            '2011-12-05 00:00:00',
+            array(
+                 '2011-12-05 00:00:00',
+                 '2012-03-05 00:00:00',
+                 '2012-06-05 00:00:00',
+                 '2012-09-05 00:00:00',
+                 '2012-12-05 00:00:00',
+            )
+        );
+
+    }
+
+    function testMonlthyEndOfMonth() {
+
+        $this->parse(
+            'FREQ=MONTHLY;INTERVAL=2;COUNT=12',
+            '2011-12-31 00:00:00',
+            array(
+                '2011-12-31 00:00:00',
+                '2012-08-31 00:00:00',
+                '2012-10-31 00:00:00',
+                '2012-12-31 00:00:00',
+                '2013-08-31 00:00:00',
+                '2013-10-31 00:00:00',
+                '2013-12-31 00:00:00',
+                '2014-08-31 00:00:00',
+                '2014-10-31 00:00:00',
+                '2014-12-31 00:00:00',
+                '2015-08-31 00:00:00',
+                '2015-10-31 00:00:00',
+            )
+        );
+
+    }
+
+    function testMonthlyByMonthDay() {
+
+        $this->parse(
+            'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7',
+            '2011-01-01 00:00:00',
+            array(
+                '2011-01-01 00:00:00',
+                '2011-01-25 00:00:00',
+                '2011-01-31 00:00:00',
+                '2011-06-01 00:00:00',
+                '2011-06-24 00:00:00',
+                '2011-11-01 00:00:00',
+                '2011-11-24 00:00:00',
+                '2012-04-01 00:00:00',
+                '2012-04-24 00:00:00',
+            )
+        );
+
+    }
+
+    function testMonthlyByDay() {
+
+        $this->parse(
+            'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH',
+            '2011-01-03 00:00:00',
+            array(
+                '2011-01-03 00:00:00',
+                '2011-01-05 00:00:00',
+                '2011-01-10 00:00:00',
+                '2011-01-17 00:00:00',
+                '2011-01-18 00:00:00',
+                '2011-01-20 00:00:00',
+                '2011-01-24 00:00:00',
+                '2011-01-31 00:00:00',
+                '2011-03-02 00:00:00',
+                '2011-03-07 00:00:00',
+                '2011-03-14 00:00:00',
+                '2011-03-17 00:00:00',
+                '2011-03-21 00:00:00',
+                '2011-03-22 00:00:00',
+                '2011-03-28 00:00:00',
+                '2011-05-02 00:00:00',
+            )
+        );
+
+    }
+
+    function testMonthlyByDayByMonthDay() {
+
+        $this->parse(
+            'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1',
+            '2011-08-01 00:00:00',
+            array(
+                '2011-08-01 00:00:00',
+                '2012-10-01 00:00:00',
+                '2013-04-01 00:00:00',
+                '2013-07-01 00:00:00',
+                '2014-09-01 00:00:00',
+                '2014-12-01 00:00:00',
+                '2015-06-01 00:00:00',
+                '2016-02-01 00:00:00',
+                '2016-08-01 00:00:00',
+                '2017-05-01 00:00:00',
+            )
+        );
+
+    }
+
+    function testMonthlyByDayBySetPos() {
+
+        $this->parse(
+            'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1',
+            '2011-01-03 00:00:00',
+            array(
+                '2011-01-03 00:00:00',
+                '2011-01-31 00:00:00',
+                '2011-02-01 00:00:00',
+                '2011-02-28 00:00:00',
+                '2011-03-01 00:00:00',
+                '2011-03-31 00:00:00',
+                '2011-04-01 00:00:00',
+                '2011-04-29 00:00:00',
+                '2011-05-02 00:00:00',
+                '2011-05-31 00:00:00',
+            )
+        );
+
+    }
+
+    function testYearly() {
+
+        $this->parse(
+            'FREQ=YEARLY;COUNT=10;INTERVAL=3',
+            '2011-01-01 00:00:00',
+            array(
+                '2011-01-01 00:00:00',
+                '2014-01-01 00:00:00',
+                '2017-01-01 00:00:00',
+                '2020-01-01 00:00:00',
+                '2023-01-01 00:00:00',
+                '2026-01-01 00:00:00',
+                '2029-01-01 00:00:00',
+                '2032-01-01 00:00:00',
+                '2035-01-01 00:00:00',
+                '2038-01-01 00:00:00',
+            )
+        );
+    }
+
+    function testYearlyLeapYear() {
+
+        $this->parse(
+            'FREQ=YEARLY;COUNT=3',
+            '2012-02-29 00:00:00',
+            array(
+                '2012-02-29 00:00:00',
+                '2016-02-29 00:00:00',
+                '2020-02-29 00:00:00',
+            )
+        );
+    }
+
+    function testYearlyByMonth() {
+
+        $this->parse(
+            'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10',
+            '2011-04-07 00:00:00',
+            array(
+                '2011-04-07 00:00:00',
+                '2011-10-07 00:00:00',
+                '2015-04-07 00:00:00',
+                '2015-10-07 00:00:00',
+                '2019-04-07 00:00:00',
+                '2019-10-07 00:00:00',
+                '2023-04-07 00:00:00',
+                '2023-10-07 00:00:00',
+            )
+        );
+
+    }
+
+    function testYearlyByMonthByDay() {
+
+        $this->parse(
+            'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU',
+            '2011-04-04 00:00:00',
+            array(
+                '2011-04-04 00:00:00',
+                '2011-04-24 00:00:00',
+                '2011-10-03 00:00:00',
+                '2011-10-30 00:00:00',
+                '2016-04-04 00:00:00',
+                '2016-04-24 00:00:00',
+                '2016-10-03 00:00:00',
+                '2016-10-30 00:00:00',
+            )
+        );
+
+    }
+
+    function testFastForward() {
+
+        // The idea is that we're fast-forwarding too far in the future, so
+        // there will be no results left.
+        $this->parse(
+            'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU',
+            '2011-04-04 00:00:00',
+            array(),
+            '2020-05-05 00:00:00'
+        );
+
+    } 
+
+    /**
+     * The bug that was in the
+     * system before would fail on the 5th tuesday of the month, if the 5th
+     * tuesday did not exist.
+     *
+     * A pretty slow test. Had to be marked as 'medium' for phpunit to not die
+     * after 1 second. Would be good to optimize later.
+     *
+     * @medium
+     */
+    function testFifthTuesdayProblem() {
+
+        $this->parse(
+            'FREQ=MONTHLY;INTERVAL=1;UNTIL=20071030T035959Z;BYDAY=5TU',
+            '2007-10-04 14:46:42',
+            array(
+                "2007-10-04 14:46:42",
+            )
+        );
+
+    }
+
+    /**
+     * This bug came from a Fruux customer. This would result in a never-ending
+     * request.
+     */
+    function testFastFowardTooFar() {
+
+        $this->parse(
+            'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1',
+            '2009-04-20 18:00:00',
+            array(
+                '2009-04-20 18:00:00',
+                '2009-04-27 18:00:00',
+                '2009-05-04 18:00:00',
+                '2009-05-11 18:00:00',
+                '2009-05-18 18:00:00',
+                '2009-05-25 18:00:00',
+                '2009-06-01 18:00:00',
+                '2009-06-08 18:00:00',
+                '2009-06-15 18:00:00',
+                '2009-06-22 18:00:00',
+                '2009-06-29 18:00:00',
+            )
+        );
+
+    }
+
+    /**
+     * This also at one point caused an infinite loop. We're keeping the test.
+     */
+    function testYearlyByMonthLoop() {
+
+        $this->parse(
+            'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA',
+            '2012-01-01 15:45:00',
+            array(
+                '2012-02-01 15:45:00',
+            ),
+            '2012-01-29 23:00:00'
+        );
+
+
+    }
+
+    /**
+     * Something, somewhere produced an ics with an interval set to 0. Because
+     * this means we increase the current day (or week, month) by 0, this also
+     * results in an infinite loop.
+     *
+     * @expectedException InvalidArgumentException
+     */
+    function testZeroInterval() {
+
+        $this->parse(
+            'FREQ=YEARLY;INTERVAL=0',
+            '2012-08-24 14:57:00',
+            array(),
+            '2013-01-01 23:00:00'
+        );
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testInvalidFreq() {
+
+        $this->parse(
+            'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z',
+            '2011-10-07',
+            array()
+        );
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testByDayBadOffset() {
+
+        $this->parse(
+            'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=0MO;WKST=SA',
+            '2014-08-01 00:00:00',
+            array()
+        );
+
+    }
+
+    function testUntilBeginHAsTimezone() {
+
+        $this->parse(
+            'FREQ=WEEKLY;UNTIL=20131118T183000',
+            '2013-09-23 18:30:00',
+            array(
+                '2013-09-23 18:30:00',
+                '2013-09-30 18:30:00',
+                '2013-10-07 18:30:00',
+                '2013-10-14 18:30:00',
+                '2013-10-21 18:30:00',
+                '2013-10-28 18:30:00',
+                '2013-11-04 18:30:00',
+                '2013-11-11 18:30:00',
+                '2013-11-18 18:30:00',
+            ),
+            null,
+            'America/New_York'
+        );
+
+    }
+
+    function testUntilBeforeDtStart() {
+
+        $this->parse(
+            'FREQ=DAILY;UNTIL=20140101T000000Z',
+            '2014-08-02 00:15:00',
+            array(
+                '2014-08-02 00:15:00',
+            )
+        );
+
+    }
+
+    function testIgnoredStuff() {
+
+        $this->parse(
+            'FREQ=DAILY;BYSECOND=1;BYMINUTE=1;BYYEARDAY=1;BYWEEKNO=1;COUNT=2',
+            '2014-08-02 00:15:00',
+            array(
+                '2014-08-02 00:15:00',
+                '2014-08-03 00:15:00',
+            )
+        );
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testUnsupportedPart() {
+
+        $this->parse(
+            'FREQ=DAILY;BYWODAN=1',
+            '2014-08-02 00:15:00',
+            array()
+        );
+
+    }
+
+    function testIteratorFunctions() {
+
+        $parser = new RRuleIterator('FREQ=DAILY', new DateTime('2014-08-02 00:00:13'));
+        $parser->next();
+        $this->assertEquals(
+            new DateTime('2014-08-03 00:00:13'),
+            $parser->current()
+        );
+        $this->assertEquals(
+            1,
+            $parser->key()
+        );
+
+        $parser->rewind();
+
+        $this->assertEquals(
+            new DateTime('2014-08-02 00:00:13'),
+            $parser->current()
+        );
+        $this->assertEquals(
+            0,
+            $parser->key()
+        );
+
+    }
+
+    function parse($rule, $start, $expected, $fastForward = null, $tz = 'UTC') {
+
+        $dt = new DateTime($start, new DateTimeZone($tz));
+        $parser = new RRuleIterator($rule, $dt);
+
+        if ($fastForward) {
+            $parser->fastForward(new DateTime($fastForward));
+        }
+
+        $result = array();
+        while($parser->valid()) {
+
+            $item = $parser->current();
+            $result[] = $item->format('Y-m-d H:i:s');
+
+            if ($parser->isInfinite() && count($result) >= count($expected)) {
+                break;
+            }
+            $parser->next();
+
+        }
+
+        $this->assertEquals(
+            $expected,
+            $result
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/RecurrenceIterator/UntilRespectsTimezoneTest.ics	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,39 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+X-WR-TIMEZONE:America/New_York
+PRODID:-//www.churchcommunitybuilder.com//Church Community Builder//EN
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-WR-CALNAME:Test Event
+BEGIN:VTIMEZONE
+TZID:America/New_York
+X-LIC-LOCATION:America/New_York
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19700308T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19701101T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:10621-1440@ccbchurch.com
+DTSTART;TZID=America/New_York:20130923T183000
+DTEND;TZID=America/New_York:20130923T203000
+DTSTAMP:20131216T170211
+RRULE:FREQ=WEEKLY;UNTIL=20131118T183000
+CREATED:20130423T161111
+DESCRIPTION:Test Event ending November 11, 2013
+LAST-MODIFIED:20131126T163428
+SEQUENCE:1387231331
+SUMMARY:Test
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/SlashRTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,20 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * This issue was pointed out in Issue 55. \r should be stripped completely 
+ * when encoding property values.
+ */
+class SlashRTest extends \PHPUnit_Framework_TestCase {
+
+    function testEncode() {
+
+        $vcal = new Component\VCalendar();
+        $prop = $vcal->add('test', "abc\r\ndef");
+        $this->assertEquals("TEST:abc\\ndef\r\n", $prop->serialize());
+
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Splitter/ICalendarTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,325 @@
+<?php
+
+namespace Sabre\VObject\Splitter;
+
+use Sabre\VObject;
+
+class ICalendarTest extends \PHPUnit_Framework_TestCase {
+
+    protected $version;
+
+    function setUp() {
+        $this->version = VObject\Version::VERSION;
+    }
+
+    function createStream($data) {
+
+        $stream = fopen('php://memory','r+');
+        fwrite($stream, $data);
+        rewind($stream);
+        return $stream;
+
+    }
+
+    function testICalendarImportValidEvent() {
+
+        $data = <<<EOT
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foo
+DTSTAMP:20140122T233226Z
+DTSTART:20140101T070000Z
+END:VEVENT
+END:VCALENDAR
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new ICalendar($tempFile);
+
+        $return = "";
+        while($object=$objects->getNext()) {
+            $return .= $object->serialize();
+        }
+        $this->assertEquals(array(), VObject\Reader::read($return)->validate());
+    }
+
+    /**
+     * @expectedException Sabre\VObject\ParseException
+     */
+    function testICalendarImportWrongType() {
+
+        $data = <<<EOT
+BEGIN:VCARD
+UID:foo1
+END:VCARD
+BEGIN:VCARD
+UID:foo2
+END:VCARD
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new ICalendar($tempFile);
+
+    }
+
+    function testICalendarImportEndOfData() {
+        $data = <<<EOT
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foo
+DTSTAMP:20140122T233226Z
+END:VEVENT
+END:VCALENDAR
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new ICalendar($tempFile);
+
+        $return = "";
+        while($object=$objects->getNext()) {
+            $return .= $object->serialize();
+        }
+        $this->assertNull($object=$objects->getNext());
+    }
+
+    /**
+     * @expectedException Sabre\VObject\ParseException
+     */
+    function testICalendarImportInvalidEvent() {
+        $data = <<<EOT
+EOT;
+        $tempFile = $this->createStream($data);
+        $objects = new ICalendar($tempFile);
+
+    }
+
+    function testICalendarImportMultipleValidEvents() {
+
+        $event[] = <<<EOT
+BEGIN:VEVENT
+UID:foo1
+DTSTAMP:20140122T233226Z
+DTSTART:20140101T050000Z
+END:VEVENT
+EOT;
+
+$event[] = <<<EOT
+BEGIN:VEVENT
+UID:foo2
+DTSTAMP:20140122T233226Z
+DTSTART:20140101T060000Z
+END:VEVENT
+EOT;
+
+        $data = <<<EOT
+BEGIN:VCALENDAR
+$event[0]
+$event[1]
+END:VCALENDAR
+
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new ICalendar($tempFile);
+
+        $return = "";
+        $i = 0;
+        while($object=$objects->getNext()) {
+
+            $expected = <<<EOT
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $this->version//EN
+CALSCALE:GREGORIAN
+$event[$i]
+END:VCALENDAR
+
+EOT;
+
+            $return .= $object->serialize();
+            $expected = str_replace("\n", "\r\n", $expected);
+            $this->assertEquals($expected, $object->serialize());
+            $i++;
+        }
+        $this->assertEquals(array(), VObject\Reader::read($return)->validate());
+    }
+
+    function testICalendarImportEventWithoutUID() {
+
+        $data = <<<EOT
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $this->version//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+DTSTART:20140101T040000Z
+DTSTAMP:20140122T233226Z
+END:VEVENT
+END:VCALENDAR
+
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new ICalendar($tempFile);
+
+        $return = "";
+        while($object=$objects->getNext()) {
+            $return .= $object->serialize();
+        }
+
+        $messages = VObject\Reader::read($return)->validate();
+
+        if ($messages) {
+            $messages = array_map(
+                function($item) { return $item['message']; },
+                $messages
+            );
+            $this->fail('Validation errors: ' . implode("\n", $messages));
+        } else {
+            $this->assertEquals(array(), $messages);
+        }
+    }
+
+    function testICalendarImportMultipleVTIMEZONESAndMultipleValidEvents() {
+
+        $timezones = <<<EOT
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+DTSTART:19810329T020000
+TZNAME:MESZ
+TZOFFSETTO:+0200
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+DTSTART:19961027T030000
+TZNAME:MEZ
+TZOFFSETTO:+0100
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VTIMEZONE
+TZID:Europe/London
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+DTSTART:19810329T010000
+TZNAME:GMT+01:00
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0100
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+DTSTART:19961027T020000
+TZNAME:GMT
+TZOFFSETTO:+0000
+END:STANDARD
+END:VTIMEZONE
+EOT;
+
+        $event[] = <<<EOT
+BEGIN:VEVENT
+UID:foo1
+DTSTAMP:20140122T232710Z
+DTSTART:20140101T010000Z
+END:VEVENT
+EOT;
+
+        $event[] = <<<EOT
+BEGIN:VEVENT
+UID:foo2
+DTSTAMP:20140122T232710Z
+DTSTART:20140101T020000Z
+END:VEVENT
+EOT;
+
+        $event[] = <<<EOT
+BEGIN:VEVENT
+UID:foo3
+DTSTAMP:20140122T232710Z
+DTSTART:20140101T030000Z
+END:VEVENT
+EOT;
+
+        $data = <<<EOT
+BEGIN:VCALENDAR
+$timezones
+$event[0]
+$event[1]
+$event[2]
+END:VCALENDAR
+
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new ICalendar($tempFile);
+
+        $return = "";
+        $i = 0;
+        while($object=$objects->getNext()) {
+
+            $expected = <<<EOT
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $this->version//EN
+CALSCALE:GREGORIAN
+$timezones
+$event[$i]
+END:VCALENDAR
+
+EOT;
+            $expected = str_replace("\n", "\r\n", $expected);
+
+            $this->assertEquals($expected, $object->serialize());
+            $return .= $object->serialize();
+            $i++;
+
+        }
+
+        $this->assertEquals(array(), VObject\Reader::read($return)->validate());
+    }
+
+    function testICalendarImportWithOutVTIMEZONES() {
+
+        $data = <<<EOT
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//Mac OS X 10.8//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20120605T072109Z
+UID:D6716295-C10F-4B20-82F9-E1A3026C7DCF
+DTEND;VALUE=DATE:20120717
+TRANSP:TRANSPARENT
+SUMMARY:Start Vorbereitung
+DTSTART;VALUE=DATE:20120716
+DTSTAMP:20120605T072115Z
+SEQUENCE:2
+BEGIN:VALARM
+X-WR-ALARMUID:A99EDA6A-35EB-4446-B8BC-CDA3C60C627D
+UID:A99EDA6A-35EB-4446-B8BC-CDA3C60C627D
+TRIGGER:-PT15H
+X-APPLE-DEFAULT-ALARM:TRUE
+ATTACH;VALUE=URI:Basso
+ACTION:AUDIO
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new ICalendar($tempFile);
+
+        $return = "";
+        while($object=$objects->getNext()) {
+            $return .= $object->serialize();
+        }
+
+        $messages = VObject\Reader::read($return)->validate();
+        $this->assertEquals(array(), $messages);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/Splitter/VCardTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,195 @@
+<?php
+
+namespace Sabre\VObject\Splitter;
+
+use Sabre\VObject;
+
+class VCardTest extends \PHPUnit_Framework_TestCase {
+
+    function createStream($data) {
+
+        $stream = fopen('php://memory','r+');
+        fwrite($stream, $data);
+        rewind($stream);
+        return $stream;
+
+    }
+
+    function testVCardImportValidVCard() {
+        $data = <<<EOT
+BEGIN:VCARD
+UID:foo
+END:VCARD
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new VCard($tempFile);
+
+        $count = 0;
+        while($objects->getNext()) {
+            $count++;
+        }
+        $this->assertEquals(1, $count);
+
+    }
+
+    /**
+     * @expectedException Sabre\VObject\ParseException
+     */
+    function testVCardImportWrongType() {
+        $event[] = <<<EOT
+BEGIN:VEVENT
+UID:foo1
+DTSTAMP:20140122T233226Z
+DTSTART:20140101T050000Z
+END:VEVENT
+EOT;
+
+$event[] = <<<EOT
+BEGIN:VEVENT
+UID:foo2
+DTSTAMP:20140122T233226Z
+DTSTART:20140101T060000Z
+END:VEVENT
+EOT;
+
+        $data = <<<EOT
+BEGIN:VCALENDAR
+$event[0]
+$event[1]
+END:VCALENDAR
+
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $splitter = new VCard($tempFile);
+
+        while($object=$splitter->getNext()) {
+        }
+
+    }
+
+    function testVCardImportValidVCardsWithCategories() {
+        $data = <<<EOT
+BEGIN:VCARD
+UID:card-in-foo1-and-foo2
+CATEGORIES:foo1,foo2
+END:VCARD
+BEGIN:VCARD
+UID:card-in-foo1
+CATEGORIES:foo1
+END:VCARD
+BEGIN:VCARD
+UID:card-in-foo3
+CATEGORIES:foo3
+END:VCARD
+BEGIN:VCARD
+UID:card-in-foo1-and-foo3
+CATEGORIES:foo1\,foo3
+END:VCARD
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $splitter = new VCard($tempFile);
+
+        $count = 0;
+        while($object=$splitter->getNext()) {
+            $count++;
+        }
+        $this->assertEquals(4, $count);
+
+    }
+
+    function testVCardImportEndOfData() {
+        $data = <<<EOT
+BEGIN:VCARD
+UID:foo
+END:VCARD
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new VCard($tempFile);
+        $object=$objects->getNext();
+
+        $this->assertNull($objects->getNext());
+
+
+    }
+
+    /**
+     * @expectedException \Sabre\VObject\ParseException
+     */
+    function testVCardImportCheckInvalidArgumentException() {
+        $data = <<<EOT
+BEGIN:FOO
+END:FOO
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new VCard($tempFile);
+        while($objects->getNext()) { }
+
+    }
+
+    function testVCardImportMultipleValidVCards() {
+        $data = <<<EOT
+BEGIN:VCARD
+UID:foo
+END:VCARD
+BEGIN:VCARD
+UID:foo
+END:VCARD
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new VCard($tempFile);
+
+        $count = 0;
+        while($objects->getNext()) {
+            $count++;
+        }
+        $this->assertEquals(2, $count);
+
+    }
+
+    function testImportMultipleSeparatedWithNewLines() {
+        $data = <<<EOT
+BEGIN:VCARD
+UID:foo
+END:VCARD
+
+
+BEGIN:VCARD
+UID:foo
+END:VCARD
+
+
+EOT;
+        $tempFile = $this->createStream($data);
+        $objects = new VCard($tempFile);
+
+        $count = 0;
+        while ($objects->getNext()) {
+            $count++;
+        }
+        $this->assertEquals(2, $count);
+    }
+
+    function testVCardImportVCardWithoutUID() {
+        $data = <<<EOT
+BEGIN:VCARD
+END:VCARD
+EOT;
+        $tempFile = $this->createStream($data);
+
+        $objects = new VCard($tempFile);
+
+        $count = 0;
+        while($objects->getNext()) {
+            $count++;
+        }
+
+        $this->assertEquals(1, $count);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/StringUtilTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,55 @@
+<?php
+
+namespace Sabre\VObject;
+
+class StringUtilTest extends \PHPUnit_Framework_TestCase {
+
+    function testNonUTF8() {
+
+        $string = StringUtil::isUTF8(chr(0xbf));
+
+        $this->assertEquals(false, $string);
+
+    }
+
+    function testIsUTF8() {
+
+        $string = StringUtil::isUTF8('I 💚 SabreDAV');
+
+        $this->assertEquals(true, $string);
+
+    }
+
+    function testUTF8ControlChar() {
+
+        $string = StringUtil::isUTF8(chr(0x00));
+
+        $this->assertEquals(false, $string);
+
+    }
+
+    function testConvertToUTF8nonUTF8() {
+
+        $string = StringUtil::convertToUTF8(chr(0xbf));
+
+        $this->assertEquals(utf8_encode(chr(0xbf)), $string);
+
+    }
+
+    function testConvertToUTF8IsUTF8() {
+
+        $string = StringUtil::convertToUTF8('I 💚 SabreDAV');
+
+        $this->assertEquals('I 💚 SabreDAV', $string);
+
+    }
+
+    function testConvertToUTF8ControlChar() {
+
+        $string = StringUtil::convertToUTF8(chr(0x00));
+
+        $this->assertEquals('', $string);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/TestCase.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,50 @@
+<?php
+
+namespace Sabre\VObject;
+
+class TestCase extends \PHPUnit_Framework_TestCase {
+
+    /**
+     * This method tests wether two vcards or icalendar objects are
+     * semantically identical.
+     *
+     * It supports objects being supplied as strings, streams or
+     * Sabre\VObject\Component instances.
+     *
+     * PRODID is removed from both objects as this is often variable.
+     *
+     * @param resource|string|Component $expected
+     * @param resource|string|Component $actual
+     * @param string $message
+     */
+    function assertVObjEquals($expected, $actual, $message = '') {
+
+        $self = $this;
+        $getObj = function($input) use ($self) {
+
+            if (is_resource($input)) {
+                $input = stream_get_contents($input);
+            }
+            if (is_string($input)) {
+                $input = Reader::read($input);
+            }
+            if (!$input instanceof Component) {
+                $this->fail('Input must be a string, stream or VObject component');
+            }
+            unset($input->PRODID);
+            return $input;
+
+        };
+
+        $expected = $getObj($expected);
+        $actual = $getObj($actual);
+
+        $this->assertEquals(
+            $expected->serialize(),
+            $actual->serialize(),
+            $message
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/TimeZoneUtilTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,360 @@
+<?php
+
+namespace Sabre\VObject;
+
+class TimezoneUtilTest extends \PHPUnit_Framework_TestCase {
+
+    function setUp() {
+
+        // clearning the tz cache
+        TimezoneUtil::$map = null;
+
+    }
+
+    /**
+     * @dataProvider getMapping
+     */
+    function testCorrectTZ($timezoneName) {
+
+        $tz = new \DateTimeZone($timezoneName);
+        $this->assertInstanceOf('DateTimeZone', $tz);
+
+    }
+
+    function getMapping() {
+
+        TimeZoneUtil::loadTzMaps();
+
+        // PHPUNit requires an array of arrays
+        return array_map(
+            function($value) {
+                return array($value);
+            },
+            TimeZoneUtil::$map
+        );
+
+    }
+
+    function testExchangeMap() {
+
+        $vobj = <<<HI
+BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:foo
+X-MICROSOFT-CDO-TZID:2
+BEGIN:STANDARD
+DTSTART:16010101T030000
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:16010101T020000
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20120416T092149Z
+DTSTART;TZID="foo":20120418T1
+ 00000
+SUMMARY:Begin Unterhaltsreinigung
+UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
+ 0100000008FECD2E607780649BE5A4C9EE6418CBC
+ 000
+END:VEVENT
+END:VCALENDAR
+HI;
+
+        $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
+        $ex = new \DateTimeZone('Europe/Lisbon');
+
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    function testWetherMicrosoftIsStillInsane() {
+
+        $vobj = <<<HI
+BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:(GMT+01.00) Sarajevo/Warsaw/Zagreb
+X-MICROSOFT-CDO-TZID:2
+BEGIN:STANDARD
+DTSTART:16010101T030000
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+HI;
+
+        $tz = TimeZoneUtil::getTimeZone('(GMT+01.00) Sarajevo/Warsaw/Zagreb', Reader::read($vobj));
+        $ex = new \DateTimeZone('Europe/Sarajevo');
+
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    function testUnknownExchangeId() {
+
+        $vobj = <<<HI
+BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:foo
+X-MICROSOFT-CDO-TZID:2000
+BEGIN:STANDARD
+DTSTART:16010101T030000
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:16010101T020000
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20120416T092149Z
+DTSTART;TZID="foo":20120418T1
+ 00000
+SUMMARY:Begin Unterhaltsreinigung
+UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
+ 0100000008FECD2E607780649BE5A4C9EE6418CBC
+DTEND;TZID="Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb":20120418T103
+ 000
+END:VEVENT
+END:VCALENDAR
+HI;
+
+        $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
+        $ex = new \DateTimeZone(date_default_timezone_get());
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    function testWindowsTimeZone() {
+
+        $tz = TimeZoneUtil::getTimeZone('Eastern Standard Time');
+        $ex = new \DateTimeZone('America/New_York');
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    /**
+     * @dataProvider getPHPTimeZoneIdentifiers
+     */
+    function testTimeZoneIdentifiers($tzid) {
+
+        $tz = TimeZoneUtil::getTimeZone($tzid);
+        $ex = new \DateTimeZone($tzid);
+
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    /**
+     * @dataProvider getPHPTimeZoneBCIdentifiers
+     */
+    function testTimeZoneBCIdentifiers($tzid) {
+
+        $tz = TimeZoneUtil::getTimeZone($tzid);
+        $ex = new \DateTimeZone($tzid);
+
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    function getPHPTimeZoneIdentifiers() {
+
+        // PHPUNit requires an array of arrays
+        return array_map(
+            function($value) {
+                return array($value);
+            },
+            \DateTimeZone::listIdentifiers()
+        );
+
+    }
+
+    function getPHPTimeZoneBCIdentifiers() {
+
+        // PHPUNit requires an array of arrays
+        return array_map(
+            function($value) {
+                return array($value);
+            },
+            TimeZoneUtil::getIdentifiersBC()
+        );
+
+    }
+
+    function testTimezoneOffset() {
+
+        $tz = TimeZoneUtil::getTimeZone('GMT-0400', null, true);
+
+        if (version_compare(PHP_VERSION, '5.5.10', '>=') && !defined('HHVM_VERSION')) {
+            $ex = new \DateTimeZone('-04:00');
+        } else {
+            $ex = new \DateTimeZone('Etc/GMT-4');
+        }
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testTimezoneFail() {
+
+        $tz = TimeZoneUtil::getTimeZone('FooBar', null, true);
+
+    }
+
+    function testFallBack() {
+
+        $vobj = <<<HI
+BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:foo
+BEGIN:STANDARD
+DTSTART:16010101T030000
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:16010101T020000
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20120416T092149Z
+DTSTART;TZID="foo":20120418T1
+ 00000
+SUMMARY:Begin Unterhaltsreinigung
+UID:040000008200E00074C5B7101A82E0080000000010DA091DC31BCD01000000000000000
+ 0100000008FECD2E607780649BE5A4C9EE6418CBC
+ 000
+END:VEVENT
+END:VCALENDAR
+HI;
+
+        $tz = TimeZoneUtil::getTimeZone('foo', Reader::read($vobj));
+        $ex = new \DateTimeZone(date_default_timezone_get());
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    function testLjubljanaBug() {
+
+        $vobj = <<<HI
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Ximian//NONSGML Evolution Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana
+X-LIC-LOCATION:Europe/Ljubljana
+BEGIN:STANDARD
+TZNAME:CET
+DTSTART:19701028T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+DTSTART:19700325T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:foo
+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana:
+ 20121003T080000
+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana:
+ 20121003T083000
+TRANSP:OPAQUE
+SEQUENCE:2
+SUMMARY:testing
+CREATED:20121002T172613Z
+LAST-MODIFIED:20121002T172613Z
+END:VEVENT
+END:VCALENDAR
+
+HI;
+
+
+        $tz = TimeZoneUtil::getTimeZone('/freeassociation.sourceforge.net/Tzfile/Europe/Ljubljana', Reader::read($vobj));
+        $ex = new \DateTimeZone('Europe/Ljubljana');
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+    function testWeirdSystemVLICs() {
+
+$vobj = <<<HI
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Ximian//NONSGML Evolution Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT
+X-LIC-LOCATION:SystemV/EST5EDT
+BEGIN:STANDARD
+TZNAME:EST
+DTSTART:19701104T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:EDT
+DTSTART:19700311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:20121026T021107Z-6301-1000-1-0@chAir
+DTSTAMP:20120905T172126Z
+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT:
+ 20121026T153000
+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT:
+ 20121026T160000
+TRANSP:OPAQUE
+SEQUENCE:5
+SUMMARY:pick up Ibby
+CLASS:PUBLIC
+CREATED:20121026T021108Z
+LAST-MODIFIED:20121026T021118Z
+X-EVOLUTION-MOVE-CALENDAR:1
+END:VEVENT
+END:VCALENDAR
+HI;
+
+        $tz = TimeZoneUtil::getTimeZone('/freeassociation.sourceforge.net/Tzfile/SystemV/EST5EDT', Reader::read($vobj), true);
+        $ex = new \DateTimeZone('America/New_York');
+        $this->assertEquals($ex->getName(), $tz->getName());
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/UUIDUtilTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,37 @@
+<?php
+
+namespace Sabre\VObject;
+
+class UUIDUtilTest extends \PHPUnit_Framework_TestCase {
+
+    function testValidateUUID() {
+
+        $this->assertTrue(
+            UUIDUtil::validateUUID('11111111-2222-3333-4444-555555555555')
+        );
+        $this->assertFalse(
+            UUIDUtil::validateUUID(' 11111111-2222-3333-4444-555555555555')
+        );
+        $this->assertTrue(
+            UUIDUtil::validateUUID('ffffffff-2222-3333-4444-555555555555')
+        );
+        $this->assertFalse(
+            UUIDUtil::validateUUID('fffffffg-2222-3333-4444-555555555555')
+        );
+
+    }
+
+    /**
+     * @depends testValidateUUID
+     */
+    function testGetUUID() {
+
+        $this->assertTrue(
+            UUIDUtil::validateUUID(
+                UUIDUtil::getUUID()
+            )
+        );
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/VCard21Test.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,52 @@
+<?php
+
+namespace Sabre\VObject;
+
+/**
+ * Assorted vcard 2.1 tests.
+ */
+class VCard21Test extends \PHPUnit_Framework_TestCase {
+
+    function testPropertyWithNoName() {
+
+        $input = <<<VCF
+BEGIN:VCARD\r
+VERSION:2.1\r
+EMAIL;HOME;WORK:evert@fruux.com\r
+END:VCARD\r
+
+VCF;
+
+        $vobj = Reader::read($input);
+        $output = $vobj->serialize($input);
+
+        $this->assertEquals($input, $output);
+
+    }
+
+    function testPropertyPadValueCount() {
+
+        $input = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+N:Foo
+END:VCARD
+
+VCF;
+
+        $vobj = Reader::read($input);
+        $output = $vobj->serialize($input);
+
+        $expected = <<<VCF
+BEGIN:VCARD\r
+VERSION:2.1\r
+N:Foo;;;;\r
+END:VCARD\r
+
+VCF;
+
+
+        $this->assertEquals($expected, $output);
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/VCardConverterTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,531 @@
+<?php
+
+namespace Sabre\VObject;
+
+class VCardConverterTest extends TestCase {
+
+    function testConvert30to40() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:3.0
+PRODID:foo
+FN;CHARSET=UTF-8:Steve
+TEL;TYPE=PREF,HOME:+1 555 666 777
+ITEM1.TEL:+1 444 555 666
+ITEM1.X-ABLABEL:CustomLabel
+PHOTO;ENCODING=b;TYPE=JPEG,HOME:Zm9v
+PHOTO;ENCODING=b;TYPE=GIF:Zm9v
+PHOTO;X-PARAM=FOO;ENCODING=b;TYPE=PNG:Zm9v
+PHOTO;VALUE=URI:http://example.org/foo.png
+X-ABShowAs:COMPANY
+END:VCARD
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:4.0
+FN:Steve
+TEL;PREF=1;TYPE=HOME:+1 555 666 777
+ITEM1.TEL:+1 444 555 666
+ITEM1.X-ABLABEL:CustomLabel
+PHOTO;TYPE=HOME:data:image/jpeg;base64,Zm9v
+PHOTO:data:image/gif;base64,Zm9v
+PHOTO;X-PARAM=FOO:data:image/png;base64,Zm9v
+PHOTO:http://example.org/foo.png
+KIND:ORG
+END:VCARD
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testConvert40to40() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:4.0
+FN:Steve
+TEL;PREF=1;TYPE=HOME:+1 555 666 777
+PHOTO:data:image/jpeg;base64,Zm9v
+PHOTO:data:image/gif;base64,Zm9v
+PHOTO;X-PARAM=FOO:data:image/png;base64,Zm9v
+PHOTO:http://example.org/foo.png
+END:VCARD
+
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:4.0
+FN:Steve
+TEL;PREF=1;TYPE=HOME:+1 555 666 777
+PHOTO:data:image/jpeg;base64,Zm9v
+PHOTO:data:image/gif;base64,Zm9v
+PHOTO;X-PARAM=FOO:data:image/png;base64,Zm9v
+PHOTO:http://example.org/foo.png
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testConvert21to40() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:2.1
+N:Family;Johnson
+FN:Johnson Family
+TEL;HOME;VOICE:555-12345-345
+ADR;HOME:;;100 Street Lane;Saubel Beach;ON;H0H0H0
+LABEL;HOME;ENCODING=QUOTED-PRINTABLE:100 Street Lane=0D=0ASaubel Beach,
+ ON H0H0H0
+REV:20110731T040251Z
+UID:12345678
+END:VCARD
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:4.0
+N:Family;Johnson;;;
+FN:Johnson Family
+TEL;TYPE=HOME,VOICE:555-12345-345
+ADR;TYPE=HOME:;;100 Street Lane;Saubel Beach;ON;H0H0H0;
+REV:20110731T040251Z
+UID:12345678
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testConvert30to30() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:3.0
+PRODID:foo
+FN;CHARSET=UTF-8:Steve
+TEL;TYPE=PREF,HOME:+1 555 666 777
+PHOTO;ENCODING=b;TYPE=JPEG:Zm9v
+PHOTO;ENCODING=b;TYPE=GIF:Zm9v
+PHOTO;X-PARAM=FOO;ENCODING=b;TYPE=PNG:Zm9v
+PHOTO;VALUE=URI:http://example.org/foo.png
+END:VCARD
+
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:3.0
+PRODID:foo
+FN;CHARSET=UTF-8:Steve
+TEL;TYPE=PREF,HOME:+1 555 666 777
+PHOTO;ENCODING=b;TYPE=JPEG:Zm9v
+PHOTO;ENCODING=b;TYPE=GIF:Zm9v
+PHOTO;X-PARAM=FOO;ENCODING=b;TYPE=PNG:Zm9v
+PHOTO;VALUE=URI:http://example.org/foo.png
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD30);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testConvert40to30() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:4.0
+PRODID:foo
+FN:Steve
+TEL;PREF=1;TYPE=HOME:+1 555 666 777
+PHOTO:data:image/jpeg;base64,Zm9v
+PHOTO:data:image/gif,foo
+PHOTO;X-PARAM=FOO:data:image/png;base64,Zm9v
+PHOTO:http://example.org/foo.png
+KIND:ORG
+END:VCARD
+
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:3.0
+FN:Steve
+TEL;TYPE=PREF,HOME:+1 555 666 777
+PHOTO;ENCODING=b;TYPE=JPEG:Zm9v
+PHOTO;ENCODING=b;TYPE=GIF:Zm9v
+PHOTO;ENCODING=b;TYPE=PNG;X-PARAM=FOO:Zm9v
+PHOTO;VALUE=URI:http://example.org/foo.png
+X-ABSHOWAS:COMPANY
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD30);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testConvertGroupCard() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:3.0
+PRODID:foo
+X-ADDRESSBOOKSERVER-KIND:GROUP
+END:VCARD
+
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:4.0
+KIND:GROUP
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+        $input = $output;
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:3.0
+X-ADDRESSBOOKSERVER-KIND:GROUP
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD30);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testBDAYConversion() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:3.0
+PRODID:foo
+BDAY;X-APPLE-OMIT-YEAR=1604:1604-04-16
+END:VCARD
+
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:4.0
+BDAY:--04-16
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+        $input = $output;
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:3.0
+BDAY;X-APPLE-OMIT-YEAR=1604:1604-04-16
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD30);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testUnknownSourceVCardVersion() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:4.2
+PRODID:foo
+FN;CHARSET=UTF-8:Steve
+TEL;TYPE=PREF,HOME:+1 555 666 777
+ITEM1.TEL:+1 444 555 666
+ITEM1.X-ABLABEL:CustomLabel
+PHOTO;ENCODING=b;TYPE=JPEG,HOME:Zm9v
+PHOTO;ENCODING=b;TYPE=GIF:Zm9v
+PHOTO;X-PARAM=FOO;ENCODING=b;TYPE=PNG:Zm9v
+PHOTO;VALUE=URI:http://example.org/foo.png
+X-ABShowAs:COMPANY
+END:VCARD
+
+IN;
+
+        $vcard = Reader::read($input);
+        $vcard->convert(Document::VCARD40);
+
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testUnknownTargetVCardVersion() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:3.0
+PRODID:foo
+END:VCARD
+
+IN;
+
+        $vcard = Reader::read($input);
+        $vcard->convert(Document::VCARD21);
+
+    }
+
+    function testConvertIndividualCard() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:4.0
+PRODID:foo
+KIND:INDIVIDUAL
+END:VCARD
+
+IN;
+
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:3.0
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD30);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+        $input = $output;
+        $output = <<<OUT
+BEGIN:VCARD
+VERSION:4.0
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testAnniversary() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:4.0
+ITEM1.ANNIVERSARY:20081210
+END:VCARD
+
+IN;
+
+        $output = <<<'OUT'
+BEGIN:VCARD
+VERSION:3.0
+ITEM1.X-ABDATE;VALUE=DATE-AND-OR-TIME:20081210
+ITEM1.X-ABLABEL:_$!<Anniversary>!$_
+ITEM1.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20081210
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD30);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+        // Swapping input and output
+        list(
+            $input,
+            $output
+        ) = array(
+            $output,
+            $input
+        );
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testMultipleAnniversaries() {
+
+        $input = <<<IN
+BEGIN:VCARD
+VERSION:4.0
+ITEM1.ANNIVERSARY:20081210
+ITEM2.ANNIVERSARY:20091210
+ITEM3.ANNIVERSARY:20101210
+END:VCARD
+
+IN;
+
+        $output = <<<'OUT'
+BEGIN:VCARD
+VERSION:3.0
+ITEM1.X-ABDATE;VALUE=DATE-AND-OR-TIME:20081210
+ITEM1.X-ABLABEL:_$!<Anniversary>!$_
+ITEM1.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20081210
+ITEM2.X-ABDATE;VALUE=DATE-AND-OR-TIME:20091210
+ITEM2.X-ABLABEL:_$!<Anniversary>!$_
+ITEM2.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20091210
+ITEM3.X-ABDATE;VALUE=DATE-AND-OR-TIME:20101210
+ITEM3.X-ABLABEL:_$!<Anniversary>!$_
+ITEM3.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20101210
+END:VCARD
+
+OUT;
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD30);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+        // Swapping input and output
+        list(
+            $input,
+            $output
+        ) = array(
+            $output,
+            $input
+        );
+
+        $vcard = Reader::read($input);
+        $vcard = $vcard->convert(Document::VCARD40);
+
+        $this->assertVObjEquals(
+            $output,
+            $vcard
+        );
+
+    }
+
+    function testNoLabel() {
+
+      $input = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+UID:foo
+N:Doe;John;;;
+FN:John Doe
+item1.X-ABDATE;type=pref:2008-12-11
+END:VCARD
+
+VCF;
+
+      $vcard = Reader::read($input);
+
+      $this->assertInstanceOf('Sabre\\VObject\\Component\\VCard', $vcard);
+      $vcard = $vcard->convert(Document::VCARD40);
+      $vcard = $vcard->serialize();
+
+      $converted = Reader::read($vcard);
+      $converted->validate();
+
+      $version = Version::VERSION;
+
+      $expected = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+UID:foo
+N:Doe;John;;;
+FN:John Doe
+ITEM1.X-ABDATE;PREF=1:2008-12-11
+END:VCARD
+
+VCF;
+
+      $this->assertEquals($expected, str_replace("\r","", $vcard));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/VersionTest.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,14 @@
+<?php
+
+namespace Sabre\VObject;
+
+class VersionTest extends \PHPUnit_Framework_TestCase {
+
+    function testString() {
+
+        $v = Version::VERSION;
+        $this->assertEquals(-1, version_compare('2.0.0',$v));
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/issue153.vcf	Sat Jan 13 09:06:10 2018 -0500
@@ -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
+  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
+  zbVbk4/OrNpefLsnyyg5UUAf/9k=
+END:VCARD
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/VObject/issue64.vcf	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,351 @@
+BEGIN:VCARD
+VERSION:2.1
+PHOTO;ENCODING=BASE64;JPEG:
+  /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
+  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
+  zbVbk4/OrNpefLsnyyg5UUAf/9k=
+
+END:VCARD
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/bootstrap.php	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,25 @@
+<?php
+
+date_default_timezone_set('UTC');
+
+$try = array(
+    __DIR__ . '/../vendor/autoload.php',
+    __DIR__ . '/../../../autoload.php',
+);
+
+foreach($try as $path) {
+    if (file_exists($path)) {
+        $autoLoader = include $path;
+        break;
+    }
+}
+
+$autoLoader->addPsr4('Sabre\\VObject\\',__DIR__ . '/VObject');
+
+if (!defined('SABRE_TEMPDIR')) {
+  define('SABRE_TEMPDIR', __DIR__ . '/temp/');
+}
+
+if (!file_exists(SABRE_TEMPDIR)) {
+  mkdir(SABRE_TEMPDIR);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/phpcs/ruleset.xml	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<ruleset name="sabre.php">
+    <description>sabre.io codesniffer ruleset</description>
+
+     <!-- Include the whole PSR-1 standard -->
+     <rule ref="PSR1" />
+
+     <!-- All PHP files MUST use the Unix LF (linefeed) line ending. -->
+     <rule ref="Generic.Files.LineEndings">
+      <properties>
+       <property name="eolChar" value="\n"/>
+      </properties>
+     </rule>
+
+     <!-- The closing ?> tag MUST be omitted from files containing only PHP. -->
+     <rule ref="Zend.Files.ClosingTag"/>
+
+     <!-- There MUST NOT be trailing whitespace at the end of non-blank lines. -->
+     <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
+      <properties>
+       <property name="ignoreBlankLines" value="true"/>
+       </properties>
+   </rule>
+
+   <!-- There MUST NOT be more than one statement per line. -->
+   <rule ref="Generic.Formatting.DisallowMultipleStatements"/>
+
+   <rule ref="Generic.WhiteSpace.ScopeIndent">
+      <properties>
+       <property name="ignoreIndentationTokens" type="array" value="T_COMMENT,T_DOC_COMMENT"/>
+      </properties>
+   </rule>
+   <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
+
+   <!-- PHP keywords MUST be in lower case. -->
+   <rule ref="Generic.PHP.LowerCaseKeyword"/>
+
+   <!-- The PHP constants true, false, and null MUST be in lower case. -->
+   <rule ref="Generic.PHP.LowerCaseConstant"/>
+
+   <!-- <rule ref="Squiz.Scope.MethodScope"/> -->
+   <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
+
+   <!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. -->
+   <!--
+   <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
+    <properties>
+     <property name="equalsSpacing" value="1"/>
+    </properties>
+   </rule>
+   <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint">
+    <severity>0</severity>
+   </rule>
+    -->
+   <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/>
+
+</ruleset>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/sabre/vobject/tests/phpunit.xml	Sat Jan 13 09:06:10 2018 -0500
@@ -0,0 +1,21 @@
+<phpunit
+  colors="true"
+  bootstrap="bootstrap.php"
+  convertErrorsToExceptions="true"
+  convertNoticesToExceptions="true"
+  convertWarningsToExceptions="true"
+  strict="true"
+  >
+  <testsuite name="Sabre\VObject">
+    <directory>VObject/</directory>
+  </testsuite>
+
+  <filter>
+    <whitelist addUncoveredFilesFromWhitelist="true">
+        <directory suffix=".php">../lib/</directory>
+        <exclude>
+            <file>../lib/Sabre/VObject/includes.php</file>
+        </exclude>
+   </whitelist>
+  </filter>
+</phpunit>