1## 2# Copyright (c) 2007-2011 Cyrus Daboo. All rights reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15## 16 17from cStringIO import StringIO 18from pycalendar.componentbase import PyCalendarComponentBase 19from pycalendar.exceptions import PyCalendarInvalidData 20from pycalendar.parser import ParserContext 21from pycalendar.utils import readFoldedLine 22from pycalendar.vcard import definitions 23from pycalendar.vcard.definitions import VCARD, Property_VERSION,\ 24 Property_PRODID, Property_UID 25from pycalendar.vcard.property import Property 26 27class Card(PyCalendarComponentBase): 28 29 sProdID = "-//mulberrymail.com//Mulberry v4.0//EN" 30 sDomain = "mulberrymail.com" 31 32 @staticmethod 33 def setPRODID(prodid): 34 Card.sProdID = prodid 35 36 @staticmethod 37 def setDomain(domain): 38 Card.sDomain = domain 39 40 def __init__(self, add_defaults=True): 41 super(Card, self).__init__() 42 43 if add_defaults: 44 self.addDefaultProperties() 45 46 def duplicate(self): 47 return super(Card, self).duplicate() 48 49 def getType(self): 50 return VCARD 51 52 def finalise(self): 53 pass 54 55 def sortedPropertyKeyOrder(self): 56 return ( 57 Property_VERSION, 58 Property_PRODID, 59 Property_UID, 60 ) 61 62 @staticmethod 63 def parseMultiple(ins): 64 65 results = [] 66 67 card = Card(add_defaults=False) 68 69 LOOK_FOR_VCARD = 0 70 GET_PROPERTY = 1 71 72 state = LOOK_FOR_VCARD 73 74 # Get lines looking for start of calendar 75 lines = [None, None] 76 77 while readFoldedLine(ins, lines): 78 79 line = lines[0] 80 if state == LOOK_FOR_VCARD: 81 82 # Look for start 83 if line == card.getBeginDelimiter(): 84 # Next state 85 state = GET_PROPERTY 86 87 # Handle blank line 88 elif len(line) == 0: 89 # Raise if requested, otherwise just ignore 90 if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE: 91 raise PyCalendarInvalidData("vCard data has blank lines") 92 93 # Unrecognized data 94 else: 95 raise PyCalendarInvalidData("vCard data not recognized", line) 96 97 elif state == GET_PROPERTY: 98 99 # Look for end of object 100 if line == card.getEndDelimiter(): 101 102 # Finalise the current calendar 103 card.finalise() 104 105 # Validate some things 106 if not card.hasProperty("VERSION"): 107 raise PyCalendarInvalidData("vCard missing VERSION", "") 108 109 results.append(card) 110 111 # Change state 112 card = Card(add_defaults=False) 113 state = LOOK_FOR_VCARD 114 115 # Blank line 116 elif len(line) == 0: 117 # Raise if requested, otherwise just ignore 118 if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE: 119 raise PyCalendarInvalidData("vCard data has blank lines") 120 121 # Must be a property 122 else: 123 124 # Parse attribute/value for top-level calendar item 125 prop = Property() 126 if prop.parse(line): 127 128 # Check for valid property 129 if not card.validProperty(prop): 130 raise PyCalendarInvalidData("Invalid property", str(prop)) 131 else: 132 card.addProperty(prop) 133 134 # Check for truncated data 135 if state != LOOK_FOR_VCARD: 136 raise PyCalendarInvalidData("vCard data not complete") 137 138 return results 139 140 @staticmethod 141 def parseText(data): 142 143 cal = Card(add_defaults=False) 144 if cal.parse(StringIO(data)): 145 return cal 146 else: 147 return None 148 149 def parse(self, ins): 150 151 result = False 152 153 self.setProperties({}) 154 155 LOOK_FOR_VCARD = 0 156 GET_PROPERTY = 1 157 158 state = LOOK_FOR_VCARD 159 160 # Get lines looking for start of calendar 161 lines = [None, None] 162 163 while readFoldedLine(ins, lines): 164 165 line = lines[0] 166 if state == LOOK_FOR_VCARD: 167 168 # Look for start 169 if line == self.getBeginDelimiter(): 170 # Next state 171 state = GET_PROPERTY 172 173 # Indicate success at this point 174 result = True 175 176 # Handle blank line 177 elif len(line) == 0: 178 # Raise if requested, otherwise just ignore 179 if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE: 180 raise PyCalendarInvalidData("vCard data has blank lines") 181 182 # Unrecognized data 183 else: 184 raise PyCalendarInvalidData("vCard data not recognized", line) 185 186 elif state == GET_PROPERTY: 187 188 # Look for end of object 189 if line == self.getEndDelimiter(): 190 191 # Finalise the current calendar 192 self.finalise() 193 194 # Change state 195 state = LOOK_FOR_VCARD 196 197 # Blank line 198 elif len(line) == 0: 199 # Raise if requested, otherwise just ignore 200 if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE: 201 raise PyCalendarInvalidData("vCard data has blank lines") 202 203 # Must be a property 204 else: 205 206 # Parse attribute/value for top-level calendar item 207 prop = Property() 208 try: 209 if prop.parse(line): 210 211 # Check for valid property 212 if not self.validProperty(prop): 213 raise PyCalendarInvalidData("Invalid property", str(prop)) 214 else: 215 self.addProperty(prop) 216 except IndexError: 217 print line 218 219 # Check for truncated data 220 if state != LOOK_FOR_VCARD: 221 raise PyCalendarInvalidData("vCard data not complete", "") 222 223 # Validate some things 224 if result and not self.hasProperty("VERSION"): 225 raise PyCalendarInvalidData("vCard missing VERSION", "") 226 227 return result 228 229 def addDefaultProperties(self): 230 self.addProperty(Property(definitions.Property_PRODID, Card.sProdID)) 231 self.addProperty(Property(definitions.Property_VERSION, "3.0")) 232 233 def validProperty(self, prop): 234 if prop.getName() == definitions.Property_VERSION: 235 236 tvalue = prop.getValue() 237 if ((tvalue is None) or (tvalue.getValue() != "3.0")): 238 return False 239 240 return True 241