Ejemplo n.º 1
0
        def _doNonEquality(caldata):
            card1 = Card()
            card1.parse(StringIO.StringIO(caldata))

            card2 = Card()
            card2.parse(StringIO.StringIO(caldata))
            card2.addProperty(Property("X-FOO", "BAR"))

            self.assertNotEqual(card1, card2)
Ejemplo n.º 2
0
        def _doRoundtrip(caldata, resultdata=None):
            test1 = resultdata if resultdata is not None else caldata

            card = Card()
            card.parse(StringIO.StringIO(caldata))

            s = StringIO.StringIO()
            card.generate(s)
            test2 = s.getvalue()

            self.assertEqual(test1, test2, "\n".join(difflib.unified_diff(test1.splitlines(), test2.splitlines())))
Ejemplo n.º 3
0
 def fromStream(clazz, stream):
     """
     Construct a L{Component} from a stream.
     @param stream: a C{read()}able stream containing vCard data.
     @return: a L{Component} representing the first component described by
         C{stream}.
     """
     cal = Card()
     try:
         result = cal.parse(stream)
     except PyCalendarError:
         result = None
     if not result:
         stream.seek(0)
         raise InvalidVCardDataError("%s" % (stream.read(),))
     return clazz(None, pycard=cal)
Ejemplo n.º 4
0
        def _doNonEquality(caldata):
            card1 = Card()
            card1.parse(StringIO.StringIO(caldata))

            card2 = Card()
            card2.parse(StringIO.StringIO(caldata))
            card2.addProperty(Property("X-FOO", "BAR"))

            self.assertNotEqual(card1, card2)
Ejemplo n.º 5
0
        def _doEquality(caldata):
            card1 = Card()
            card1.parse(StringIO.StringIO(caldata))

            card2 = Card()
            card2.parse(StringIO.StringIO(caldata))

            self.assertEqual(
                card1, card2, "\n".join(
                    difflib.unified_diff(
                        str(card1).splitlines(),
                        str(card2).splitlines())))
Ejemplo n.º 6
0
 def allFromStream(clazz, stream):
     """
     FIXME: Just default to reading a single VCARD - actually need more
     """
     try:
         results = Card.parseMultiple(stream)
     except PyCalendarError:
         results = None
     if not results:
         stream.seek(0)
         raise InvalidVCardDataError("%s" % (stream.read(),))
     return [clazz(None, pycard=result) for result in results]
Ejemplo n.º 7
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,
                ))
Ejemplo n.º 8
0
        def _doDuplicateRoundtrip(caldata, result):
            card = Card()
            card.parse(StringIO.StringIO(caldata))
            card = card.duplicate()

            s = StringIO.StringIO()
            card.generate(s)
            test = s.getvalue()
            self.assertEqual(
                test, result, "\n".join(
                    difflib.unified_diff(test.splitlines(),
                                         result.splitlines())))
Ejemplo n.º 9
0
        def _doDuplicateRoundtrip(caldata, result):
            card = Card()
            card.parse(StringIO.StringIO(caldata))
            card = card.duplicate()

            s = StringIO.StringIO()
            card.generate(s)
            test = s.getvalue()
            self.assertEqual(test, result, "\n".join(difflib.unified_diff(test.splitlines(), result.splitlines())))
Ejemplo n.º 10
0
        def _doEquality(caldata):
            card1 = Card()
            card1.parse(StringIO.StringIO(caldata))

            card2 = Card()
            card2.parse(StringIO.StringIO(caldata))

            self.assertEqual(
                card1, card2, "\n".join(difflib.unified_diff(str(card1).splitlines(), str(card2).splitlines()))
            )
Ejemplo n.º 11
0
    def test_basic(self):

        data = (
            (
                "No problems",
                """BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                set(),
                set(),
            ),
            (
                "No VERSION",
                """BEGIN:VCARD
VERSION:3.0
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                set(),
                set(("[VCARD] Missing or too many required property: N",)),
            ),
        )

        for title, item, test_fixed, test_unfixed in data:
            card = Card.parseText(item)
            fixed, unfixed = card.validate(doFix=False)
            self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
            self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
