예제 #1
0
    def test_objectresource_component(self):
        """
        Test that a remote object resource L{component} works.
        """

        home01 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user01", create=True)
        self.assertTrue(home01 is not None)
        calendar01 = yield home01.childWithName("calendar")
        yield calendar01.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1))
        yield calendar01.createCalendarObjectWithName("2.ics", Component.fromString(self.caldata2))
        yield self.commitTransaction(0)

        home = yield self._remoteHome(self.theTransactionUnderTest(1), "user01")
        self.assertTrue(home is not None)
        calendar = yield home.childWithName("calendar")

        resource = yield calendar.objectResourceWithName("1.ics")
        caldata = yield resource.component()
        self.assertEqual(str(caldata), self.caldata1)

        resource = yield calendar.objectResourceWithName("2.ics")
        caldata = yield resource.component()
        self.assertEqual(str(caldata), self.caldata2)

        yield self.commitTransaction(1)
예제 #2
0
    def test_oneEventCalendar(self):
        """
        Exporting an calendar with one event in it will result in just that
        event.
        """
        yield populateCalendarsFrom(
            {
                "home1": {
                    "calendar1": {
                        "valentines-day.ics": (valentines, {})
                    }
                }
            }, self.store
        )

        expected = Component.newCalendar()
        [theComponent] = Component.fromString(valentines).subcomponents()
        expected.addComponent(theComponent)

        io = StringIO()
        yield exportToFile(
            [(yield self.txn().calendarHomeWithUID("home1"))
              .calendarWithName("calendar1")], io
        )
        self.assertEquals(Component.fromString(io.getvalue()),
                          expected)
예제 #3
0
    def test_vcalendar_no_effect(self):

        data = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ORGANIZER;CN=User 01:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")

        no_effect = CalendarData(
            CalendarComponent(
                name="VCALENDAR"
            )
        )
        for item in (data, Component.fromString(data),):
            self.assertEqual(str(CalendarDataFilter(no_effect).filter(item)), data)

        no_effect = CalendarData(
            CalendarComponent(
                AllComponents(),
                AllProperties(),
                name="VCALENDAR"
            )
        )
        for item in (data, Component.fromString(data),):
            self.assertEqual(str(CalendarDataFilter(no_effect).filter(item)), data)
예제 #4
0
    def test_twoSimpleEvents(self):
        """
        Exporting a calendar with two events in it will result in a VCALENDAR
        component with both VEVENTs in it.
        """
        yield populateCalendarsFrom(
            {
                "home1": {
                    "calendar1": {
                        "valentines-day.ics": (valentines, {}),
                        "new-years-day.ics": (newYears, {})
                    }
                }
            }, self.store
        )

        expected = Component.newCalendar()
        a = Component.fromString(valentines)
        b = Component.fromString(newYears)
        for comp in a, b:
            for sub in comp.subcomponents():
                expected.addComponent(sub)

        io = StringIO()
        yield exportToFile(
            [(yield self.txn().calendarHomeWithUID("home1"))
              .calendarWithName("calendar1")], io
        )
        self.assertEquals(Component.fromString(io.getvalue()),
                          expected)
예제 #5
0
    def test_objectresource_objectwith(self):
        """
        Test that a remote home child L{objectResourceWithName} and L{objectResourceWithUID} works.
        """

        home01 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user01", create=True)
        self.assertTrue(home01 is not None)
        calendar01 = yield home01.childWithName("calendar")
        resource01 = yield calendar01.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1))
        yield calendar01.createCalendarObjectWithName("2.ics", Component.fromString(self.caldata2))
        yield self.commitTransaction(0)

        home = yield self._remoteHome(self.theTransactionUnderTest(1), "user01")
        self.assertTrue(home is not None)
        calendar = yield home.childWithName("calendar")

        resource = yield calendar.objectResourceWithName("2.ics")
        self.assertEqual(resource.name(), "2.ics")

        resource = yield calendar.objectResourceWithName("foo.ics")
        self.assertEqual(resource, None)

        resource = yield calendar.objectResourceWithUID("uid1")
        self.assertEqual(resource.name(), "1.ics")

        resource = yield calendar.objectResourceWithUID("foo")
        self.assertEqual(resource, None)

        resource = yield calendar.objectResourceWithID(resource01.id())
        self.assertEqual(resource.name(), "1.ics")

        resource = yield calendar.objectResourceWithID(12345)
        self.assertEqual(resource, None)

        yield self.commitTransaction(1)
예제 #6
0
    def test_listobjects(self):
        """
        Test that action=listobjects works.
        """

        yield self.createShare("user01", "puser01")

        shared = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", name="shared-calendar")
        objects = yield shared.listObjectResources()
        self.assertEqual(set(objects), set())
        yield self.commitTransaction(1)

        calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar")
        yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1))
        yield calendar1.createCalendarObjectWithName("2.ics", Component.fromString(self.caldata2))
        objects = yield calendar1.listObjectResources()
        self.assertEqual(set(objects), set(("1.ics", "2.ics",)))
        yield self.commitTransaction(0)

        shared = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", name="shared-calendar")
        objects = yield shared.listObjectResources()
        self.assertEqual(set(objects), set(("1.ics", "2.ics",)))
        yield self.commitTransaction(1)

        calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar")
        object1 = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics")
        yield object1.remove()
        objects = yield calendar1.listObjectResources()
        self.assertEqual(set(objects), set(("2.ics",)))
        yield self.commitTransaction(0)

        shared = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", name="shared-calendar")
        objects = yield shared.listObjectResources()
        self.assertEqual(set(objects), set(("2.ics",)))
        yield self.commitTransaction(1)
예제 #7
0
    def migrate(self, txn, mapIDsCallback):
        """
        See L{ScheduleWork.migrate}
        """

        # Try to find a mapping
        new_home, new_resource = yield mapIDsCallback(self.resourceID)

        # If we previously had a resource ID and now don't, then don't create work
        if self.resourceID is not None and new_resource is None:
            returnValue(False)

        if self.icalendarTextOld:
            calendar_old = Component.fromString(self.icalendarTextOld)
            uid = calendar_old.resourceUID()
        else:
            calendar_new = Component.fromString(self.icalendarTextNew)
            uid = calendar_new.resourceUID()

        # Insert new work - in paused state
        yield ScheduleOrganizerWork.schedule(
            txn, uid, scheduleActionFromSQL[self.scheduleAction],
            new_home, new_resource, self.icalendarTextOld, self.icalendarTextNew,
            new_home.uid(), self.attendeeCount, self.smartMerge,
            pause=1
        )

        returnValue(True)
예제 #8
0
    def doWork(self):
        """
        Do the export, stopping the reactor when done.
        """
        try:
            if self.options.inputDirectoryName:
                dirname = self.options.inputDirectoryName
                if not os.path.exists(dirname):
                    sys.stderr.write(
                        "Directory does not exist: {}\n".format(dirname)
                    )
                    sys.exit(1)
                for filename in os.listdir(dirname):
                    fullpath = os.path.join(dirname, filename)
                    print("Importing {}".format(fullpath))
                    fileobj = open(fullpath, 'r')
                    component = Component.allFromStream(fileobj)
                    fileobj.close()
                    yield importCollectionComponent(self.store, component)

            else:
                try:
                    input = self.options.openInput()
                except IOError, e:
                    sys.stderr.write(
                        "Unable to open input file for reading: %s\n" % (e)
                    )
                    sys.exit(1)

                component = Component.allFromStream(input)
                input.close()
                yield importCollectionComponent(self.store, component)
        except:
            log.failure("doWork()")
예제 #9
0
    def test_objectresource_resourceuidforname(self):
        """
        Test that a remote home child L{resourceUIDForName} works.
        """

        home01 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user01", create=True)
        self.assertTrue(home01 is not None)
        calendar01 = yield home01.childWithName("calendar")
        yield calendar01.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1))
        yield calendar01.createCalendarObjectWithName("2.ics", Component.fromString(self.caldata2))
        yield self.commitTransaction(0)

        home = yield self._remoteHome(self.theTransactionUnderTest(1), "user01")
        self.assertTrue(home is not None)
        calendar = yield home.childWithName("calendar")

        uid = yield calendar.resourceUIDForName("1.ics")
        self.assertEqual(uid, "uid1")

        uid = yield calendar.resourceUIDForName("2.ics")
        self.assertEqual(uid, "uid2")

        uid = yield calendar.resourceUIDForName("foo.ics")
        self.assertEqual(uid, None)

        yield self.commitTransaction(1)
예제 #10
0
 def init_perinstance_component():
     peruser = Component(PerUserDataFilter.PERINSTANCE_COMPONENT)
     rid = component.getRecurrenceIDUTC()
     if rid:
         peruser.addProperty(Property("RECURRENCE-ID", rid))
     perinstance_components[rid] = peruser
     return peruser
예제 #11
0
    def test_full(self):
        """
        Running C{calendarserver_export} on the command line exports an ics
        file. (Almost-full integration test, starting from the main point, using
        as few test fakes as possible.)

        Note: currently the only test for directory interaction.
        """
        yield populateCalendarsFrom(
            {
                "user02": {
                    # TODO: more direct test for skipping inbox
                    "inbox": {
                        "inbox-item.ics": (valentines, {})
                    },
                    "calendar1": {
                        "peruser.ics": (dataForTwoUsers, {}), # EST
                    }
                }
            }, self.store
        )

        output = FilePath(self.mktemp())
        main(['calendarserver_export', '--output',
              output.path, '--user', 'user02'], reactor=self)

        yield self.waitToStop

        self.assertEquals(
            Component.fromString(resultForUser2),
            Component.fromString(output.getContent())
        )
예제 #12
0
    def test_setcomponent(self):
        """
        Test that action=setcomponent works.
        """

        yield self.createShare("user01", "puser01")

        calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar")
        yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1))
        yield self.commitTransaction(0)

        shared_object = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", calendar_name="shared-calendar", name="1.ics")
        ical = yield shared_object.component()
        self.assertTrue(isinstance(ical, Component))
        self.assertEqual(normalize_iCalStr(str(ical)), normalize_iCalStr(self.caldata1))
        yield self.commitTransaction(1)

        shared_object = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", calendar_name="shared-calendar", name="1.ics")
        changed = yield shared_object.setComponent(Component.fromString(self.caldata1_changed))
        self.assertFalse(changed)
        ical = yield shared_object.component()
        self.assertTrue(isinstance(ical, Component))
        self.assertEqual(normalize_iCalStr(str(ical)), normalize_iCalStr(self.caldata1_changed))
        yield self.commitTransaction(1)

        object1 = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics")
        ical = yield object1.component()
        self.assertTrue(isinstance(ical, Component))
        self.assertEqual(normalize_iCalStr(str(ical)), normalize_iCalStr(self.caldata1_changed))
        yield self.commitTransaction(0)
예제 #13
0
파일: work.py 프로젝트: nunb/calendarserver
    def doWork(self):

        try:
            home = (yield self.transaction.calendarHomeWithResourceID(self.homeResourceID))
            resource = (yield home.objectResourceWithID(self.resourceID))
            organizerAddress = yield calendarUserFromCalendarUserUID(home.uid(), self.transaction)
            organizer = organizerAddress.record.canonicalCalendarUserAddress()
            calendar_old = Component.fromString(self.icalendarTextOld) if self.icalendarTextOld else None
            calendar_new = Component.fromString(self.icalendarTextNew) if self.icalendarTextNew else None

            log.debug("ScheduleOrganizerWork - running for ID: {id}, UID: {uid}, organizer: {org}", id=self.workID, uid=self.icalendarUid, org=organizer)

            # We need to get the UID lock for implicit processing.
            yield NamedLock.acquire(self.transaction, "ImplicitUIDLock:%s" % (hashlib.md5(self.icalendarUid).hexdigest(),))

            from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
            scheduler = ImplicitScheduler()
            yield scheduler.queuedOrganizerProcessing(
                self.transaction,
                scheduleActionFromSQL[self.scheduleAction],
                home,
                resource,
                self.icalendarUid,
                calendar_old,
                calendar_new,
                self.smartMerge
            )

            self._dequeued()

        except Exception, e:
            log.debug("ScheduleOrganizerWork - exception ID: {id}, UID: '{uid}', {err}", id=self.workID, uid=self.icalendarUid, err=str(e))
            log.debug(traceback.format_exc())
            raise
예제 #14
0
    def test_full(self):
        """
        Running C{calendarserver_export} on the command line exports an ics
        file. (Almost-full integration test, starting from the main point, using
        as few test fakes as possible.)

        Note: currently the only test for directory interaction.
        """
        yield populateCalendarsFrom(
            {
                "user02": {
                    # TODO: more direct test for skipping inbox
                    "inbox": {
                        "inbox-item.ics": (valentines, {})
                    },
                    "calendar1": {
                        "peruser.ics": (dataForTwoUsers, {}), # EST
                    }
                }
            }, self.store
        )

        augmentsData = """
            <augments>
              <record>
                <uid>Default</uid>
                <enable>true</enable>
                <enable-calendar>true</enable-calendar>
                <enable-addressbook>true</enable-addressbook>
              </record>
            </augments>
        """
        augments = FilePath(self.mktemp())
        augments.setContent(augmentsData)

        accountsData = """
            <accounts realm="Test Realm">
                <user>
                    <uid>user-under-test</uid>
                    <guid>user02</guid>
                    <name>Not Interesting</name>
                    <password>very-secret</password>
                </user>
            </accounts>
        """
        accounts = FilePath(self.mktemp())
        accounts.setContent(accountsData)
        output = FilePath(self.mktemp())
        self.accountsFile = accounts.path
        self.augmentsFile = augments.path
        main(['calendarserver_export', '--output',
              output.path, '--user', 'user-under-test'], reactor=self)

        yield self.waitToStop

        self.assertEquals(
            Component.fromString(resultForUser2),
            Component.fromString(output.getContent())
        )
예제 #15
0
 def assertEqualCalendarData(self, cal1, cal2):
     if isinstance(cal1, str):
         cal1 = Component.fromString(cal1)
     if isinstance(cal2, str):
         cal2 = Component.fromString(cal2)
     ncal1 = normalize_iCalStr(cal1)
     ncal2 = normalize_iCalStr(cal2)
     self.assertEqual(ncal1, ncal2, msg=diff_iCalStrs(ncal1, ncal2))
