diff options
Diffstat (limited to 'exchange/commands.py')
-rw-r--r-- | exchange/commands.py | 116 |
1 files changed, 54 insertions, 62 deletions
diff --git a/exchange/commands.py b/exchange/commands.py index 8198ca6..a2ab471 100644 --- a/exchange/commands.py +++ b/exchange/commands.py | |||
@@ -11,17 +11,14 @@ development code but it does the trick. Watch out, it doesn't consider | |||
11 | many corner cases. | 11 | many corner cases. |
12 | """ | 12 | """ |
13 | 13 | ||
14 | import xml.etree.cElementTree as etree | 14 | import xml.etree.cElementTree as ElementTree |
15 | import dateutil.parser as date_parser | ||
15 | 16 | ||
16 | from httplib import HTTPSConnection | 17 | from httplib import HTTPSConnection |
17 | from string import Template | 18 | from string import Template |
18 | from datetime import datetime, timedelta | 19 | from datetime import datetime, timedelta |
19 | 20 | from icalendar import Calendar, Event as _Event, Alarm | |
20 | import dateutil.parser | 21 | from exchange import ExchangeException, EST |
21 | from icalendar import Calendar, Event, Alarm | ||
22 | |||
23 | from exchange import ExchangeException | ||
24 | from exchange.timezones import EST | ||
25 | 22 | ||
26 | 23 | ||
27 | class ExchangeCommand(object): | 24 | class ExchangeCommand(object): |
@@ -40,36 +37,32 @@ class ExchangeCommand(object): | |||
40 | "Translate": "f", | 37 | "Translate": "f", |
41 | } | 38 | } |
42 | 39 | ||
43 | def __init__(self, server, authenticator=None): | 40 | def __init__(self, session): |
44 | self.server = server | 41 | self.server = session.server |
45 | self.authenticator = authenticator | 42 | self.session = session |
46 | 43 | ||
47 | def _get_xml(self, **kwargs): | 44 | def _get_xml(self, **kwargs): |
48 | """ | 45 | """ |
49 | Try to get an XML response from the server. | 46 | Try to get an XML response from the server. |
50 | @return: ElementTree response | 47 | @return: ElementTree response |
51 | """ | 48 | """ |
52 | if not self.authenticator.authenticated: | 49 | kwargs["username"] = self.session.username |
53 | raise ExchangeException("Not authenticated. Call authenticate() first.") | ||
54 | |||
55 | # Lets forcibly override the username with the user we're querying as | ||
56 | kwargs["username"] = self.authenticator.username | ||
57 | 50 | ||
58 | xml = self._get_query(**kwargs) | 51 | xml = self._get_query(**kwargs) |
59 | url = self.BASE_URL.substitute({ "username": self.authenticator.username, | 52 | url = self.BASE_URL.substitute({ "username": self.session.username, |
60 | "method": self.exchange_method }) | 53 | "method": self.exchange_method }) |
61 | query = Template(xml).substitute(kwargs) | 54 | query = Template(xml).substitute(kwargs) |
62 | send_headers = self.authenticator.patch_headers(self.BASE_HEADERS) | 55 | send_headers = self.BASE_HEADERS.copy() |
56 | send_headers['Cookie'] = self.session.token | ||
63 | 57 | ||
64 | conn = HTTPSConnection(self.server) | 58 | conn = HTTPSConnection(self.server) |
65 | conn.request(self.dav_method.upper(), url, query, headers=send_headers) | 59 | conn.request(self.dav_method.upper(), url, query, headers=send_headers) |
66 | resp = conn.getresponse() | 60 | resp = conn.getresponse() |
67 | 61 | ||
68 | # TODO: Lets determine authentication errors here and fix them. | ||
69 | if int(resp.status) > 299 or int(resp.status) < 200: | 62 | if int(resp.status) > 299 or int(resp.status) < 200: |
70 | raise ExchangeException("%s %s" % (resp.status, resp.reason)) | 63 | raise ExchangeException("%s %s" % (resp.status, resp.reason)) |
71 | 64 | ||
72 | return etree.fromstring(resp.read()) | 65 | return ElementTree.fromstring(resp.read()) |
73 | 66 | ||
74 | def _get_query(self, **kwargs): | 67 | def _get_query(self, **kwargs): |
75 | """ | 68 | """ |
@@ -77,18 +70,46 @@ class ExchangeCommand(object): | |||
77 | of template substitutions, also does a little bit of elementtree | 70 | of template substitutions, also does a little bit of elementtree |
78 | magic to to build the XML query. | 71 | magic to to build the XML query. |
79 | """ | 72 | """ |
80 | declaration = etree.ProcessingInstruction("xml", 'version="1.0"') | 73 | declaration = ElementTree.ProcessingInstruction("xml", 'version="1.0"') |
81 | 74 | ||
82 | request = etree.Element("g:searchrequest", { "xmlns:g": "DAV:" }) | 75 | request = ElementTree.Element("g:searchrequest", { "xmlns:g": "DAV:" }) |
83 | query = etree.SubElement(request, "g:sql") | 76 | query = ElementTree.SubElement(request, "g:sql") |
84 | query.text = Template(self.sql).substitute(kwargs) | 77 | query.text = Template(self.sql).substitute(kwargs) |
85 | 78 | ||
86 | output = etree.tostring(declaration) | 79 | output = ElementTree.tostring(declaration) |
87 | output += etree.tostring(request) | 80 | output += ElementTree.tostring(request) |
88 | 81 | ||
89 | return output | 82 | return output |
90 | 83 | ||
91 | 84 | ||
85 | class Event(_Event): | ||
86 | |||
87 | def add_alarm(self, alarm_offset): | ||
88 | alarm = Alarm() | ||
89 | alarm.add("action", "DISPLAY") | ||
90 | alarm.add("description", "REMINDER") | ||
91 | alarm.add("trigger", timedelta(minutes=alarm_offset)) | ||
92 | self.add_component(alarm) | ||
93 | |||
94 | def _get_element_text(self, element, key): | ||
95 | value = element.find(key) | ||
96 | |||
97 | if hasattr(value, 'text'): | ||
98 | return value.text | ||
99 | |||
100 | def add_text(self, element, key, add_as=None): | ||
101 | value = self._get_element_text(element, key) | ||
102 | |||
103 | add_as = key if not add_as else add_as | ||
104 | self.add(add_as, value) | ||
105 | |||
106 | def add_date(self, element, key, add_as=None): | ||
107 | value = date_parser.parse(self._get_element_text(element, key)) | ||
108 | |||
109 | add_as = key if not add_as else add_as | ||
110 | self.add(add_as, value) | ||
111 | |||
112 | |||
92 | class FetchCalendar(ExchangeCommand): | 113 | class FetchCalendar(ExchangeCommand): |
93 | exchange_method = "calendar" | 114 | exchange_method = "calendar" |
94 | dav_method = "search" | 115 | dav_method = "search" |
@@ -119,45 +140,16 @@ class FetchCalendar(ExchangeCommand): | |||
119 | 140 | ||
120 | for item in exchange_xml.getchildren(): | 141 | for item in exchange_xml.getchildren(): |
121 | item = item.find("{DAV:}propstat").find("{DAV:}prop") | 142 | item = item.find("{DAV:}propstat").find("{DAV:}prop") |
122 | event = Event() | ||
123 | 143 | ||
124 | # These tests may look funny but the result of item.find | 144 | event = Event() |
125 | # does NOT evaluate to true even though it is not None | 145 | event.add_text(item, 'subject', add_as='summary') |
126 | # so, we have to check the interface of the returned item | 146 | event.add_text(item, 'location') |
127 | # to make sure its usable. | 147 | event.add_text(item, 'description') |
128 | 148 | event.add_date(item, 'start_date', add_as='dtstart') | |
129 | subject = item.find("subject") | 149 | event.add_date(item, 'end_date', add_as='dtend') |
130 | if hasattr(subject, "text"): | 150 | |
131 | event.add("summary", subject.text) | 151 | # TODO: Handle timezone_info |
132 | 152 | # TODO: Handle alarms. Previous implementation didn't work | |
133 | location = item.find("location") | ||
134 | if hasattr(location, "text"): | ||
135 | event.add("location", location.text) | ||
136 | |||
137 | description = item.find("description") | ||
138 | if hasattr(description, "text"): | ||
139 | event.add("description", description.text) | ||
140 | |||
141 | # Dates should always exist | ||
142 | start_date = dateutil.parser.parse(item.find("start_date").text) | ||
143 | event.add("dtstart", start_date) | ||
144 | |||
145 | end_date = dateutil.parser.parse(item.find("end_date").text) | ||
146 | event.add("dtend", end_date) | ||
147 | |||
148 | if item.get("timezone_info"): | ||
149 | """This comes back from Exchange as already formatted | ||
150 | ical data. We probably need to parse and re-construct | ||
151 | it unless the icalendar api lets us just dump it out. | ||
152 | """ | ||
153 | pass | ||
154 | |||
155 | if alarms and start_date > datetime.now(tz=EST()): | ||
156 | alarm = Alarm() | ||
157 | alarm.add("action", "DISPLAY") | ||
158 | alarm.add("description", "REMINDER") | ||
159 | alarm.add("trigger", timedelta(minutes=alarm_offset)) | ||
160 | event.add_component(alarm) | ||
161 | 153 | ||
162 | calendar.add_component(event) | 154 | calendar.add_component(event) |
163 | 155 | ||