示例#1
0
    def _addEvent(self):
        if not self._client.started:
            return succeed(None)

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

        while calendars:
            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))

            href = '%s%s.ics' % (calendar.url, uid)
            d = self._client.addEvent(href, vcalendar)
            return self._newOperation("create", d)
示例#2
0
    def _initEvent(self):
        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)
示例#3
0
    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:
                    self._failedOperation("invite", "Cannot add attendee")
                    return succeed(None)

            href = '%s%s.ics' % (calendar.url, uid)
            d = self._client.addInvite(href, vcalendar)
            return self._newOperation("invite", d)
示例#4
0
def getEventDetails(event):
    detail = {}

    nowPyDT = DateTime.getNowUTC()
    nowDT = datetime.datetime.utcnow()
    oneYearInFuture = DateTime.getNowUTC()
    oneYearInFuture.offsetDay(365)

    component = yield event.component()
    mainSummary = component.mainComponent().propertyValue("SUMMARY", u"<no title>")
    whenTrashed = event.whenTrashed()
    ago = nowDT - whenTrashed

    detail["summary"] = mainSummary
    detail["whenTrashed"] = agoString(ago)
    detail["recoveryID"] = event._resourceID

    if component.isRecurring():
        detail["recurring"] = True
        detail["instances"] = []
        instances = component.cacheExpandedTimeRanges(oneYearInFuture)
        instances = sorted(instances.instances.values(), key=lambda x: x.start)
        limit = 3
        count = 0
        for instance in instances:
            if instance.start >= nowPyDT:
                summary = instance.component.propertyValue("SUMMARY", u"<no title>")
                location = locationString(instance.component)
                tzid = instance.component.getProperty("DTSTART").parameterValue("TZID", None)
                dtstart = instance.start
                if tzid is not None:
                    timezone = Timezone(tzid=tzid)
                    dtstart.adjustTimezone(timezone)
                detail["instances"].append(
                    {
                        "summary": summary,
                        "starttime": dtstart.getLocaleDateTime(DateTime.FULLDATE, False, True, dtstart.getTimezoneID()),
                        "location": location,
                    }
                )
                count += 1
                limit -= 1
            if limit == 0:
                break

    else:
        detail["recurring"] = False
        dtstart = component.mainComponent().propertyValue("DTSTART")
        detail["starttime"] = dtstart.getLocaleDateTime(DateTime.FULLDATE, False, True, dtstart.getTimezoneID())
        detail["location"] = locationString(component.mainComponent())

    returnValue(detail)
示例#5
0
def printEventDetails(event):
    nowPyDT = DateTime.getNowUTC()
    nowDT = datetime.datetime.utcnow()
    oneYearInFuture = DateTime.getNowUTC()
    oneYearInFuture.offsetDay(365)

    component = yield event.component()
    mainSummary = component.mainComponent().propertyValue("SUMMARY", u"<no title>")
    whenTrashed = event.whenTrashed()
    ago = nowDT - whenTrashed
    print("   Trashed {}:".format(agoString(ago)))

    if component.isRecurring():
        print(
            "      \"{}\" (repeating)  Recovery ID = {}".format(
                mainSummary, event._resourceID
            )
        )
        print("         ...upcoming instances:")
        instances = component.cacheExpandedTimeRanges(oneYearInFuture)
        instances = sorted(instances.instances.values(), key=lambda x: x.start)
        limit = 3
        count = 0
        for instance in instances:
            if instance.start >= nowPyDT:
                summary = instance.component.propertyValue("SUMMARY", u"<no title>")
                location = locationString(instance.component)
                tzid = instance.component.getProperty("DTSTART").parameterValue("TZID", None)
                dtstart = instance.start
                if tzid is not None:
                    timezone = Timezone(tzid=tzid)
                    dtstart.adjustTimezone(timezone)
                print("            \"{}\" {} {}".format(summary, startString(dtstart), location))
                count += 1
                limit -= 1
            if limit == 0:
                break
        if not count:
            print("            (none)")

    else:
        print(
            "      \"{}\" (non-repeating)  Recovery ID = {}".format(
                mainSummary, event._resourceID
            )
        )
        dtstart = component.mainComponent().propertyValue("DTSTART")
        location = locationString(component.mainComponent())
        print("         {} {}".format(startString(dtstart), location))
    def __init__(self, threshold, past):
        """
        @param threshold: the size in bytes that will trigger a split
        @type threshold: C{int}
        @param past: number of days in the past where the split will occur
        @type past: C{int}

        """
        self.threshold = threshold
        self.past = DateTime.getNowUTC()
        self.past.setHHMMSS(0, 0, 0)
        self.past.offsetDay(-past)
        self.now = DateTime.getNowUTC()
        self.now.setHHMMSS(0, 0, 0)
        self.now.offsetDay(-1)