예제 #16
0
    def test_validation_replaceMissingToDoProperties_Completed(self):
        """
        Test that VTODO completed status is fixed.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VTODO
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VTODO
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
        yield self.commit()

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VTODO
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
SUMMARY:Changed
COMPLETED:20080601T140000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VTODO
END:VCALENDAR
"""

        calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
        calendar = Component.fromString(data2)
        txn = self.transactionUnderTest()
        txn._authz_uid = "user01"
        yield calendar_resource.setComponent(calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("ORGANIZER" in calendar1)
        self.assertTrue("ATTENDEE" in calendar1)
        self.assertTrue("SUMMARY:Changed" in calendar1)
        self.assertTrue("PARTSTAT=COMPLETED" in calendar1)
        yield self.commit()
예제 #17
0
 def test_emptyCalendar(self):
     """
     Exporting an empty calendar results in an empty calendar.
     """
     io = StringIO()
     value = yield exportToFile([], io)
     # it doesn't return anything, it writes to the file.
     self.assertEquals(value, None)
     # but it should write a valid component to the file.
     self.assertEquals(Component.fromString(io.getvalue()),
                       Component.newCalendar())
예제 #18
0
    def test_ImportComponentNoScheduling(self):

        component = Component.allFromString(DATA_NO_SCHEDULING)
        yield importCollectionComponent(self.store, component)

        txn = self.store.newTransaction()
        home = yield txn.calendarHomeWithUID("user01")
        collection = yield home.childWithName("calendar")

        # Verify properties have been set
        collectionProperties = collection.properties()
        for element, value in (
            (davxml.DisplayName, "Sample Import Calendar"),
            (customxml.CalendarColor, "#0E61B9FF"),
        ):
            self.assertEquals(
                value,
                collectionProperties[PropertyName.fromElement(element)]
            )

        # Verify child objects
        objects = yield collection.listObjectResources()
        self.assertEquals(len(objects), 2)

        yield txn.commit()

        # Reimport different component into same collection

        component = Component.allFromString(DATA_NO_SCHEDULING_REIMPORT)

        yield importCollectionComponent(self.store, component)

        txn = self.store.newTransaction()
        home = yield txn.calendarHomeWithUID("user01")
        collection = yield home.childWithName("calendar")

        # Verify properties have been changed
        collectionProperties = collection.properties()
        for element, value in (
            (davxml.DisplayName, "Sample Import Calendar Reimported"),
            (customxml.CalendarColor, "#FFFFFFFF"),
        ):
            self.assertEquals(
                value,
                collectionProperties[PropertyName.fromElement(element)]
            )

        # Verify child objects (should be 3 now)
        objects = yield collection.listObjectResources()
        self.assertEquals(len(objects), 3)

        yield txn.commit()
예제 #19
0
    def test_validation_preserveOrganizerPrivateComments(self):
        """
        Test that organizer private comments are restored.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-organizer
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:x-uid:user01";
 X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
        yield self.commit()

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-organizer
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
SUMMARY:Changed
END:VEVENT
END:VCALENDAR
"""

        txn = self.transactionUnderTest()
        txn._authz_uid = "user01"
        calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
        calendar = Component.fromString(data2)
        yield calendar_resource.setComponent(calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF=\"urn:x-uid:user01\";X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment" in calendar1)
        self.assertTrue("SUMMARY:Changed" in calendar1)
        yield self.commit()
예제 #20
0
    def test_testImplicitSchedulingPUT_FixScheduleState(self):
        """
        Test that testImplicitSchedulingPUT will fix an old cached schedule object state by
        re-evaluating the calendar data.
        """

        calendarOld = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""")

        calendarNew = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""")

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calresource = (yield calendar_collection.createCalendarObjectWithName(
            "1.ics", calendarOld
        ))
        calresource.isScheduleObject = False

        scheduler = ImplicitScheduler()
        try:
            doAction, isScheduleObject = (yield scheduler.testImplicitSchedulingPUT(calendar_collection, calresource, calendarNew, False))
        except Exception as e:
            print e
            self.fail("Exception must not be raised")
        self.assertTrue(doAction)
        self.assertTrue(isScheduleObject)
예제 #21
0
    def test_validation_noPreservePrivateComments(self):
        """
        Test that attendee private comments are no longer restored.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
X-CALENDARSERVER-PRIVATE-COMMENT:My Comment
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
        yield self.commit()

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
SUMMARY:Changed
END:VEVENT
END:VCALENDAR
"""

        txn = self.transactionUnderTest()
        txn._authz_uid = "user01"
        calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
        calendar = Component.fromString(data2)
        yield calendar_resource.setComponent(calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertFalse("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
        self.assertTrue("SUMMARY:Changed" in calendar1)
        yield self.commit()
예제 #22
0
    def test_testImplicitSchedulingPUT_NoChangeScheduleState(self):
        """
        Test that testImplicitSchedulingPUT will prevent attendees from changing the
        schedule object state.
        """

        request = SimpleRequest(self.site, "PUT", "/calendar/1.ics")
        calresource = yield request.locateResource("/calendar/1.ics")
        self.assertEqual(calresource.isScheduleObject, None)
        calresource.isScheduleObject = False

        calendarOld = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
END:VEVENT
END:VCALENDAR
""")

        calendarNew = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 02":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""")

        calresource.exists = lambda : True
        calresource.iCalendarForUser = lambda request: succeed(calendarOld)

        scheduler = ImplicitScheduler()
        try:
            yield scheduler.testImplicitSchedulingPUT(request, calresource, "/calendars/users/user01/calendar/1.ics", calendarNew, False)
        except HTTPError:
            pass
        except:
            self.fail("HTTPError exception must be raised")
        else:
            self.fail("Exception must be raised")
        request._newStoreTransaction.abort()
예제 #23
0
    def test_testImplicitSchedulingPUT_FixScheduleState(self):
        """
        Test that testImplicitSchedulingPUT will fix an old cached schedule object state by
        re-evaluating the calendar data.
        """

        request = SimpleRequest(self.site, "PUT", "/calendar/1.ics")
        calresource = yield request.locateResource("/calendar/1.ics")
        self.assertEqual(calresource.isScheduleObject, None)
        calresource.isScheduleObject = False

        calendarOld = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 02":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""")

        calendarNew = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 02":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""")

        calresource.exists = lambda : True
        calresource.iCalendarForUser = lambda request: succeed(calendarOld)

        scheduler = ImplicitScheduler()
        try:
            doAction, isScheduleObject = (yield scheduler.testImplicitSchedulingPUT(request, calresource, "/calendars/users/user01/calendar/1.ics", calendarNew, False))
        except:
            self.fail("Exception must not be raised")
        self.assertTrue(doAction)
        self.assertTrue(isScheduleObject)
예제 #24
0
    def test_testImplicitSchedulingPUT_NoChangeScheduleState(self):
        """
        Test that testImplicitSchedulingPUT will prevent attendees from changing the
        schedule object state.
        """

        calendarOld = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
END:VEVENT
END:VCALENDAR
""")

        calendarNew = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 02":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""")

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calresource = (yield calendar_collection.createCalendarObjectWithName(
            "1.ics", calendarOld
        ))
        calresource.isScheduleObject = False

        scheduler = ImplicitScheduler()
        try:
            yield scheduler.testImplicitSchedulingPUT(calendar_collection, calresource, calendarNew, False)
        except HTTPError:
            pass
        except:
            self.fail("HTTPError exception must be raised")
        else:
            self.fail("Exception must be raised")
예제 #25
0
    def test_doCreateResource(self):
        """
        Test that resource creation works.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar1 = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName("test.ics", calendar1)
        yield self.commit()

        calendar_resource1 = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
        calendar1 = (yield calendar_resource1.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("urn:uuid:user01" in calendar1)
        self.assertTrue("urn:uuid:user02" in calendar1)
        self.assertTrue("CN=" in calendar1)
        yield self.commit()
예제 #26
0
 def test_cancelAsAttendeeMultipleOccurrences(self):
     # Multiple meeting occurrences with no master, where purged CUA is
     # an attendee
     event = Component.fromString(INVITED_TO_MULTIPLE_OCCURRENCES_ICS)
     action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
         "urn:uuid:9DC04A71-E6DD-11DF-9492-0800200C9A66")
     self.assertEquals(action, PurgePrincipalService.CANCELEVENT_SHOULD_DELETE)
예제 #27
0
    def test_validation_validAttendeeListSizeCheck(self):
        """
        Test that resource with too many attendees are rejected.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
"""

        self.patch(config, "MaxAttendeesPerInstance", 2)
        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), TooManyAttendeesError)
        yield self.commit()
예제 #28
0
    def test_queueAttendeeUpdate_with_refresh(self):

        self.patch(config.Scheduling.Options, "AttendeeRefreshBatch", 5)

        calendar = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER:urn:uuid:user01
ATTENDEE:urn:uuid:user01
ATTENDEE:urn:uuid:user02
ATTENDEE:urn:uuid:user03
END:VEVENT
END:VCALENDAR
""")
        processor = FakeImplicitProcessor()
        processor.txn = ""
        processor.uid = "12345-67890"
        processor.recipient_calendar_resource = FakeResource()
        processor.recipient_calendar = calendar
        yield processor.queueAttendeeUpdate(("urn:uuid:user02", "urn:uuid:user01",))
        self.assertEqual(processor.batches, 1)
예제 #29
0
 def test_cancelAllDayRepeating(self):
     # A repeating All Day event where purged CUA is organizer
     event = Component.fromString(REPEATING_2_ICS_BEFORE)
     action = PurgePrincipalService._cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
         "urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
     self.assertEquals(action, PurgePrincipalService.CANCELEVENT_MODIFIED)
     self.assertEquals(str(event), REPEATING_2_ICS_AFTER)
예제 #30
0
    def test_no_freebusy(self):

        data = """BEGIN:VCALENDAR
VERSION:2.0
METHOD:REQUEST
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VFREEBUSY
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VFREEBUSY
END:VCALENDAR
"""

        scheduler = iMIPProcessing.FakeSchedule(Component.fromString(data))
        recipients = (RemoteCalendarUser("mailto:[email protected]"),)
        responses = ScheduleResponseQueue("REQUEST", responsecode.OK)

        delivery = ScheduleViaIMip(scheduler, recipients, responses, True)
        yield delivery.generateSchedulingResponses()

        self.assertEqual(len(responses.responses), 1)
        self.assertEqual(str(responses.responses[0].children[1]), iTIPRequestStatus.SERVICE_UNAVAILABLE)
예제 #31
0
    def _calendarAvailabilityUpgrade_setup(self):

        av1 = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VAVAILABILITY
ORGANIZER:mailto:[email protected]
UID:[email protected]
DTSTAMP:20061005T133225Z
DTEND:20140101T000000Z
BEGIN:AVAILABLE
UID:[email protected]
DTSTAMP:20061005T133225Z
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART:20130101T090000Z
DTEND:20130101T170000Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""")
        av2 = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VAVAILABILITY
ORGANIZER:mailto:[email protected]
UID:[email protected]
DTSTAMP:20061005T133225Z
DTEND:20140101T000000Z
BEGIN:AVAILABLE
UID:[email protected]
DTSTAMP:20061005T133225Z
SUMMARY:Monday to Friday from 12:00 to 17:00
DTSTART:20130101T120000Z
DTEND:20130101T170000Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""")

        user_details = (
            ("user01", av1),
            ("user02", av2),
            ("user03", None),
        )

        # Set dead properties on calendars
        for user, av in user_details:
            calendar = (yield self.calendarUnderTest(name="inbox", home=user))
            if av:
                calendar.properties()[PropertyName.fromElement(
                    customxml.CalendarAvailability
                )] = customxml.CalendarAvailability.fromString(str(av))

            # Force data version to previous
            home = (yield self.homeUnderTest(name=user))
            ch = home._homeSchema
            yield Update(
                {
                    ch.DATAVERSION: 4
                },
                Where=ch.RESOURCE_ID == home._resourceID,
            ).on(self.transactionUnderTest())

        yield self.commit()

        for user, av in user_details:
            home = (yield self.homeUnderTest(name=user))
            calendar = (yield self.calendarUnderTest(name="inbox", home=user))
            self.assertEqual(home.getAvailability(), None)
            self.assertEqual(
                PropertyName.fromElement(customxml.CalendarAvailability)
                in calendar.properties(), av is not None)
        yield self.commit()

        returnValue(user_details)
예제 #32
0
 def calendar(self):
     """
     Returns a calendar component derived from this element.
     """
     return iComponent.fromString(str(self))