Ejemplo n.º 12
0
    def test_basic(self):

        data = (
            (
                "No problems",
                """BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
                set(),
                set(),
            ),
            (
                "No VERSION",
                """BEGIN:VCARD
VERSION:3.0
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
                set(),
                set(("[VCARD] Missing or too many required property: N", )),
            ),
        )

        for title, item, test_fixed, test_unfixed in data:
            card = Card.parseText(item)
            fixed, unfixed = card.validate(doFix=False)
            self.assertEqual(set(fixed),
                             test_fixed,
                             msg="Failed test: %s" % (title, ))
            self.assertEqual(set(unfixed),
                             test_unfixed,
                             msg="Failed test: %s" % (title, ))
Ejemplo n.º 13
0
    def testABapp(self):

        data = """BEGIN:VCARD
VERSION:3.0
N:Card;Test;;;
FN:Test Card
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:555-1212
TEL;type=HOME:532-1234
PHOTO;BASE64:
  TU0AKgAAMAj////////////////////////////////////////////////////////////+///+
  SW1hZ2VNYWdpY2sgNS4zLjkgMTAvMDEvMDEgUToxNiBodHRwOi8vd3d3LmltYWdlbWFnaWNrLm9y
  ZwA=
CATEGORIES:My Contacts
X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43\:ABPerson
UID:128ad7ee-a656-4773-95ce-f07f77e8cc23
REV:2011-03-23T20:20:04Z
END:VCARD
""".replace(
            "\n", "\r\n"
        )

        result = """BEGIN:VCARD
VERSION:3.0
UID:128ad7ee-a656-4773-95ce-f07f77e8cc23
CATEGORIES:My Contacts
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Test Card
N:Card;Test;;;
PHOTO;ENCODING=B:
 TU0AKgAAMAj////////////////////////////////////////////////////////////+
 ///+SW1hZ2VNYWdpY2sgNS4zLjkgMTAvMDEvMDEgUToxNiBodHRwOi8vd3d3LmltYWdlbWFn
 aWNrLm9yZwA=
REV:20110323T202004Z
TEL;type=WORK;type=pref:555-1212
TEL;type=HOME:532-1234
X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43\:ABPerson
END:VCARD
""".replace(
            "\n", "\r\n"
        )

        card = Card.parseText(data)
        self.assertEqual(str(card), result)
Ejemplo n.º 14
0
    def testABapp(self):

        data = """BEGIN:VCARD
VERSION:3.0
N:Card;Test;;;
FN:Test Card
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:555-1212
TEL;type=HOME:532-1234
PHOTO;BASE64:
  TU0AKgAAMAj////////////////////////////////////////////////////////////+///+
  SW1hZ2VNYWdpY2sgNS4zLjkgMTAvMDEvMDEgUToxNiBodHRwOi8vd3d3LmltYWdlbWFnaWNrLm9y
  ZwA=
CATEGORIES:My Contacts
X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43\:ABPerson
UID:128ad7ee-a656-4773-95ce-f07f77e8cc23
REV:2011-03-23T20:20:04Z
END:VCARD
""".replace("\n", "\r\n")

        result = """BEGIN:VCARD
VERSION:3.0
UID:128ad7ee-a656-4773-95ce-f07f77e8cc23
CATEGORIES:My Contacts
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Test Card
N:Card;Test;;;
PHOTO;ENCODING=B:
 TU0AKgAAMAj////////////////////////////////////////////////////////////+
 ///+SW1hZ2VNYWdpY2sgNS4zLjkgMTAvMDEvMDEgUToxNiBodHRwOi8vd3d3LmltYWdlbWFn
 aWNrLm9yZwA=
REV:20110323T202004Z
TEL;type=WORK;type=pref:555-1212
TEL;type=HOME:532-1234
X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43\:ABPerson
END:VCARD
""".replace("\n", "\r\n")

        card = Card.parseText(data)
        self.assertEqual(str(card), result)
Ejemplo n.º 15
0
        def _doRoundtrip(caldata, resultdata=None):
            test1 = resultdata if resultdata is not None else caldata

            card = Card()
            card.parse(StringIO.StringIO(caldata))

            s = StringIO.StringIO()
            card.generate(s)
            test2 = s.getvalue()

            self.assertEqual(
                test1,
                test2,
                "\n".join(
                    difflib.unified_diff(test1.splitlines(),
                                         test2.splitlines())),
            )
