示例#1
0
 def __init__(self):
     from pycalendar.icalendar.calendar import Calendar
     self.dbpath = None
     self.calendar = Calendar()
     self.tzcache = {}
     self.stdtzcache = set()
     self.notstdtzcache = set()
    def getTimezone(self, tzid):
        """
        Generate a PyCalendar containing the requested timezone.
        """
        # We will just use our existing TimezoneCache here
        calendar = Calendar()
        try:
            vtz = readVTZ(tzid)
            calendar.addComponent(vtz.getComponents()[0].duplicate())
        except TimezoneException:

            # Check if an alias exists and create data for that
            if tzid in self.aliases:
                try:
                    vtz = readVTZ(self.aliases[tzid])
                except TimezoneException:
                    log.error("Failed to find timezone data for alias: %s" % (tzid,))
                    return None
                else:
                    vtz = vtz.duplicate()
                    vtz.getComponents()[0].getProperties("TZID")[0].setValue(tzid)
                    addVTZ(tzid, vtz)
                    calendar.addComponent(vtz.getComponents()[0].duplicate())
            else:
                log.error("Failed to find timezone data for: %s" % (tzid,))
                return None

        return calendar
示例#3
0
    def generateZoneinfoFiles(self, outputdir, minYear, maxYear=2018, links=True, windowsAliases=None, filterzones=None):

        # Empty current directory
        try:
            for root, dirs, files in os.walk(outputdir, topdown=False):
                for name in files:
                    os.remove(os.path.join(root, name))
                for name in dirs:
                    os.rmdir(os.path.join(root, name))
        except OSError:
            pass

        for zone in self.zones.itervalues():
            if filterzones and zone.name not in filterzones:
                continue
            cal = Calendar()
            vtz = zone.vtimezone(cal, self.rules, minYear, maxYear)
            cal.addComponent(vtz)

            icsdata = cal.getText()
            fpath = os.path.join(outputdir, zone.name + ".ics")
            if not os.path.exists(os.path.dirname(fpath)):
                os.makedirs(os.path.dirname(fpath))
            with open(fpath, "w") as f:
                f.write(icsdata)
            if self.verbose:
                print("Write path: %s" % (fpath,))

        if links:
            if windowsAliases is not None:
                self.parseWindowsAliases(windowsAliases)

            link_list = []
            for linkTo, linkFrom in sorted(self.links.iteritems(), key=lambda x: x[0]):

                # Check for existing output file
                fromPath = os.path.join(outputdir, linkFrom + ".ics")
                if not os.path.exists(fromPath):
                    print("Missing link from: %s to %s" % (linkFrom, linkTo,))
                    continue

                with open(fromPath) as f:
                    icsdata = f.read()
                icsdata = icsdata.replace(linkFrom, linkTo)

                toPath = os.path.join(outputdir, linkTo + ".ics")
                if not os.path.exists(os.path.dirname(toPath)):
                    os.makedirs(os.path.dirname(toPath))
                with open(toPath, "w") as f:
                    f.write(icsdata)
                if self.verbose:
                    print("Write link: %s" % (linkTo,))

                link_list.append("%s\t%s" % (linkTo, linkFrom,))

            # Generate link mapping file
            linkPath = os.path.join(outputdir, "links.txt")
            with open(linkPath, "w") as f:
                f.write("\n".join(link_list))
示例#4
0
        def _doNonEquality(caldata):
            cal1 = Calendar()
            cal1.parse(StringIO.StringIO(caldata))

            cal2 = Calendar()
            cal2.parse(StringIO.StringIO(caldata))
            cal2.addProperty(Property("X-FOO", "BAR"))

            self.assertNotEqual(cal1, cal2)
示例#5
0
def loadCalendar(file, verbose):

    cal = Calendar()
    if verbose:
        print "Parsing calendar data: %s" % (file,)
    with open(file, "r") as fin:
        try:
            cal.parse(fin)
        except InvalidData, e:
            print "Failed to parse bad data: %s" % (e.mData,)
            raise
示例#6
0
        def _doRoundtrip(caldata, jcaldata):
            cal1 = Calendar.parseText(caldata)
            test1 = cal1.getText()

            cal2 = Calendar.parseJSONData(jcaldata)
            test2 = cal2.getText()

            self.assertEqual(
                test1, test2, "\n".join(
                    difflib.unified_diff(
                        str(test1).splitlines(), test2.splitlines())))
                    def _doTest():
                        result = None
                        if test[0] == '@':
                            if '=' in test:
                                attr, value = test[1:].split('=')
                                value = value[1:-1]
                            else:
                                attr = test[1:]
                                value = None
                            if attr not in node.keys():
                                result = "        Missing attribute returned in XML for %s\n" % (path,)
                            if value is not None and node.get(attr) != value:
                                result = "        Incorrect attribute value returned in XML for %s\n" % (path,)
                        elif test[0] == '=':
                            if node.text != test[1:]:
                                result = "        Incorrect value returned in XML for %s\n" % (path,)
                        elif test[0] == '!':
                            if node.text == test[1:]:
                                result = "        Incorrect value returned in XML for %s\n" % (path,)
                        elif test[0] == '*':
                            if node.text is None or node.text.find(test[1:]) == -1:
                                result = "        Incorrect value returned in XML for %s\n" % (path,)
                        elif test[0] == '$':
                            if node.text is None or node.text.find(test[1:]) != -1:
                                result = "        Incorrect value returned in XML for %s\n" % (path,)
                        elif test[0] == '+':
                            if node.text is None or not node.text.startswith(test[1:]):
                                result = "        Incorrect value returned in XML for %s\n" % (path,)
                        elif test[0] == '^':
                            if "=" in test:
                                element, value = test[1:].split("=", 1)
                            else:
                                element = test[1:]
                                value = None
                            for child in node.getchildren():
                                if child.tag == element and (value is None or child.text == value):
                                    break
                            else:
                                result = "        Missing child returned in XML for %s\n" % (path,)

                        # Try to parse as iCalendar
                        elif test == 'icalendar':
                            try:
                                Calendar.parseText(node.text)
                            except:
                                result = "        Incorrect value returned in iCalendar for %s\n" % (path,)

                        # Try to parse as JSON
                        elif test == 'json':
                            try:
                                json.loads(node.text)
                            except:
                                result = "        Incorrect value returned in XML for %s\n" % (path,)
                        return result
示例#8
0
def loadCalendar(file, verbose):

    cal = Calendar()
    if verbose:
        print "Parsing calendar data: %s" % (file, )
    fin = open(file, "r")
    try:
        cal.parse(fin)
    except InvalidData, e:
        print "Failed to parse bad data: %s" % (e.mData, )
        raise
示例#9
0
    def setUp(self):
        super(TestTimezoneDB, self).setUp()

        # Standard components explicitly added
        for vtz in StandardTZs:
            cal = Calendar()
            TimezoneDatabase.getTimezoneDatabase()._addStandardTimezone(cal.parseComponent(StringIO(vtz)))

        # Just parsing will add as non-standard
        for vtz in NonStandardTZs:
            Calendar.parseData(vtz)
示例#10
0
        def _doRoundtrip(caldata, resultdata=None):
            test1 = resultdata if resultdata is not None else caldata

            cal = Calendar()
            cal.parse(StringIO.StringIO(caldata))

            test2 = cal.getTextXML()

            self.assertEqual(
                test1, test2, "\n".join(
                    difflib.unified_diff(
                        str(test1).splitlines(), test2.splitlines())))
示例#11
0
        def _doRoundtrip(caldata, jcaldata):
            cal1 = Calendar.parseText(caldata)
            test1 = cal1.getText()

            cal2 = Calendar.parseJSONData(jcaldata)
            test2 = cal2.getText()

            self.assertEqual(
                test1,
                test2,
                "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
            )
示例#12
0
def loadCalendar(files, verbose):

    cal = Calendar()
    for file in files:
        if verbose:
            print "Parsing calendar data: %s" % (file, )
        with open(file, "r") as fin:
            try:
                cal.parse(fin)
            except InvalidData, e:
                print "Failed to parse bad data: %s" % (e.mData, )
                raise
示例#13
0
    def vtimezones(self, minYear, maxYear=2018, filterzones=None):
        """
        Generate iCalendar data for all VTIMEZONEs or just those specified
        """

        cal = Calendar()
        for zone in self.zones.itervalues():
            if filterzones and zone.name not in filterzones:
                continue
            vtz = zone.vtimezone(cal, self.rules, minYear, maxYear)
            cal.addComponent(vtz)

        return cal.getText()
示例#14
0
    def getTimezoneInCalendar(tzid):
        """
        Return a VTIMEZONE inside a valid VCALENDAR
        """

        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            from pycalendar.icalendar.calendar import Calendar
            cal = Calendar()
            cal.addComponent(tz.duplicate(cal))
            return cal
        else:
            return None
示例#15
0
    def vtimezones(self, minYear, maxYear=2018, filterzones=None):
        """
        Generate iCalendar data for all VTIMEZONEs or just those specified
        """

        cal = Calendar()
        for zone in self.zones.itervalues():
            if filterzones and zone.name not in filterzones:
                continue
            vtz = zone.vtimezone(cal, self.rules, minYear, maxYear)
            cal.addComponent(vtz)

        return cal.getText()