예제 #33
0
class XML(twistedcaldav.test.util.TestCase):
    """
    XML tests
    """
    calendar_file = os.path.join(os.path.dirname(__file__), "data", "Holidays",
                                 "C3184A66-1ED0-11D9-A5E0-000A958A3252.ics")
    calendar = Component.fromStream(file(calendar_file))
    calendar.validCalendarData()
    calendar.validCalendarForCalDAV(methodAllowed=False)

    def test_ComponentFilter(self):
        """
        Component filter element.
        """
        for component_name, has in (
            ("VEVENT", True),
            ("VTODO", False),
        ):
            if has:
                no = "no "
            else:
                no = ""

            if has != storeComponentFilter(
                    ComponentFilter(ComponentFilter(name=component_name),
                                    name="VCALENDAR")).match(
                                        self.calendar, None):
                self.fail("Calendar has %s%s?" % (no, component_name))

    def test_PropertyFilter(self):
        """
        Property filter element.
        """
        for property_name, has in (
            ("UID", True),
            ("BOOGER", False),
        ):
            if has:
                no = "no "
            else:
                no = ""

            if has != storeComponentFilter(
                    ComponentFilter(ComponentFilter(
                        PropertyFilter(name=property_name), name="VEVENT"),
                                    name="VCALENDAR")).match(
                                        self.calendar, None):
                self.fail("Calendar has %sVEVENT with %s?" %
                          (no, property_name))

    def test_ParameterFilter(self):
        """
        Parameter filter element.
        """
        raise SkipTest("test unimplemented")

    def test_TextMatch(self):
        """
        Text match element.
        """
        for uid, caseless, has in (
            ("C3184A66-1ED0-11D9-A5E0-000A958A3252", False, True),
            ("c3184a66-1ed0-11d9-a5e0-000a958a3252", True, True),
            ("BOOGER", False, False),
            ("BOOGER", True, False),
        ):
            if has:
                no = "no "
            else:
                no = ""

            if has != storeComponentFilter(
                    ComponentFilter(ComponentFilter(PropertyFilter(
                        TextMatch.fromString(uid, caseless=caseless),
                        name="UID"),
                                                    name="VEVENT"),
                                    name="VCALENDAR")).match(
                                        self.calendar, None):
                self.fail("Calendar has %sVEVENT with UID %s? (caseless=%s)" %
                          (no, uid, caseless))

    def test_TimeRange(self):
        """
        Time range match element.
        """
        for start, end, has in (
            ("20020101T000000Z", "20020101T000001Z", True),
            ("20020101T000000Z", "20020101T000000Z",
             True),  # Timespan of zero duration
            ("20020101", "20020101", True),  # Timespan of zero duration
            ("20020101", "20020102", True),
            ("20020101", "20020103", True),
            ("20020102", "20020103", False),
            ("20011201", "20020101", False),  # End is non-inclusive

                # Expanded recurrence
            ("20030101T000000Z", "20030101T000001Z", True),
            ("20030101T000000Z", "20030101T000000Z",
             True),  # Timespan of zero duration
            ("20030101", "20030101", True),  # Timespan of zero duration
            ("20030101", "20030102", True),
            ("20030101", "20030103", True),
            ("20030102", "20030103", False),
            ("20021201", "20030101", False),  # End is non-inclusive
        ):
            if has:
                no = "no "
            else:
                no = ""

            if has != storeFilter(
                    Filter(
                        ComponentFilter(
                            ComponentFilter(TimeRange(start=start, end=end),
                                            name="VEVENT"),
                            name="VCALENDAR"))).match(self.calendar):
                self.fail("Calendar has %sVEVENT with timerange %s?" %
                          (no, (start, end)))

    test_TimeRange.todo = "recurrence expansion"
예제 #34
0
    def test_validation_replaceMissingToDoProperties_Completed(self):
        """
        Test that VTODO completed status is fixed.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VTODO
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VTODO
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName(
            "test.ics", calendar)
        yield self.commit()

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VTODO
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
SUMMARY:Changed
COMPLETED:20080601T140000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VTODO
END:VCALENDAR
"""

        txn = self.transactionUnderTest()
        txn._authz_uid = "user01"
        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data2)
        yield calendar_resource.setComponent(calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("ORGANIZER" in calendar1)
        self.assertTrue("ATTENDEE" in calendar1)
        self.assertTrue("SUMMARY:Changed" in calendar1)
        self.assertTrue("PARTSTAT=COMPLETED" in calendar1)
        yield self.commit()
예제 #35
0
    def doWork(self):

        try:
            home = (yield self.transaction.calendarHomeWithResourceID(
                self.homeResourceID))
            resource = (yield home.objectResourceWithID(self.resourceID))
            itipmsg = Component.fromString(self.itipMsg)

            organizerAddress = yield calendarUserFromCalendarUserUID(
                home.uid(), self.transaction)
            organizer = organizerAddress.record.canonicalCalendarUserAddress()
            log.debug(
                "ScheduleOrganizerSendWork - running for ID: {id}, UID: {uid}, organizer: {org}, attendee: {att}",
                id=self.workID,
                uid=self.icalendarUID,
                org=organizer,
                att=self.attendee)

            # We need to get the UID lock for implicit processing.
            yield NamedLock.acquire(
                self.transaction, "ImplicitUIDLock:%s" %
                (hashlib.md5(self.icalendarUID).hexdigest(), ))

            from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
            scheduler = ImplicitScheduler()
            yield scheduler.queuedOrganizerSending(
                self.transaction, scheduleActionFromSQL[self.scheduleAction],
                home, resource, self.icalendarUID, organizer, self.attendee,
                itipmsg, self.noRefresh)

            # Handle responses - update the actual resource in the store. Note that for a create the resource did not previously
            # exist and is stored as None for the work item, but the scheduler will attempt to find the new resources and use
            # that. We need to grab the scheduler's resource for further processing.
            resource = scheduler.resource
            if resource is not None:
                responses, all_delivered = self.extractSchedulingResponse(
                    scheduler.queuedResponses)
                if not all_delivered:

                    # Check for all connection failed
                    yield self.checkTemporaryFailure(responses)

                    # Update calendar data to reflect error status
                    calendar = (yield resource.componentForUser())
                    changed = self.handleSchedulingResponse(
                        responses, calendar, True)
                    if changed:
                        yield resource._setComponentInternal(
                            calendar,
                            internal_state=ComponentUpdateState.
                            ORGANIZER_ITIP_UPDATE)

            self._dequeued()

        except Exception, e:
            log.debug(
                "ScheduleOrganizerSendWork - exception ID: {id}, UID: '{uid}', {err}",
                id=self.workID,
                uid=self.icalendarUID,
                err=str(e))
            log.debug(traceback.format_exc())
            raise
예제 #36
0
    def test_validation_deleteWithDuplicatePrivateComments(self):
        """
        Test that attendee private comments are no longer restored.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
"""

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:
 user02";X-CALENDARSERVER-DTSTAMP=20140224T181133Z:Comment
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:
 user02";X-CALENDARSERVER-DTSTAMP=20140224T181133Z:Comment
END:VEVENT
END:VCALENDAR
"""

        data3 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE;PARTSTAT=ACCEPTED:mailto:[email protected]
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName(
            "test.ics", calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data2)
        yield calendar_resource._setComponentInternal(
            calendar, internal_state=ComponentUpdateState.RAW)
        yield self.commit()

        def raiseHere(otherself, component, inserting, internal_state):
            if component.hasDuplicatePrivateComments(doFix=False):
                raise ValueError

        self.patch(CalendarObject, "preservePrivateComments", raiseHere)

        calendar2 = (yield self.calendarUnderTest(name="calendar_1",
                                                  home="user02"))
        cobjs = (yield calendar2.calendarObjects())
        self.assertTrue(len(cobjs) == 1)
        yield cobjs[0].setComponent(Component.fromString(data3))
        yield self.commit()

        calendar2 = (yield self.calendarUnderTest(name="calendar_1",
                                                  home="user02"))
        cobjs = (yield calendar2.calendarObjects())
        calendar = yield cobjs[0].component()
        self.assertTrue('SCHEDULE-STATUS=5.0' in normalize_iCalStr(calendar))
        yield self.commit()

        calendar2 = (yield self.calendarUnderTest(name="calendar_1",
                                                  home="user02"))
        cobjs = (yield calendar2.calendarObjects())
        self.assertTrue(len(cobjs) == 1)
        yield cobjs[0].remove()
        yield self.commit()

        calendar2 = (yield self.calendarUnderTest(name="calendar_1",
                                                  home="user02"))
        cobjs = (yield calendar2.calendarObjects())
        self.assertTrue(len(cobjs) == 0)
        yield self.commit()

        self.flushLoggedErrors(ValueError)
예제 #37
0
    def test_validation_validAccess_authzChangeNotAllowed(self):
        """
        Test that resource access mode changes are rejected.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
X-CALENDARSERVER-ACCESS:PRIVATE
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
END:VEVENT
END:VCALENDAR
"""

        self.patch(config, "EnablePrivateEvents", True)
        txn = self.transactionUnderTest()
        txn._authz_uid = "user02"
        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield self.failUnlessFailure(
            calendar_collection.createCalendarObjectWithName(
                "test.ics", calendar), InvalidCalendarAccessError)
        yield self.commit()

        # This one should be OK
        txn = self.transactionUnderTest()
        txn._authz_uid = "user01"
        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName(
            "test.ics", calendar)
        yield self.commit()

        # This one should re-insert access mode
        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
SUMMARY:Changed
END:VEVENT
END:VCALENDAR
"""

        txn = self.transactionUnderTest()
        txn._authz_uid = "user01"
        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data2)
        yield calendar_resource.setComponent(calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("X-CALENDARSERVER-ACCESS:PRIVATE" in calendar1)
        self.assertTrue("SUMMARY:Changed" in calendar1)
        yield self.commit()
예제 #38
0
    def test_validation_duplicatePrivateCommentsOKWIthiTIP(self):
        """
        Test that an iTIP update to an organizer event with duplicate private comments
        does not fail.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
"""

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:
 user02";X-CALENDARSERVER-DTSTAMP=20140224T181133Z:Comment
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:
 user02";X-CALENDARSERVER-DTSTAMP=20140224T181133Z:Comment
END:VEVENT
END:VCALENDAR
"""

        data3 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE;PARTSTAT=ACCEPTED:mailto:[email protected]
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:
 user02";X-CALENDARSERVER-DTSTAMP=20140224T181133Z:Comment
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:
 user02";X-CALENDARSERVER-DTSTAMP=20140224T181133Z:Comment
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName(
            "test.ics", calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data2)
        yield calendar_resource._setComponentInternal(
            calendar, internal_state=ComponentUpdateState.RAW)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data3)
        yield calendar_resource._setComponentInternal(
            calendar,
            internal_state=ComponentUpdateState.ORGANIZER_ITIP_UPDATE)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data3)
        yield self.failUnlessFailure(calendar_resource.setComponent(calendar),
                                     DuplicatePrivateCommentsError)
        yield self.commit()
예제 #39
0
    def _calendarTimezoneUpgrade_setup(self):

        tz1 = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VTIMEZONE
TZID:Etc/GMT+1
X-LIC-LOCATION:Etc/GMT+1
BEGIN:STANDARD
DTSTART:18000101T000000
RDATE:18000101T000000
TZNAME:GMT+1
TZOFFSETFROM:-0100
TZOFFSETTO:-0100
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""")
        tz2 = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VTIMEZONE
TZID:Etc/GMT+2
X-LIC-LOCATION:Etc/GMT+2
BEGIN:STANDARD
DTSTART:18000101T000000
RDATE:18000101T000000
TZNAME:GMT+2
TZOFFSETFROM:-0200
TZOFFSETTO:-0200
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""")
        tz3 = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VTIMEZONE
TZID:Etc/GMT+3
X-LIC-LOCATION:Etc/GMT+3
BEGIN:STANDARD
DTSTART:18000101T000000
RDATE:18000101T000000
TZNAME:GMT+3
TZOFFSETFROM:-0300
TZOFFSETTO:-0300
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""")

        # Share user01 calendar with user03
        calendar = (yield self.calendarUnderTest(name="calendar_1",
                                                 home="user01"))
        home3 = yield self.homeUnderTest(name="user03")
        shared_name = yield calendar.shareWith(home3, _BIND_MODE_WRITE)

        user_details = (
            ("user01", "calendar_1", tz1),
            ("user02", "calendar_1", tz2),
            ("user03", "calendar_1", None),
            ("user03", shared_name, tz3),
        )

        # Set dead properties on calendars
        for user, calname, tz in user_details:
            calendar = (yield self.calendarUnderTest(name=calname, home=user))
            if tz:
                calendar.properties()[PropertyName.fromElement(
                    caldavxml.CalendarTimeZone
                )] = caldavxml.CalendarTimeZone.fromString(str(tz))

            # Force data version to previous
            home = (yield self.homeUnderTest(name=user))
            ch = home._homeSchema
            yield Update(
                {
                    ch.DATAVERSION: 4
                },
                Where=ch.RESOURCE_ID == home._resourceID,
            ).on(self.transactionUnderTest())

        yield self.commit()

        for user, calname, tz in user_details:
            calendar = (yield self.calendarUnderTest(name=calname, home=user))
            self.assertEqual(calendar.getTimezone(), None)
            self.assertEqual(
                PropertyName.fromElement(caldavxml.CalendarTimeZone)
                in calendar.properties(), tz is not None)
        yield self.commit()

        # Create "fake" entry for non-existent share
        txn = self.transactionUnderTest()
        calendar = (yield self.calendarUnderTest(name="calendar_1",
                                                 home="user01"))
        rp = schema.RESOURCE_PROPERTY
        yield Insert({
            rp.RESOURCE_ID:
            calendar._resourceID,
            rp.NAME:
            PropertyName.fromElement(caldavxml.CalendarTimeZone).toString(),
            rp.VALUE:
            caldavxml.CalendarTimeZone.fromString(str(tz3)).toxml(),
            rp.VIEWER_UID:
            "user04",
        }).on(txn)
        yield self.commit()

        returnValue(user_details)