示例#7
0
    def _updateEvent(self):
        """
        Try to add a new attendee to an 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)

        # 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)
示例#8
0
    def initNextTrigger(self):
        # Do not bother if its completed
        if self.mAlarmStatus == definitions.eAlarm_Status_Completed:
            return
        self.mStatusInit = True

        # Look for trigger immediately preceeding or equal to utc now
        nowutc = DateTime.getNowUTC()

        # Init done counter
        self.mDoneCount = 0

        # Determine the first trigger
        trigger = DateTime()
        self.getFirstTrigger(trigger)

        while self.mDoneCount < self.mRepeats:
            # See if next trigger is later than now
            next_trigger = trigger + self.mRepeatInterval
            if next_trigger > nowutc:
                break
            self.mDoneCount += 1
            trigger = next_trigger

        # Check for completion
        if trigger == self.mLastTrigger or (nowutc - trigger).getTotalSeconds() > 24 * 60 * 60:
            if self.mDoneCount == self.mRepeats:
                self.mAlarmStatus = definitions.eAlarm_Status_Completed
                return
            else:
                trigger = trigger + self.mRepeatInterval
                self.mDoneCount += 1

        self.mNextTrigger = trigger
    def createNewDatabase(self):
        """
        Create a new DB xml file from scratch by scanning zoneinfo.
        """

        self.dtstamp = DateTime.getNowUTC().getXMLText()
        self._scanTZs("")
        self._dumpTZs()
示例#10
0
    def doRequest(self):
        """
        Execute the actual HTTP request.
        """

        now = DateTime.getNowUTC()
        href = joinURL(self.sessions[0].calendarHref, "put.ics")
        self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1,), "text/calendar")
示例#11
0
    def _purgeUID(self, uid):

        if self.when is None:
            self.when = DateTime.getNowUTC()

        # Does the record exist?
        record = self.directory.recordWithUID(uid)
        if record is None:
            # The user has already been removed from the directory service.  We
            # need to fashion a temporary, fake record

            # FIXME: probably want a more elegant way to accomplish this,
            # since it requires the aggregate directory to examine these first:
            record = DirectoryRecord(self.directory, "users", uid, shortNames=(uid,), enabledForCalendaring=True)
            self.directory._tmpRecords["shortNames"][uid] = record
            self.directory._tmpRecords["uids"][uid] = record

        # Override augments settings for this record
        record.enabled = True
        record.enabledForCalendaring = True
        record.enabledForAddressBooks = True

        cua = "urn:uuid:%s" % (uid,)

        principalCollection = self.directory.principalCollection
        principal = principalCollection.principalForRecord(record)

        # See if calendar home is provisioned
        txn = self.store.newTransaction()
        storeCalHome = (yield txn.calendarHomeWithUID(uid))
        calHomeProvisioned = storeCalHome is not None

        # If in "completely" mode, unshare collections, remove notifications
        if calHomeProvisioned and self.completely:
            yield self._cleanHome(txn, storeCalHome)

        yield txn.commit()

        count = 0
        assignments = []

        if calHomeProvisioned:
            count = (yield self._cancelEvents(txn, uid, cua))

        # Remove empty calendar collections (and calendar home if no more
        # calendars)
        yield self._removeCalendarHome(uid)

        # Remove VCards
        count += (yield self._removeAddressbookHome(uid))

        if self.proxies and not self.dryrun:
            if self.verbose:
                print("Deleting any proxy assignments")
            assignments = (yield self._purgeProxyAssignments(principal))

        returnValue((count, assignments))
 def updateDatabase(self):
     """
     Update existing DB info by comparing md5's.
     """
     self.dtstamp = DateTime.getNowUTC().getXMLText()
     self.changeCount = 0
     self.changed = set()
     self._scanTZs("", checkIfChanged=True)
     if self.changeCount:
         self._dumpTZs()
示例#13
0
 def prepare(self):
     """
     Do some setup prior to the real request.
     """
     # Add resources to create required number of changes
     self.start = DateTime.getNowUTC()
     self.start.setHHMMSS(12, 0, 0)
     self.end = self.start.duplicate()
     self.end.offsetHours(1)
     for i in range(self.count):
         href = joinURL(self.sessions[0].calendarHref, "tr-query-%d.ics" % (i + 1,))
         self.sessions[0].writeData(URL(path=href), ICAL % (self.start.getText(), i + 1,), "text/calendar")
示例#14
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 = md5(str(fbcalendar) + str(time.time())).hexdigest()
        fb.addProperty(Property("UID", uid))

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

    return fbcalendar
示例#15
0
    def _transferVPOLLData(self, serverComponent, clientComponent):

        changed = False

        # Get the matching VVOTER component in each VPOLL
        serverVoter = serverComponent.voterComponentForVoter(self.attendee)
        clientVoter = clientComponent.voterComponentForVoter(self.attendee)

        # Now get a map of each response
        serverMap = serverVoter.voteMap()
        clientMap = clientVoter.voteMap()

        # Remove missing
        for poll_id in set(serverMap.keys()) - set(clientMap.keys()):
            serverVoter.removeComponent(serverMap[poll_id])
            changed = True

        # Add new ones
        for poll_id in set(clientMap.keys()) - set(serverMap.keys()):
            vote = clientMap[poll_id].duplicate()
            vote.replaceProperty(Property("LAST-MODIFIED", DateTime.getNowUTC()))
            serverVoter.addComponent(vote)
            changed = True

        # Look for response change
        for poll_id in set(serverMap.keys()) & set(clientMap.keys()):
            server_vote = serverMap[poll_id]
            client_vote = clientMap[poll_id]
            server_response = server_vote.propertyValue("RESPONSE")
            client_response = client_vote.propertyValue("RESPONSE")
            if server_response != client_response:
                if client_response is not None:
                    server_vote.replaceProperty(Property("RESPONSE", client_response))
                else:
                    server_vote.removeProperty("RESPONSE")
                server_vote.replaceProperty(Property("LAST-MODIFIED", DateTime.getNowUTC()))
                changed = True

        return changed
示例#16
0
    def doRequest(self):
        """
        Execute the actual HTTP request.
        """

        # Invite as user02
        now = DateTime.getNowUTC()
        href = joinURL(self.sessions[1].calendarHref, "organizer.ics")
        attendees = "\r\n".join(["ATTENDEE:mailto:[email protected]"] + [ATTENDEE % (ctr + 3,) for ctr in range(self.count - 1)])
        self.sessions[1].writeData(
            URL(path=href),
            ICAL.format(year=now.getYear() + 1, count=self.count, attendees=attendees),
            "text/calendar",
        )
示例#17
0
    def prepare(self):
        """
        Do some setup prior to the real request.
        """
        if not self.full:
            # Get current sync token
            results, _ignore_bad = self.sessions[0].getProperties(URL(path=self.sessions[0].calendarHref), (davxml.sync_token,))
            self.synctoken = results[davxml.sync_token]

            # Add resources to create required number of changes
            now = DateTime.getNowUTC()
            for i in range(self.count):
                href = joinURL(self.sessions[0].calendarHref, "sync-collection-%d.ics" % (i + 1,))
                self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1, i + 1,), "text/calendar")
示例#18
0
    def ensureEvents(self, session, calendarhref, n):
        """
        Make sure the required number of events are present in the calendar.

        @param n: number of events
        @type n: C{int}
        """
        now = DateTime.getNowUTC()
        for i in range(n - self.currentCount):
            index = self.currentCount + i + 1
            href = joinURL(calendarhref, "%d.ics" % (index,))
            session.writeData(URL(path=href), ICAL % (now.getYear() + 1, index), "text/calendar")

        self.currentCount = n
示例#19
0
    def prepare(self):
        """
        Do some setup prior to the real request.
        """
        if not self.full:
            # Get current sync token
            results, _ignore_bad = self.sessions[0].getProperties(URL(path=self.sessions[0].calendarHref), (davxml.sync_token,))
            self.synctoken = results[davxml.sync_token]

            # Add resources to create required number of changes
            now = DateTime.getNowUTC()
            for i in range(self.count):
                href = joinURL(self.sessions[0].calendarHref, "sync-collection-%d.ics" % (i + 1,))
                self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1, i + 1,), "text/calendar")
示例#20
0
    def doRequest(self):
        """
        Execute the actual HTTP request.
        """

        # Invite as user02
        now = DateTime.getNowUTC()
        href = joinURL(self.sessions[1].calendarHref, "organizer.ics")
        attendees = "\r\n".join(["ATTENDEE:mailto:[email protected]"] + [ATTENDEE % (ctr + 3,) for ctr in range(self.count - 1)])
        self.sessions[1].writeData(
            URL(path=href),
            ICAL.format(year=now.getYear() + 1, count=self.count, attendees=attendees),
            "text/calendar",
        )
示例#21
0
    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))

        choice = self.random.randint(1, 100)
        if choice <= self._fileAttachPercentage:
            attachmentSize = int(self._fileSizeDistribution.sample())
        else:
            attachmentSize = 0  # no attachment

        href = '%s%s.ics' % (calendar.url, uid)
        d = self._client.addEvent(href, vcalendar, attachmentSize=attachmentSize)
        return self._newOperation("create", d)
示例#22
0
    def _addTask(self):
        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)
示例#23
0
    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)
示例#24
0
    def _addTask(self):
        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)
    def setUp(self):
        yield super(SchedulerFreeBusyRequest, self).setUp()
        yield self.buildStoreAndDirectory()
        yield self.populate()

        self.now = DateTime.getNowUTC()
        self.now.setHHMMSS(0, 0, 0)

        self.now_12H = self.now.duplicate()
        self.now_12H.offsetHours(12)

        self.now_13H = self.now.duplicate()
        self.now_13H.offsetHours(13)

        self.now_1D = self.now.duplicate()
        self.now_1D.offsetDay(1)
    def setUp(self):
        yield super(GenerateFreeBusyInfo, self).setUp()
        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
        yield self.populate()

        self.now = DateTime.getNowUTC()
        self.now.setHHMMSS(0, 0, 0)

        self.now_12H = self.now.duplicate()
        self.now_12H.offsetHours(12)

        self.now_13H = self.now.duplicate()
        self.now_13H.offsetHours(13)

        self.now_1D = self.now.duplicate()
        self.now_1D.offsetDay(1)
示例#27
0
    def setUp(self):
        yield super(GenerateFreeBusyInfo, self).setUp()
        yield self.buildStoreAndDirectory()
        yield self.populate()

        self.now = DateTime.getNowUTC()
        self.now.setHHMMSS(0, 0, 0)

        self.now_12H = self.now.duplicate()
        self.now_12H.offsetHours(12)

        self.now_13H = self.now.duplicate()
        self.now_13H.offsetHours(13)

        self.now_1D = self.now.duplicate()
        self.now_1D.offsetDay(1)
示例#28
0
 def prepare(self):
     """
     Do some setup prior to the real request.
     """
     # Add resources to create required number of changes
     self.start = DateTime.getNowUTC()
     self.start.setHHMMSS(12, 0, 0)
     self.end = self.start.duplicate()
     self.end.offsetHours(1)
     for i in range(self.count):
         href = joinURL(self.sessions[0].calendarHref,
                        "tr-query-%d.ics" % (i + 1, ))
         self.sessions[0].writeData(URL(path=href), ICAL % (
             self.start.getText(),
             i + 1,
         ), "text/calendar")
示例#29
0
    def buildFreeBusyResult(self, fbinfo, method=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 method:        the METHOD property value to insert.
        @return:              the L{Component} containing the calendar data.
        """

        # Merge overlapping time ranges in each fb info section
        normalizePeriodList(fbinfo.busy)
        normalizePeriodList(fbinfo.tentative)
        normalizePeriodList(fbinfo.unavailable)

        # 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 self.organizerProp is not None:
            fb.addProperty(self.organizerProp)
        if self.attendeeProp is not None:
            fb.addProperty(self.attendeeProp)
        fb.addProperty(Property("DTSTART", self.timerange.getStart()))
        fb.addProperty(Property("DTEND", self.timerange.getEnd()))
        fb.addProperty(Property("DTSTAMP", DateTime.getNowUTC()))
        if len(fbinfo.busy) != 0:
            fb.addProperty(Property("FREEBUSY", fbinfo.busy, {"FBTYPE": "BUSY"}))
        if len(fbinfo.tentative) != 0:
            fb.addProperty(Property("FREEBUSY", fbinfo.tentative, {"FBTYPE": "BUSY-TENTATIVE"}))
        if len(fbinfo.unavailable) != 0:
            fb.addProperty(Property("FREEBUSY", fbinfo.unavailable, {"FBTYPE": "BUSY-UNAVAILABLE"}))
        if self.uid is not None:
            fb.addProperty(Property("UID", self.uid))
        else:
            uid = str(uuid.uuid4())
            fb.addProperty(Property("UID", uid))

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

        return fbcalendar