示例#16
0
        def _doRoundtrip(caldata, resultdata=None):
            test1 = resultdata if resultdata is not None else caldata

            cal = Calendar()
            cal.parse(StringIO.StringIO(caldata))

            test2 = cal.getTextXML()

            self.assertEqual(
                test1,
                test2,
                "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
            )
示例#17
0
    def getTimezoneInCalendar(tzid):
        """
        Return a VTIMEZONE inside a valid VCALENDAR
        """

        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            from pycalendar.icalendar.calendar import Calendar
            cal = Calendar()
            cal.addComponent(tz.duplicate(cal))
            return cal
        else:
            return None
示例#18
0
    def _getTimezoneFromServer(self, tzinfo):
        # List all from the server
        url = "%s?action=get&tzid=%s" % (self.uri, tzinfo.tzid,)
        log.debug("Getting timezone from secondary server: %s" % (url,))
        response = (yield getURL(url))
        if response is None or response.code / 100 != 2:
            returnValue(None)

        ct = response.headers.getRawHeaders("content-type", ("bogus/type",))[0]
        ct = ct.split(";", 1)
        ct = ct[0]
        if ct not in ("text/calendar",):
            log.error("Invalid content-type '%s' for tzid : %s" % (ct, tzinfo.tzid,))
            returnValue(None)

        ical = response.data
        try:
            calendar = Calendar.parseText(ical)
        except InvalidData:
            log.error("Invalid calendar data for tzid: %s" % (tzinfo.tzid,))
            returnValue(None)
        ical = calendar.getText()

        tzinfo.md5 = hashlib.md5(ical).hexdigest()

        try:
            tzpath = os.path.join(self.basepath, tzinfo.tzid) + ".ics"
            if not os.path.exists(os.path.dirname(tzpath)):
                os.makedirs(os.path.dirname(tzpath))
            f = open(tzpath, "w")
            f.write(ical)
            f.close()
        except IOError, e:
            log.error("Unable to write calendar file for %s: %s" % (tzinfo.tzid, str(e),))
示例#19
0
    def setUp(self):
        super(TestTimezoneDBCache, self).setUp()

        # Use temp dbpath
        tmpdir = tempfile.mkdtemp()
        TimezoneDatabase.createTimezoneDatabase(tmpdir)

        # Save standard components to temp directory
        for vtz in StandardTZs:
            cal = Calendar()
            tz = cal.parseComponent(StringIO(vtz))
            tzid_parts = tz.getID().split("/")
            if not os.path.exists(os.path.join(tmpdir, tzid_parts[0])):
                os.makedirs(os.path.join(tmpdir, tzid_parts[0]))
            with open(os.path.join(tmpdir, "{}.ics".format(tz.getID())), "w") as f:
                f.write(vtz)
    def _getTimezoneFromServer(self, tzinfo):
        # List all from the server
        url = "%s?action=get&tzid=%s" % (self.uri, tzinfo.tzid)
        log.debug("Getting timezone from secondary server: %s" % (url,))
        response = (yield getURL(url))
        if response is None or response.code / 100 != 2:
            returnValue(None)

        ct = response.headers.getRawHeaders("content-type", ("bogus/type",))[0]
        ct = ct.split(";", 1)
        ct = ct[0]
        if ct not in ("text/calendar",):
            log.error("Invalid content-type '%s' for tzid : %s" % (ct, tzinfo.tzid))
            returnValue(None)

        ical = response.data
        try:
            calendar = Calendar.parseText(ical)
        except InvalidData:
            log.error("Invalid calendar data for tzid: %s" % (tzinfo.tzid,))
            returnValue(None)
        ical = calendar.getText()

        tzinfo.md5 = hashlib.md5(ical).hexdigest()

        try:
            tzpath = os.path.join(self.basepath, tzinfo.tzid) + ".ics"
            if not os.path.exists(os.path.dirname(tzpath)):
                os.makedirs(os.path.dirname(tzpath))
            f = open(tzpath, "w")
            f.write(ical)
            f.close()
        except IOError, e:
            log.error("Unable to write calendar file for %s: %s" % (tzinfo.tzid, str(e)))
示例#21
0
        def _doNonEquality(caldata):
            cal1 = Calendar()
            cal1.parse(StringIO.StringIO(caldata))

            cal2 = Calendar()
            cal2.parse(StringIO.StringIO(caldata))
            cal2.addProperty(Property("X-FOO", "BAR"))

            self.assertNotEqual(cal1, cal2)
示例#22
0
    def testjCalExample1(self):

        jcaldata = """["vcalendar",
  [
    ["calscale", {}, "text", "GREGORIAN"],
    ["prodid", {}, "text", "-//Example Inc.//Example Calendar//EN"],
    ["version", {}, "text", "2.0"]
  ],
  [
    ["vevent",
      [
        ["dtstamp", {}, "date-time", "2008-02-05T19:12:24Z"],
        ["dtstart", {}, "date", "2008-10-06"],
        ["summary", {}, "text", "Planning meeting"],
        ["uid", {}, "text", "4088E990AD89CB3DBB484909"]
      ],
      []
    ]
  ]
]
"""

        icaldata = """BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Example Inc.//Example Calendar//EN
VERSION:2.0
BEGIN:VEVENT
DTSTAMP:20080205T191224Z
DTSTART;VALUE=DATE:20081006
SUMMARY:Planning meeting
UID:4088E990AD89CB3DBB484909
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")

        cal1 = Calendar.parseText(icaldata)
        test1 = cal1.getText()

        cal2 = Calendar.parseJSONData(jcaldata)
        test2 = cal2.getText()

        self.assertEqual(
            test1,
            test2,
            "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
        )
示例#23
0
    def testDuplicateWithRecurrenceChange(self):

        data = (
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;COUNT=400
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
        )

        cal1 = Calendar()
        cal1.parse(StringIO.StringIO(data[0]))
        cal2 = cal1.duplicate()
        vevent = cal2.getComponents()[0]
        rrules = vevent.getRecurrenceSet()
        for rrule in rrules.getRules():
            rrule.setUseCount(True)
            rrule.setCount(400)
            rrules.changed()

        self.assertEqual(data[0], str(cal1))
        self.assertEqual(data[1], str(cal2))
示例#24
0
    def testjCalExample1(self):

        jcaldata = """["vcalendar",
  [
    ["calscale", {}, "text", "GREGORIAN"],
    ["prodid", {}, "text", "-//Example Inc.//Example Calendar//EN"],
    ["version", {}, "text", "2.0"]
  ],
  [
    ["vevent",
      [
        ["dtstamp", {}, "date-time", "2008-02-05T19:12:24Z"],
        ["dtstart", {}, "date", "2008-10-06"],
        ["summary", {}, "text", "Planning meeting"],
        ["uid", {}, "text", "4088E990AD89CB3DBB484909"]
      ],
      []
    ]
  ]
]
"""

        icaldata = """BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Example Inc.//Example Calendar//EN
VERSION:2.0
BEGIN:VEVENT
DTSTAMP:20080205T191224Z
DTSTART;VALUE=DATE:20081006
SUMMARY:Planning meeting
UID:4088E990AD89CB3DBB484909
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")

        cal1 = Calendar.parseText(icaldata)
        test1 = cal1.getText()

        cal2 = Calendar.parseJSONData(jcaldata)
        test2 = cal2.getText()

        self.assertEqual(
            test1, test2, "\n".join(
                difflib.unified_diff(
                    str(test1).splitlines(), test2.splitlines())))
示例#25
0
    def testDuplicateWithRecurrenceChange(self):

        data = (
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;COUNT=400
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
        )

        cal1 = Calendar()
        cal1.parse(StringIO.StringIO(data[0]))
        cal2 = cal1.duplicate()
        vevent = cal2.getComponents()[0]
        rrules = vevent.getRecurrenceSet()
        for rrule in rrules.getRules():
            rrule.setUseCount(True)
            rrule.setCount(400)
            rrules.changed()

        self.assertEqual(data[0], str(cal1))
        self.assertEqual(data[1], str(cal2))
示例#26
0
        def _doEquality(caldata):
            cal1 = Calendar()
            cal1.parse(StringIO.StringIO(caldata))

            cal2 = Calendar()
            cal2.parse(StringIO.StringIO(caldata))

            self.assertEqual(cal1, cal2, "%s\n\n%s" % (
                cal1,
                cal2,
            ))
示例#27
0
        def _doDuplicateRoundtrip(caldata):
            cal = Calendar()
            cal.parse(StringIO.StringIO(caldata))
            cal = cal.duplicate()

            s = StringIO.StringIO()
            cal.generate(s)
            self.assertEqual(caldata, s.getvalue())
示例#28
0
    def testAddCN(self):

        data = (
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
ORGANIZER:[email protected]
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
            "まだ",
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
ORGANIZER;CN=まだ:[email protected]
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
        )

        cal1 = Calendar()
        cal1.parse(StringIO.StringIO(data[0]))

        vevent = cal1.getComponents("VEVENT")[0]
        organizer = vevent.getProperties("ORGANIZER")[0]
        organizer.addParameter(Parameter("CN", data[1]))

        cal2 = Calendar()
        cal2.parse(StringIO.StringIO(data[2]))

        self.assertEqual(str(cal1), str(cal2))