예제 #40
0
class Tasker(ProfileBase):
    """
    A Calendar user who creates new tasks.
    """
    _taskTemplate = Component.fromString("""\
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.3//EN
CALSCALE:GREGORIAN
BEGIN:VTODO
CREATED:20101018T155431Z
UID:C98AD237-55AD-4F7D-9009-0D355D835822
SUMMARY:Simple task
DUE;TZID=America/New_York:20101021T120000
DTSTAMP:20101018T155438Z
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"))

    def setParameters(
        self,
        enabled=True,
        interval=25,
        taskDueDistribution=NearFutureDistribution(),
    ):
        self.enabled = enabled
        self._interval = interval
        self._taskStartDistribution = taskDueDistribution

    def run(self):
        self._call = LoopingCall(self._addTask)
        self._call.clock = self._reactor
        self._reactor.callLater(
            self.random.randint(1, self._interval),
            self._call.start,
            self._interval
        )
        return Deferred()

    def _addTask(self):
        # Don't perform any operations until the client is up and running
        if not self._client.started:
            return succeed(None)

        calendars = self._calendarsOfType(caldavxml.calendar, "VTODO")

        while calendars:
            calendar = self.random.choice(calendars)
            calendars.remove(calendar)

            # Copy the template task and fill in some of its fields
            # to make a new task to create on the calendar.
            vcalendar = self._taskTemplate.duplicate()
            vtodo = vcalendar.mainComponent()
            uid = str(uuid4())
            due = self._taskStartDistribution.sample()
            vtodo.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
            vtodo.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
            vtodo.replaceProperty(Property("DUE", due))
            vtodo.replaceProperty(Property("UID", uid))

            href = '%s%s.ics' % (calendar.url, uid)
            d = self._client.addEvent(href, vcalendar)
            return self._newOperation("create", d)
예제 #41
0
    def test_calendarObjectRevisions_Modified(self):
        """
        Verify that a calendar object created before the revision cut-off, but modified after it is correctly reported as changed
        after revision clean-up
        """

        # Need to add one non-event change that creates a revision after the last event change revisions in order
        # for the logic in this test to work correctly
        home = yield self.homeUnderTest(name="user01")
        yield home.createCalendarWithName("_ignore_me")
        yield self.commit()

        # get initial sync token
        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
        initial_token = yield calendar.syncToken()
        yield self.commit()

        # Pause to give some space in the modified time
        time.sleep(1)
        modified = datetime.datetime.utcnow()
        time.sleep(1)

        # Patch the work item to use the modified cut-off we need
        def _dateCutoff(self):
            return modified

        self.patch(FindMinValidRevisionWork, "dateCutoff", _dateCutoff)

        # Make a change to get a pre-update token
        cal2Object = yield self.calendarObjectUnderTest(
            self.transactionUnderTest(),
            name="cal2.ics",
            calendar_name="calendar",
            home="user01")
        yield cal2Object.remove()
        yield self.commit()

        # get changed sync token
        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
        pre_update_token = yield calendar.syncToken()
        yield self.commit()

        # make changes
        cal1Object = yield self.calendarObjectUnderTest(
            self.transactionUnderTest(),
            name="cal1.ics",
            calendar_name="calendar",
            home="user01")
        yield cal1Object.setComponent(Component.fromString(self.cal1_mod))
        yield self.commit()

        # get changed sync token
        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
        update_token = yield calendar.syncToken()
        yield self.commit()

        # do FindMinValidRevisionWork and RevisionCleanupWork
        yield FindMinValidRevisionWork.reschedule(self.transactionUnderTest(),
                                                  0)
        yield self.commit()
        yield JobItem.waitEmpty(self.storeUnderTest().newTransaction, reactor,
                                60)

        # initial sync token fails
        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
        yield self.failUnlessFailure(
            calendar.resourceNamesSinceToken(initial_token),
            SyncTokenValidException)
        yield self.commit()

        # Pre-update sync token returns one item
        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
        names = yield calendar.resourceNamesSinceToken(pre_update_token)
        self.assertEqual(names, (['cal1.ics'], [], []))
        yield self.commit()

        # Post-update sync token returns one item
        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
        names = yield calendar.resourceNamesSinceToken(update_token)
        self.assertEqual(names, ([], [], []))
        yield self.commit()
예제 #42
0
    def test_validation_processAlarms_DuplicateRemoval(self):
        """
        Test that duplicate alarms are removed.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName(
            "test.ics", calendar)
        yield self.commit()

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
SUMMARY:Changed
BEGIN:VALARM
X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
DESCRIPTION:Event reminder
TRIGGER:-PT8M
ACTION:DISPLAY
END:VALARM
BEGIN:VALARM
X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
DESCRIPTION:Event reminder
TRIGGER:-PT8M
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
"""

        txn = self.transactionUnderTest()
        txn._authz_uid = "user01"
        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data2)
        result = (yield calendar_resource.setComponent(calendar))
        yield self.commit()
        self.assertTrue(result)

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)
        self.assertTrue("SUMMARY:Changed" in calendar1)
        yield self.commit()
예제 #43
0
    def test_migrateMergeConflictingObjects(self):
        """
        When merging two homes together, calendar objects may conflict in the
        following ways:

        First, an object may have the same name and the same UID as an object
        in the target calendar.  We assume the target object is always be newer
        than the source object, so this type of conflict will leave the source
        object unmodified.  This type of conflict is expected, and may happen
        as a result of an implicitly scheduled event where the principal owning
        the merged calendars is an attendee of the conflicting object, and
        received a re-invitation.

        Second, an object may have a different name, but the same UID as an
        object in the target calendar.  While this type of conflict is not
        expected -- most clients will choose names for objects that correspond
        to the iCalendar UIDs of their main component -- it is treated the same
        way as the first conflict.

        Third, an object may have the same UID as an object on a different
        calendar in the target home.  This may also happen if a scheduled event
        was previously on a different (most likely non-default) calendar.
        Technically this is actually valid, and it is possible to have the same
        object in multiple calendars as long as the object is not scheduled;
        however, that type of conflict is extremely unlikely as the client
        would have to generate the same event twice.

        Basically, in all expected cases, conflicts will only occur because an
        update to a scheduled event was sent out and the target home accepted
        it.  Therefore, conflicts are always resolved in favor of ignoring the
        source data and trusting that the target data is more reliable.
        """
        # Note: these tests are all performed with un-scheduled data because it
        # is simpler.  Although the expected conflicts will involve scheduled
        # data the behavior will be exactly the same.
        yield self.createConflicted(
            {
                "same-name": self.sampleEvent("same-name", "source"),
                "other-name": self.sampleEvent("other-uid", "source other"),
                "other-calendar": self.sampleEvent("oc", "source calendar"),
                "no-conflict": self.sampleEvent("no-conflict", "okay"),
            },
            {
                "same-name": self.sampleEvent("same-name", "target"),
                "different-name": self.sampleEvent("other-uid", "tgt other"),
            },
        )

        txn = self.transactionUnderTest()
        c2 = yield txn.calendarHomeWithUID("conflict2")
        otherCal = yield c2.createCalendarWithName("othercal")
        yield otherCal.createCalendarObjectWithName(
            "some-name",
            Component.fromString(self.sampleEvent("oc", "target calendar")[0]))
        yield self.commit()

        txn = self.transactionUnderTest()
        c1 = yield txn.calendarHomeWithUID("conflict1")
        c2 = yield txn.calendarHomeWithUID("conflict2")
        yield migrateHome(c1, c2, merge=True)
        yield self.commit()

        txn = self.transactionUnderTest()
        c2 = yield txn.calendarHomeWithUID("conflict2")
        targetCal = yield c2.calendarWithName("conflicted")
        yield self.checkSummary("same-name", "target", targetCal)
        yield self.checkSummary("different-name", "tgt other", targetCal)
        yield self.checkSummary("other-calendar", None, targetCal)
        yield self.checkSummary("other-name", None, targetCal)
        yield self.checkSummary("no-conflict", "okay", targetCal)
        yield self.checkSummary("oc", "target calendar", otherCal)
예제 #44
0
    def test_validation_mergePerUserData(self):
        """
        Test that per-user data is correctly stored and retrieved.
        """

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        sharee_home = (yield self.homeUnderTest(name="user02"))
        shared_name = (yield calendar_collection.shareWith(
            sharee_home,
            _BIND_MODE_WRITE,
        ))
        yield self.commit()

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
BEGIN:VALARM
X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
DESCRIPTION:Event reminder
TRIGGER:-PT5M
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName(
            "test.ics", calendar)
        yield self.commit()

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
BEGIN:VALARM
X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
DESCRIPTION:Event reminder
TRIGGER:-PT10M
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
"""

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            calendar_name=shared_name,
            home="user02",
        ))
        calendar = Component.fromString(data2)
        yield calendar_resource.setComponent(calendar)
        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))

        # Unfiltered view of event
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("TRIGGER:-PT5M" in calendar1)
        self.assertTrue("TRIGGER:-PT10M" in calendar1)
        self.assertEqual(calendar1.count("BEGIN:VALARM"), 2)

        # user01 view of event
        calendar1 = (yield calendar_resource.componentForUser("user01"))
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("TRIGGER:-PT5M" in calendar1)
        self.assertFalse("TRIGGER:-PT10M" in calendar1)
        self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)

        # user02 view of event
        calendar1 = (yield calendar_resource.componentForUser("user02"))
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertFalse("TRIGGER:-PT5M" in calendar1)
        self.assertTrue("TRIGGER:-PT10M" in calendar1)
        self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)

        yield self.commit()

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            calendar_name=shared_name,
            home="user02",
        ))

        # Unfiltered view of event
        calendar1 = (yield calendar_resource.component())
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("TRIGGER:-PT5M" in calendar1)
        self.assertTrue("TRIGGER:-PT10M" in calendar1)
        self.assertEqual(calendar1.count("BEGIN:VALARM"), 2)

        # user01 view of event
        calendar1 = (yield calendar_resource.componentForUser("user01"))
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertTrue("TRIGGER:-PT5M" in calendar1)
        self.assertFalse("TRIGGER:-PT10M" in calendar1)
        self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)

        # user02 view of event
        calendar1 = (yield calendar_resource.componentForUser("user02"))
        calendar1 = str(calendar1).replace("\r\n ", "")
        self.assertFalse("TRIGGER:-PT5M" in calendar1)
        self.assertTrue("TRIGGER:-PT10M" in calendar1)
        self.assertEqual(calendar1.count("BEGIN:VALARM"), 1)

        yield self.commit()
예제 #45
0
    def test_outbound(self):
        """
        Make sure outbound( ) stores tokens properly so they can be looked up
        """

        config.Scheduling.iMIP.Sending.Address = "*****@*****.**"
        self.patch(config.Localization, "LocalesDirectory",
                   os.path.join(os.path.dirname(__file__), "locales"))
        self._actualGenerateEmail = self.sender.generateEmail
        self.patch(self.sender, "generateEmail", self._interceptEmail)

        data = (
            # Initial invite
            (
                initialInviteText,
                "CFDD5E46-4F74-478A-9311-B3FF905449C3",
                "urn:uuid:C3B38B00-4166-11DD-B22C-A07C87E02F6A",
                "mailto:[email protected]",
                "new",
                "*****@*****.**",
                u"Th\xe9 Organizer",
                [
                    (u'Th\xe9 Attendee', u'*****@*****.**'),
                    (u'Th\xe9 Organizer', u'*****@*****.**'),
                    (u'An Attendee without CUTYPE', u'*****@*****.**'),
                    (None, u'*****@*****.**'),
                ],
                u"Th\xe9 Organizer <*****@*****.**>",
                "=?utf-8?q?Th=C3=A9_Organizer_=3Corganizer=40example=2Ecom=3E?=",
                "*****@*****.**",
                "Event invitation: testing outbound( ) Embedded: Header",
            ),

            # Update
            (
                u"""BEGIN:VCALENDAR
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
UID:CFDD5E46-4F74-478A-9311-B3FF905449C3
DTSTART:20100325T154500Z
DTEND:20100325T164500Z
ATTENDEE;CN=Th\xe9 Attendee;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:
 mailto:[email protected]
ATTENDEE;CN=Th\xe9 Organizer;CUTYPE=INDIVIDUAL;[email protected];PAR
 TSTAT=ACCEPTED:urn:uuid:C3B38B00-4166-11DD-B22C-A07C87E02F6A
ORGANIZER;CN=Th\xe9 Organizer;[email protected]:urn:uuid:C3B38B00-41
 66-11DD-B22C-A07C87E02F6A
SUMMARY:t\xe9sting outbound( ) *update*
END:VEVENT
END:VCALENDAR
""".encode("utf-8"),
                "CFDD5E46-4F74-478A-9311-B3FF905449C3",
                "urn:uuid:C3B38B00-4166-11DD-B22C-A07C87E02F6A",
                "mailto:[email protected]",
                "update",
                "*****@*****.**",
                u"Th\xe9 Organizer",
                [(u'Th\xe9 Attendee', u'*****@*****.**'),
                 (u'Th\xe9 Organizer', u'*****@*****.**')],
                u"Th\xe9 Organizer <*****@*****.**>",
                "=?utf-8?q?Th=C3=A9_Organizer_=3Corganizer=40example=2Ecom=3E?=",
                "*****@*****.**",
                "=?utf-8?q?Event_update=3A_t=C3=A9sting_outbound=28_=29_*update*?=",
            ),

            # Reply
            (
                u"""BEGIN:VCALENDAR
VERSION:2.0
METHOD:REPLY
BEGIN:VEVENT
UID:DFDD5E46-4F74-478A-9311-B3FF905449C4
DTSTART:20100325T154500Z
DTEND:20100325T164500Z
ATTENDEE;CN=Th\xe9 Attendee;CUTYPE=INDIVIDUAL;[email protected];PARTST
 AT=ACCEPTED:urn:uuid:C3B38B00-4166-11DD-B22C-A07C87E02F6A
ORGANIZER;CN=Th\xe9 Organizer;[email protected]:mailto:organizer@exam
 ple.com
SUMMARY:t\xe9sting outbound( ) *reply*
END:VEVENT
END:VCALENDAR
""".encode("utf-8"),
                None,
                "urn:uuid:C3B38B00-4166-11DD-B22C-A07C87E02F6A",
                "mailto:[email protected]",
                "reply",
                "*****@*****.**",
                u"Th\xe9 Organizer",
                [
                    (u'Th\xe9 Attendee', u'*****@*****.**'),
                ],
                "*****@*****.**",
                "*****@*****.**",
                "*****@*****.**",
                "=?utf-8?q?Event_reply=3A_t=C3=A9sting_outbound=28_=29_*reply*?=",
            ),
        )
        for (inputCalendar, UID, inputOriginator, inputRecipient, inviteState,
             outputOrganizerEmail, outputOrganizerName, outputAttendeeList,
             outputFrom, encodedFrom, outputRecipient, outputSubject) in data:

            txn = self.store.newTransaction()
            yield self.sender.outbound(txn,
                                       inputOriginator,
                                       inputRecipient,
                                       Component.fromString(
                                           inputCalendar.replace("\n",
                                                                 "\r\n")),
                                       onlyAfter=DateTime(2010, 1, 1, 0, 0, 0))
            yield txn.commit()

            msg = email.message_from_string(self.sender.smtpSender.message)
            self.assertEquals(msg["From"], encodedFrom)
            self.assertEquals(self.inviteState, inviteState)
            self.assertEquals(self.orgEmail, outputOrganizerEmail)
            self.assertEquals(self.orgCn, outputOrganizerName)
            self.assertEquals(self.attendees, outputAttendeeList)
            self.assertEquals(self.fromAddress, outputFrom)
            self.assertEquals(self.toAddress, outputRecipient)
            self.assertEquals(msg["Subject"], outputSubject)

            if UID:  # The organizer is local, and server is sending to remote
                # attendee
                txn = self.store.newTransaction()
                record = (yield txn.imipGetToken(inputOriginator,
                                                 inputRecipient, UID))
                yield txn.commit()
                self.assertNotEquals(record, None)
                self.assertEquals(msg["Reply-To"],
                                  "*****@*****.**" % (record.token, ))

                # Make sure attendee property for organizer exists and matches
                # the CUA of the organizer property
                orgValue = self.calendar.getOrganizerProperty().value()
                self.assertEquals(
                    orgValue,
                    self.calendar.getAttendeeProperty([orgValue]).value())

            else:  # Reply only -- the attendee is local, and server is sending reply to remote organizer

                self.assertEquals(msg["Reply-To"], self.fromAddress)

            # Check that we don't send any messages for events completely in
            # the past.
            self.sender.smtpSender.reset()
            txn = self.store.newTransaction()
            yield self.sender.outbound(txn,
                                       inputOriginator,
                                       inputRecipient,
                                       Component.fromString(
                                           inputCalendar.replace("\n",
                                                                 "\r\n")),
                                       onlyAfter=DateTime(2021, 1, 1, 0, 0, 0))
            yield txn.commit()
            self.assertFalse(self.sender.smtpSender.sendMessageCalled)
