1.1 --- a/vCalendar.py Mon Oct 30 23:14:45 2017 +0100
1.2 +++ b/vCalendar.py Fri Nov 24 16:59:33 2017 +0100
1.3 @@ -4,7 +4,7 @@
1.4 Parsing of vCalendar and iCalendar files.
1.5
1.6 Copyright (C) 2008, 2009, 2011, 2013, 2014, 2015,
1.7 - 2016 Paul Boddie <paul@boddie.org.uk>
1.8 + 2016, 2017 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -54,9 +54,13 @@
1.13 MULTIVALUED_PARAMETERS = set([
1.14 "DELEGATED-FROM", "DELEGATED-TO", "MEMBER"
1.15 ])
1.16 +NON_MULTIVALUED_PROPERTIES = set([
1.17 + "RRULE"
1.18 + ])
1.19 QUOTED_TYPES = set(["URI"])
1.20
1.21 unquoted_separator_regexp = re.compile(r"(?<!\\)([,;])")
1.22 +unquoted_semicolon_regexp = re.compile(r"(?<!\\)([;])")
1.23
1.24 # Parser classes.
1.25
1.26 @@ -74,22 +78,27 @@
1.27 name, parameters, value = vContent.StreamParser.next(self)
1.28 return name, self.decode_parameters(parameters), value
1.29
1.30 - def decode_content(self, value):
1.31 + def decode_content(self, name, value):
1.32
1.33 """
1.34 - Decode the given 'value' (which may represent a collection of distinct
1.35 - values), replacing quoted separator characters.
1.36 + Decode for property 'name' the given 'value' (which may represent a
1.37 + collection of distinct values), replacing quoted separator characters.
1.38 """
1.39
1.40 sep = None
1.41 values = []
1.42
1.43 - for i, s in enumerate(unquoted_separator_regexp.split(value)):
1.44 + if name in NON_MULTIVALUED_PROPERTIES:
1.45 + split = unquoted_semicolon_regexp.split
1.46 + else:
1.47 + split = unquoted_separator_regexp.split
1.48 +
1.49 + for i, s in enumerate(split(value)):
1.50 if i % 2 != 0:
1.51 if not sep:
1.52 sep = s
1.53 continue
1.54 - values.append(self.decode_content_value(s))
1.55 + values.append(self.decode_content_value(name, s))
1.56
1.57 if sep == ",":
1.58 return values
1.59 @@ -98,13 +107,16 @@
1.60 else:
1.61 return values[0]
1.62
1.63 - def decode_content_value(self, value):
1.64 + def decode_content_value(self, name, value):
1.65
1.66 - "Decode the given 'value', replacing quoted separator characters."
1.67 + """
1.68 + Decode for property 'name' the given 'value', replacing quoted separator
1.69 + characters.
1.70 + """
1.71
1.72 # Replace quoted characters (see 4.3.11 in RFC 2445).
1.73
1.74 - value = vContent.StreamParser.decode_content(self, value)
1.75 + value = vContent.StreamParser.decode_content(self, name, value)
1.76 return value.replace(r"\,", ",").replace(r"\;", ";")
1.77
1.78 # Internal methods.
1.79 @@ -201,11 +213,12 @@
1.80
1.81 return encoded_parameters
1.82
1.83 - def encode_content(self, value):
1.84 + def encode_content(self, name, value):
1.85
1.86 """
1.87 - Encode the given 'value' (which may be a list or tuple of separate
1.88 - values), quoting characters and separating collections of values.
1.89 + Encode for property 'name' the given 'value' (which may be a list or
1.90 + tuple of separate values), quoting characters and separating collections
1.91 + of values.
1.92 """
1.93
1.94 if isinstance(value, list):
1.95 @@ -216,17 +229,32 @@
1.96 value = [value]
1.97 sep = ""
1.98
1.99 - return sep.join([self.encode_content_value(v) for v in value])
1.100 + l = []
1.101 + for v in value:
1.102 + l.append(self.encode_content_value(name, v))
1.103 + return sep.join(l)
1.104
1.105 - def encode_content_value(self, value):
1.106 + def encode_content_value(self, name, value):
1.107
1.108 - "Encode the given 'value', quoting characters."
1.109 + "Encode for property 'name' the given 'value', quoting characters."
1.110
1.111 # Replace quoted characters (see 4.3.11 in RFC 2445).
1.112
1.113 value = vContent.StreamWriter.encode_content(self, value)
1.114 +
1.115 + if name in NON_MULTIVALUED_PROPERTIES:
1.116 + quote = self.quote_semicolons
1.117 + else:
1.118 + quote = self.quote_separators
1.119 +
1.120 + return quote(value)
1.121 +
1.122 + def quote_separators(self, value):
1.123 return value.replace(";", r"\;").replace(",", r"\,")
1.124
1.125 + def quote_semicolons(self, value):
1.126 + return value.replace(";", r"\;")
1.127 +
1.128 # Public functions.
1.129
1.130 def parse(stream_or_string, encoding=None, non_standard_newline=0):
2.1 --- a/vContent.py Mon Oct 30 23:14:45 2017 +0100
2.2 +++ b/vContent.py Fri Nov 24 16:59:33 2017 +0100
2.3 @@ -4,7 +4,7 @@
2.4 Parsing of vCard, vCalendar and iCalendar files.
2.5
2.6 Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011, 2013,
2.7 - 2014, 2015 Paul Boddie <paul@boddie.org.uk>
2.8 + 2014, 2015, 2017 Paul Boddie <paul@boddie.org.uk>
2.9
2.10 This program is free software; you can redistribute it and/or modify it under
2.11 the terms of the GNU General Public License as published by the Free Software
2.12 @@ -280,9 +280,12 @@
2.13
2.14 return self.parse_content_line()
2.15
2.16 - def decode_content(self, value):
2.17 + def decode_content(self, name, value):
2.18
2.19 - "Decode the given 'value', replacing quoted characters."
2.20 + """
2.21 + Decode for property 'name' the given 'value', replacing quoted
2.22 + characters.
2.23 + """
2.24
2.25 return value.replace("\r", "").replace("\\N", "\n").replace("\\n", "\n")
2.26
2.27 @@ -346,7 +349,7 @@
2.28 encoding = parameters.get("ENCODING")
2.29 charset = parameters.get("CHARSET")
2.30
2.31 - value = self.decode_content(value)
2.32 + value = self.decode_content(name, value)
2.33
2.34 if encoding == "QUOTED-PRINTABLE":
2.35 return unicode(quopri.decodestring(value), charset or "iso-8859-1")
2.36 @@ -582,7 +585,7 @@
2.37 elif encoding == "BASE64":
2.38 value = base64.encodestring(value)
2.39
2.40 - return self.encode_content(value)
2.41 + return self.encode_content(name, value)
2.42 except TypeError:
2.43 raise WriteError, "Property %r value with parameters %r cannot be encoded: %r" % (name, parameters, value)
2.44
2.45 @@ -609,9 +612,9 @@
2.46
2.47 return encoded_parameters
2.48
2.49 - def encode_content(self, value):
2.50 + def encode_content(self, name, value):
2.51
2.52 - "Encode the given 'value', quoting characters."
2.53 + "Encode for property 'name' the given 'value', quoting characters."
2.54
2.55 return (value or "").replace("\n", "\\n")
2.56