示例#30
0
    def buildFreeBusyResult(self, fbinfo, method=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 method:        the METHOD property value to insert.
        @return:              the L{Component} containing the calendar data.
        """

        # Merge overlapping time ranges in each fb info section
        normalizePeriodList(fbinfo.busy)
        normalizePeriodList(fbinfo.tentative)
        normalizePeriodList(fbinfo.unavailable)

        # 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 self.organizerProp is not None:
            fb.addProperty(self.organizerProp)
        if self.attendeeProp is not None:
            fb.addProperty(self.attendeeProp)
        fb.addProperty(Property("DTSTART", self.timerange.getStart()))
        fb.addProperty(Property("DTEND", self.timerange.getEnd()))
        fb.addProperty(Property("DTSTAMP", DateTime.getNowUTC()))
        if len(fbinfo.busy) != 0:
            fb.addProperty(Property("FREEBUSY", fbinfo.busy, {"FBTYPE": "BUSY"}))
        if len(fbinfo.tentative) != 0:
            fb.addProperty(Property("FREEBUSY", fbinfo.tentative, {"FBTYPE": "BUSY-TENTATIVE"}))
        if len(fbinfo.unavailable) != 0:
            fb.addProperty(Property("FREEBUSY", fbinfo.unavailable, {"FBTYPE": "BUSY-UNAVAILABLE"}))
        if self.uid is not None:
            fb.addProperty(Property("UID", self.uid))
        else:
            uid = str(uuid.uuid4())
            fb.addProperty(Property("UID", uid))

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

        return fbcalendar
示例#31
0
    def ensureEvents(self, session, calendarhref, n):
        """
        Make sure the required number of events are present in the calendar.

        @param n: number of events
        @type n: C{int}
        """
        now = DateTime.getNowUTC()
        for i in range(n - self.currentCount):
            index = self.currentCount + i + 1
            href = joinURL(calendarhref, "%d.ics" % (index, ))
            session.writeData(URL(path=href), ICAL % (
                now.getYear() + 1,
                index,
            ), "text/calendar")

        self.currentCount = n
示例#32
0
    def action(self):
        # Don't perform any operations until the client is up and running
        if not self._client.started:
            returnValue(None)

        event = self._getRandomEventOfType('VEVENT', justOwned=True)
        if not event:
            returnValue(None)
        component = event.component
        vevent = component.mainComponent()

        label = yield self.modifyEvent(event.url, vevent)
        if label:
            vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))

            event.component = component
            yield self._newOperation(label,
                                     self._client.changeEvent(event.url))
示例#33
0
    def resetAttendeePartstat(self, component, cuas, partstat, hadRSVP=False):
        """
        Change the PARTSTAT on any ATTENDEE properties that match the list of calendar user
        addresses on the component passed in. Also adjust the TRANSP property to match the
        new PARTSTAT value.

        @param component: an iCalendar component to modify
        @type attendees: L{Component}
        @param cuas: a list of calendar user addresses to match
        @type attendees: C{list} or C{tuple}
        @param partstat: new PARTSTAT to set
        @type partstat: C{str}
        @param hadRSVP: indicates whether RSVP should be added when changing to NEEDS-ACTION
        @type hadRSVP: C{bool}

        @return: C{True} if any change was made, C{False} otherwise
        """

        madeChanges = False
        attendee = component.getAttendeeProperty(cuas)
        if attendee:
            if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") != partstat:
                attendee.setParameter("PARTSTAT", partstat)
                madeChanges = True

            # Always remove RSVP when a state other than NEEDS-ACTION is set - this
            # is only an attendee change so madeChanges does not need to be changed
            try:
                if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") != "NEEDS-ACTION":
                    attendee.removeParameter("RSVP")
                elif hadRSVP:
                    attendee.setParameter("RSVP", "TRUE")
            except KeyError:
                pass

            # Adjust TRANSP to OPAQUE if PARTSTAT is ACCEPTED, otherwise TRANSPARENT
            component.replaceProperty(Property("TRANSP", "OPAQUE" if partstat == "ACCEPTED" else "TRANSPARENT"))

            if madeChanges:
                attendee.setParameter("X-CALENDARSERVER-AUTO", DateTime.getNowUTC().getText())
                attendee.removeParameter("X-CALENDARSERVER-DTSTAMP")

        return madeChanges
示例#34
0
    def _purgeUID(self, uid):

        if self.when is None:
            self.when = DateTime.getNowUTC()

        cuas = set((
            "urn:uuid:{}".format(uid),
            "urn:x-uid:{}".format(uid)
        ))

        # See if calendar home is provisioned
        txn = self.store.newTransaction()
        storeCalHome = yield txn.calendarHomeWithUID(uid)
        calHomeProvisioned = storeCalHome is not None

        # Always, unshare collections, remove notifications
        if calHomeProvisioned:
            yield self._cleanHome(txn, storeCalHome)

        yield txn.commit()

        count = 0

        if calHomeProvisioned:
            count = yield self._cancelEvents(txn, uid, cuas)

        # Remove empty calendar collections (and calendar home if no more
        # calendars)
        yield self._removeCalendarHome(uid)

        # Remove VCards
        count += (yield self._removeAddressbookHome(uid))

        if self.proxies and not self.dryrun:
            if self.verbose:
                print("Deleting any proxy assignments")
            yield self._purgeProxyAssignments(self.store, uid)

        returnValue(count)
示例#35
0
    def _purgeUID(self, uid):

        if self.when is None:
            self.when = DateTime.getNowUTC()

        cuas = set((
            "urn:uuid:{}".format(uid),
            "urn:x-uid:{}".format(uid)
        ))

        # See if calendar home is provisioned
        txn = self.store.newTransaction()
        storeCalHome = yield txn.calendarHomeWithUID(uid)
        calHomeProvisioned = storeCalHome is not None

        # Always, unshare collections, remove notifications
        if calHomeProvisioned:
            yield self._cleanHome(txn, storeCalHome)

        yield txn.commit()

        count = 0

        if calHomeProvisioned:
            count = yield self._cancelEvents(txn, uid, cuas)

        # Remove empty calendar collections (and calendar home if no more
        # calendars)
        yield self._removeCalendarHome(uid)

        # Remove VCards
        count += (yield self._removeAddressbookHome(uid))

        if self.proxies and not self.dryrun:
            if self.verbose:
                print("Deleting any proxy assignments")
            yield self._purgeProxyAssignments(self.store, uid)

        returnValue(count)
示例#36
0
    def _transferAttendeeData(self, serverComponent, clientComponent,
                              declines):

        # We are skipping this check now - instead we let the server data override the broken client data
        # First check validity of date-time related properties and get removed components which are declines
        self._checkInvalidChanges(serverComponent, clientComponent, declines)

        # Now look for items to transfer from one to the other.
        # We care about the ATTENDEE's PARTSTAT, TRANSP, VALARMS, X-APPLE-NEEDS-REPLY,
        # DTSTAMP, LAST-MODIFIED, COMPLETED, and ATTACH's referring to a dropbox

        replyNeeded = False

        # ATTENDEE/PARTSTAT/RSVP
        serverAttendee = serverComponent.getAttendeeProperty((self.attendee, ))
        clientAttendee = clientComponent.getAttendeeProperty((self.attendee, ))

        # Possible case where one ATTENDEE prop is missing - this happens with a "fake" master sometimes
        if serverAttendee is None or clientAttendee is None:
            log.error(
                "ATTENDEE for user making an attendee change is missing: {attendee}",
                attendee=self.attendee)
            return False, False

        if serverAttendee.parameterValue(
                "PARTSTAT", "NEEDS-ACTION") != clientAttendee.parameterValue(
                    "PARTSTAT", "NEEDS-ACTION"):
            serverAttendee.setParameter(
                "PARTSTAT",
                clientAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION"))

            # If PARTSTAT was changed by the attendee, add a timestamp if needed
            if config.Scheduling.Options.TimestampAttendeePartStatChanges:
                serverAttendee.setParameter(DTSTAMP_PARAM,
                                            DateTime.getNowUTC().getText())
            serverAttendee.removeParameter("X-CALENDARSERVER-AUTO")

            replyNeeded = True

            # Apply client fix only if the PARTSTAT was changed
            if self.forceTRANSP:
                if clientAttendee.parameterValue("PARTSTAT",
                                                 "NEEDS-ACTION") in (
                                                     "ACCEPTED",
                                                     "TENTATIVE",
                                                 ):
                    clientComponent.replaceProperty(
                        Property("TRANSP", "OPAQUE"))
                else:
                    clientComponent.replaceProperty(
                        Property("TRANSP", "TRANSPARENT"))

        if serverAttendee.parameterValue(
                "RSVP", "FALSE") != clientAttendee.parameterValue(
                    "RSVP", "FALSE"):
            if clientAttendee.parameterValue("RSVP", "FALSE") == "FALSE":
                try:
                    serverAttendee.removeParameter("RSVP")
                except KeyError:
                    pass
            else:
                serverAttendee.setParameter("RSVP", "TRUE")

        for pname in config.Scheduling.CalDAV.AttendeePublicParameters:
            serverValue = serverAttendee.parameterValue(pname)
            clientValue = clientAttendee.parameterValue(pname)
            if serverValue != clientValue:
                if clientValue is None:
                    serverAttendee.removeParameter(pname)
                else:
                    serverAttendee.setParameter(pname, clientValue)
                replyNeeded = True

        # Transfer these properties from the client data
        self._transferProperty("TRANSP", serverComponent, clientComponent)
        self._transferProperty("DTSTAMP", serverComponent, clientComponent)
        self._transferProperty("LAST-MODIFIED", serverComponent,
                               clientComponent)
        self._transferProperty("COMPLETED", serverComponent, clientComponent)
        for pname in config.Scheduling.CalDAV.PerAttendeeProperties:
            self._transferProperty(pname, serverComponent, clientComponent)
        replyNeeded |= self._transferProperty(PRIVATE_COMMENT, serverComponent,
                                              clientComponent)
        for pname in config.Scheduling.CalDAV.AttendeePublicProperties:
            replyNeeded |= self._transferProperty(pname, serverComponent,
                                                  clientComponent)

        # Dropbox - this now never returns false
        if config.EnableDropBox:
            self._transferDropBoxData(serverComponent, clientComponent)

        # Handle VALARMs
        serverComponent.removeAlarms()
        for comp in clientComponent.subcomponents():
            if comp.name() == "VALARM":
                serverComponent.addComponent(comp)

        # VPOLL
        if serverComponent.name() == "VPOLL":
            replyNeeded = self._transferVPOLLData(serverComponent,
                                                  clientComponent)

        return True, replyNeeded
示例#37
0
 def __init__(self, *children):
     super(DTStamp, self).__init__(*children)
     if not self.children:
         self.children = (PCDATAElement(DateTime.getNowUTC().getText()), )
示例#38
0
    def doCapabilities(self, request):
        """
        Return a list of all timezones known to the server.
        """

        # Determine min/max date-time for iSchedule
        now = DateTime.getNowUTC()
        minDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0,
                               Timezone.UTCTimezone)
        minDateTime.offsetYear(-1)
        maxDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0,
                               Timezone.UTCTimezone)
        maxDateTime.offsetYear(10)

        dataTypes = []
        dataTypes.append(
            ischedulexml.CalendarDataType(**{
                "content-type": "text/calendar",
                "version": "2.0",
            }))
        if config.EnableJSONData:
            dataTypes.append(
                ischedulexml.CalendarDataType(
                    **{
                        "content-type": "application/calendar+json",
                        "version": "2.0",
                    }))

        componentTypes = []
        from twistedcaldav.ical import allowedSchedulingComponents
        for name in allowedSchedulingComponents:
            if name == "VFREEBUSY":
                componentTypes.append(
                    ischedulexml.Component(ischedulexml.Method(name="REQUEST"),
                                           name=name))
            else:
                componentTypes.append(
                    ischedulexml.Component(ischedulexml.Method(name="REQUEST"),
                                           ischedulexml.Method(name="CANCEL"),
                                           ischedulexml.Method(name="REPLY"),
                                           name=name))

        result = ischedulexml.QueryResult(
            ischedulexml.Capabilities(
                ischedulexml.Version.fromString(
                    config.Scheduling.iSchedule.SerialNumber),
                ischedulexml.Versions(
                    ischedulexml.Version.fromString("1.0"), ),
                ischedulexml.SchedulingMessages(*componentTypes),
                ischedulexml.CalendarDataTypes(*dataTypes),
                ischedulexml.Attachments(ischedulexml.External(), ),
                ischedulexml.MaxContentLength.fromString(
                    config.MaxResourceSize),
                ischedulexml.MinDateTime.fromString(minDateTime.getText()),
                ischedulexml.MaxDateTime.fromString(maxDateTime.getText()),
                ischedulexml.MaxInstances.fromString(
                    config.MaxAllowedInstances),
                ischedulexml.MaxRecipients.fromString(
                    config.MaxAttendeesPerInstance),
                ischedulexml.Administrator.fromString(
                    request.unparseURL(params="", querystring="",
                                       fragment="")),
            ), )
        response = XMLResponse(responsecode.OK, result)
        response.headers.addRawHeader(
            ISCHEDULE_CAPABILITIES,
            str(config.Scheduling.iSchedule.SerialNumber))
        return response
示例#39
0
 def isNow(self):
     # Check instance start/end against current date-time
     now = DateTime.getNowUTC()
     return self.mInstanceStart <= now and self.mInstanceEnd > now
示例#40
0
    def setupDateTimeValues(self):

        self.dtsubs = {}

        # Set of "now" values that are directly accessible
        self.now = DateTime.getNowUTC()
        self.now.setHHMMSS(0, 0, 0)
        self.now12 = DateTime.getNowUTC()
        self.now12.setHHMMSS(12, 0, 0)
        self.nowDate = self.now.duplicate()
        self.nowDate.setDateOnly(True)
        self.nowFloating = self.now.duplicate()
        self.nowFloating.setTimezoneID(None)

        self.dtsubs["now"] = self.now
        self.dtsubs["now12"] = self.now12
        self.dtsubs["nowDate"] = self.nowDate
        self.dtsubs["nowFloating"] = self.nowFloating

        # Values going 30 days back from now
        for i in range(30):
            attrname = "now_back%s" % (i + 1, )
            setattr(self, attrname, self.now.duplicate())
            getattr(self, attrname).offsetDay(-(i + 1))
            self.dtsubs[attrname] = getattr(self, attrname)

            attrname_12h = "now_back%s_12h" % (i + 1, )
            setattr(self, attrname_12h, getattr(self, attrname).duplicate())
            getattr(self, attrname_12h).offsetHours(12)
            self.dtsubs[attrname_12h] = getattr(self, attrname_12h)

            attrname_1 = "now_back%s_1" % (i + 1, )
            setattr(self, attrname_1, getattr(self, attrname).duplicate())
            getattr(self, attrname_1).offsetSeconds(-1)
            self.dtsubs[attrname_1] = getattr(self, attrname_1)

            attrname = "nowDate_back%s" % (i + 1, )
            setattr(self, attrname, self.nowDate.duplicate())
            getattr(self, attrname).offsetDay(-(i + 1))
            self.dtsubs[attrname] = getattr(self, attrname)

            attrname = "nowFloating_back%s" % (i + 1, )
            setattr(self, attrname, self.nowFloating.duplicate())
            getattr(self, attrname).offsetDay(-(i + 1))
            self.dtsubs[attrname] = getattr(self, attrname)

            attrname_1 = "nowFloating_back%s_1" % (i + 1, )
            setattr(self, attrname_1, getattr(self, attrname).duplicate())
            getattr(self, attrname_1).offsetSeconds(-1)
            self.dtsubs[attrname_1] = getattr(self, attrname_1)

        # Values going 30 days forward from now
        for i in range(30):
            attrname = "now_fwd%s" % (i + 1, )
            setattr(self, attrname, self.now.duplicate())
            getattr(self, attrname).offsetDay(i + 1)
            self.dtsubs[attrname] = getattr(self, attrname)

            attrname_12h = "now_fwd%s_12h" % (i + 1, )
            setattr(self, attrname_12h, getattr(self, attrname).duplicate())
            getattr(self, attrname_12h).offsetHours(12)
            self.dtsubs[attrname_12h] = getattr(self, attrname_12h)

            attrname = "nowDate_fwd%s" % (i + 1, )
            setattr(self, attrname, self.nowDate.duplicate())
            getattr(self, attrname).offsetDay(i + 1)
            self.dtsubs[attrname] = getattr(self, attrname)

            attrname = "nowFloating_fwd%s" % (i + 1, )
            setattr(self, attrname, self.nowFloating.duplicate())
            getattr(self, attrname).offsetDay(i + 1)
            self.dtsubs[attrname] = getattr(self, attrname)
示例#41
0
    def _processFBURL(self, request):
        #
        # Check authentication and access controls
        #
        yield self.authorize(request, (davxml.Read(),))

        # Extract query parameters from the URL
        args = ('start', 'end', 'duration', 'token', 'format', 'user',)
        for arg in args:
            setattr(self, arg, request.args.get(arg, [None])[0])

        # Some things we do not handle
        if self.token or self.user:
            raise HTTPError(ErrorResponse(
                responsecode.NOT_ACCEPTABLE,
                (calendarserver_namespace, "supported-query-parameter"),
                "Invalid query parameter",
            ))

        # Check format
        if self.format:
            self.format = self.format.split(";")[0]
            if self.format not in ("text/calendar", "text/plain"):
                raise HTTPError(ErrorResponse(
                    responsecode.NOT_ACCEPTABLE,
                    (calendarserver_namespace, "supported-format"),
                    "Invalid return format requested",
                ))
        else:
            self.format = "text/calendar"

        # Start/end/duration must be valid iCalendar DATE-TIME UTC or DURATION values
        try:
            if self.start:
                self.start = DateTime.parseText(self.start)
                if not self.start.utc():
                    raise ValueError()
            if self.end:
                self.end = DateTime.parseText(self.end)
                if not self.end.utc():
                    raise ValueError()
            if self.duration:
                self.duration = Duration.parseText(self.duration)
        except ValueError:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Sanity check start/end/duration

        # End and duration cannot both be present
        if self.end and self.duration:
            raise HTTPError(ErrorResponse(
                responsecode.NOT_ACCEPTABLE,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Duration must be positive
        if self.duration and self.duration.getTotalSeconds() < 0:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Now fill in the missing pieces
        if self.start is None:
            self.start = DateTime.getNowUTC()
            self.start.setHHMMSS(0, 0, 0)
        if self.duration:
            self.end = self.start + self.duration
        if self.end is None:
            self.end = self.start + Duration(days=config.FreeBusyURL.TimePeriod)

        # End > start
        if self.end <= self.start:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # TODO: We should probably verify that the actual time-range is within sensible bounds (e.g. not too far in the past or future and not too long)

        # Now lookup the principal details for the targeted user
        principal = (yield self.parent.principalForRecord())

        # Pick the first mailto cu address or the first other type
        cuaddr = None
        for item in principal.calendarUserAddresses():
            if cuaddr is None:
                cuaddr = item
            if item.startswith("mailto:"):
                cuaddr = item
                break

        # Get inbox details
        inboxURL = principal.scheduleInboxURL()
        if inboxURL is None:
            raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, "No schedule inbox URL for principal: %s" % (principal,)))
        try:
            inbox = (yield request.locateResource(inboxURL))
        except:
            log.error("No schedule inbox for principal: {p}", p=principal)
            inbox = None
        if inbox is None:
            raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, "No schedule inbox for principal: %s" % (principal,)))

        organizer = recipient = LocalCalendarUser(cuaddr, principal.record)
        recipient.inbox = inbox._newStoreObject
        attendeeProp = Property("ATTENDEE", recipient.cuaddr)
        timerange = Period(self.start, self.end)

        fbresult = yield FreebusyQuery(
            organizer=organizer,
            recipient=recipient,
            attendeeProp=attendeeProp,
            timerange=timerange,
        ).generateAttendeeFreeBusyResponse()

        response = Response()
        response.stream = MemoryStream(str(fbresult))
        response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (self.format,)))

        returnValue(response)
示例#42
0
    def updateLastModified(self):
        self.removeProperties(definitions.cICalProperty_LAST_MODIFIED)

        prop = Property(definitions.cICalProperty_LAST_MODIFIED, DateTime.getNowUTC())
        self.addProperty(prop)
示例#43
0
    def initDTSTAMP(self):
        self.removeProperties(definitions.cICalProperty_DTSTAMP)

        prop = Property(definitions.cICalProperty_DTSTAMP, DateTime.getNowUTC())
        self.addProperty(prop)
示例#44
0
    def outbound(self, txn, originator, recipient, calendar, onlyAfter=None):
        """
        Generates and sends an outbound IMIP message.

        @param txn: the transaction to use for looking up/creating tokens
        @type txn: L{CommonStoreTransaction}
        """

        if onlyAfter is None:
            duration = Duration(days=self.suppressionDays)
            onlyAfter = DateTime.getNowUTC() - duration

        icaluid = calendar.resourceUID()
        method = calendar.propertyValue("METHOD")

        # Clean up the attendee list which is purely used within the human
        # readable email message (not modifying the calendar body)
        attendees = []
        for attendeeProp in calendar.getAllAttendeeProperties():
            cutype = attendeeProp.parameterValue("CUTYPE", "INDIVIDUAL")
            if cutype == "INDIVIDUAL":
                cn = attendeeProp.parameterValue("CN", None)
                if cn is not None:
                    cn = cn.decode("utf-8")
                cuaddr = normalizeCUAddr(attendeeProp.value())
                if cuaddr.startswith("mailto:"):
                    mailto = cuaddr[7:]
                    if not cn:
                        cn = mailto
                else:
                    emailAddress = attendeeProp.parameterValue("EMAIL", None)
                    if emailAddress:
                        mailto = emailAddress
                    else:
                        mailto = None

                if cn or mailto:
                    attendees.append((cn, mailto))

        toAddr = recipient
        if not recipient.lower().startswith("mailto:"):
            raise ValueError("ATTENDEE address '%s' must be mailto: for iMIP "
                             "operation." % (recipient, ))
        recipient = recipient[7:]

        if method != "REPLY":
            # Invites and cancellations:

            # Reuse or generate a token based on originator, toAddr, and
            # event uid
            token = (yield txn.imipGetToken(originator, toAddr.lower(),
                                            icaluid))
            if token is None:

                # Because in the past the originator was sometimes in mailto:
                # form, lookup an existing token by mailto: as well
                organizerProperty = calendar.getOrganizerProperty()
                organizerEmailAddress = organizerProperty.parameterValue(
                    "EMAIL", None)
                if organizerEmailAddress is not None:
                    token = (yield txn.imipGetToken(
                        "mailto:%s" % (organizerEmailAddress.lower(), ),
                        toAddr.lower(), icaluid))

            if token is None:
                token = (yield txn.imipCreateToken(originator, toAddr.lower(),
                                                   icaluid))
                self.log.debug(
                    "Mail gateway created token %s for %s "
                    "(originator), %s (recipient) and %s (icaluid)" %
                    (token, originator, toAddr, icaluid))
                inviteState = "new"

            else:
                self.log.debug(
                    "Mail gateway reusing token %s for %s "
                    "(originator), %s (recipient) and %s (icaluid)" %
                    (token, originator, toAddr, icaluid))
                inviteState = "update"

            fullServerAddress = self.address
            _ignore_name, serverAddress = email.utils.parseaddr(
                fullServerAddress)
            pre, post = serverAddress.split('@')
            addressWithToken = "%s+%s@%s" % (pre, token, post)

            organizerProperty = calendar.getOrganizerProperty()
            organizerEmailAddress = organizerProperty.parameterValue(
                "EMAIL", None)
            organizerValue = organizerProperty.value()
            organizerProperty.setValue("mailto:%s" % (addressWithToken, ))

            # If the organizer is also an attendee, update that attendee value
            # to match
            organizerAttendeeProperty = calendar.getAttendeeProperty(
                [organizerValue])
            if organizerAttendeeProperty is not None:
                organizerAttendeeProperty.setValue("mailto:%s" %
                                                   (addressWithToken, ))

            # The email's From will include the originator's real name email
            # address if available.  Otherwise it will be the server's email
            # address (without # + addressing)
            if organizerEmailAddress:
                orgEmail = fromAddr = organizerEmailAddress
            else:
                fromAddr = serverAddress
                orgEmail = None
            cn = calendar.getOrganizerProperty().parameterValue('CN', None)
            if cn is None:
                cn = u'Calendar Server'
                orgCN = orgEmail
            else:
                orgCN = cn = cn.decode("utf-8")

            # a unicode cn (rather than an encode string value) means the
            # from address will get properly encoded per rfc2047 within the
            # MIMEMultipart in generateEmail
            formattedFrom = "%s <%s>" % (cn, fromAddr)

            # Reply-to address will be the server+token address

        else:  # REPLY
            inviteState = "reply"

            # Look up the attendee property corresponding to the originator
            # of this reply
            originatorAttendeeProperty = calendar.getAttendeeProperty(
                [originator])
            formattedFrom = fromAddr = originator = ""
            if originatorAttendeeProperty:
                originatorAttendeeEmailAddress = (
                    originatorAttendeeProperty.parameterValue("EMAIL", None))
                if originatorAttendeeEmailAddress:
                    formattedFrom = fromAddr = originator = (
                        originatorAttendeeEmailAddress)

            organizerMailto = str(calendar.getOrganizer())
            if not organizerMailto.lower().startswith("mailto:"):
                raise ValueError("ORGANIZER address '%s' must be mailto: "
                                 "for REPLY." % (organizerMailto, ))
            orgEmail = organizerMailto[7:]

            orgCN = calendar.getOrganizerProperty().parameterValue('CN', None)
            addressWithToken = formattedFrom

        # At the point we've created the token in the db, which we always
        # want to do, but if this message is for an event completely in
        # the past we don't want to actually send an email.
        if not calendar.hasInstancesAfter(onlyAfter):
            self.log.debug("Skipping IMIP message for old event")
            returnValue(True)

        # Now prevent any "internal" CUAs from being exposed by converting
        # to mailto: if we have one
        for attendeeProp in calendar.getAllAttendeeProperties():
            cutype = attendeeProp.parameterValue('CUTYPE', None)
            if cutype == "INDIVIDUAL":
                cuaddr = normalizeCUAddr(attendeeProp.value())
                if not cuaddr.startswith("mailto:"):
                    emailAddress = attendeeProp.parameterValue("EMAIL", None)
                    if emailAddress:
                        attendeeProp.setValue("mailto:%s" % (emailAddress, ))

        msgId, message = self.generateEmail(inviteState,
                                            calendar,
                                            orgEmail,
                                            orgCN,
                                            attendees,
                                            formattedFrom,
                                            addressWithToken,
                                            recipient,
                                            language=self.language)

        try:
            success = (yield
                       self.smtpSender.sendMessage(fromAddr, toAddr, msgId,
                                                   message))
            returnValue(success)
        except Exception, e:
            self.log.error("Failed to send IMIP message (%s)" % (str(e), ))
            returnValue(False)
示例#45
0
 def sample(self):
     now = DateTime.getNowUTC()
     now.offsetSeconds(int(self._offset.sample()))
     return now
示例#46
0
    def outbound(self, txn, originator, recipient, calendar, onlyAfter=None):
        """
        Generates and sends an outbound IMIP message.

        @param txn: the transaction to use for looking up/creating tokens
        @type txn: L{CommonStoreTransaction}
        """

        if onlyAfter is None:
            duration = Duration(days=self.suppressionDays)
            onlyAfter = DateTime.getNowUTC() - duration

        icaluid = calendar.resourceUID()
        method = calendar.propertyValue("METHOD")

        # Clean up the attendee list which is purely used within the human
        # readable email message (not modifying the calendar body)
        attendees = []
        for attendeeProp in calendar.getAllAttendeeProperties():
            cutype = attendeeProp.parameterValue("CUTYPE", "INDIVIDUAL")
            if cutype == "INDIVIDUAL":
                cn = attendeeProp.parameterValue("CN", None)
                if cn is not None:
                    cn = cn.decode("utf-8")
                cuaddr = normalizeCUAddr(attendeeProp.value())
                if cuaddr.startswith("mailto:"):
                    mailto = cuaddr[7:]
                    if not cn:
                        cn = mailto
                else:
                    emailAddress = attendeeProp.parameterValue("EMAIL", None)
                    if emailAddress:
                        mailto = emailAddress
                    else:
                        mailto = None

                if cn or mailto:
                    attendees.append((cn, mailto))

        toAddr = recipient
        if not recipient.lower().startswith("mailto:"):
            raise ValueError("ATTENDEE address '%s' must be mailto: for iMIP "
                             "operation." % (recipient,))
        recipient = recipient[7:]

        if method != "REPLY":
            # Invites and cancellations:

            # Reuse or generate a token based on originator, toAddr, and
            # event uid
            token = (yield txn.imipGetToken(originator, toAddr.lower(), icaluid))
            if token is None:

                # Because in the past the originator was sometimes in mailto:
                # form, lookup an existing token by mailto: as well
                organizerProperty = calendar.getOrganizerProperty()
                organizerEmailAddress = organizerProperty.parameterValue("EMAIL", None)
                if organizerEmailAddress is not None:
                    token = (yield txn.imipGetToken("mailto:%s" % (organizerEmailAddress.lower(),), toAddr.lower(), icaluid))

            if token is None:
                token = (yield txn.imipCreateToken(originator, toAddr.lower(), icaluid))
                self.log.debug("Mail gateway created token %s for %s "
                               "(originator), %s (recipient) and %s (icaluid)"
                               % (token, originator, toAddr, icaluid))
                inviteState = "new"

            else:
                self.log.debug("Mail gateway reusing token %s for %s "
                               "(originator), %s (recipient) and %s (icaluid)"
                               % (token, originator, toAddr, icaluid))
                inviteState = "update"

            fullServerAddress = self.address
            _ignore_name, serverAddress = email.utils.parseaddr(fullServerAddress)
            pre, post = serverAddress.split('@')
            addressWithToken = "%s+%s@%s" % (pre, token, post)

            organizerProperty = calendar.getOrganizerProperty()
            organizerEmailAddress = organizerProperty.parameterValue("EMAIL",
                                                                     None)
            organizerValue = organizerProperty.value()
            organizerProperty.setValue("mailto:%s" % (addressWithToken,))

            # If the organizer is also an attendee, update that attendee value
            # to match
            organizerAttendeeProperty = calendar.getAttendeeProperty(
                [organizerValue])
            if organizerAttendeeProperty is not None:
                organizerAttendeeProperty.setValue("mailto:%s" %
                                                   (addressWithToken,))

            # The email's From will include the originator's real name email
            # address if available.  Otherwise it will be the server's email
            # address (without # + addressing)
            if organizerEmailAddress:
                orgEmail = fromAddr = organizerEmailAddress
            else:
                fromAddr = serverAddress
                orgEmail = None
            cn = calendar.getOrganizerProperty().parameterValue('CN', None)
            if cn is None:
                cn = u'Calendar Server'
                orgCN = orgEmail
            else:
                orgCN = cn = cn.decode("utf-8")

            # a unicode cn (rather than an encode string value) means the
            # from address will get properly encoded per rfc2047 within the
            # MIMEMultipart in generateEmail
            formattedFrom = "%s <%s>" % (cn, fromAddr)

            # Reply-to address will be the server+token address

        else: # REPLY
            inviteState = "reply"

            # Look up the attendee property corresponding to the originator
            # of this reply
            originatorAttendeeProperty = calendar.getAttendeeProperty(
                [originator])
            formattedFrom = fromAddr = originator = ""
            if originatorAttendeeProperty:
                originatorAttendeeEmailAddress = (
                    originatorAttendeeProperty.parameterValue("EMAIL", None)
                )
                if originatorAttendeeEmailAddress:
                    formattedFrom = fromAddr = originator = (
                        originatorAttendeeEmailAddress
                    )

            organizerMailto = str(calendar.getOrganizer())
            if not organizerMailto.lower().startswith("mailto:"):
                raise ValueError("ORGANIZER address '%s' must be mailto: "
                                 "for REPLY." % (organizerMailto,))
            orgEmail = organizerMailto[7:]

            orgCN = calendar.getOrganizerProperty().parameterValue('CN', None)
            addressWithToken = formattedFrom

        # At the point we've created the token in the db, which we always
        # want to do, but if this message is for an event completely in
        # the past we don't want to actually send an email.
        if not calendar.hasInstancesAfter(onlyAfter):
            self.log.debug("Skipping IMIP message for old event")
            returnValue(True)

        # Now prevent any "internal" CUAs from being exposed by converting
        # to mailto: if we have one
        for attendeeProp in calendar.getAllAttendeeProperties():
            cutype = attendeeProp.parameterValue('CUTYPE', None)
            if cutype == "INDIVIDUAL":
                cuaddr = normalizeCUAddr(attendeeProp.value())
                if not cuaddr.startswith("mailto:"):
                    emailAddress = attendeeProp.parameterValue("EMAIL", None)
                    if emailAddress:
                        attendeeProp.setValue("mailto:%s" % (emailAddress,))

        msgId, message = self.generateEmail(inviteState, calendar, orgEmail,
            orgCN, attendees, formattedFrom, addressWithToken, recipient,
            language=self.language)

        try:
            success = (yield self.smtpSender.sendMessage(fromAddr, toAddr,
                msgId, message))
            returnValue(success)
        except Exception, e:
            self.log.error("Failed to send IMIP message (%s)" % (str(e),))
            returnValue(False)
示例#47
0
 def isNow(self):
     # Check instance start/end against current date-time
     now = DateTime.getNowUTC()
     return self.mInstanceStart <= now and self.mInstanceEnd > now
示例#48
0
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, Deferred

from twistedcaldav.config import config
from twistedcaldav.test.util import StoreTestCase

from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
from txdav.common.datastore.test.util import populateCalendarsFrom

from txweb2.http_headers import MimeType

import datetime



future = DateTime.getNowUTC()
future.offsetDay(1)
future = future.getText()

past = DateTime.getNowUTC()
past.offsetDay(-1)
past = past.getText()

# For test_purgeExistingGUID

# No organizer/attendee
NON_INVITE_ICS = """BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
UID:151AFC76-6036-40EF-952B-97D1840760BF
SUMMARY:Non Invitation
示例#49
0
    def updateAttendeeData(from_component, to_component):
        """
        Called when processing a REPLY only.

        Copy the PARTSTAT of the Attendee in the from_component to the matching ATTENDEE
        in the to_component. Ignore if no match found. Also update the private comments.

        For VPOLL we need to copy POLL-ITEM-ID response values into the actual matching
        polled sub-components as VOTER properties.

        @param from_component: component to copy from
        @type from_component: L{Component}
        @param to_component: component to copy to
        @type to_component: L{Component}
        """

        # Track what changed
        partstat_changed = False
        private_comment_changed = False

        # Get REQUEST-STATUS as we need to write that into the saved ATTENDEE property
        reqstatus = tuple(from_component.properties("REQUEST-STATUS"))
        if reqstatus:
            reqstatus = ",".join(status.value()[0] for status in reqstatus)
        else:
            reqstatus = "2.0"

        # Get attendee in from_component - there MUST be only one
        attendees = tuple(from_component.properties(from_component.recipientPropertyName()))
        if len(attendees) != 1:
            log.error("There must be one and only one ATTENDEE property in a REPLY\n%s" % (str(from_component),))
            return None, False, False

        attendee = attendees[0]
        partstat = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")

        # Now find matching ATTENDEE in to_component
        existing_attendee = to_component.getAttendeeProperty((attendee.value(),))
        if existing_attendee:
            oldpartstat = existing_attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
            existing_attendee.setParameter("PARTSTAT", partstat)
            existing_attendee.setParameter("SCHEDULE-STATUS", reqstatus)
            partstat_changed = (oldpartstat != partstat)

            # Always delete RSVP on PARTSTAT change
            if partstat_changed:
                try:
                    existing_attendee.removeParameter("RSVP")
                except KeyError:
                    pass

            # Handle attendee comments
            if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
                # Look for X-CALENDARSERVER-PRIVATE-COMMENT property in iTIP component (State 1 in spec)
                attendee_comment = tuple(from_component.properties("X-CALENDARSERVER-PRIVATE-COMMENT"))
                attendee_comment = attendee_comment[0] if len(attendee_comment) else None

                # Look for matching X-CALENDARSERVER-ATTENDEE-COMMENT property in existing data (State 2 in spec)
                private_comments = tuple(to_component.properties("X-CALENDARSERVER-ATTENDEE-COMMENT"))
                for comment in private_comments:
                    attendeeref = comment.parameterValue("X-CALENDARSERVER-ATTENDEE-REF")
                    if attendeeref == attendee.value():
                        private_comment = comment
                        break
                else:
                    private_comment = None
            else:
                attendee_comment = None
                private_comment = None

            # Now do update logic
            if attendee_comment is None and private_comment is None:
                # Nothing to do
                pass

            elif attendee_comment is None and private_comment is not None:
                # We now remove the private comment on the organizer's side if the attendee removed it
                to_component.removeProperty(private_comment)

                private_comment_changed = True

            elif attendee_comment is not None and private_comment is None:

                # Add new property
                private_comment = Property(
                    "X-CALENDARSERVER-ATTENDEE-COMMENT",
                    attendee_comment.value(),
                    params={
                        "X-CALENDARSERVER-ATTENDEE-REF": attendee.value(),
                        "X-CALENDARSERVER-DTSTAMP": DateTime.getNowUTC().getText(),
                    }
                )
                to_component.addProperty(private_comment)

                private_comment_changed = True

            else:
                # Only change if different
                if private_comment.value() != attendee_comment.value():
                    # Remove all property parameters
                    private_comment.removeAllParameters()

                    # Add default parameters
                    private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
                    private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText())

                    # Set new value
                    private_comment.setValue(attendee_comment.value())

                    private_comment_changed = True

            # Do VPOLL transfer
            if from_component.name() == "VPOLL":
                # TODO: figure out how to report changes back
                iTipProcessing.updateVPOLLData(from_component, to_component, attendee)

        return attendee.value(), partstat_changed, private_comment_changed