예제 #46
0
    def processReply(self, msg):
        # extract the token from the To header
        _ignore_name, addr = email.utils.parseaddr(msg['To'])
        if addr:
            # addr looks like: [email protected]
            token = self._extractToken(addr)
            if not token:
                log.error("Mail gateway didn't find a token in message "
                          "%s (%s)" % (msg['Message-ID'], msg['To']))
                returnValue(self.NO_TOKEN)
        else:
            log.error("Mail gateway couldn't parse To: address (%s) in "
                      "message %s" % (msg['To'], msg['Message-ID']))
            returnValue(self.MALFORMED_TO_ADDRESS)

        txn = self.store.newTransaction(label="MailReceiver.processReply")
        result = (yield txn.imipLookupByToken(token))
        yield txn.commit()
        try:
            # Note the results are returned as utf-8 encoded strings
            organizer, attendee, _ignore_icaluid = result[0]
        except:
            # This isn't a token we recognize
            log.error("Mail gateway found a token (%s) but didn't "
                      "recognize it in message %s" %
                      (token, msg['Message-ID']))
            returnValue(self.UNKNOWN_TOKEN)

        for part in msg.walk():
            if part.get_content_type() == "text/calendar":
                calBody = part.get_payload(decode=True)
                break
        else:
            # No icalendar attachment
            log.warn("Mail gateway didn't find an icalendar attachment "
                     "in message %s" % (msg['Message-ID'], ))

            toAddr = None
            fromAddr = attendee[7:]
            if organizer.startswith("mailto:"):
                toAddr = organizer[7:]
            elif organizer.startswith("urn:x-uid:"):
                uid = organizer[10:]
                record = yield self.directory.recordWithUID(uid)
                try:
                    if record and record.emailAddresses:
                        toAddr = list(record.emailAddresses)[0]
                except AttributeError:
                    pass

            if toAddr is None:
                log.error("Don't have an email address for the organizer; "
                          "ignoring reply.")
                returnValue(self.NO_ORGANIZER_ADDRESS)

            settings = config.Scheduling["iMIP"]["Sending"]
            smtpSender = SMTPSender(settings.Username, settings.Password,
                                    settings.UseSSL, settings.Server,
                                    settings.Port)

            del msg["From"]
            msg["From"] = fromAddr
            del msg["Reply-To"]
            msg["Reply-To"] = fromAddr
            del msg["To"]
            msg["To"] = toAddr
            log.warn("Mail gateway forwarding reply back to organizer")
            yield smtpSender.sendMessage(fromAddr, toAddr, messageid(),
                                         msg.as_string())
            returnValue(self.REPLY_FORWARDED_TO_ORGANIZER)

        # Process the imip attachment; inject to calendar server

        log.debug(calBody)
        calendar = Component.fromString(calBody)
        event = calendar.mainComponent()

        # Don't let a missing PRODID prevent the reply from being processed
        if not calendar.hasProperty("PRODID"):
            calendar.addProperty(Property("PRODID", "Unknown"))

        calendar.removeAllButOneAttendee(attendee)
        organizerProperty = calendar.getOrganizerProperty()
        if organizerProperty is None:
            # ORGANIZER is required per rfc2446 section 3.2.3
            log.warn("Mail gateway didn't find an ORGANIZER in REPLY %s" %
                     (msg['Message-ID'], ))
            event.addProperty(Property("ORGANIZER", organizer))
        else:
            organizerProperty.setValue(organizer)

        if not calendar.getAttendees():
            # The attendee we're expecting isn't there, so add it back
            # with a SCHEDULE-STATUS of SERVICE_UNAVAILABLE.
            # The organizer will then see that the reply was not successful.
            attendeeProp = Property("ATTENDEE",
                                    attendee,
                                    params={
                                        "SCHEDULE-STATUS":
                                        iTIPRequestStatus.SERVICE_UNAVAILABLE,
                                    })
            event.addProperty(attendeeProp)

            # TODO: We have talked about sending an email to the reply-to
            # at this point, to let them know that their reply was missing
            # the appropriate ATTENDEE.  This will require a new localizable
            # email template for the message.

        txn = self.store.newTransaction(label="MailReceiver.processReply")
        yield txn.enqueue(IMIPReplyWork,
                          organizer=organizer,
                          attendee=attendee,
                          icalendarText=str(calendar))
        yield txn.commit()
        returnValue(self.INJECTION_SUBMITTED)
예제 #47
0
def getComp(str):
    calendar = Component.fromString(str)
    comp = calendar.mainComponent()
    return comp
예제 #48
0
    def test_index_revisions(self):
        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
