1.1 --- a/vContent.py Sun Nov 02 00:32:48 2008 +0100
1.2 +++ b/vContent.py Sun Nov 02 04:06:23 2008 +0100
1.3 @@ -46,7 +46,7 @@
1.4
1.5 import re
1.6
1.7 -# Simple reader class.
1.8 +# Reader and parser classes.
1.9
1.10 class Reader:
1.11
1.12 @@ -195,13 +195,19 @@
1.13
1.14 return self.parse_content_line()
1.15
1.16 + def decode_content(self, value):
1.17 +
1.18 + "Decode the given 'value', replacing quoted characters."
1.19 +
1.20 + return value.replace("\r", "").replace("\\N", "\n").replace("\\n", "\n")
1.21 +
1.22 # Internal methods.
1.23
1.24 def parse_content_line(self):
1.25
1.26 """
1.27 - Return the name, parameters and a list containing value information for
1.28 - the current content line in the file being parsed.
1.29 + Return the name, parameters and value information for the current
1.30 + content line in the file being parsed.
1.31 """
1.32
1.33 f = self.f
1.34 @@ -254,21 +260,18 @@
1.35
1.36 # Decode the value.
1.37
1.38 - value = self.decode("".join(value_lines), parameters)
1.39 + value = self.decode(name, parameters, "".join(value_lines))
1.40
1.41 return name, parameters, value
1.42
1.43 - def decode(self, value, parameters):
1.44 + def decode(self, name, parameters, value):
1.45
1.46 - "Decode the 'value' using the given 'parameters'."
1.47 + "Decode using 'name' and 'parameters' the given 'value'."
1.48
1.49 encoding = parameters.get("ENCODING")
1.50 charset = parameters.get("CHARSET")
1.51
1.52 - # NOTE: Introducing newline conversions.
1.53 - # Replace quoted characters (see 4.3.11 in RFC 2445).
1.54 -
1.55 - value = value.replace("\r", "").replace("\\N", "\n").replace("\\n", "\n").replace("\\,", ",").replace("\\;", ";")
1.56 + value = self.decode_content(value)
1.57
1.58 if encoding == "QUOTED-PRINTABLE":
1.59 return unicode(quopri.decodestring(value), charset or "iso-8859-1")
1.60 @@ -379,6 +382,86 @@
1.61 ParserBase.parse(self, f, parser_cls)
1.62 return self.components[0]
1.63
1.64 +# Writer classes.
1.65 +
1.66 +class StreamWriter:
1.67 +
1.68 + "A stream writer for content in vCard/vCalendar/iCalendar-like formats."
1.69 +
1.70 + def __init__(self, f, line_length=76):
1.71 +
1.72 + "Initialise the parser for the given file 'f'."
1.73 +
1.74 + self.f = f
1.75 + self.line_length = line_length
1.76 +
1.77 + def write(self, name, parameters, value):
1.78 +
1.79 + """
1.80 + Write a content line for the given 'name', 'parameters' and 'value'
1.81 + information.
1.82 + """
1.83 +
1.84 + f = self.f
1.85 +
1.86 + f.write(name)
1.87 + self.write_parameters(parameters)
1.88 + f.write(":")
1.89 +
1.90 + for line in self.fold(self.encode(name, parameters, value)):
1.91 + f.write(line)
1.92 + f.write("\r\n")
1.93 +
1.94 + def encode_content(self, value):
1.95 +
1.96 + "Encode the given 'value', quoting characters."
1.97 +
1.98 + return value.replace("\n", "\\n")
1.99 +
1.100 + # Internal methods.
1.101 +
1.102 + def write_parameters(self, parameters):
1.103 +
1.104 + "Write the given 'parameters'."
1.105 +
1.106 + f = self.f
1.107 +
1.108 + for parameter_name, parameter_value in parameters.items():
1.109 + f.write(";")
1.110 + f.write(parameter_name)
1.111 + f.write("=")
1.112 + f.write(parameter_value)
1.113 +
1.114 + def encode(self, name, parameters, value):
1.115 +
1.116 + "Encode using 'name' and 'parameters' the given 'value'."
1.117 +
1.118 + encoding = parameters.get("ENCODING")
1.119 + charset = parameters.get("CHARSET")
1.120 +
1.121 + if encoding == "QUOTED-PRINTABLE":
1.122 + value = quopri.encodestring(value.encode(charset or "iso-8859-1"))
1.123 + elif encoding == "BASE64":
1.124 + value = base64.encodestring(value)
1.125 +
1.126 + return self.encode_content(value)
1.127 +
1.128 + def fold(self, text):
1.129 +
1.130 + "Fold the given 'text'."
1.131 +
1.132 + line_length = self.line_length
1.133 + i = 0
1.134 + lines = []
1.135 +
1.136 + line = text[i:i+line_length]
1.137 + while line:
1.138 + lines.append(line)
1.139 + i += line_length
1.140 + line = text[i:i+line_length]
1.141 +
1.142 + return lines
1.143 +
1.144 # Public functions.
1.145
1.146 def parse(f, non_standard_newline=0, parser_cls=None):