示例#29
0
        def _doRoundtrip(caldata, resultdata):
            test1 = json.dumps(json.loads(resultdata), indent=2, separators=(',', ':'), sort_keys=True)

            cal = Calendar.parseText(caldata)

            test2 = cal.getTextJSON(sort_keys=True)
            self.assertEqual(
                test1,
                test2,
                "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
            )
示例#30
0
    def getTimezone(self, tzid):
        """
        Generate a PyCalendar containing the requested timezone.
        """
        # We will just use our existing TimezoneCache here
        calendar = Calendar()
        try:
            vtz = readVTZ(tzid)
            calendar.addComponent(vtz.getComponents()[0].duplicate())
        except TimezoneException:

            # Check if an alias exists and create data for that
            if tzid in self.aliases:
                try:
                    vtz = readVTZ(self.aliases[tzid])
                except TimezoneException:
                    log.error("Failed to find timezone data for alias: %s" % (tzid,))
                    return None
                else:
                    vtz = vtz.duplicate()
                    vtz.getComponents()[0].getProperties("TZID")[0].setValue(tzid)
                    addVTZ(tzid, vtz)
                    calendar.addComponent(vtz.getComponents()[0].duplicate())
            else:
                log.error("Failed to find timezone data for: %s" % (tzid,))
                return None

        return calendar
示例#31
0
        def _doRoundtrip(caldata, resultdata):
            test1 = json.dumps(json.loads(resultdata),
                               indent=2,
                               separators=(',', ':'),
                               sort_keys=True)

            cal = Calendar.parseText(caldata)

            test2 = cal.getTextJSON(sort_keys=True)
            self.assertEqual(
                test1, test2, "\n".join(
                    difflib.unified_diff(
                        str(test1).splitlines(), test2.splitlines())))
示例#32
0
def validate(fname):
    """
    Check whether the contents of the specified file is valid iCalendar or vCard data.
    """

    with open(fname) as f:
        data = f.read()

    ParserContext.allRaise()

    if data.find("BEGIN:VCALENDAR") != -1:
        try:
            cal = Calendar.parseText(data)
        except ErrorBase as e:
            print("Failed to parse iCalendar: {}: {}".format(
                e.mReason,
                e.mData,
            ))
            sys.exit(1)
    elif data.find("BEGIN:VCARD") != -1:
        try:
            cal = Card.parseText(data)
        except ErrorBase as e:
            print("Failed to parse vCard: {}: {}".format(
                e.mReason,
                e.mData,
            ))
            sys.exit(1)
    else:
        print("Failed to find valid iCalendar or vCard data")
        sys.exit(1)

    _ignore_fixed, unfixed = cal.validate(doFix=False, doRaise=False)
    if unfixed:
        print("List of problems: {}".format(unfixed, ))
    else:
        print("No problems")

    # Control character check - only HTAB, CR, LF allowed for characters in the range 0x00-0x1F
    s = str(data)
    if len(
            s.translate(
                None,
                "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
            )) != len(s):
        for ctr, i in enumerate(data):
            if i in "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F":
                print("Control character {} at position {}".format(
                    ord(i),
                    ctr,
                ))
示例#33
0
        def _doDuplicateRoundtrip(caldata):
            cal = Calendar()
            cal.parse(StringIO.StringIO(caldata))
            cal = cal.duplicate()

            s = StringIO.StringIO()
            cal.generate(s)
            self.assertEqual(caldata, s.getvalue())
示例#34
0
        def _doEquality(caldata):
            cal1 = Calendar()
            cal1.parse(StringIO.StringIO(caldata))

            cal2 = Calendar()
            cal2.parse(StringIO.StringIO(caldata))

            self.assertEqual(cal1, cal2, "%s\n\n%s" % (cal1, cal2,))
示例#35
0
def validate(fname):
    """
    Check whether the contents of the specified file is valid iCalendar or vCard data.
    """

    data = open(fname).read()

    ParserContext.allRaise()

    if data.find("BEGIN:VCALENDAR") != -1:
        try:
            cal = Calendar.parseText(data)
        except ErrorBase, e:
            print "Failed to parse iCalendar: %r" % (e, )
            sys.exit(1)
示例#36
0
def validate(fname):
    """
    Check whether the contents of the specified file is valid iCalendar or vCard data.
    """

    with open(fname) as f:
        data = f.read()

    ParserContext.allRaise()

    if data.find("BEGIN:VCALENDAR") != -1:
        try:
            cal = Calendar.parseText(data)
        except ErrorBase, e:
            print "Failed to parse iCalendar: %r" % (e,)
            sys.exit(1)
示例#37
0
    def testAddCN(self):

        data = (
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
ORGANIZER:[email protected]
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),

            "まだ",

            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
ORGANIZER;CN=まだ:[email protected]
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),

        )

        cal1 = Calendar()
        cal1.parse(StringIO.StringIO(data[0]))

        vevent = cal1.getComponents("VEVENT")[0]
        organizer = vevent.getProperties("ORGANIZER")[0]
        organizer.addParameter(Parameter("CN", data[1]))

        cal2 = Calendar()
        cal2.parse(StringIO.StringIO(data[2]))

        self.assertEqual(str(cal1), str(cal2))
示例#38
0
    def _calProperty(self, propertyname, respdata):

        try:
            cal = Calendar.parseText(respdata)
        except Exception:
            return None

        # propname is a path consisting of component names and the last one a property name
        # e.g. VEVENT/ATTACH
        bits = propertyname.split("/")
        components = bits[:-1]
        prop = bits[-1]
        bits = prop.split("$")
        pname = bits[0]
        pvalue = bits[1] if len(bits) > 1 else None

        while components:
            for c in cal.getComponents():
                if c.getType() == components[0]:
                    cal = c
                    components = components[1:]
                    break
            else:
                break

        if components:
            return None

        props = cal.getProperties(pname)
        if pvalue:
            for prop in props:
                if prop.getValue().getValue() == pvalue:
                    return prop
            else:
                return None
        else:
            return props[0] if props else None
    def _calProperty(self, propertyname, respdata):

        try:
            cal = Calendar.parseText(respdata)
        except Exception:
            return None

        # propname is a path consisting of component names and the last one a property name
        # e.g. VEVENT/ATTACH
        bits = propertyname.split("/")
        components = bits[:-1]
        prop = bits[-1]
        bits = prop.split("$")
        pname = bits[0]
        pvalue = bits[1] if len(bits) > 1 else None

        while components:
            for c in cal.getComponents():
                if c.getType() == components[0]:
                    cal = c
                    components = components[1:]
                    break
            else:
                break

        if components:
            return None

        props = cal.getProperties(pname)
        if pvalue:
            for prop in props:
                if prop.getValue().getValue() == pvalue:
                    return prop
            else:
                return None
        else:
            return props[0] if props else None
示例#40
0
    def testParseBlank(self):

        data = (
            """
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
            """

BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR


""".replace("\n", "\r\n"),
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT

UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
            """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT


UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
        )

        save = ParserContext.BLANK_LINES_IN_DATA
        for item in data:
            ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_RAISE
            self.assertRaises(InvalidData, Calendar.parseText, item)

            ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_IGNORE
            lines = item.split("\r\n")
            result = "\r\n".join([line for line in lines if line]) + "\r\n"
            self.assertEqual(str(Calendar.parseText(item)), result)

        ParserContext.BLANK_LINES_IN_DATA = save
示例#41
0
 def clear(self):
     from pycalendar.icalendar.calendar import Calendar
     self.calendar = Calendar()
     self.tzcache.clear()
     self.stdtzcache.clear()
示例#42
0
 def __init__(self):
     from pycalendar.icalendar.calendar import Calendar
     self.dbpath = None
     self.calendar = Calendar()
     self.tzcache = {}
     self.stdtzcache = set()