"""
        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
END:VCALENDAR
"""
        data3 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.3
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
END:VCALENDAR
"""

        calendar = Component.fromString(data1)
        self.db.addResource("data1.ics", calendar)
        calendar = Component.fromString(data2)
        self.db.addResource("data2.ics", calendar)
        calendar = Component.fromString(data3)
        self.db.addResource("data3.ics", calendar)
        self.db.deleteResource("data3.ics")

        tests = (
            (0, (
                [
                    "data1.ics",
                    "data2.ics",
                ],
                [],
                [],
            )),
            (1, (
                [
                    "data2.ics",
                ],
                [
                    "data3.ics",
                ],
                [],
            )),
            (2, (
                [],
                [
                    "data3.ics",
                ],
                [],
            )),
            (3, (
                [],
                [
                    "data3.ics",
                ],
                [],
            )),
            (4, (
                [],
                [],
                [],
            )),
            (5, (
                [],
                [],
                [],
            )),
        )

        for revision, results in tests:
            self.assertEquals(
                self.db.whatchanged(revision), results,
                "Mismatched results for whatchanged with revision %d" %
                (revision, ))
예제 #49
0
    def test_ImportComponentAttendee(self):

        # Have user02 invite this user01
        yield storeComponentInHomeAndCalendar(
            self.store,
            Component.allFromString(DATA_USER02_INVITES_USER01_ORGANIZER_COPY),
            "user02", "calendar", "invite.ics")

        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)

        # Delete the attendee's copy, thus declining the event
        txn = self.store.newTransaction()
        home = yield txn.calendarHomeWithUID("user01")
        collection = yield home.childWithName("calendar")
        objects = yield collection.objectResources()
        self.assertEquals(len(objects), 1)
        yield objects[0].remove()
        yield txn.commit()
        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)

        # Make sure attendee's copy is gone
        txn = self.store.newTransaction()
        home = yield txn.calendarHomeWithUID("user01")
        collection = yield home.childWithName("calendar")
        objects = yield collection.objectResources()
        self.assertEquals(len(objects), 0)
        yield txn.commit()

        # Make sure attendee shows as declined to the organizer
        txn = self.store.newTransaction()
        home = yield txn.calendarHomeWithUID("user02")
        collection = yield home.childWithName("calendar")
        objects = yield collection.objectResources()
        self.assertEquals(len(objects), 1)
        component = yield objects[0].component()
        prop = component.getAttendeeProperty(("urn:x-uid:user01", ))
        self.assertEquals(prop.parameterValue("PARTSTAT"), "DECLINED")
        yield txn.commit()

        # When importing the event again, update through the organizer's copy
        # of the event as if it were an iTIP reply
        component = Component.allFromString(
            DATA_USER02_INVITES_USER01_ATTENDEE_COPY)
        yield importCollectionComponent(self.store, component)

        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)

        # Make sure organizer now sees the right partstats
        txn = self.store.newTransaction()
        home = yield txn.calendarHomeWithUID("user02")
        collection = yield home.childWithName("calendar")
        objects = yield collection.objectResources()
        self.assertEquals(len(objects), 1)
        component = yield objects[0].component()
        # print(str(component))
        props = component.getAttendeeProperties(("urn:x-uid:user01", ))
        # The master is ACCEPTED
        self.assertEquals(props[0].parameterValue("PARTSTAT"), "ACCEPTED")
        # 2nd instance is TENTATIVE
        self.assertEquals(props[1].parameterValue("PARTSTAT"), "TENTATIVE")
        # 3rd instance is not in the attendee's copy, so remains DECLILNED
        self.assertEquals(props[2].parameterValue("PARTSTAT"), "DECLINED")
        yield txn.commit()

        # Make sure attendee now sees the right partstats
        txn = self.store.newTransaction()
        home = yield txn.calendarHomeWithUID("user01")
        collection = yield home.childWithName("calendar")
        objects = yield collection.objectResources()
        self.assertEquals(len(objects), 1)
        component = yield objects[0].component()
        # print(str(component))
        props = component.getAttendeeProperties(("urn:x-uid:user01", ))
        # The master is ACCEPTED
        self.assertEquals(props[0].parameterValue("PARTSTAT"), "ACCEPTED")
        # 2nd instance is TENTATIVE
        self.assertEquals(props[1].parameterValue("PARTSTAT"), "TENTATIVE")
        # 3rd instance is not in the organizer's copy, so should inherit
        # the value from the master, which is ACCEPTED
        self.assertEquals(props[2].parameterValue("PARTSTAT"), "ACCEPTED")
        yield txn.commit()
예제 #50
0
    def test_index_timespan(self):
        data = (
            (
                "#1.1 Simple component - busy",
                "1.1",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""",
                "20080601T000000Z",
                "20080602T000000Z",
                "mailto:[email protected]",
                (('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00", 'B',
                  'F'), ),
            ),
            (
                "#1.2 Simple component - transparent",
                "1.2",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.2
DTSTART:20080602T120000Z
DTEND:20080602T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR
""",
                "20080602T000000Z",
                "20080603T000000Z",
                "mailto:[email protected]",
                (('N', "2008-06-02 12:00:00", "2008-06-02 13:00:00", 'B',
                  'T'), ),
            ),
            (
                "#1.3 Simple component - canceled",
                "1.3",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.3
DTSTART:20080603T120000Z
DTEND:20080603T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
STATUS:CANCELLED
END:VEVENT
END:VCALENDAR
""",
                "20080603T000000Z",
                "20080604T000000Z",
                "mailto:[email protected]",
                (('N', "2008-06-03 12:00:00", "2008-06-03 13:00:00", 'F',
                  'F'), ),
            ),
            (
                "#1.4 Simple component - tentative",
                "1.4",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.4
DTSTART:20080604T120000Z
DTEND:20080604T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
STATUS:TENTATIVE
END:VEVENT
END:VCALENDAR
""",
                "20080604T000000Z",
                "20080605T000000Z",
                "mailto:[email protected]",
                (('N', "2008-06-04 12:00:00", "2008-06-04 13:00:00", 'T',
                  'F'), ),
            ),
            (
                "#2.1 Recurring component - busy",
                "2.1",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.1
DTSTART:20080605T120000Z
DTEND:20080605T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=DAILY;COUNT=2
END:VEVENT
END:VCALENDAR
""",
                "20080605T000000Z",
                "20080607T000000Z",
                "mailto:[email protected]",
                (
                    ('N', "2008-06-05 12:00:00", "2008-06-05 13:00:00", 'B',
                     'F'),
                    ('N', "2008-06-06 12:00:00", "2008-06-06 13:00:00", 'B',
                     'F'),
                ),
            ),
            (
                "#2.2 Recurring component - busy",
                "2.2",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.2
DTSTART:20080607T120000Z
DTEND:20080607T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=DAILY;COUNT=2
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-2.2
RECURRENCE-ID:20080608T120000Z
DTSTART:20080608T140000Z
DTEND:20080608T150000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR
""",
                "20080607T000000Z",
                "20080609T000000Z",
                "mailto:[email protected]",
                (
                    ('N', "2008-06-07 12:00:00", "2008-06-07 13:00:00", 'B',
                     'F'),
                    ('N', "2008-06-08 14:00:00", "2008-06-08 15:00:00", 'B',
                     'T'),
                ),
            ),
        )

        for description, name, calendar_txt, trstart, trend, organizer, instances in data:
            calendar = Component.fromString(calendar_txt)

            with open(os.path.join(self.indexDirPath.path, name), "w") as f:
                f.write(calendar_txt)

            self.db.addResource(name, calendar)
            self.assertTrue(self.db.resourceExists(name), msg=description)

            # Create fake filter element to match time-range
            filter = caldavxml.Filter(
                caldavxml.ComponentFilter(
                    caldavxml.ComponentFilter(
                        TimeRange(
                            start=trstart,
                            end=trend,
                        ),
                        name=("VEVENT", "VFREEBUSY", "VAVAILABILITY"),
                    ),
                    name="VCALENDAR",
                ))
            filter = Filter(filter)

            resources = yield self.db.indexedSearch(filter, fbtype=True)
            index_results = set()
            for _ignore_name, _ignore_uid, type, test_organizer, float, start, end, fbtype, transp in resources:
                self.assertEqual(test_organizer, organizer, msg=description)
                index_results.add((
                    float,
                    start,
                    end,
                    fbtype,
                    transp,
                ))

            self.assertEqual(set(instances), index_results, msg=description)
예제 #51
0
class Inviter(BaseProfile):
    """
    A Calendar user who invites other users to new events.
    """
    _eventTemplate = Component.fromString("""\
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.3//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
CREATED:20101018T155431Z
UID:C98AD237-55AD-4F7D-9009-0D355D835822
DTEND;TZID=America/New_York:20101021T130000
TRANSP:OPAQUE
SUMMARY:Simple event
DTSTART;TZID=America/New_York:20101021T120000
DTSTAMP:20101018T155438Z
SEQUENCE:2
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"))

    def setParameters(
            self,
            enabled=True,
            sendInvitationDistribution=NormalDistribution(600, 60),
            inviteeDistribution=UniformDiscreteDistribution(range(-10, 11)),
            inviteeClumping=True,
            inviteeCountDistribution=LogNormalDistribution(1.2, 1.2),
            eventStartDistribution=NearFutureDistribution(),
            eventDurationDistribution=UniformDiscreteDistribution(
                [15 * 60, 30 * 60, 45 * 60, 60 * 60, 120 * 60]),
            recurrenceDistribution=RecurrenceDistribution(False),
    ):
        self.enabled = enabled
        self._sendInvitationDistribution = sendInvitationDistribution
        self._inviteeDistribution = inviteeDistribution
        self._inviteeClumping = inviteeClumping
        self._inviteeCountDistribution = inviteeCountDistribution
        self._eventStartDistribution = eventStartDistribution
        self._eventDurationDistribution = eventDurationDistribution
        self._recurrenceDistribution = recurrenceDistribution

    def run(self):
        return loopWithDistribution(self._reactor,
                                    self._sendInvitationDistribution,
                                    self._invite)

    def _addAttendee(self, event, attendees):
        """
        Create a new attendee to add to the list of attendees for the
        given event.
        """
        selfRecord = self._sim.getUserRecord(self._number)
        invitees = set([u'mailto:%s' % (selfRecord.email, )])
        for att in attendees:
            invitees.add(att.value())

        for _ignore_i in range(10):

            sample = self._inviteeDistribution.sample()
            if self._inviteeClumping:
                sample = self._number + sample
            invitee = max(0, sample)

            try:
                record = self._sim.getUserRecord(invitee)
            except IndexError:
                continue
            cuaddr = u'mailto:%s' % (record.email, )
            if cuaddr not in invitees:
                break
        else:
            raise CannotAddAttendee("Can't find uninvited user to invite.")

        attendee = Property(
            name=u'ATTENDEE',
            value=cuaddr.encode("utf-8"),
            params={
                'CN': record.commonName,
                'CUTYPE': 'INDIVIDUAL',
                'PARTSTAT': 'NEEDS-ACTION',
                'ROLE': 'REQ-PARTICIPANT',
                'RSVP': 'TRUE',
            },
        )

        event.addProperty(attendee)
        attendees.append(attendee)

    def _invite(self):
        """
        Try to add a new event, or perhaps remove an
        existing attendee from an event.

        @return: C{None} if there are no events to play with,
            otherwise a L{Deferred} which fires when the attendee
            change has been made.
        """

        if not self._client.started:
            return succeed(None)

        # Find calendars which are eligible for invites
        calendars = self._calendarsOfType(caldavxml.calendar, "VEVENT")

        while calendars:
            # Pick one at random from which to try to create an event
            # to modify.
            calendar = self.random.choice(calendars)
            calendars.remove(calendar)

            # Copy the template event and fill in some of its fields
            # to make a new event to create on the calendar.
            vcalendar = self._eventTemplate.duplicate()
            vevent = vcalendar.mainComponent()
            uid = str(uuid4())
            dtstart = self._eventStartDistribution.sample()
            dtend = dtstart + Duration(
                seconds=self._eventDurationDistribution.sample())
            vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
            vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
            vevent.replaceProperty(Property("DTSTART", dtstart))
            vevent.replaceProperty(Property("DTEND", dtend))
            vevent.replaceProperty(Property("UID", uid))

            rrule = self._recurrenceDistribution.sample()
            if rrule is not None:
                vevent.addProperty(Property(None, None, None,
                                            pycalendar=rrule))

            vevent.addProperty(self._client._makeSelfOrganizer())
            vevent.addProperty(self._client._makeSelfAttendee())

            attendees = list(vevent.properties('ATTENDEE'))
            for _ignore in range(int(self._inviteeCountDistribution.sample())):
                try:
                    self._addAttendee(vevent, attendees)
                except CannotAddAttendee:
                    continue

            href = '%s%s.ics' % (calendar.url, uid)
            d = self._client.addInvite(href, vcalendar)
            return self._newOperation("invite", d)
예제 #52
0
class HomeMigrationTests(CommonCommonTests, TestCase):
    """
    Tests for L{UpgradeToDatabaseStep}.
    """

    av1 = Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VAVAILABILITY
ORGANIZER:mailto:[email protected]
UID:[email protected]
DTSTAMP:20061005T133225Z
DTEND:20140101T000000Z
BEGIN:AVAILABLE
UID:[email protected]
DTSTAMP:20061005T133225Z
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART:20130101T090000Z
DTEND:20130101T170000Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""")

    @inlineCallbacks
    def setUp(self):
        """
        Set up two stores to migrate between.
        """

        yield super(HomeMigrationTests, self).setUp()
        yield self.buildStoreAndDirectory(extraUids=(
            u"home1",
            u"home2",
            u"home3",
            u"home_defaults",
            u"home_no_splits",
            u"home_splits",
            u"home_splits_shared",
        ))
        self.sqlStore = self.store

        # Add some files to the file store.

        self.filesPath = CachingFilePath(self.mktemp())
        self.filesPath.createDirectory()
        fileStore = self.fileStore = CommonDataStore(
            self.filesPath, {"push": StubNotifierFactory()}, self.directory,
            True, True)
        self.upgrader = UpgradeToDatabaseStep(self.fileStore, self.sqlStore)

        requirements = CommonTests.requirements
        extras = deriveValue(self, "extraRequirements", lambda t: {})
        requirements = self.mergeRequirements(requirements, extras)

        yield populateCalendarsFrom(requirements, fileStore)
        md5s = CommonTests.md5s
        yield resetCalendarMD5s(md5s, fileStore)
        self.filesPath.child("calendars").child("__uids__").child("ho").child(
            "me").child("home1").child(".some-extra-data").setContent(
                "some extra data")

        requirements = ABCommonTests.requirements
        yield populateAddressBooksFrom(requirements, fileStore)
        md5s = ABCommonTests.md5s
        yield resetAddressBookMD5s(md5s, fileStore)
        self.filesPath.child("addressbooks").child("__uids__").child(
            "ho").child("me").child("home1").child(
                ".some-extra-data").setContent("some extra data")

        # Add some properties we want to check get migrated over
        txn = self.fileStore.newTransaction()
        home = yield txn.calendarHomeWithUID("home_defaults")

        cal = yield home.calendarWithName("calendar_1")
        props = cal.properties()
        props[PropertyName.fromElement(
            caldavxml.SupportedCalendarComponentSet
        )] = caldavxml.SupportedCalendarComponentSet(
            caldavxml.CalendarComponent(name="VEVENT"),
            caldavxml.CalendarComponent(name="VTODO"),
        )
        props[PropertyName.fromElement(
            element.ResourceType)] = element.ResourceType(
                element.Collection(),
                caldavxml.Calendar(),
            )
        props[PropertyName.fromElement(
            customxml.GETCTag)] = customxml.GETCTag.fromString("foobar")

        inbox = yield home.calendarWithName("inbox")
        props = inbox.properties()
        props[PropertyName.fromElement(
            customxml.CalendarAvailability
        )] = customxml.CalendarAvailability.fromString(str(self.av1))
        props[PropertyName.fromElement(
            caldavxml.ScheduleDefaultCalendarURL
        )] = caldavxml.ScheduleDefaultCalendarURL(
            element.HRef.fromString(
                "/calendars/__uids__/home_defaults/calendar_1"), )

        yield txn.commit()

    def mergeRequirements(self, a, b):
        """
        Merge two requirements dictionaries together, modifying C{a} and
        returning it.

        @param a: Some requirements, in the format of
            L{CommonTests.requirements}.
        @type a: C{dict}

        @param b: Some additional requirements, to be merged into C{a}.
        @type b: C{dict}

        @return: C{a}
        @rtype: C{dict}
        """
        for homeUID in b:
            homereq = a.setdefault(homeUID, {})
            homeExtras = b[homeUID]
            for calendarUID in homeExtras:
                calreq = homereq.setdefault(calendarUID, {})
                calendarExtras = homeExtras[calendarUID]
                calreq.update(calendarExtras)
        return a

    @withSpecialValue(
        "extraRequirements", {
            "home1": {
                "calendar_1": {
                    "bogus.ics":
                    (getModule("twistedcaldav").filePath.sibling("zoneinfo").
                     child("EST.ics").getContent(), CommonTests.metadata1)
                }
            }
        })
    @inlineCallbacks
    def test_unknownTypeNotMigrated(self):
        """
        The only types of calendar objects that should get migrated are VEVENTs
        and VTODOs.  Other component types, such as free-standing VTIMEZONEs,
        don't have a UID and can't be stored properly in the database, so they
        should not be migrated.
        """
        yield self.upgrader.stepWithResult(None)
        txn = self.sqlStore.newTransaction()
        self.addCleanup(txn.commit)
        self.assertIdentical(
            None, (yield
                   (yield
                    (yield
                     (yield txn.calendarHomeWithUID("home1")).calendarWithName(
                         "calendar_1"))).calendarObjectWithName("bogus.ics")))

    @inlineCallbacks
    def test_upgradeCalendarHomes(self):
        """
        L{UpgradeToDatabaseService.startService} will do the upgrade, then
        start its dependent service by adding it to its service hierarchy.
        """

        # Create a fake directory in the same place as a home, but with a non-existent uid
        fake_dir = self.filesPath.child("calendars").child("__uids__").child(
            "ho").child("me").child("foobar")
        fake_dir.makedirs()

        # Create a fake file in the same place as a home,with a name that matches the hash uid prefix
        fake_file = self.filesPath.child("calendars").child("__uids__").child(
            "ho").child("me").child("home_file")
        fake_file.setContent("")

        yield self.upgrader.stepWithResult(None)
        txn = self.sqlStore.newTransaction()
        self.addCleanup(txn.commit)
        for uid in CommonTests.requirements:
            if CommonTests.requirements[uid] is not None:
                self.assertNotIdentical(None,
                                        (yield txn.calendarHomeWithUID(uid)))
        # Successfully migrated calendar homes are deleted
        self.assertFalse(
            self.filesPath.child("calendars").child("__uids__").child(
                "ho").child("me").child("home1").exists())

        # Want metadata preserved
        home = (yield txn.calendarHomeWithUID("home1"))
        calendar = (yield home.calendarWithName("calendar_1"))
        for name, metadata, md5 in (
            ("1.ics", CommonTests.metadata1, CommonTests.md5Values[0]),
            ("2.ics", CommonTests.metadata2, CommonTests.md5Values[1]),
            ("3.ics", CommonTests.metadata3, CommonTests.md5Values[2]),
        ):
            object = (yield calendar.calendarObjectWithName(name))
            self.assertEquals(object.getMetadata(), metadata)
            self.assertEquals(object.md5(), md5)

    @withSpecialValue("extraRequirements", {"nonexistent": {"calendar_1": {}}})
    @inlineCallbacks
    def test_upgradeCalendarHomesMissingDirectoryRecord(self):
        """
        Test an upgrade where a directory record is missing for a home;
        the original home directory will remain on disk.
        """
        yield self.upgrader.stepWithResult(None)
        txn = self.sqlStore.newTransaction()
        self.addCleanup(txn.commit)
        for uid in CommonTests.requirements:
            if CommonTests.requirements[uid] is not None:
                self.assertNotIdentical(None,
                                        (yield txn.calendarHomeWithUID(uid)))
        self.assertIdentical(None,
                             (yield txn.calendarHomeWithUID(u"nonexistent")))
        # Skipped calendar homes are not deleted
        self.assertTrue(
            self.filesPath.child("calendars").child("__uids__").child(
                "no").child("ne").child("nonexistent").exists())

    @inlineCallbacks
    def test_upgradeExistingHome(self):
        """
        L{UpgradeToDatabaseService.startService} will skip migrating existing
        homes.
        """
        startTxn = self.sqlStore.newTransaction("populate empty sample")
        yield startTxn.calendarHomeWithUID("home1", create=True)
        yield startTxn.commit()
        yield self.upgrader.stepWithResult(None)
        vrfyTxn = self.sqlStore.newTransaction("verify sample still empty")
        self.addCleanup(vrfyTxn.commit)
        home = yield vrfyTxn.calendarHomeWithUID("home1")
        # The default calendar is still there.
        self.assertNotIdentical(None,
                                (yield home.calendarWithName("calendar")))
        # The migrated calendar isn't.
        self.assertIdentical(None, (yield home.calendarWithName("calendar_1")))

    @inlineCallbacks
    def test_upgradeAttachments(self):
        """
        L{UpgradeToDatabaseService.startService} upgrades calendar attachments
        as well.
        """

        # Need to tweak config and settings to setup dropbox to work
        self.patch(config, "EnableDropBox", True)
        self.patch(config, "EnableManagedAttachments", False)
        self.sqlStore.enableManagedAttachments = False

        txn = self.sqlStore.newTransaction()
        cs = schema.CALENDARSERVER
        yield Delete(From=cs, Where=cs.NAME == "MANAGED-ATTACHMENTS").on(txn)
        yield txn.commit()

        txn = self.fileStore.newTransaction()
        committed = []

        def maybeCommit():
            if not committed:
                committed.append(True)
                return txn.commit()

        self.addCleanup(maybeCommit)

        @inlineCallbacks
        def getSampleObj():
            home = (yield txn.calendarHomeWithUID("home1"))
            calendar = (yield home.calendarWithName("calendar_1"))
            object = (yield calendar.calendarObjectWithName("1.ics"))
            returnValue(object)

        inObject = yield getSampleObj()
        someAttachmentName = "some-attachment"
        someAttachmentType = MimeType.fromString("application/x-custom-type")
        attachment = yield inObject.createAttachmentWithName(
            someAttachmentName, )
        transport = attachment.store(someAttachmentType)
        someAttachmentData = "Here is some data for your attachment, enjoy."
        transport.write(someAttachmentData)
        yield transport.loseConnection()
        yield maybeCommit()
        yield self.upgrader.stepWithResult(None)
        committed = []
        txn = self.sqlStore.newTransaction()
        outObject = yield getSampleObj()
        outAttachment = yield outObject.attachmentWithName(someAttachmentName)
        allDone = Deferred()

        class SimpleProto(Protocol):
            data = ''

            def dataReceived(self, data):
                self.data += data

            def connectionLost(self, reason):
                allDone.callback(self.data)

        self.assertEquals(outAttachment.contentType(), someAttachmentType)
        outAttachment.retrieve(SimpleProto())
        allData = yield allDone
        self.assertEquals(allData, someAttachmentData)

    @inlineCallbacks
    def test_upgradeAddressBookHomes(self):
        """
        L{UpgradeToDatabaseService.startService} will do the upgrade, then
        start its dependent service by adding it to its service hierarchy.
        """
        yield self.upgrader.stepWithResult(None)
        txn = self.sqlStore.newTransaction()
        self.addCleanup(txn.commit)
        for uid in ABCommonTests.requirements:
            if ABCommonTests.requirements[uid] is not None:
                self.assertNotIdentical(
                    None, (yield txn.addressbookHomeWithUID(uid)))
        # Successfully migrated addressbook homes are deleted
        self.assertFalse(
            self.filesPath.child("addressbooks").child("__uids__").child(
                "ho").child("me").child("home1").exists())

        # Want metadata preserved
        home = (yield txn.addressbookHomeWithUID("home1"))
        adbk = (yield home.addressbookWithName("addressbook"))
        for name, md5 in (
            ("1.vcf", ABCommonTests.md5Values[0]),
            ("2.vcf", ABCommonTests.md5Values[1]),
            ("3.vcf", ABCommonTests.md5Values[2]),
        ):
            object = (yield adbk.addressbookObjectWithName(name))
            self.assertEquals(object.md5(), md5)

    @inlineCallbacks
    def test_upgradeProperties(self):
        """
        L{UpgradeToDatabaseService.startService} will do the upgrade, then
        start its dependent service by adding it to its service hierarchy.
        """
        yield self.upgrader.stepWithResult(None)
        txn = self.sqlStore.newTransaction()
        self.addCleanup(txn.commit)

        # Want metadata preserved
        home = (yield txn.calendarHomeWithUID("home_defaults"))
        cal = (yield home.calendarWithName("calendar_1"))
        inbox = (yield home.calendarWithName("inbox"))

        # Supported components
        self.assertEqual(cal.getSupportedComponents(), "VEVENT")
        self.assertTrue(cal.properties().get(
            PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet))
                        is None)

        # Resource type removed
        self.assertTrue(cal.properties().get(
            PropertyName.fromElement(element.ResourceType)) is None)

        # Ctag removed
        self.assertTrue(cal.properties().get(
            PropertyName.fromElement(customxml.GETCTag)) is None)

        # Availability
        self.assertEquals(str(home.getAvailability()), str(self.av1))
        self.assertTrue(inbox.properties().get(
            PropertyName.fromElement(customxml.CalendarAvailability)) is None)

        # Default calendar
        self.assertTrue(home.isDefaultCalendar(cal))
        self.assertTrue(inbox.properties().get(
            PropertyName.fromElement(caldavxml.ScheduleDefaultCalendarURL)) is
                        None)

    def test_fileStoreFromPath(self):
        """
        Verify that fileStoreFromPath() will return a CommonDataStore if
        the given path contains either "calendars" or "addressbooks"
        sub-directories.  Otherwise it returns None
        """

        # No child directories
        docRootPath = CachingFilePath(self.mktemp())
        docRootPath.createDirectory()
        step = UpgradeToDatabaseStep.fileStoreFromPath(docRootPath)
        self.assertEquals(step, None)

        # "calendars" child directory exists
        childPath = docRootPath.child("calendars")
        childPath.createDirectory()
        step = UpgradeToDatabaseStep.fileStoreFromPath(docRootPath)
        self.assertTrue(isinstance(step, CommonDataStore))
        childPath.remove()

        # "addressbooks" child directory exists
        childPath = docRootPath.child("addressbooks")
        childPath.createDirectory()
        step = UpgradeToDatabaseStep.fileStoreFromPath(docRootPath)
        self.assertTrue(isinstance(step, CommonDataStore))
        childPath.remove()
예제 #53
0
    def test_index(self):
        data = (
            (
                "#1.1 Simple component",
                "1.1",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""",
                False,
                True,
            ),
            (
                "#2.1 Recurring component",
                "2.1",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
END:VCALENDAR
""",
                False,
                True,
            ),
            (
                "#2.2 Recurring component with override",
                "2.2",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.2
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-2.2
RECURRENCE-ID:20080608T120000Z
DTSTART:20080608T120000Z
DTEND:20080608T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""",
                False,
                True,
            ),
            (
                "#2.3 Recurring component with broken override - new",
                "2.3",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.3
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-2.3
RECURRENCE-ID:20080609T120000Z
DTSTART:20080608T120000Z
DTEND:20080608T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""",
                False,
                False,
            ),
            (
                "#2.4 Recurring component with broken override - existing",
                "2.4",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-2.4
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=WEEKLY;COUNT=2
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-2.4
RECURRENCE-ID:20080609T120000Z
DTSTART:20080608T120000Z
DTEND:20080608T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""",
                True,
                True,
            ),
        )

        for description, name, calendar_txt, reCreate, ok in data:
            calendar = Component.fromString(calendar_txt)
            if ok:
                with open(os.path.join(self.indexDirPath.path, name),
                          "w") as f:
                    f.write(calendar_txt)

                self.db.addResource(name, calendar, reCreate=reCreate)
                self.assertTrue(self.db.resourceExists(name), msg=description)
            else:
                self.assertRaises(InvalidOverriddenInstanceError,
                                  self.db.addResource, name, calendar)
                self.assertFalse(self.db.resourceExists(name), msg=description)

        self.db._db_recreate()
        for description, name, calendar_txt, reCreate, ok in data:
            if ok:
                self.assertTrue(self.db.resourceExists(name), msg=description)
            else:
                self.assertFalse(self.db.resourceExists(name), msg=description)

        self.db.testAndUpdateIndex(DateTime(2020, 1, 1))
        for description, name, calendar_txt, reCreate, ok in data:
            if ok:
                self.assertTrue(self.db.resourceExists(name), msg=description)
            else:
                self.assertFalse(self.db.resourceExists(name), msg=description)