Ejemplo n.º 16
0
    def __init__(self, name, **kwargs):
        """
        Use this constructor to initialize an empty L{Component}.
        To create a new L{Component} from X{vCard} data, don't use this
        constructor directly.  Use one of the factory methods instead.
        @param name: the name (L{str}) of the X{iCalendar} component type for the
            component.
        """
        if name is None:
            if "pycard" in kwargs:
                pyobj = kwargs["pycard"]

                if pyobj is not None:
                    if not isinstance(pyobj, PyCalendarComponentBase):
                        raise TypeError("Not a PyCalendarComponentBase: %r" % (pyobj,))

                self._pycard = pyobj
            else:
                raise AssertionError("name may not be None")

            # FIXME: _parent is not use internally, and appears to be used elsewhere,
            # even though it's names as a private variable.
            if "parent" in kwargs:
                parent = kwargs["parent"]
                
                if parent is not None:
                    if not isinstance(parent, Component):
                        raise TypeError("Not a Component: %r" % (parent,))
                    
                self._parent = parent
            else:
                self._parent = None
        elif name == "VCARD":
            self._pycard = Card(add_defaults=False)
            self._parent = None
        else:
            raise ValueError("VCards have no child components")
Ejemplo n.º 17
0
    """

    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)
    elif data.find("BEGIN:VCARD") != -1:
        try:
            cal = Card.parseText(data)
        except ErrorBase, e:
            print "Failed to parse vCard: %r" % (e,)
            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: %s" % (unfixed,)
    else:
        print "No problems"

    # Control character check - only HTAB, CR, LF allowed for characters in the range 0x00-0x1F
    s = str(data)
Ejemplo n.º 18
0
    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)
        carddata = args.get("data", [])
        filters = args.get("filter", [])

        # Always remove these
        filters.extend(manager.server_info.addressdatafilters)

        # status code must be 200, 201, 207
        if response.status not in (200, 201, 207):
            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(carddata) != 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 = carddata[0] if len(carddata) else None

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

        data = manager.server_info.subs(data)

        def removePropertiesParameters(component):

            allProps = []
            for properties in component.getProperties().itervalues():
                allProps.extend(properties)
            for property in allProps:
                for filter in filters:
                    if ":" in filter:
                        propname, parameter = filter.split(":")
                        if property.getName() == propname:
                            if property.hasParameter(parameter):
                                property.removeParameters(parameter)
                    else:
                        if "=" in filter:
                            filter_name, filter_value = filter.split("=")
                            if property.getName() == filter_name and property.getValue().getValue() == filter_value:
                                component.removeProperty(property)
                        else:
                            if property.getName() == filter:
                                component.removeProperty(property)

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

            resp_adbk = Card.parseData(respdata, format=format)
            removePropertiesParameters(resp_adbk)
            respdata = resp_adbk.getText(format=format)

            data_adbk = Card.parseData(data, format=format)
            removePropertiesParameters(data_adbk)
            data = data_adbk.getText(format=format)

            result = resp_adbk == data_adbk

            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 address data: %s" % (e,)
Ejemplo n.º 19
0
    """

    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)
    elif data.find("BEGIN:VCARD") != -1:
        try:
            cal = Card.parseText(data)
        except ErrorBase, e:
            print "Failed to parse vCard: %r" % (e, )
            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: %s" % (unfixed, )
    else:
        print "No problems"

    # Control character check - only HTAB, CR, LF allowed for characters in the range 0x00-0x1F
    s = str(data)
Ejemplo n.º 20
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)
Ejemplo n.º 21
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)
Ejemplo n.º 22
0
    def verify(self, manager, uri, response, respdata, args): #@UnusedVariable
        # Get arguments
        files = args.get("filepath", [])
        carddata = args.get("data", [])
        filters = args.get("filter", [])

        # Always remove these
        filters.append("PRODID")
        filters.append("REV")

        # status code must be 200, 201, 207
        if response.status not in (200, 201, 207):
            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(carddata) != 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 = carddata[0] if len(carddata) else None

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

        data = manager.server_info.subs(data)

        def removePropertiesParameters(component):

            allProps = []
            for properties in component.getProperties().itervalues():
                allProps.extend(properties)
            for property in allProps:
                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_adbk = Card.parseText(respdata)
            removePropertiesParameters(resp_adbk)
            respdata = resp_adbk.getText()

            data_adbk = Card.parseText(data)
            removePropertiesParameters(data_adbk)
            data = data_adbk.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 address data: %s" % (e,)