示例#43
0
class TimezoneDatabase(object):
    """
    On demand timezone database cache. This scans a TZdb directory for .ics files matching a
    TZID and caches the component data in a calendar from whence the actual component is returned.
    """

    sTimezoneDatabase = None


    @staticmethod
    def createTimezoneDatabase(dbpath):
        TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase()
        TimezoneDatabase.sTimezoneDatabase.setPath(dbpath)


    @staticmethod
    def clearTimezoneDatabase():
        if TimezoneDatabase.sTimezoneDatabase is not None:
            TimezoneDatabase.sTimezoneDatabase.clear()


    def __init__(self):
        from pycalendar.icalendar.calendar import Calendar
        self.dbpath = None
        self.calendar = Calendar()
        self.tzcache = {}
        self.stdtzcache = set()


    def setPath(self, dbpath):
        self.dbpath = dbpath


    def clear(self):
        from pycalendar.icalendar.calendar import Calendar
        self.calendar = Calendar()
        self.tzcache.clear()
        self.stdtzcache.clear()


    @staticmethod
    def getTimezoneDatabase():
        if TimezoneDatabase.sTimezoneDatabase is None:
            TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase()
        return TimezoneDatabase.sTimezoneDatabase


    @staticmethod
    def getTimezone(tzid):
        return TimezoneDatabase.getTimezoneDatabase()._getTimezone(tzid)


    @staticmethod
    def getTimezoneInCalendar(tzid):
        """
        Return a VTIMEZONE inside a valid VCALENDAR
        """

        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            from pycalendar.icalendar.calendar import Calendar
            cal = Calendar()
            cal.addComponent(tz.duplicate(cal))
            return cal
        else:
            return None


    @staticmethod
    def getTimezoneOffsetSeconds(tzid, dt, relative_to_utc=False):
        # Cache it first
        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            return tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
        else:
            return 0


    @staticmethod
    def getTimezoneDescriptor(tzid, dt):
        # Cache it first
        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            return tz.getTimezoneDescriptor(dt)
        else:
            return ""


    @staticmethod
    def isStandardTimezone(tzid):
        return TimezoneDatabase.getTimezoneDatabase()._isStandardTimezone(tzid)


    def cacheTimezone(self, tzid):
        """
        Load the specified timezone identifier's timezone data from a file and parse it
        into the L{Calendar} used to store timezones used by this object.

        @param tzid: the timezone identifier to load
        @type tzid: L{str}
        """

        if self.dbpath is None:
            return

        tzpath = os.path.join(self.dbpath, "%s.ics" % (tzid,))
        tzpath = os.path.normpath(tzpath)
        if tzpath.startswith(self.dbpath) and os.path.isfile(tzpath):
            try:
                with open(tzpath) as f:
                    self.calendar.parseComponent(f)
            except (IOError, InvalidData):
                raise NoTimezoneInDatabase(self.dbpath, tzid)
        else:
            raise NoTimezoneInDatabase(self.dbpath, tzid)


    def addTimezone(self, tz):
        """
        Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component
        is assumed to be a non-standard timezone - i.e., not loaded from the timezone database.

        @param tz: the VTIMEZONE component to add
        @type tz: L{Component}
        """
        copy = tz.duplicate(self.calendar)
        self.calendar.addComponent(copy)
        self.tzcache[copy.getID()] = copy


    def _addStandardTimezone(self, tz):
        """
        Same as L{addTimezone} except that the timezone is marked as a standard timezone. This
        is only meant to be used for testing which happens int he absence of a real standard
        timezone database.

        @param tz: the VTIMEZONE component to add
        @type tz: L{Component}
        """
        if tz.getID() not in self.tzcache:
            self.addTimezone(tz)
        self.stdtzcache.add(tz.getID())


    def _isStandardTimezone(self, tzid):
        """
        Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component
        is assumed to be a non-standard timezone - i.e., not loaded from the timezone database.

        @param tzid: the timezone identifier to lookup
        @type tzid: L{str}
        """
        return tzid in self.stdtzcache


    def _getTimezone(self, tzid):
        """
        Get a timezone matching the specified timezone identifier. Use this object's
        cache - if not in the cache try to load it from a tz database file and store in
        this object's calendar.

        @param tzid: the timezone identifier to lookup
        @type tzid: L{str}
        """
        if tzid not in self.tzcache:
            tz = self.calendar.getTimezone(tzid)
            if tz is None:
                try:
                    self.cacheTimezone(tzid)
                except NoTimezoneInDatabase:
                    pass
                tz = self.calendar.getTimezone(tzid)
            self.tzcache[tzid] = tz
            if tz is not None and tzid is not None:
                self.stdtzcache.add(tzid)

        return self.tzcache[tzid]


    @staticmethod
    def mergeTimezones(cal, tzs):
        """
        Merge each timezone from other calendar.
        """

        tzdb = TimezoneDatabase.getTimezoneDatabase()

        # Not if our own calendar
        if cal is tzdb.calendar:
            return

        # Merge each timezone from other calendar
        for tz in tzs:
            tzdb.mergeTimezone(tz)


    def mergeTimezone(self, tz):
        """
        If the supplied VTIMEZONE is not in our cache then store it in memory.
        """

        if self._getTimezone(tz.getID()) is None:
            self.addTimezone(tz)