예제 #54
0
class Eventer(BaseProfile):
    """
    A Calendar user who creates new events.
    """
    _eventTemplate = Component.fromString("""\
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.3//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
CREATED:20101018T155431Z
UID:C98AD237-55AD-4F7D-9009-0D355D835822
DTEND;TZID=America/New_York:20101021T130000
TRANSP:OPAQUE
SUMMARY:Simple event
DTSTART;TZID=America/New_York:20101021T120000
DTSTAMP:20101018T155438Z
SEQUENCE:2
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"))

    def setParameters(
            self,
            enabled=True,
            interval=25,
            eventStartDistribution=NearFutureDistribution(),
            eventDurationDistribution=UniformDiscreteDistribution(
                [15 * 60, 30 * 60, 45 * 60, 60 * 60, 120 * 60]),
            recurrenceDistribution=RecurrenceDistribution(False),
    ):
        self.enabled = enabled
        self._interval = interval
        self._eventStartDistribution = eventStartDistribution
        self._eventDurationDistribution = eventDurationDistribution
        self._recurrenceDistribution = recurrenceDistribution

    def run(self):
        self._call = LoopingCall(self._addEvent)
        self._call.clock = self._reactor
        return self._call.start(self._interval)

    def _addEvent(self):
        # Don't perform any operations until the client is up and running
        if not self._client.started:
            return succeed(None)

        calendar = self._getRandomCalendarOfType('VEVENT')

        if not calendar:
            # No VEVENT calendars, so no new event...
            return succeed(None)

        # Copy the template event and fill in some of its fields
        # to make a new event to create on the calendar.
        vcalendar = self._eventTemplate.duplicate()
        vevent = vcalendar.mainComponent()
        uid = str(uuid4())
        dtstart = self._eventStartDistribution.sample()
        dtend = dtstart + Duration(
            seconds=self._eventDurationDistribution.sample())
        vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
        vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
        vevent.replaceProperty(Property("DTSTART", dtstart))
        vevent.replaceProperty(Property("DTEND", dtend))
        vevent.replaceProperty(Property("UID", uid))

        rrule = self._recurrenceDistribution.sample()
        if rrule is not None:
            vevent.addProperty(Property(None, None, None, pycalendar=rrule))

        href = '%s%s.ics' % (calendar.url, uid)
        d = self._client.addEvent(href, vcalendar)
        return self._newOperation("create", d)
예제 #55
0
    def test_index_timespan_per_user(self):
        data = (
            (
                "#1.1 Single per-user non-recurring component",
                "1.1",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.1
X-CALENDARSERVER-PERUSER-UID:user01
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
TRANSP:TRANSPARENT
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
                "20080601T000000Z",
                "20080602T000000Z",
                "mailto:[email protected]",
                (
                    (
                        "user01",
                        (('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                          'B', 'T'), ),
                    ),
                    (
                        "user02",
                        (('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                          'B', 'F'), ),
                    ),
                ),
            ),
            (
                "#1.2 Two per-user non-recurring component",
                "1.2",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.2
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.2
X-CALENDARSERVER-PERUSER-UID:user01
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
TRANSP:TRANSPARENT
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.2
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
                "20080601T000000Z",
                "20080602T000000Z",
                "mailto:[email protected]",
                (
                    (
                        "user01",
                        (('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                          'B', 'T'), ),
                    ),
                    (
                        "user02",
                        (('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                          'B', 'F'), ),
                    ),
                    (
                        "user03",
                        (('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                          'B', 'F'), ),
                    ),
                ),
            ),
            (
                "#2.1 Single per-user simple recurring component",
                "2.1",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=DAILY;COUNT=10
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.1
X-CALENDARSERVER-PERUSER-UID:user01
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
TRANSP:TRANSPARENT
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
                "20080601T000000Z",
                "20080603T000000Z",
                "mailto:[email protected]",
                (
                    (
                        "user01",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'T'),
                            ('N', "2008-06-02 12:00:00", "2008-06-02 13:00:00",
                             'B', 'T'),
                        ),
                    ),
                    (
                        "user02",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'F'),
                            ('N', "2008-06-02 12:00:00", "2008-06-02 13:00:00",
                             'B', 'F'),
                        ),
                    ),
                ),
            ),
            (
                "#2.2 Two per-user simple recurring component",
                "2.2",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.2
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=DAILY;COUNT=10
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.2
X-CALENDARSERVER-PERUSER-UID:user01
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
TRANSP:TRANSPARENT
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.2
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
                "20080601T000000Z",
                "20080603T000000Z",
                "mailto:[email protected]",
                (
                    (
                        "user01",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'T'),
                            ('N', "2008-06-02 12:00:00", "2008-06-02 13:00:00",
                             'B', 'T'),
                        ),
                    ),
                    (
                        "user02",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'F'),
                            ('N', "2008-06-02 12:00:00", "2008-06-02 13:00:00",
                             'B', 'F'),
                        ),
                    ),
                    (
                        "user03",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'F'),
                            ('N', "2008-06-02 12:00:00", "2008-06-02 13:00:00",
                             'B', 'F'),
                        ),
                    ),
                ),
            ),
            (
                "#3.1 Single per-user complex recurring component",
                "3.1",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.1
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=DAILY;COUNT=10
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1.1
RECURRENCE-ID:20080602T120000Z
DTSTART:20080602T130000Z
DTEND:20080602T140000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.1
X-CALENDARSERVER-PERUSER-UID:user01
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
TRANSP:TRANSPARENT
END:X-CALENDARSERVER-PERINSTANCE
BEGIN:X-CALENDARSERVER-PERINSTANCE
RECURRENCE-ID:20080602T120000Z
TRANSP:OPAQUE
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
                "20080601T000000Z",
                "20080604T000000Z",
                "mailto:[email protected]",
                (
                    (
                        "user01",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'T'),
                            ('N', "2008-06-02 13:00:00", "2008-06-02 14:00:00",
                             'B', 'F'),
                            ('N', "2008-06-03 12:00:00", "2008-06-03 13:00:00",
                             'B', 'T'),
                        ),
                    ),
                    (
                        "user02",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'F'),
                            ('N', "2008-06-02 13:00:00", "2008-06-02 14:00:00",
                             'B', 'F'),
                            ('N', "2008-06-03 12:00:00", "2008-06-03 13:00:00",
                             'B', 'F'),
                        ),
                    ),
                ),
            ),
            (
                "#3.2 Two per-user complex recurring component",
                "3.2",
                """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-1.2
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
RRULE:FREQ=DAILY;COUNT=10
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1.2
RECURRENCE-ID:20080602T120000Z
DTSTART:20080602T130000Z
DTEND:20080602T140000Z
DTSTAMP:20080601T120000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.2
X-CALENDARSERVER-PERUSER-UID:user01
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
TRANSP:TRANSPARENT
END:X-CALENDARSERVER-PERINSTANCE
BEGIN:X-CALENDARSERVER-PERINSTANCE
RECURRENCE-ID:20080602T120000Z
TRANSP:OPAQUE
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
BEGIN:X-CALENDARSERVER-PERUSER
UID:12345-67890-1.2
X-CALENDARSERVER-PERUSER-UID:user02
BEGIN:X-CALENDARSERVER-PERINSTANCE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test
TRIGGER;RELATED=START:-PT10M
END:VALARM
END:X-CALENDARSERVER-PERINSTANCE
BEGIN:X-CALENDARSERVER-PERINSTANCE
RECURRENCE-ID:20080603T120000Z
TRANSP:TRANSPARENT
END:X-CALENDARSERVER-PERINSTANCE
END:X-CALENDARSERVER-PERUSER
END:VCALENDAR
""",
                "20080601T000000Z",
                "20080604T000000Z",
                "mailto:[email protected]",
                (
                    (
                        "user01",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'T'),
                            ('N', "2008-06-02 13:00:00", "2008-06-02 14:00:00",
                             'B', 'F'),
                            ('N', "2008-06-03 12:00:00", "2008-06-03 13:00:00",
                             'B', 'T'),
                        ),
                    ),
                    (
                        "user02",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'F'),
                            ('N', "2008-06-02 13:00:00", "2008-06-02 14:00:00",
                             'B', 'F'),
                            ('N', "2008-06-03 12:00:00", "2008-06-03 13:00:00",
                             'B', 'T'),
                        ),
                    ),
                    (
                        "user03",
                        (
                            ('N', "2008-06-01 12:00:00", "2008-06-01 13:00:00",
                             'B', 'F'),
                            ('N', "2008-06-02 13:00:00", "2008-06-02 14:00:00",
                             'B', 'F'),
                            ('N', "2008-06-03 12:00:00", "2008-06-03 13:00:00",
                             'B', 'F'),
                        ),
                    ),
                ),
            ),
        )

        for description, name, calendar_txt, trstart, trend, organizer, peruserinstances in data:
            calendar = Component.fromString(calendar_txt)

            with open(os.path.join(self.indexDirPath.path, name), "w") as f:
                f.write(calendar_txt)

            self.db.addResource(name, calendar)
            self.assertTrue(self.db.resourceExists(name), msg=description)

            # Create fake filter element to match time-range
            filter = caldavxml.Filter(
                caldavxml.ComponentFilter(
                    caldavxml.ComponentFilter(
                        TimeRange(
                            start=trstart,
                            end=trend,
                        ),
                        name=("VEVENT", "VFREEBUSY", "VAVAILABILITY"),
                    ),
                    name="VCALENDAR",
                ))
            filter = Filter(filter)

            for useruid, instances in peruserinstances:
                resources = yield self.db.indexedSearch(filter,
                                                        useruid=useruid,
                                                        fbtype=True)
                index_results = set()
                for _ignore_name, _ignore_uid, type, test_organizer, float, start, end, fbtype, transp in resources:
                    self.assertEqual(test_organizer,
                                     organizer,
                                     msg=description)
                    index_results.add((
                        str(float),
                        str(start),
                        str(end),
                        str(fbtype),
                        str(transp),
                    ))

                self.assertEqual(set(instances),
                                 index_results,
                                 msg="%s, user:%s" % (
                                     description,
                                     useruid,
                                 ))

            self.db.deleteResource(name)
예제 #56
0
class AlarmAcknowledger(BaseProfile):
    """
    A Calendar user who creates a new event, and then updates its alarm.
    """
    _eventTemplate = Component.fromString("""\
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.3//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
CREATED:20101018T155431Z
UID:C98AD237-55AD-4F7D-9009-0D355D835822
DTEND;TZID=America/New_York:20101021T130000
TRANSP:OPAQUE
SUMMARY:Simple event
DTSTART;TZID=America/New_York:20101021T120000
DTSTAMP:20101018T155438Z
SEQUENCE:2
BEGIN:VALARM
X-WR-ALARMUID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
UID:D9D1AC84-F629-4B9D-9B6B-4A6CA9A11FEF
DESCRIPTION:Event reminder
TRIGGER:-PT8M
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"))

    def setParameters(
            self,
            enabled=True,
            interval=5,
            pastTheHour=[0, 15, 30, 45],
            eventStartDistribution=NearFutureDistribution(),
            eventDurationDistribution=UniformDiscreteDistribution(
                [15 * 60, 30 * 60, 45 * 60, 60 * 60, 120 * 60]),
            recurrenceDistribution=RecurrenceDistribution(False),
    ):
        self.enabled = enabled
        self._interval = interval
        self._pastTheHour = pastTheHour
        self._eventStartDistribution = eventStartDistribution
        self._eventDurationDistribution = eventDurationDistribution
        self._recurrenceDistribution = recurrenceDistribution
        self._lastMinuteChecked = -1

    def initialize(self):
        """
        Called before the profile runs for real. Can be used to initialize client state.

        @return: a L{Deferred} that fires when initialization is done
        """

        return self._initEvent()

    def run(self):
        self._call = LoopingCall(self._updateEvent)
        self._call.clock = self._reactor
        return self._call.start(self._interval)

    def _initEvent(self):
        # Don't perform any operations until the client is up and running
        if not self._client.started:
            return succeed(None)

        # If it already exists, don't re-create
        calendar = self._calendarsOfType(caldavxml.calendar, "VEVENT")[0]
        if calendar.events:
            events = [
                event for event in calendar.events.values()
                if event.url.endswith("event_to_update.ics")
            ]
            if events:
                return succeed(None)

        # Copy the template event and fill in some of its fields
        # to make a new event to create on the calendar.
        vcalendar = self._eventTemplate.duplicate()
        vevent = vcalendar.mainComponent()
        uid = str(uuid4())
        dtstart = self._eventStartDistribution.sample()
        dtend = dtstart + Duration(
            seconds=self._eventDurationDistribution.sample())
        vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
        vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
        vevent.replaceProperty(Property("DTSTART", dtstart))
        vevent.replaceProperty(Property("DTEND", dtend))
        vevent.replaceProperty(Property("UID", uid))

        rrule = self._recurrenceDistribution.sample()
        if rrule is not None:
            vevent.addProperty(Property(None, None, None, pycalendar=rrule))

        href = '%s%s' % (calendar.url, "event_to_update.ics")
        d = self._client.addEvent(href, vcalendar)
        return self._newOperation("create", d)

    def _shouldUpdate(self, minutePastTheHour):
        """
        We want to only acknowledge our alarm at the "past the hour" minutes
        we've been configured for.
        """
        should = False
        if minutePastTheHour in self._pastTheHour:
            # This is one of the minutes we should update on, but only update
            # as we pass into this minute, and not subsequent times
            if minutePastTheHour != self._lastMinuteChecked:
                should = True

        self._lastMinuteChecked = minutePastTheHour
        return should

    def _updateEvent(self):
        """
        Set the ACKNOWLEDGED property on an event.

        @return: C{None} if there are no events to play with,
            otherwise a L{Deferred} which fires when the acknowledged
            change has been made.
        """

        # Only do updates when we reach of the designated minutes past the hour
        if not self._shouldUpdate(datetime.now().minute):
            return succeed(None)

        if not self._client.started:
            return succeed(None)

        # If it does not exist, try to create it
        calendar = self._calendarsOfType(caldavxml.calendar, "VEVENT")[0]
        if not calendar.events:
            return self._initEvent()
        events = [
            event for event in calendar.events.values()
            if event.url.endswith("event_to_update.ics")
        ]
        if not events:
            return self._initEvent()
        event = events[0]

        # Add/update the ACKNOWLEDGED property
        component = event.component.mainComponent()
        component.replaceProperty(
            Property("ACKNOWLEDGED", DateTime.getNowUTC()))
        d = self._client.changeEvent(event.url)
        return self._newOperation("update", d)
예제 #57
0
def buildFreeBusyResult(fbinfo,
                        timerange,
                        organizer=None,
                        attendee=None,
                        uid=None,
                        method=None,
                        event_details=None):
    """
    Generate a VCALENDAR object containing a single VFREEBUSY that is the
    aggregate of the free busy info passed in.

    @param fbinfo:        the array of busy periods to use.
    @param timerange:     the L{TimeRange} for the query.
    @param organizer:     the L{Property} for the Organizer of the free busy request, or None.
    @param attendee:      the L{Property} for the Attendee responding to the free busy request, or None.
    @param uid:           the UID value from the free busy request.
    @param method:        the METHOD property value to insert.
    @param event_details: VEVENT components to add.
    @return:              the L{Component} containing the calendar data.
    """

    # Merge overlapping time ranges in each fb info section
    normalizePeriodList(fbinfo[0])
    normalizePeriodList(fbinfo[1])
    normalizePeriodList(fbinfo[2])

    # Now build a new calendar object with the free busy info we have
    fbcalendar = Component("VCALENDAR")
    fbcalendar.addProperty(Property("VERSION", "2.0"))
    fbcalendar.addProperty(Property("PRODID", iCalendarProductID))
    if method:
        fbcalendar.addProperty(Property("METHOD", method))
    fb = Component("VFREEBUSY")
    fbcalendar.addComponent(fb)
    if organizer is not None:
        fb.addProperty(organizer)
    if attendee is not None:
        fb.addProperty(attendee)
    fb.addProperty(Property("DTSTART", timerange.start))
    fb.addProperty(Property("DTEND", timerange.end))
    fb.addProperty(Property("DTSTAMP", DateTime.getNowUTC()))
    if len(fbinfo[0]) != 0:
        fb.addProperty(Property("FREEBUSY", fbinfo[0], {"FBTYPE": "BUSY"}))
    if len(fbinfo[1]) != 0:
        fb.addProperty(
            Property("FREEBUSY", fbinfo[1], {"FBTYPE": "BUSY-TENTATIVE"}))
    if len(fbinfo[2]) != 0:
        fb.addProperty(
            Property("FREEBUSY", fbinfo[2], {"FBTYPE": "BUSY-UNAVAILABLE"}))
    if uid is not None:
        fb.addProperty(Property("UID", uid))
    else:
        uid = str(uuid.uuid4())
        fb.addProperty(Property("UID", uid))

    if event_details:
        for vevent in event_details:
            fbcalendar.addComponent(vevent)

    return fbcalendar
예제 #58
0
        def __init__(self, data):

            self.ical = Component.fromString(data)
예제 #59
0
    def test_validation_processScheduleTags(self):
        """
        Test that schedule tags are correctly updated.
        """

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
END:VEVENT
END:VCALENDAR
"""

        calendar_collection = (yield self.calendarUnderTest(home="user01"))
        calendar = Component.fromString(data1)
        yield calendar_collection.createCalendarObjectWithName(
            "test.ics", calendar)
        yield self.commit()

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
SUMMARY:Changed #1
END:VEVENT
END:VCALENDAR
"""

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data2)
        yield calendar_resource.setComponent(calendar)
        schedule_tag = calendar_resource.scheduleTag
        yield self.commit()

        data3 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
