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)
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())))
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)
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())))
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]
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, ))
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())))
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())))
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())) )
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,))
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, ))
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)
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)
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())), )
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")
""" 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)
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,)
""" 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)
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)
# # 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)
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,)
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,))
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
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")
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())) )
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())))
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
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, ))