示例#44
0
    def testOffsets(self):

        data = (
            (
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:STANDARD
DTSTART:18831118T120358
RDATE:18831118T120358
TZNAME:EST
TZOFFSETFROM:-045602
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19200101T000000
RDATE:19200101T000000
RDATE:19420101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:EST
TZOFFSETFROM:-0500
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19200328T020000
RDATE:19200328T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19201031T020000
RDATE:19201031T020000
RDATE:19450930T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19210424T020000
RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19210925T020000
RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:EWT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T190000
RDATE:19450814T190000
TZNAME:EPT
TZOFFSETFROM:-0400
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19460428T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19460929T020000
RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19551030T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""",
                (
                    (DateTime(1942, 2, 8), False, -5),
                    (DateTime(1942, 2, 10), False, -4),
                    (DateTime(2011, 1, 1), False, -5),
                    (DateTime(2011, 4, 1), False, -4),
                    (DateTime(2011, 10, 24), False, -4),
                    (DateTime(2011, 11, 8), False, -5),
                    (DateTime(2006, 1, 1), False, -5),
                    (DateTime(2006, 4, 1), False, -5),
                    (DateTime(2006, 5, 1), False, -4),
                    (DateTime(2006, 10, 1), False, -4),
                    (DateTime(2006, 10, 24), False, -4),
                    (DateTime(2006, 11, 8), False, -5),
                    (DateTime(2014, 3, 8, 23, 0, 0), False, -5),
                    (DateTime(2014, 3, 9, 0, 0, 0), False, -5),
                    (DateTime(2014, 3, 9, 3, 0, 0), False, -4),
                    (DateTime(2014, 3, 9, 8, 0, 0), False, -4),
                    (DateTime(2014, 3, 8, 23, 0, 0), True, -5),
                    (DateTime(2014, 3, 9, 0, 0, 0), True, -5),
                    (DateTime(2014, 3, 9, 3, 0, 0), True, -5),
                    (DateTime(2014, 3, 9, 8, 0, 0), True, -4),
                    (DateTime(2014, 11, 1, 23, 0, 0), False, -4),
                    (DateTime(2014, 11, 2, 0, 0, 0), False, -4),
                    (DateTime(2014, 11, 2, 3, 0, 0), False, -5),
                    (DateTime(2014, 11, 2, 8, 0, 0), False, -5),
                    (DateTime(2014, 11, 1, 23, 0, 0), True, -4),
                    (DateTime(2014, 11, 2, 0, 0, 0), True, -4),
                    (DateTime(2014, 11, 2, 3, 0, 0), True, -4),
                    (DateTime(2014, 11, 2, 8, 0, 0), True, -5),
                )
            ),
            (
                """BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Etc/GMT+8
X-LIC-LOCATION:Etc/GMT+8
BEGIN:STANDARD
DTSTART:18000101T000000
RDATE:18000101T000000
TZNAME:GMT+8
TZOFFSETFROM:-0800
TZOFFSETTO:-0800
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""",
                (
                    (DateTime(1942, 2, 8), False, -8),
                    (DateTime(1942, 2, 10), False, -8),
                    (DateTime(2011, 1, 1), False, -8),
                    (DateTime(2011, 4, 1), False, -8),
                )
            ),
        )

        for tzdata, offsets in data:

            cal = Calendar.parseText(tzdata.replace("\n", "\r\n"))
            tz = cal.getComponents()[0]

            for dt, relative_to_utc, offset in offsets:
                tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching" % (tz.getID(), dt,))
            for dt, relative_to_utc, offset in reversed(offsets):
                tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching, reversed" % (tz.getID(), dt,))

            for dt, relative_to_utc, offset in offsets:
                tz.mCachedExpandAllMax = None
                tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching" % (tz.getID(), dt,))
            for dt, relative_to_utc, offset in reversed(offsets):
                tz.mCachedExpandAllMax = None
                tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching, reversed" % (tz.getID(), dt,))
示例#45
0
    def verify(self, manager, uri, response, respdata, args): #@UnusedVariable

        # Must have status 200
        if response.status != 200:
            return False, "        HTTP Status Code Wrong: %d" % (response.status,)

        # Get expected FREEBUSY info
        busy = args.get("busy", [])
        tentative = args.get("tentative", [])
        unavailable = args.get("unavailable", [])
        duration = args.get("duration") is not None

        # Parse data as calendar object
        try:
            calendar = Calendar.parseText(respdata)

            # Check for calendar
            if calendar is None:
                raise ValueError("Not a calendar: %s" % (respdata,))

            # Only one component
            comps = calendar.getComponents("VFREEBUSY")
            if len(comps) != 1:
                raise ValueError("Wrong number or unexpected components in calendar")

            # Must be VFREEBUSY
            fb = comps[0]

            # Extract periods
            busyp = []
            tentativep = []
            unavailablep = []
            for fp in fb.getProperties("FREEBUSY"):
                periods = fp.getValue().getValues()
                # Convert start/duration to start/end
                for i in range(len(periods)):
                    periods[i].getValue().setUseDuration(duration)
                # Check param
                fbtype = "BUSY"
                if fp.hasParameter("FBTYPE"):
                    fbtype = fp.getParameterValue("FBTYPE")
                if fbtype == "BUSY":
                    busyp.extend(periods)
                elif fbtype == "BUSY-TENTATIVE":
                    tentativep.extend(periods)
                elif fbtype == "BUSY-UNAVAILABLE":
                    unavailablep.extend(periods)
                else:
                    raise ValueError("Unknown FBTYPE: %s" % (fbtype,))

            # Set sizes must match
            if (
                (len(busy) != len(busyp)) or
                (len(unavailable) != len(unavailablep)) or
                (len(tentative) != len(tentativep))
            ):
                raise ValueError("Period list sizes do not match.")

            # Convert to string sets
            busy = set(busy)
            busyp = [x.getValue().getText() for x in busyp]
            busyp = set(busyp)
            tentative = set(tentative)
            tentativep = [x.getValue().getText() for x in tentativep]
            tentativep = set(tentativep)
            unavailable = set(unavailable)
            unavailablep = [x.getValue().getText() for x in unavailablep]
            unavailablep = set(unavailablep)

            # Compare all periods
            if len(busyp.symmetric_difference(busy)):
                raise ValueError("Busy periods do not match: {}".format(busyp.symmetric_difference(busy)))
            elif len(tentativep.symmetric_difference(tentative)):
                raise ValueError("Busy-tentative periods do not match")
            elif len(unavailablep.symmetric_difference(unavailable)):
                raise ValueError("Busy-unavailable periods do not match")

        except InvalidData:
            return False, "        HTTP response data is not a calendar"
        except ValueError, txt:
            return False, "        HTTP response data is invalid: %s" % (txt,)
示例#46
0
import twistedcaldav.customxml
import twistedcaldav.timezonexml

twistedcaldav # Shhh.. pyflakes

#
# DefaultHTTPHandler
#

from txweb2.http_headers import DefaultHTTPHandler, last, singleHeader

DefaultHTTPHandler.updateParsers({
    "If-Schedule-Tag-Match": (last, str),
})
DefaultHTTPHandler.updateGenerators({
    "Schedule-Tag": (str, singleHeader),
})

# Do some PyCalendar init
from pycalendar.icalendar.calendar import Calendar
from pycalendar.icalendar.property import Property
from pycalendar.vcard.card import Card
from pycalendar.value import Value

Calendar.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
Card.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")

# These are properties we use directly and we want the default value type set for TEXT
Property.registerDefaultValue("X-CALENDARSERVER-PRIVATE-COMMENT", Value.VALUETYPE_TEXT)
Property.registerDefaultValue("X-CALENDARSERVER-ATTENDEE-COMMENT", Value.VALUETYPE_TEXT)
示例#47
0
    def testConversions(self):

        tzdata = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:STANDARD
DTSTART:18831118T120358
RDATE:18831118T120358
TZNAME:EST
TZOFFSETFROM:-045602
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19200101T000000
RDATE:19200101T000000
RDATE:19420101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:EST
TZOFFSETFROM:-0500
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19200328T020000
RDATE:19200328T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19201031T020000
RDATE:19201031T020000
RDATE:19450930T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19210424T020000
RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19210925T020000
RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:EWT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T190000
RDATE:19450814T190000
TZNAME:EPT
TZOFFSETFROM:-0400
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19460428T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19460929T020000
RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19551030T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:STANDARD
DTSTART:18831118T120702
RDATE:18831118T120702
TZNAME:PST
TZOFFSETFROM:-075258
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T100000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T090000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:PWT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T160000
RDATE:19450814T160000
TZNAME:PPT
TZOFFSETFROM:-0700
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19450930T020000
RDATE:19450930T020000
RDATE:19490101T020000
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:STANDARD
DTSTART:19460101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:PST
TZOFFSETFROM:-0800
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19480314T020000
RDATE:19480314T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19500430T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T100000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19500924T020000
RRULE:FREQ=YEARLY;UNTIL=19610924T090000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:STANDARD
DTSTART:19621028T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T090000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T100000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T090000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T100000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T100000Z;BYDAY=1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
"""
        data = (
            (
                DateTime(2014, 3, 8, 23, 0, 0, Timezone(tzid="America/New_York")),
                DateTime(2014, 3, 8, 20, 0, 0, Timezone(tzid="America/Los_Angeles")),
            ),
            (
                DateTime(2014, 3, 9, 3, 0, 0, Timezone(utc=True)),
                DateTime(2014, 3, 8, 19, 0, 0, Timezone(tzid="America/Los_Angeles")),
            ),
            (
                DateTime(2014, 3, 9, 13, 0, 0, Timezone(utc=True)),
                DateTime(2014, 3, 9, 6, 0, 0, Timezone(tzid="America/Los_Angeles")),
            ),
        )

        Calendar.parseText(tzdata.replace("\n", "\r\n"))

        for dtfrom, dtto in data:

            self.assertEqual(dtfrom, dtto)

            newdtfrom = dtfrom.duplicate()
            newdtfrom.adjustTimezone(dtto.getTimezone())
            self.assertEqual(newdtfrom, dtto)
            self.assertEqual(newdtfrom.getHours(), dtto.getHours())
    def verify(self, manager, uri, response, respdata, args): #@UnusedVariable

        # Must have status 200
        if response.status != 200:
            return False, "        HTTP Status Code Wrong: %d" % (response.status,)

        # Get expected FREEBUSY info
        users = args.get("attendee", [])
        busy = args.get("busy", [])
        tentative = args.get("tentative", [])
        unavailable = args.get("unavailable", [])

        # Extract each calendar-data object
        try:
            tree = ElementTree(file=StringIO.StringIO(respdata))
        except ExpatError:
            return False, "           Could not parse proper XML response\n"

        for calendar in tree.findall("./{urn:ietf:params:xml:ns:caldav}response/{urn:ietf:params:xml:ns:caldav}calendar-data"):
            # Parse data as calendar object
            try:
                calendar = Calendar.parseText(calendar.text)

                # Check for calendar
                if calendar is None:
                    raise ValueError("Not a calendar: %s" % (calendar,))

                # Only one component
                comps = calendar.getComponents("VFREEBUSY")
                if len(comps) != 1:
                    raise ValueError("Wrong number or unexpected components in calendar")

                # Must be VFREEBUSY
                fb = comps[0]

                    # Check for attendee value
                for attendee in fb.getProperties("ATTENDEE"):
                    if attendee.getValue().getValue() in users:
                        users.remove(attendee.getValue().getValue())
                        break
                else:
                    continue

                # Extract periods
                busyp = []
                tentativep = []
                unavailablep = []
                for fp in fb.getProperties("FREEBUSY"):
                    periods = fp.getValue().getValues()
                    # Convert start/duration to start/end
                    for i in range(len(periods)):
                        periods[i].getValue().setUseDuration(False)
                    # Check param
                    fbtype = "BUSY"
                    if fp.hasParameter("FBTYPE"):
                        fbtype = fp.getParameterValue("FBTYPE")
                    if fbtype == "BUSY":
                        busyp.extend(periods)
                    elif fbtype == "BUSY-TENTATIVE":
                        tentativep.extend(periods)
                    elif fbtype == "BUSY-UNAVAILABLE":
                        unavailablep.extend(periods)
                    else:
                        raise ValueError("Unknown FBTYPE: %s" % (fbtype,))

                # Set sizes must match
                if ((len(busy) != len(busyp)) or
                    (len(unavailable) != len(unavailablep)) or
                    (len(tentative) != len(tentativep))):
                    raise ValueError("Period list sizes do not match.")

                # Convert to string sets
                busy = set(busy)
                busyp = [x.getValue().getText() for x in busyp]
                busyp = set(busyp)
                tentative = set(tentative)
                tentativep = [x.getValue().getText() for x in tentativep]
                tentativep = set(tentativep)
                unavailable = set(unavailable)
                unavailablep = [x.getValue().getText() for x in unavailablep]
                unavailablep = set(unavailablep)

                # Compare all periods
                if len(busyp.symmetric_difference(busy)):
                    raise ValueError("Busy periods do not match")
                elif len(tentativep.symmetric_difference(tentative)):
                    raise ValueError("Busy-tentative periods do not match")
                elif len(unavailablep.symmetric_difference(unavailable)):
                    raise ValueError("Busy-unavailable periods do not match")

                break

            except InvalidData:
                return False, "        HTTP response data is not a calendar"
            except ValueError, txt:
                return False, "        HTTP response data is invalid: %s" % (txt,)
    def verify(self, manager, uri, response, respdata, args, is_json=False): #@UnusedVariable
        # Get arguments
        files = args.get("filepath", [])
        if manager.data_dir:
            files = map(lambda x: os.path.join(manager.data_dir, x), files)
        caldata = args.get("data", [])
        filters = args.get("filter", [])
        statusCode = args.get("status", ["200", "201", "207"])
        doTimezones = args.get("doTimezones", None)

        if "EMAIL parameter" not in manager.server_info.features:
            filters.append("ATTENDEE:EMAIL")
            filters.append("ORGANIZER:EMAIL")
        filters.append("ATTENDEE:X-CALENDARSERVER-DTSTAMP")
        filters.append("ATTENDEE:X-CALENDARSERVER-AUTO")
        filters.append("ATTENDEE:X-CALENDARSERVER-RESET-PARTSTAT")
        filters.append("CALSCALE")
        filters.append("PRODID")
        filters.append("DTSTAMP")
        filters.append("CREATED")
        filters.append("LAST-MODIFIED")
        filters.append("X-WR-CALNAME")

        for afilter in tuple(filters):
            if afilter[0] == "!" and afilter[1:] in filters:
                filters.remove(afilter[1:])
        filters = filter(lambda x: x[0] != "!", filters)

        if doTimezones is None:
            doTimezones = "timezones-by-reference" not in manager.server_info.features
        else:
            doTimezones = doTimezones == "true"

        # status code must be 200, 201, 207 or explicitly specified code
        if str(response.status) not in statusCode:
            return False, "        HTTP Status Code Wrong: %d" % (response.status,)

        # look for response data
        if not respdata:
            return False, "        No response body"

        # look for one file
        if len(files) != 1 and len(caldata) != 1:
            return False, "        No file to compare response to"

        # read in all data from specified file or use provided data
        if len(files):
            fd = open(files[0], "r")
            try:
                try:
                    data = fd.read()
                finally:
                    fd.close()
            except:
                data = None
        else:
            data = caldata[0] if len(caldata) else None

        if data is None:
            return False, "        Could not read data file"

        data = manager.server_info.extrasubs(manager.server_info.subs(data))

        def removePropertiesParameters(component):

            if not doTimezones:
                for subcomponent in tuple(component.getComponents()):
                    if subcomponent.getType() == "VTIMEZONE":
                        component.removeComponent(subcomponent)

            for subcomponent in component.getComponents():
                removePropertiesParameters(subcomponent)

            allProps = []
            for properties in component.getProperties().itervalues():
                allProps.extend(properties)
            for property in allProps:
                # Always reset DTSTAMP on these properties
                if property.getName() in ("ATTENDEE", "X-CALENDARSERVER-ATTENDEE-COMMENT"):
                    if property.hasParameter("X-CALENDARSERVER-DTSTAMP"):
                        property.replaceParameter(Parameter("X-CALENDARSERVER-DTSTAMP", "20080101T000000Z"))

                for filter in filters:
                    if ":" in filter:
                        propname, parameter = filter.split(":")
                        if property.getName() == propname:
                            if property.hasParameter(parameter):
                                property.removeParameters(parameter)
                    else:
                        if property.getName() == filter:
                            component.removeProperty(property)

        def reconcileRecurrenceOverrides(calendar1, calendar2):
            """
            Make sure that the same set of overridden components appears in both calendar objects.
            """
            def _getRids(calendar):
                """
                Get all the recurrence ids of the specified calendar.
                """
                results = set()
                master = None
                for subcomponent in calendar.getComponents():
                    if isinstance(subcomponent, ComponentRecur):
                        rid = subcomponent.getRecurrenceID()
                        if rid:
                            results.add(rid.duplicateAsUTC())
                        else:
                            master = subcomponent
                return results, master

            def _addOverrides(calendar, master, missing_rids):
                """
                Derive instances for the missing overrides in the specified calendar object.
                """
                if master is None or not missing_rids:
                    return
                for rid in missing_rids:
                    # If we were fed an already derived component, use that, otherwise make a new one
                    newcomp = calendar.deriveComponent(rid)
                    if newcomp is not None:
                        calendar.addComponent(newcomp)

            rids1, master1 = _getRids(calendar1)
            rids2, master2 = _getRids(calendar2)

            _addOverrides(calendar1, master1, rids2 - rids1)
            _addOverrides(calendar2, master2, rids1 - rids2)

        try:
            format = Calendar.sFormatJSON if is_json else Calendar.sFormatText

            resp_calendar = Calendar.parseData(respdata, format=format)
            removePropertiesParameters(resp_calendar)

            data_calendar = Calendar.parseData(data, format=format)
            removePropertiesParameters(data_calendar)

            reconcileRecurrenceOverrides(resp_calendar, data_calendar)

            respdata = resp_calendar.getText(includeTimezones=Calendar.NO_TIMEZONES, format=format)
            data = data_calendar.getText(includeTimezones=Calendar.NO_TIMEZONES, format=format)

            result = resp_calendar == data_calendar

            if result:
                return True, ""
            else:
                error_diff = "\n".join([line for line in unified_diff(data.split("\n"), respdata.split("\n"))])
                return False, "        Response data does not exactly match file data%s" % (error_diff,)
        except Exception, e:
            return False, "        Response data is not calendar data: %s" % (e,)
示例#50
0
    def test_vtimezone(self):

        zonedef = """Zone America/New_York\t-4:56:02\t-\tLMT\t1883 Nov 18 12:03:58
\t\t\t-5:00\tUS\tE%sT"""
        rules = """Rule\tUS\t1918\t1919\t-\tMar\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1918\t1919\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1942\tonly\t-\tFeb\t9\t2:00\t1:00\tW
Rule\tUS\t1945\tonly\t-\tAug\t14\t23:00u\t1:00\tP
Rule\tUS\t1945\tonly\t-\tSep\t30\t2:00\t0\tS
Rule\tUS\t1967\t2006\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1967\t1973\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1974\tonly\t-\tJan\t6\t2:00\t1:00\tD
Rule\tUS\t1975\tonly\t-\tFeb\t23\t2:00\t1:00\tD
Rule\tUS\t1976\t1986\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1987\t2006\t-\tApr\tSun>=1\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tMar\tSun>=8\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tNov\tSun>=1\t2:00\t0\tS"""
        result = """BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:DAYLIGHT
DTSTART:20060402T020000
RDATE:20060402T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20061029T020000
RDATE:20061029T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
""".replace("\n", "\r\n")

        zone = Zone()
        zone.parse(zonedef)

        ruleset = RuleSet()
        ruleset.parse(rules)
        rules = {ruleset.name: ruleset}

        cal = Calendar()
        vtz = zone.vtimezone(cal, rules, 2006, 2011)

        self.assertEqual(str(vtz), result)
    def verify(self, manager, uri, response, respdata, args): #@UnusedVariable
        # Get arguments
        files = args.get("filepath", [])
        caldata = args.get("data", [])
        filters = args.get("filter", [])
        statusCode = args.get("status", ["200", "201", "207"])
        doTimezones = args.get("doTimezones", None)

        if "EMAIL parameter" not in manager.server_info.features:
            filters.append("ATTENDEE:EMAIL")
            filters.append("ORGANIZER:EMAIL")
        filters.append("ATTENDEE:X-CALENDARSERVER-DTSTAMP")
        filters.append("CALSCALE")
        filters.append("PRODID")
        filters.append("DTSTAMP")
        filters.append("CREATED")
        filters.append("LAST-MODIFIED")
        filters.append("X-WR-CALNAME")

        for afilter in tuple(filters):
            if afilter[0] == "!" and afilter[1:] in filters:
                filters.remove(afilter[1:])
        filters = filter(lambda x: x[0] != "!", filters)

        if doTimezones is None:
            doTimezones = "timezones-by-reference" not in manager.server_info.features
        else:
            doTimezones = doTimezones == "true"

        # status code must be 200, 201, 207 or explicitly specified code
        if str(response.status) not in statusCode:
            return False, "        HTTP Status Code Wrong: %d" % (response.status,)

        # look for response data
        if not respdata:
            return False, "        No response body"

        # look for one file
        if len(files) != 1 and len(caldata) != 1:
            return False, "        No file to compare response to"

        # read in all data from specified file or use provided data
        if len(files):
            fd = open(files[0], "r")
            try:
                try:
                    data = fd.read()
                finally:
                    fd.close()
            except:
                data = None
        else:
            data = caldata[0] if len(caldata) else None

        if data is None:
            return False, "        Could not read data file"

        data = manager.server_info.extrasubs(manager.server_info.subs(data))

        def removePropertiesParameters(component):

            if not doTimezones:
                for subcomponent in tuple(component.getComponents()):
                    if subcomponent.getType() == "VTIMEZONE":
                        component.removeComponent(subcomponent)

            for subcomponent in component.getComponents():
                removePropertiesParameters(subcomponent)

            allProps = []
            for properties in component.getProperties().itervalues():
                allProps.extend(properties)
            for property in allProps:
                # Always reset DTSTAMP on these properties
                if property.getName() in ("ATTENDEE", "X-CALENDARSERVER-ATTENDEE-COMMENT"):
                    if property.hasParameter("X-CALENDARSERVER-DTSTAMP"):
                        property.replaceParameter(Parameter("X-CALENDARSERVER-DTSTAMP", "20080101T000000Z"))

                for filter in filters:
                    if ":" in filter:
                        propname, parameter = filter.split(":")
                        if property.getName() == propname:
                            if property.hasParameter(parameter):
                                property.removeParameters(parameter)
                    else:
                        if property.getName() == filter:
                            component.removeProperty(property)

        try:
            resp_calendar = Calendar.parseText(respdata)
            removePropertiesParameters(resp_calendar)
            respdata = resp_calendar.getText()

            data_calendar = Calendar.parseText(data)
            removePropertiesParameters(data_calendar)
            data = data_calendar.getText()

            result = respdata == data

            if result:
                return True, ""
            else:
                error_diff = "\n".join([line for line in unified_diff(data.split("\n"), respdata.split("\n"))])
                return False, "        Response data does not exactly match file data%s" % (error_diff,)
        except Exception, e:
            return False, "        Response data is not calendar data: %s" % (e,)
示例#52
0
class TimezoneDatabase(object):
    """
    On demand timezone database cache. This scans a TZdb directory for .ics files matching a
    TZID and caches the component data in a calendar from whence the actual component is returned.
    """

    sTimezoneDatabase = None

    @staticmethod
    def createTimezoneDatabase(dbpath):
        TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase()
        TimezoneDatabase.sTimezoneDatabase.setPath(dbpath)

    @staticmethod
    def clearTimezoneDatabase():
        if TimezoneDatabase.sTimezoneDatabase is not None:
            TimezoneDatabase.sTimezoneDatabase.clear()

    def __init__(self):
        from pycalendar.icalendar.calendar import Calendar
        self.dbpath = None
        self.calendar = Calendar()
        self.tzcache = {}
        self.stdtzcache = set()

    def setPath(self, dbpath):
        self.dbpath = dbpath

    def clear(self):
        from pycalendar.icalendar.calendar import Calendar
        self.calendar = Calendar()
        self.tzcache.clear()
        self.stdtzcache.clear()

    @staticmethod
    def getTimezoneDatabase():
        if TimezoneDatabase.sTimezoneDatabase is None:
            TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase()
        return TimezoneDatabase.sTimezoneDatabase

    @staticmethod
    def getTimezone(tzid):
        return TimezoneDatabase.getTimezoneDatabase()._getTimezone(tzid)

    @staticmethod
    def getTimezoneInCalendar(tzid):
        """
        Return a VTIMEZONE inside a valid VCALENDAR
        """

        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            from pycalendar.icalendar.calendar import Calendar
            cal = Calendar()
            cal.addComponent(tz.duplicate(cal))
            return cal
        else:
            return None

    @staticmethod
    def getTimezoneOffsetSeconds(tzid, dt, relative_to_utc=False):
        # Cache it first
        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            return tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
        else:
            return 0

    @staticmethod
    def getTimezoneDescriptor(tzid, dt):
        # Cache it first
        tz = TimezoneDatabase.getTimezone(tzid)
        if tz is not None:
            return tz.getTimezoneDescriptor(dt)
        else:
            return ""

    @staticmethod
    def isStandardTimezone(tzid):
        return TimezoneDatabase.getTimezoneDatabase()._isStandardTimezone(tzid)

    def cacheTimezone(self, tzid):
        """
        Load the specified timezone identifier's timezone data from a file and parse it
        into the L{Calendar} used to store timezones used by this object.

        @param tzid: the timezone identifier to load
        @type tzid: L{str}
        """

        if self.dbpath is None:
            return

        tzpath = os.path.join(self.dbpath, "%s.ics" % (tzid, ))
        tzpath = os.path.normpath(tzpath)
        if tzpath.startswith(self.dbpath) and os.path.isfile(tzpath):
            try:
                with open(tzpath) as f:
                    self.calendar.parseComponent(f)
            except (IOError, InvalidData):
                raise NoTimezoneInDatabase(self.dbpath, tzid)
        else:
            raise NoTimezoneInDatabase(self.dbpath, tzid)

    def addTimezone(self, tz):
        """
        Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component
        is assumed to be a non-standard timezone - i.e., not loaded from the timezone database.

        @param tz: the VTIMEZONE component to add
        @type tz: L{Component}
        """
        copy = tz.duplicate(self.calendar)
        self.calendar.addComponent(copy)
        self.tzcache[copy.getID()] = copy

    def _addStandardTimezone(self, tz):
        """
        Same as L{addTimezone} except that the timezone is marked as a standard timezone. This
        is only meant to be used for testing which happens int he absence of a real standard
        timezone database.

        @param tz: the VTIMEZONE component to add
        @type tz: L{Component}
        """
        if tz.getID() not in self.tzcache:
            self.addTimezone(tz)
        self.stdtzcache.add(tz.getID())

    def _isStandardTimezone(self, tzid):
        """
        Add the specified VTIMEZONE component to this object's L{Calendar} cache. This component
        is assumed to be a non-standard timezone - i.e., not loaded from the timezone database.

        @param tzid: the timezone identifier to lookup
        @type tzid: L{str}
        """
        return tzid in self.stdtzcache

    def _getTimezone(self, tzid):
        """
        Get a timezone matching the specified timezone identifier. Use this object's
        cache - if not in the cache try to load it from a tz database file and store in
        this object's calendar.

        @param tzid: the timezone identifier to lookup
        @type tzid: L{str}
        """
        if tzid not in self.tzcache:
            tz = self.calendar.getTimezone(tzid)
            if tz is None:
                try:
                    self.cacheTimezone(tzid)
                except NoTimezoneInDatabase:
                    pass
                tz = self.calendar.getTimezone(tzid)
            self.tzcache[tzid] = tz
            if tz is not None and tzid is not None:
                self.stdtzcache.add(tzid)

        return self.tzcache[tzid]

    @staticmethod
    def mergeTimezones(cal, tzs):
        """
        Merge each timezone from other calendar.
        """

        tzdb = TimezoneDatabase.getTimezoneDatabase()

        # Not if our own calendar
        if cal is tzdb.calendar:
            return

        # Merge each timezone from other calendar
        for tz in tzs:
            tzdb.mergeTimezone(tz)

    def mergeTimezone(self, tz):
        """
        If the supplied VTIMEZONE is not in our cache then store it in memory.
        """

        if self._getTimezone(tz.getID()) is None:
            self.addTimezone(tz)
示例#53
0
    def testParseComponent(self):

        data1 = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")

        data2 = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//Example Inc.//Example Calendar//EN
BEGIN:VTIMEZONE
TZID:America/Montreal
LAST-MODIFIED:20040110T032845Z
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n")

        result = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
TZID:America/Montreal
LAST-MODIFIED:20040110T032845Z
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")

        cal = Calendar()
        cal.parse(StringIO.StringIO(data1))
        cal.parseComponent(StringIO.StringIO(data2))
        self.assertEqual(str(cal), result)
示例#54
0
#
# DefaultHTTPHandler
#

from txweb2.http_headers import DefaultHTTPHandler, last, singleHeader

DefaultHTTPHandler.updateParsers({
    "If-Schedule-Tag-Match": (last, str),
})
DefaultHTTPHandler.updateGenerators({
    "Schedule-Tag": (str, singleHeader),
})

# Do some PyCalendar init
from pycalendar.icalendar.calendar import Calendar
from pycalendar.icalendar.property import Property
from pycalendar.vcard.card import Card
from pycalendar.value import Value

Calendar.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")
Card.setPRODID("-//CALENDARSERVER.ORG//NONSGML Version 1//EN")

# These are properties we use directly and we want the default value type set for TEXT
Property.registerDefaultValue("X-CALENDARSERVER-PRIVATE-COMMENT", Value.VALUETYPE_TEXT)
Property.registerDefaultValue("X-CALENDARSERVER-ATTENDEE-COMMENT", Value.VALUETYPE_TEXT)

Property.registerDefaultValue("X-APPLE-TRAVEL-DURATION", Value.VALUETYPE_DURATION, always_write_value=True)
Property.registerDefaultValue("X-APPLE-TRAVEL-START", Value.VALUETYPE_URI, always_write_value=True)
Property.registerDefaultValue("X-APPLE-TRAVEL-RETURN-DURATION", Value.VALUETYPE_DURATION, always_write_value=True)
Property.registerDefaultValue("X-APPLE-TRAVEL-RETURN", Value.VALUETYPE_URI, always_write_value=True)
示例#55
0
    def testCachePreserveOnAdjustment(self):

        # UTC first
        dt = DateTime(2012, 6, 7, 12, 0, 0, Timezone(tzid="utc"))
        dt.getPosixTime()

        # check existing cache is complete
        self.assertTrue(dt.mPosixTimeCached)
        self.assertNotEqual(dt.mPosixTime, 0)
        self.assertEqual(dt.mTZOffset, None)

        # duplicate preserves cache details
        dt2 = dt.duplicate()
        self.assertTrue(dt2.mPosixTimeCached)
        self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
        self.assertEqual(dt2.mTZOffset, dt.mTZOffset)

        # adjust preserves cache details
        dt2.adjustToUTC()
        self.assertTrue(dt2.mPosixTimeCached)
        self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
        self.assertEqual(dt2.mTZOffset, dt.mTZOffset)

        # Now timezone
        tzdata = """BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:America/Pittsburgh
BEGIN:STANDARD
DTSTART:18831118T120358
RDATE:18831118T120358
TZNAME:EST
TZOFFSETFROM:-045602
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19200101T000000
RDATE:19200101T000000
RDATE:19420101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:EST
TZOFFSETFROM:-0500
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19200328T020000
RDATE:19200328T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19201031T020000
RDATE:19201031T020000
RDATE:19450930T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19210424T020000
RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19210925T020000
RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:EWT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T190000
RDATE:19450814T190000
TZNAME:EPT
TZOFFSETFROM:-0400
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19460428T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19460929T020000
RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19551030T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n")

        Calendar.parseText(tzdata)

        dt = DateTime(2012, 6, 7, 12, 0, 0, Timezone(tzid="America/Pittsburgh"))
        dt.getPosixTime()

        # check existing cache is complete
        self.assertTrue(dt.mPosixTimeCached)
        self.assertNotEqual(dt.mPosixTime, 0)
        self.assertEqual(dt.mTZOffset, -14400)

        # duplicate preserves cache details
        dt2 = dt.duplicate()
        self.assertTrue(dt2.mPosixTimeCached)
        self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
        self.assertEqual(dt2.mTZOffset, dt.mTZOffset)

        # adjust preserves cache details
        dt2.adjustToUTC()
        self.assertTrue(dt2.mPosixTimeCached)
        self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
        self.assertEqual(dt2.mTZOffset, 0)
示例#56
0
    def execute(self, cmdname, options):

        longlist = False
        path = None
        rtype = False
        displayname = False
        ctag = False
        etag = False
        supported_components = False
        synctoken = False
        details = False

        opts, args = getopt.getopt(shlex.split(options), 'acdDeilrs')

        for name, _ignore_value in opts:

            if name == "-a":
                pass
            elif name == "-c":
                ctag = True
                longlist = True
            elif name == "-d":
                displayname = True
                longlist = True
            elif name == "-D":
                details = True
            elif name == "-e":
                etag = True
                longlist = True
            elif name == "-i":
                supported_components = True
                longlist = True
            elif name == "-l":
                longlist = True
            elif name == "-r":
                rtype = True
                longlist = True
            elif name == "-s":
                synctoken = True
                longlist = True
            else:
                print "Unknown option: %s" % (name, )
                print self.usage(cmdname)
                raise WrongOptions

        if len(args) > 1:
            print "Wrong number of arguments: %d" % (len(args), )
            print self.usage(cmdname)
            raise WrongOptions
        elif args:
            path = args[0]
            if not path.startswith("/"):
                path = os.path.join(self.shell.wd, path)
        else:
            path = self.shell.wd
        if not path.endswith("/"):
            path += "/"
        resource = URL(url=path)

        props = (davxml.resourcetype, )
        if longlist:
            props += (
                davxml.getcontentlength,
                davxml.getlastmodified,
            )
        if ctag:
            props += (csxml.getctag, )
        if displayname:
            props += (davxml.displayname, )
        if etag:
            props += (davxml.getetag, )
        if supported_components:
            props += (caldavxml.supported_calendar_component_set, )
        if synctoken:
            props += (davxml.synctoken, )
        results = self.shell.account.session.getPropertiesOnHierarchy(
            resource, props)
        items = results.keys()
        items.sort()
        lines = []
        for rurl in items:

            summaries = []
            if details:
                data = self.shell.account.session.readData(URL(url=rurl))
                try:
                    cobject = Calendar.parseData(data[0])
                    for comp in cobject.getComponents():
                        for summary in [
                                p.getValue().getValue()
                                for p in comp.getProperties("SUMMARY")
                        ]:
                            if summary not in summaries:
                                summaries.append(summary)
                except:
                    summaries = ["<Could not parse>"]

            rurl = urllib.unquote(rurl)
            if rurl == path:
                continue
            line = []
            if longlist:
                props = results[urllib.quote(rurl)]
                size = props.get(davxml.getcontentlength, "-")
                if not size:
                    size = "0"
                line.append("%s" % (size, ))
                modtime = props.get(davxml.getlastmodified, "-")
                line.append(modtime)
                line.append(rurl[len(path):])
                if rtype:
                    if isinstance(props.get(davxml.resourcetype), str):
                        line.append("type:-")
                    else:
                        line.append("type:%s" % (",".join([
                            child.tag.split("}")[1] for child in props.get(
                                davxml.resourcetype).getchildren()
                        ])))
                if displayname:
                    line.append("name:'%s'" %
                                (props.get(davxml.displayname, '-'), ))
                if ctag:
                    line.append("ctag:'%s'" %
                                (props.get(csxml.getctag, '-'), ))
                if etag:
                    line.append("etag:'%s'" %
                                (props.get(davxml.getetag, '-'), ))
                if supported_components and props.get(
                        caldavxml.supported_calendar_component_set
                ) is not None:
                    line.append("comp:%s" % (",".join([
                        child.get("name", "") for child in props.get(
                            caldavxml.supported_calendar_component_set).
                        getchildren()
                    ])))
                if synctoken:
                    line.append("sync:'%s'" %
                                (props.get(davxml.synctoken, '-'), ))
            else:
                line.append(rurl[len(path):])
            if details:
                line.append("summary: {}".format("|".join(summaries)))
            lines.append(line)

        if lines:
            # Get column widths
            widths = [0] * len(lines[0])
            for line in lines:
                for ctr, col in enumerate(line):
                    widths[ctr] = max(widths[ctr], len(col))

            # Write out each one
            for line in lines:
                for ctr, col in enumerate(line):
                    if ctr in (0, 1) and longlist:
                        print col.rjust(widths[ctr] + 2),
                    else:
                        print col.ljust(widths[ctr] + 2),
                print
        return True
示例#57
0
    def testGetVEvents(self):

        data = (
            (
                "Non-recurring match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (DateTime(2011, 6, 1), ),
            ),
            (
                "Non-recurring no-match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110501
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (),
            ),
            (
                "Recurring match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (
                    DateTime(2011, 6, 1),
                    DateTime(2011, 6, 2),
                ),
            ),
            (
                "Recurring no match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110501
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (),
            ),
            (
                "Recurring with override match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20110601T120000
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110602T120000
DTSTART;VALUE=DATE:20110602T130000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (
                    DateTime(2011, 6, 1, 12, 0, 0),
                    DateTime(2011, 6, 2, 13, 0, 0),
                ),
            ),
            (
                "Recurring with override no match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20110501T120000
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110502T120000
DTSTART;VALUE=DATE:20110502T130000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (),
            ),
            (
                "Recurring partial match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110531
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (DateTime(2011, 6, 1), ),
            ),
            (
                "Recurring with override partial match",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20110531T120000
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110601T120000
DTSTART;VALUE=DATE:20110601T130000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                (DateTime(2011, 6, 1, 13, 0, 0), ),
            ),
        )

        for title, caldata, result in data:
            calendar = Calendar.parseText(caldata)
            instances = []
            calendar.getVEvents(
                Period(
                    start=DateTime(2011, 6, 1),
                    end=DateTime(2011, 7, 1),
                ), instances)
            instances = tuple(
                [instance.getInstanceStart() for instance in instances])
            self.assertEqual(
                instances, result, "Failed in %s: got %s, expected %s" %
                (title, instances, result))
示例#58
0
 def clear(self):
     from pycalendar.icalendar.calendar import Calendar
     self.calendar = Calendar()
     self.tzcache.clear()
     self.stdtzcache.clear()
示例#59
0
def anonymizeData(directoryMap, data):
    try:
        pyobj = Calendar.parseText(data)
    except Exception, e:
        print("Failed to parse (%s): %s" % (e, data))
        return None
示例#60
0
    def testMasterComponent(self):

        data = (
            (
                "1.1 Non-recurring no VTIMEZONE",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                """BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
""".replace("\n", "\r\n"),
            ),
            (
                "1.2 Non-recurring with VTIMEZONE",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//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
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;TZID=Etc/GMT+1:20110601T000000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                """BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;TZID=Etc/GMT+1:20110601T000000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
""".replace("\n", "\r\n"),
            ),
            (
                "2.1 Recurring no VTIMEZONE",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY
SUMMARY:New Year's Day
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110602
DTSTART;VALUE=DATE:20110602
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                """BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY
SUMMARY:New Year's Day
END:VEVENT
""".replace("\n", "\r\n"),
            ),
            (
                "2.2 Recurring with VTIMEZONE",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//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
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;TZID=Etc/GMT+1:20110601T000000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
RRULE:FREQ=DAILY
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;TZID=Etc/GMT+1:20110602T000000
DTSTART;TZID=Etc/GMT+1:20110602T000000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                """BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;TZID=Etc/GMT+1:20110601T000000
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY
SUMMARY:New Year's Day
END:VEVENT
""".replace("\n", "\r\n"),
            ),
            (
                "3.1 Recurring no master, no VTIMEZONE",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110602
DTSTART;VALUE=DATE:20110602
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                "",
            ),
            (
                "3.2 Recurring no master, with VTIMEZONE",
                """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//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
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;TZID=Etc/GMT+1:20110602T000000
DTSTART;TZID=Etc/GMT+1:20110602T000000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
                "",
            ),
        )

        for title, caldata, result in data:
            calendar = Calendar.parseText(caldata)
            master = calendar.masterComponent()
            if master is None:
                master = ""
            self.assertEqual(
                str(master), result,
                "Failed in %s: got %s, expected %s" % (title, master, result))