示例#50
0
    def doCapabilities(self, request):
        """
        Return a list of all timezones known to the server.
        """

        # Determine min/max date-time for iSchedule
        now = DateTime.getNowUTC()
        minDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0, Timezone.UTCTimezone)
        minDateTime.offsetYear(-1)
        maxDateTime = DateTime(now.getYear(), 1, 1, 0, 0, 0, Timezone.UTCTimezone)
        maxDateTime.offsetYear(10)

        dataTypes = []
        dataTypes.append(
            ischedulexml.CalendarDataType(**{
                "content-type": "text/calendar",
                "version": "2.0",
            })
        )
        if config.EnableJSONData:
            dataTypes.append(
                ischedulexml.CalendarDataType(**{
                    "content-type": "application/calendar+json",
                    "version": "2.0",
                })
            )

        componentTypes = []
        from twistedcaldav.ical import allowedSchedulingComponents
        for name in allowedSchedulingComponents:
            if name == "VFREEBUSY":
                componentTypes.append(
                    ischedulexml.Component(
                        ischedulexml.Method(name="REQUEST"),
                        name=name
                    )
                )
            else:
                componentTypes.append(
                    ischedulexml.Component(
                        ischedulexml.Method(name="REQUEST"),
                        ischedulexml.Method(name="CANCEL"),
                        ischedulexml.Method(name="REPLY"),
                        name=name
                    )
                )

        result = ischedulexml.QueryResult(

            ischedulexml.Capabilities(
                ischedulexml.Version.fromString(config.Scheduling.iSchedule.SerialNumber),
                ischedulexml.Versions(
                    ischedulexml.Version.fromString("1.0"),
                ),
                ischedulexml.SchedulingMessages(*componentTypes),
                ischedulexml.CalendarDataTypes(*dataTypes),
                ischedulexml.Attachments(
                    ischedulexml.External(),
                ),
                ischedulexml.MaxContentLength.fromString(config.MaxResourceSize),
                ischedulexml.MinDateTime.fromString(minDateTime.getText()),
                ischedulexml.MaxDateTime.fromString(maxDateTime.getText()),
                ischedulexml.MaxInstances.fromString(config.MaxAllowedInstances),
                ischedulexml.MaxRecipients.fromString(config.MaxAttendeesPerInstance),
                ischedulexml.Administrator.fromString(request.unparseURL(params="", querystring="", fragment="")),
            ),
        )
        response = XMLResponse(responsecode.OK, result)
        response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
        return response