Ejemplo n.º 23
0
    def test_mode_raise(self):

        data = (
            (
                "OK",
                """BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                set(),
                set(),
                False,
            ),
            (
                "Unfixable only",
                """BEGIN:VCARD
VERSION:3.0
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                set(),
                set(("[VCARD] Missing or too many required property: N",)),
                True,
            ),
        )

        for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
            card = Card.parseText(test_old)
            if test_raises:
                self.assertRaises(ValidationError, card.validate, doFix=False, doRaise=True)
            else:
                try:
                    fixed, unfixed = card.validate(doFix=False, doRaise=False)
                except:
                    self.fail(msg="Failed test: %s" % (title,))
                self.assertEqual(str(card), test_new, msg="Failed test: %s" % (title,))
                self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
                self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
Ejemplo n.º 24
0
    def testParseBlank(self):

        data = (
            """
BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
            """

BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
            """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD


""".replace("\n", "\r\n"),
            """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]

FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
            """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]


FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".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, Card.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(Card.parseText(item)), result)

        ParserContext.BLANK_LINES_IN_DATA = save
Ejemplo n.º 25
0
class Component (object):
    """
    X{vCard} component.
    """
    @classmethod
    def allFromString(clazz, string):
        """
        FIXME: Just default to reading a single VCARD - actually need more
        """
        if type(string) is unicode:
            string = string.encode("utf-8")
        else:
            # Valid utf-8 please
            string.decode("utf-8")
        
        # No BOMs please
        if string[:3] == codecs.BOM_UTF8:
            string = string[3:]

        return clazz.allFromStream(StringIO.StringIO(string))

    @classmethod
    def allFromStream(clazz, stream):
        """
        FIXME: Just default to reading a single VCARD - actually need more
        """
        try:
            results = Card.parseMultiple(stream)
        except PyCalendarError:
            results = None
        if not results:
            stream.seek(0)
            raise InvalidVCardDataError("%s" % (stream.read(),))
        return [clazz(None, pycard=result) for result in results]

    @classmethod
    def fromString(clazz, string):
        """
        Construct a L{Component} from a string.
        @param string: a string containing vCard data.
        @return: a L{Component} representing the first component described by
            C{string}.
        """
        if type(string) is unicode:
            string = string.encode("utf-8")
        else:
            # Valid utf-8 please
            string.decode("utf-8")
        
        # No BOMs please
        if string[:3] == codecs.BOM_UTF8:
            string = string[3:]

        return clazz.fromStream(StringIO.StringIO(string))

    @classmethod
    def fromStream(clazz, stream):
        """
        Construct a L{Component} from a stream.
        @param stream: a C{read()}able stream containing vCard data.
        @return: a L{Component} representing the first component described by
            C{stream}.
        """
        cal = Card()
        try:
            result = cal.parse(stream)
        except PyCalendarError:
            result = None
        if not result:
            stream.seek(0)
            raise InvalidVCardDataError("%s" % (stream.read(),))
        return clazz(None, pycard=cal)

    @classmethod
    def fromIStream(clazz, stream):
        """
        Construct a L{Component} from a stream.
        @param stream: an L{IStream} containing vCard data.
        @return: a deferred returning a L{Component} representing the first
            component described by C{stream}.
        """
        #
        # FIXME:
        #   This reads the request body into a string and then parses it.
        #   A better solution would parse directly and incrementally from the
        #   request stream.
        #
        def parse(data): return clazz.fromString(data)
        return allDataFromStream(IStream(stream), parse)

    def __init__(self, name, **kwargs):
        """
        Use this constructor to initialize an empty L{Component}.
        To create a new L{Component} from X{vCard} data, don't use this
        constructor directly.  Use one of the factory methods instead.
        @param name: the name (L{str}) of the X{iCalendar} component type for the
            component.
        """
        if name is None:
            if "pycard" in kwargs:
                pyobj = kwargs["pycard"]

                if pyobj is not None:
                    if not isinstance(pyobj, PyCalendarComponentBase):
                        raise TypeError("Not a PyCalendarComponentBase: %r" % (pyobj,))

                self._pycard = pyobj
            else:
                raise AssertionError("name may not be None")

            # FIXME: _parent is not use internally, and appears to be used elsewhere,
            # even though it's names as a private variable.
            if "parent" in kwargs:
                parent = kwargs["parent"]
                
                if parent is not None:
                    if not isinstance(parent, Component):
                        raise TypeError("Not a Component: %r" % (parent,))
                    
                self._parent = parent
            else:
                self._parent = None
        elif name == "VCARD":
            self._pycard = Card(add_defaults=False)
            self._parent = None
        else:
            raise ValueError("VCards have no child components")

    def __str__ (self): return str(self._pycard)
    def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, str(self._pycard))

    def __hash__(self):
        return hash(str(self))

    def __ne__(self, other): return not self.__eq__(other)
    def __eq__(self, other):
        if not isinstance(other, Component):
            return False
        return self._pycard == other._pycard

    # FIXME: Should this not be in __eq__?
    def same(self, other):
        return self._pycard == other._pycard
    
    def name(self):
        """
        @return: the name of the iCalendar type of this component.
        """
        return self._pycard.getType()

    def duplicate(self):
        """
        Duplicate this object and all its contents.
        @return: the duplicated vcard.
        """
        return Component(None, pycard=self._pycard.duplicate())
        
    def hasProperty(self, name):
        """
        @param name: the name of the property whose existence is being tested.
        @return: True if the named property exists, False otherwise.
        """
        return self._pycard.hasProperty(name)

    def getProperty(self, name):
        """
        Get one property from the property list.
        @param name: the name of the property to get.
        @return: the L{Property} found or None.
        @raise: L{ValueError} if there is more than one property of the given name.
        """
        properties = tuple(self.properties(name))
        if len(properties) == 1: return properties[0]
        if len(properties) > 1: raise InvalidVCardDataError("More than one %s property in component %r" % (name, self))
        return None
        
    def properties(self, name=None):
        """
        @param name: if given and not C{None}, restricts the returned properties
            to those with the given C{name}.
        @return: an iterable of L{Property} objects, one for each property of
            this component.
        """
        properties = []
        if name is None:
            [properties.extend(i) for i in self._pycard.getProperties().values()]
        elif self._pycard.countProperty(name) > 0:
            properties = self._pycard.getProperties(name)

        return (
            Property(None, None, None, pycard=p)
            for p in properties
        )

    def propertyValue(self, name):
        properties = tuple(self.properties(name))
        if len(properties) == 1:
            return properties[0].value()
        if len(properties) > 1:
            raise InvalidVCardDataError("More than one %s property in component %r" % (name, self))
        return None


    def addProperty(self, property):
        """
        Adds a property to this component.
        @param property: the L{Property} to add to this component.
        """
        self._pycard.addProperty(property._pycard)
        self._pycard.finalise()

    def removeProperty(self, property):
        """
        Remove a property from this component.
        @param property: the L{Property} to remove from this component.
        """
        self._pycard.removeProperty(property._pycard)
        self._pycard.finalise()

    def replaceProperty(self, property):
        """
        Add or replace a property in this component.
        @param property: the L{Property} to add or replace in this component.
        """
        
        # Remove all existing ones first
        self._pycard.removeProperties(property.name())
        self.addProperty(property)

    def resourceUID(self):
        """
        @return: the UID of the subcomponents in this component.
        """
        assert self.name() == "VCARD", "Not a vcard: %r" % (self,)

        if not hasattr(self, "_resource_uid"):
            self._resource_uid = self.propertyValue("UID")

        return self._resource_uid

    def validVCardData(self, doFix=True, doRaise=True):
        """
        @return: tuple of fixed, unfixed issues
        @raise InvalidVCardDataError: if the given vcard data is not valid.
        """
        if self.name() != "VCARD":
            log.debug("Not a vcard: %s" % (self,))
            raise InvalidVCardDataError("Not a vcard")

        # Do underlying vCard library validation with data fix
        fixed, unfixed = self._pycard.validate(doFix=doFix)
        if unfixed:
            log.debug("vCard data had unfixable problems:\n  %s" % ("\n  ".join(unfixed),))
            if doRaise:
                raise InvalidVCardDataError("Calendar data had unfixable problems:\n  %s" % ("\n  ".join(unfixed),))
        if fixed:
            log.debug("vCard data had fixable problems:\n  %s" % ("\n  ".join(fixed),))
        
        return fixed, unfixed

    def validForCardDAV(self):
        """
        @raise ValueError: if the given vcard data is not valid.
        """
        if self.name() != "VCARD": raise InvalidVCardDataError("Not a vcard")

        version = self.propertyValue("VERSION")
        if version != "3.0":
            raise InvalidVCardDataError("Not a version 2.0 vCard (version=%s)" % (version,))

        uid = self.propertyValue("UID")
        if uid is None:
            raise InvalidVCardDataError("All vCards must have UIDs")
        
        # Control character check - only HTAB, CR, LF allowed for characters in the range 0x00-0x1F
        s = str(self)
        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):
            raise InvalidVCardDataError("vCard contains illegal control character")
