def get_vcard_property(property_line): """ Get a single property. @param property_line: Single unfolded vCard line @return: Dictionary with name, parameters and values """ prop = {} property_parts = vcard_utils.split_unescaped(property_line, ':') if len(property_parts) < 2: raise VCardFormatError( '{0}: {1}'.format(MSG_MISSING_VALUE_STRING, property_line), {}) elif len(property_parts) > 2: # Merge - Colon doesn't have to be escaped in values property_parts[1] = ':'.join(property_parts[1:]) property_parts = property_parts[:2] property_string, values_string = property_parts # Split property name and property parameters property_name_and_params = vcard_utils.split_unescaped( property_string, ';') prop['name'] = property_name_and_params.pop(0) # String validation if not prop['name'].upper() in ALL_PROPERTIES and not re.match( '^X-[{0}]+$'.format(re.escape(ID_CHARS)), prop['name'], re.IGNORECASE ): raise VCardFormatError( '{0}: {1[name]}'.format(MSG_INVALID_PROPERTY_NAME, prop), {}) try: if len(property_name_and_params) != 0: prop['parameters'] = get_vcard_property_params( ';'.join(property_name_and_params)) prop['values'] = get_vcard_property_values(values_string) # Validate vcard_validators.validate_vcard_property(prop) except VCardFormatError as error: # Add parameter name to error error.context['Property line'] = property_line raise VCardFormatError(error.message, error.context) return prop
def get_vcard_property_param(param_string): """ Get the parameter name and value(s). RFC 2426 page 29. @param param_string: Single parameter and values @return: Dictionary with a parameter name and values """ try: param_name, values_string = vcard_utils.split_unescaped( param_string, '=') except ValueError as error: raise vcard_validators.VCardFormatError( '{0}: {1}'.format(MSG_MISSING_PARAM_VALUE, str(error)), {}) values = get_vcard_property_param_values(values_string) # Validate if not re.match('^[' + ID_CHARS + ']+$', param_name): raise vcard_validators.VCardFormatError( '{0}: {1}'.format( MSG_INVALID_PARAM_NAME, param_name), {}) return {'name': param_name, 'values': values}
def get_vcard_property_values(values_string): """ Get the property values. @param values_string: Multiple value string @return: List of values (RFC 2426 page 12) """ values = [] # Strip line ending values_string = values_string[:-len(CRLF_CHARS)] subvalue_strings = vcard_utils.split_unescaped(values_string, ';') for sub in subvalue_strings: values.append(get_vcard_property_subvalues(sub)) return values
def get_vcard_property_subvalues(value_string): """ Get the parts of the value. @param value_string: Single value string @return: List of values (RFC 2426 page 9) """ subvalues = vcard_utils.split_unescaped(value_string, ',') # Validate string for subvalue in subvalues: if not re.match('^[' + VALUE_CHARS + ']*$', subvalue): raise vcard_validators.VCardFormatError( '{0}: {1}'.format(MSG_INVALID_SUBVALUE, subvalue), {}) return subvalues
def get_vcard_property_param_values(values_string): """ Get the parameter values. RFC 2426 page 29. @param values_string: Comma separated values @return: Set of values. Assumes that sequence doesn't matter and that duplicate values can be discarded, even though RFC 2426 doesn't explicitly say this. I.e., assumes that TYPE=WORK,VOICE,WORK === TYPE=VOICE,WORK. """ values = set(vcard_utils.split_unescaped(values_string, ',')) # Validate for value in values: if not re.match(u'^[{0}]+$|^"[{1}]+"$'.format(re.escape(SAFE_CHARS), re.escape(QSAFE_CHARS)), value): raise VCardFormatError( '{0}: {1}'.format(MSG_INVALID_VALUE, value), {}) return values
def get_vcard_property_params(params_string): """ Get the parameters and their values. RFC 2426 page 28. @param params_string: Part of a vCard line between the first semicolon and the first colon. @return: Dictionary of parameters. Assumes that TYPE=WORK;TYPE=WORK,VOICE === TYPE=VOICE,WORK === TYPE=VOICE;TYPE=WORK. """ params = {} if not params_string: return params for param_string in vcard_utils.split_unescaped(params_string, ';'): param = get_vcard_property_param(param_string) param_name = param['name'].upper() # To be able to merge TYPE & type if param_name not in params: params[param_name] = param['values'] else: # Merge params[param_name] = params[param_name].union( param['values']) return params