SUMMARY:Changed #2
END:VEVENT
END:VCALENDAR
"""

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data3)
        yield calendar_resource.setComponent(calendar)
        self.assertNotEqual(calendar_resource.scheduleTag, schedule_tag)
        schedule_tag = calendar_resource.scheduleTag
        yield self.commit()

        data4 = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890-attendee-reply
DTSTAMP:20080601T120000Z
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
ORGANIZER;CN="User 01":mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE;PARTSTAT=ACCEPTED:mailto:[email protected]
SUMMARY:Changed #2
END:VEVENT
END:VCALENDAR
"""

        calendar_resource = (yield self.calendarObjectUnderTest(
            name="test.ics",
            home="user01",
        ))
        calendar = Component.fromString(data4)
        yield calendar_resource._setComponentInternal(
            calendar,
            internal_state=ComponentUpdateState.ORGANIZER_ITIP_UPDATE)
        self.assertEqual(calendar_resource.scheduleTag, schedule_tag)
        yield self.commit()
예제 #60
0
    def test_connectionRefusedForAttendee(self):
        data_organizer = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:uid_data
DTSTART:{now1:04d}0102T160000Z
DURATION:PT1H
CREATED:20060102T190000Z
DTSTAMP:20051222T210507Z
SUMMARY:data01_2
ORGANIZER:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n").format(**self.now)

        data_attendee = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:uid_data
DTSTART:{now1:04d}0102T160000Z
DURATION:PT1H
CREATED:20060102T190000Z
DTSTAMP:20051222T210507Z
SUMMARY:data01_2
ORGANIZER:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
ATTENDEE;PARTSTAT=DECLINED:mailto:[email protected]
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n").format(**self.now)

        # Organizer schedules
        home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user01", create=True)
        calendar = yield home.childWithName("calendar")
        yield calendar.createCalendarObjectWithName("1.ics", Component.fromString(data_organizer))
        yield self.commitTransaction(0)

        yield self.waitAllEmpty()

        # Data for user02
        home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user02", create=True)
        calendar = yield home.childWithName("calendar")
        cobjs = yield calendar.calendarObjects()
        self.assertEqual(len(cobjs), 1)
        self.assertEqual(cobjs[0].uid(), "uid_data")
        yield self.commitTransaction(0)

        # Data for puser02
        home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="puser02", create=True)
        calendar = yield home.childWithName("calendar")
        cobjs = yield calendar.calendarObjects()
        self.assertEqual(len(cobjs), 1)
        self.assertEqual(cobjs[0].uid(), "uid_data")
        yield self.commitTransaction(1)

        # Stop cross-pod connection from working
        self.refuseConnection = True

        # Attendee changes
        home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="puser02", create=True)
        calendar = yield home.childWithName("calendar")
        cobjs = yield calendar.calendarObjects()
        yield cobjs[0].setComponent(Component.fromString(data_attendee))
        yield self.commitTransaction(1)

        while True:
            jobs = yield JobItem.all(self.theTransactionUnderTest(1))
            yield self.commitTransaction(1)
            if len(jobs) == 1 and jobs[0].failed > 0:
                break

        # Organizer data unchanged
        cobj = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics")
        comp = yield cobj.componentForUser()
        self.assertTrue("DECLINED" not in str(comp))
        yield self.commitTransaction(0)

        # Now allow cross-pod to work
        self.refuseConnection = False

        yield self.waitAllEmpty()

        # Organizer data changed
        cobj = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics")
        comp = yield cobj.componentForUser()
        self.assertTrue("DECLINED" in str(comp))
        yield self.commitTransaction(0)