Ejemplo n.º 26
0
    def testMultiple(self):

        data = (
            (
                """BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                (
                    """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace(
                        "\n", "\r\n"
                    ),
                ),
            ),
            (
                """BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E2:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Another Thompson
N:Thompson;Another;;;
TEL;type=WORK;type=pref:1-555-555-5556
TEL;type=CELL:1-444-444-4445
item1.X-ABADR:us
END:VCARD
""".replace(
                    "\n", "\r\n"
                ),
                (
                    """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace(
                        "\n", "\r\n"
                    ),
                    """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E2:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Another Thompson
N:Thompson;Another;;;
TEL;type=WORK;type=pref:1-555-555-5556
TEL;type=CELL:1-444-444-4445
item1.X-ABADR:us
END:VCARD
""".replace(
                        "\n", "\r\n"
                    ),
                ),
            ),
        )

        for item, results in data:

            cards = Card.parseMultipleTextData(StringIO.StringIO(item))
            self.assertEqual(len(cards), len(results))
            for card, result in zip(cards, results):
                self.assertEqual(
                    str(card), result, "\n".join(difflib.unified_diff(str(card).splitlines(), result.splitlines()))
                )
Ejemplo n.º 27
0
    def testMultiple(self):

        data = (
            ("""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"), ("""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"), )),
            ("""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E2:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Another Thompson
N:Thompson;Another;;;
TEL;type=WORK;type=pref:1-555-555-5556
TEL;type=CELL:1-444-444-4445
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"), (
                """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
                """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E2:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Another Thompson
N:Thompson;Another;;;
TEL;type=WORK;type=pref:1-555-555-5556
TEL;type=CELL:1-444-444-4445
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
            )),
        )

        for item, results in data:

            cards = Card.parseMultipleTextData(StringIO.StringIO(item))
            self.assertEqual(len(cards), len(results))
            for card, result in zip(cards, results):
                self.assertEqual(
                    str(card), result, "\n".join(
                        difflib.unified_diff(
                            str(card).splitlines(), result.splitlines())))
Ejemplo n.º 28
0
    def testParseBlank(self):

        data = (
            """
BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace(
                "\n", "\r\n"
            ),
            """

BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace(
                "\n", "\r\n"
            ),
            """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD


""".replace(
                "\n", "\r\n"
            ),
            """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]

FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace(
                "\n", "\r\n"
            ),
            """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]


FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".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, Card.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(Card.parseText(item)), result)

        ParserContext.BLANK_LINES_IN_DATA = save
Ejemplo n.º 29
0
    def test_mode_raise(self):

        data = (
            (
                "OK",
                """BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
                """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
                set(),
                set(),
                False,
            ),
            (
                "Unfixable only",
                """BEGIN:VCARD
VERSION:3.0
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
                """BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
 SA
EMAIL;type=INTERNET;type=WORK;type=pref:[email protected]
FN:Default Thompson
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
                set(),
                set(("[VCARD] Missing or too many required property: N", )),
                True,
            ),
        )

        for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
            card = Card.parseText(test_old)
            if test_raises:
                self.assertRaises(ValidationError,
                                  card.validate,
                                  doFix=False,
                                  doRaise=True)
            else:
                try:
                    fixed, unfixed = card.validate(doFix=False, doRaise=False)
                except:
                    self.fail(msg="Failed test: %s" % (title, ))
                self.assertEqual(str(card),
                                 test_new,
                                 msg="Failed test: %s" % (title, ))
                self.assertEqual(set(fixed),
                                 test_fixed,
                                 msg="Failed test: %s" % (title, ))
                self.assertEqual(set(unfixed),
                                 test_unfixed,
                                 msg="Failed test: %s" % (title, ))