示例#51
0
    def generateAttendeeReply(original, attendee, changedRids=None, force_decline=False):

        # Start with a copy of the original as we may have to modify bits of it
        itip = original.duplicate()
        itip.replaceProperty(Property("PRODID", iCalendarProductID))
        itip.addProperty(Property("METHOD", "REPLY"))

        # Now filter out components except the ones specified
        itip.filterComponents(changedRids)

        # Force update to DTSTAMP everywhere so reply sequencing will work
        itip.replacePropertyInAllComponents(Property("DTSTAMP", DateTime.getNowUTC()))

        # Remove all attendees except the one we want
        itip.removeAllButOneAttendee(attendee)

        # Remove all components which are missing the attendee
        for component in itip.subcomponents():
            if component.name() in ignoredComponents:
                continue
            if not component.getAttendeeProperty((attendee,)):
                itip.removeComponent(component)

        # No alarms
        itip.removeAlarms()

        # Remove all but essential properties
        itip.filterProperties(keep=(
            "UID",
            "RECURRENCE-ID",
            "SEQUENCE",
            "STATUS",
            "DTSTAMP",
            "DTSTART",
            "DTEND",
            "DURATION",
            "RRULE",
            "RDATE",
            "EXDATE",
            "ORGANIZER",
            "ATTENDEE",
            "VOTER",
            "X-CALENDARSERVER-PRIVATE-COMMENT",
            "SUMMARY",
            "LOCATION",
            "DESCRIPTION",
        ))

        # Now set each ATTENDEE's PARTSTAT to DECLINED
        if force_decline:
            attendeeProps = itip.getAttendeeProperties((attendee,))
            assert attendeeProps, "Must have some matching ATTENDEEs"
            for attendeeProp in attendeeProps:
                attendeeProp.setParameter("PARTSTAT", "DECLINED")

        # Add REQUEST-STATUS to each top-level component
        itip.addPropertyToAllComponents(Property("REQUEST-STATUS", ["2.0", "Success", ]))

        # Strip out unwanted bits
        iTipGenerator.prepareSchedulingMessage(itip, reply=True)

        # Handle VPOLL behavior
        for component in itip.subcomponents():
            if component.name() == "VPOLL":
                iTipGenerator.generateVPOLLReply(component, attendee)

        return itip
示例#52
0
    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", justOwned=True)

        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

            choice = self.random.randint(1, 100)
            if choice <= self._fileAttachPercentage:
                attachmentSize = int(self._fileSizeDistribution.sample())
            else:
                attachmentSize = 0  # no attachment

            href = '%s%s.ics' % (calendar.url, uid)
            d = self._client.addInvite(
                href, vcalendar, attachmentSize=attachmentSize,
                lookupPercentage=self._inviteeLookupPercentage
            )
            return self._newOperation("invite", d)