summaryrefslogtreecommitdiffstats
path: root/wallace
diff options
context:
space:
mode:
authorJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2012-05-22 03:46:42 +0100
committerJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2012-05-22 03:46:42 +0100
commit3e893314d7c0a6caf007c98b9089d596669416e0 (patch)
tree30d7c1a20dfb3394def07ccff0beccc28c667d8e /wallace
parent7184d5f07958887e500907d28a38659595ea62c6 (diff)
downloadpykolab-3e893314d7c0a6caf007c98b9089d596669416e0.tar.gz
Update Wallace resource module to send iTip responses to the organizer
Create directory tree needed
Diffstat (limited to 'wallace')
-rw-r--r--wallace/module_resources.py200
1 files changed, 175 insertions, 25 deletions
diff --git a/wallace/module_resources.py b/wallace/module_resources.py
index 54cf85f..c9be1f1 100644
--- a/wallace/module_resources.py
+++ b/wallace/module_resources.py
@@ -17,6 +17,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
+import datetime
import icalendar
import json
import os
@@ -50,15 +51,27 @@ auth = None
imap = None
def __init__():
- if not os.path.isdir(mybasepath):
- os.makedirs(mybasepath)
-
modules.register('resources', execute, description=description())
+def accept(filepath):
+ new_filepath = os.path.join(mybasepath, 'ACCEPT', os.path.basename(filepath))
+ os.rename(filepath, new_filepath)
+ filepath = new_filepath
+ exec('modules.cb_action_ACCEPT(%r, %r)' % ('resources',filepath))
+
def description():
return """Resource management module."""
def execute(*args, **kw):
+ if not os.path.isdir(mybasepath):
+ os.makedirs(mybasepath)
+
+ for stage in ['incoming', 'ACCEPT', 'REJECT', 'HOLD', 'DEFER' ]:
+ if not os.path.isdir(os.path.join(mybasepath, stage)):
+ os.makedirs(os.path.join(mybasepath, stage))
+
+ log.debug(_("Resource Management called for %r, %r") % (args, kw), level=9)
+
auth = Auth()
auth.connect()
@@ -92,8 +105,11 @@ def execute(*args, **kw):
)
return
-
- log.debug(_("Resource Management called for %r, %r") % (args, kw), level=8)
+ else:
+ # Move to incoming
+ new_filepath = os.path.join(mybasepath, 'incoming', os.path.basename(filepath))
+ os.rename(filepath, new_filepath)
+ filepath = new_filepath
message = message_from_file(open(filepath, 'r'))
@@ -107,7 +123,7 @@ def execute(*args, **kw):
"iTip.")
)
- exec('modules.cb_action_ACCEPT(%r, %r)' % ('resources',filepath))
+ accept(filepath)
return
else:
@@ -120,12 +136,14 @@ def execute(*args, **kw):
# See if a resource is actually being allocated
if len([x['resources'] for x in itip_events if x.has_key('resources')]) == 0:
if len([x['attendees'] for x in itip_events if x.has_key('attendees')]) == 0:
- exec('modules.cb_action_ACCEPT(%r, %r)' % ('resources',filepath))
+ accept(filepath)
return
- # A simple list of merely resource entry IDs
+ # A simple list of merely resource entry IDs that hold any relevance to the
+ # iTip events
resource_records = resource_records_from_itip_events(itip_events)
+ # Get the resource details, which includes details on the IMAP folder
resources = {}
for resource_record in list(set(resource_records)):
# Get the attributes for the record
@@ -135,13 +153,21 @@ def execute(*args, **kw):
resource_attrs = auth.get_entry_attributes(None, resource_record, ['*'])
if not 'kolabsharedfolder' in [x.lower() for x in resource_attrs['objectclass']]:
if resource_attrs.has_key('uniquemember'):
+ resources[resource_record] = resource_attrs
for uniquemember in resource_attrs['uniquemember']:
resource_attrs = auth.get_entry_attributes(None, uniquemember, ['*'])
if 'kolabsharedfolder' in [x.lower() for x in resource_attrs['objectclass']]:
- resources[resource_record] = resource_attrs
+ resources[uniquemember] = resource_attrs
+ resources[uniquemember]['memberof'] = resource_record
else:
resources[resource_record] = resource_attrs
+ log.debug(_("Resources: %r") % (resources), level=8)
+
+ # For each resource, determine if any of the events in question is in
+ # conflict.
+ #
+ # Store the (first) conflicting event(s) alongside the resource information.
for resource in resources.keys():
if not resources[resource].has_key('kolabtargetfolder'):
continue
@@ -162,7 +188,7 @@ def execute(*args, **kw):
typ, data = imap.imap.m.search(None, 'ALL')
for num in data[0].split():
- # For efficiency, non-deterministic
+ # For efficiency, makes the routine non-deterministic
if resources[resource]['conflict']:
continue
@@ -182,9 +208,35 @@ def execute(*args, **kw):
log.debug(_(" event %r start: %r") % (event.get_uid(),event.get_start()), level=9)
log.debug(_(" event %r end: %r") % (event.get_uid(),event.get_end()), level=9)
- if event.get_start() < itip['start']:
- if event.get_start() <= itip['end']:
- if event.get_end() <= itip['start']:
+ _es = event.get_start()
+ _is = itip['start']
+
+ if type(_es) == 'datetime.date':
+ log.debug(_("_es is datetime.date"))
+ if type(_is) == 'datetime.datetime':
+ _is = datetime.date(_is.year, _is.month, _is.day)
+ else:
+ pass
+ else:
+ log.debug(_("_es is datetime.datetime"))
+ if type(_is) == 'datetime.date':
+ log.debug(_("_is is datetime.date"))
+ _es = datetime.date(_es.year, _es.month, _es.day)
+
+ _ee = event.get_end()
+ _ie = itip['end']
+ if type(_ee) == 'datetime.date':
+ if type(_ie) == 'datetime.datetime':
+ _ie = datetime.date(_ie.year, _ie.month, _ie.day)
+ else:
+ pass
+ else:
+ if type(_ie) == 'datetime.date':
+ _ee = datetime.date(_ee.year, _ee.month, _ee.day)
+
+ if _es < _is:
+ if _es <= _ie:
+ if _ie <= _is:
conflict = False
else:
log.debug(_("Event %r ends later than invitation") % (event.get_uid()), level=9)
@@ -192,11 +244,11 @@ def execute(*args, **kw):
else:
log.debug(_("Event %r starts before invitation ends") % (event.get_uid()), level=9)
conflict = True
- elif event.get_start() == itip['start']:
+ elif _es == _is:
log.debug(_("Event %r and invitation start at the same time") % (event.get_uid()), level=9)
conflict = True
- else: # event.get_start() > itip['start']
- if event.get_start() <= itip['end']:
+ else: # _es > _is
+ if _es <= _ie:
log.debug(_("Event %r starts before invitation ends") % (event.get_uid()), level=9)
conflict = True
else:
@@ -209,6 +261,91 @@ def execute(*args, **kw):
log.debug(_("Resource status information: %r") % (resources[resource]), level=8)
+
+ for resource in resources.keys():
+ log.debug(_("Polling for resource %r") % (resource), level=9)
+
+ if not resources.has_key(resource):
+ log.debug(_("Resource %r has been popped from the list") % (resource), level=9)
+ continue
+
+ if not resources[resource].has_key('conflicting_events'):
+ log.debug(_("Resource is a collection"), level=9)
+ continue
+
+ if len(resources[resource]['conflicting_events']) > 0:
+ # This is the event being conflicted with!
+ for itip_event in itip_events:
+ # Now we have the event that was conflicting
+ if resources[resource]['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]:
+ itip_event['xml'].set_attendee_participant_status([a for a in itip_event['xml'].get_attendees() if a.get_email() == resources[resource]['mail']][0], "DECLINED")
+
+ send_response(resources[resource]['mail'], itip_events)
+ else:
+ # This must have been a resource collection originally.
+ # We have inserted the reference to the original resource
+ # record in 'memberof'.
+ if resources[resource].has_key('memberof'):
+ original_resource = resources[resources[resource]['memberof']]
+
+ _target_resource = resources[original_resource['uniquemember'][random.randint(0,(len(original_resource['uniquemember'])-1))]]
+
+ # unset all
+ for _r in original_resource['uniquemember']:
+ del resources[_r]
+
+ if original_resource['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]:
+ itip_event['xml'].set_attendee_participant_status(
+ [a for a in itip_event['xml'].get_attendees() if a.get_email() == original_resource['mail']][0],
+ "DECLINED"
+ )
+
+ send_response(original_resource['mail'], itip_events)
+
+ else:
+ # No conflicts, go accept
+ for itip_event in itip_events:
+ if resources[resource]['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]:
+ itip_event['xml'].set_attendee_participant_status(
+ [a for a in itip_event['xml'].get_attendees() if a.get_email() == resources[resource]['mail']][0],
+ "ACCEPTED"
+ )
+
+ log.debug(_("Adding event to %r") % (resources[resource]['kolabtargetfolder']), level=9)
+ imap.imap.m.append(resources[resource]['kolabtargetfolder'], None, None, itip_event['xml'].to_message().as_string())
+
+ send_response(resources[resource]['mail'], itip_events)
+
+ else:
+ # This must have been a resource collection originally.
+ # We have inserted the reference to the original resource
+ # record in 'memberof'.
+ if resources[resource].has_key('memberof'):
+ original_resource = resources[resources[resource]['memberof']]
+
+ _target_resource = resources[original_resource['uniquemember'][random.randint(0,(len(original_resource['uniquemember'])-1))]]
+
+ # unset all
+ for _r in original_resource['uniquemember']:
+ del resources[_r]
+
+ if original_resource['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]:
+ itip_event['xml'].set_attendee_participant_status(
+ [a for a in itip_event['xml'].get_attendees() if a.get_email() == original_resource['mail']][0],
+ "ACCEPTED"
+ )
+
+ log.debug(_("Adding event to %r") % (_target_resource['kolabtargetfolder']), level=9)
+ imap.imap.m.append(_target_resource['kolabtargetfolder'], None, None, itip_event['xml'].to_message().as_string())
+
+ send_response(original_resource['mail'], itip_events)
+
+ # Disconnect IMAP or we lock the mailbox almost constantly
+ imap.disconnect()
+ del imap
+
+ os.unlink(filepath)
+
def itip_events_from_message(message):
"""
Obtain the iTip payload from email.message <message>
@@ -229,19 +366,14 @@ def itip_events_from_message(message):
if part.get_param('method') == "REQUEST":
# Python iCalendar prior to 3.0 uses "from_string".
- itip_payload = part.get_payload()
+ itip_payload = part.get_payload(decode=True)
if hasattr(icalendar.Calendar, 'from_ical'):
cal = icalendar.Calendar.from_ical(itip_payload)
elif hasattr(icalendar.Calendar, 'from_string'):
cal = icalendar.Calendar.from_string(itip_payload)
else:
log.error(_("Could not read iTip from message."))
- exec(
- 'modules.cb_action_ACCEPT(%r, %r)' % (
- 'resources',
- filepath
- )
- )
+ accept(filepath)
return
@@ -294,10 +426,14 @@ def resource_records_from_itip_events(itip_events):
resource_records = []
+ log.debug(_("Raw itip_events: %r") % (itip_events), level=9)
attendees_raw = []
- for list_attendees_raw in [x for x in [y['attendees'] for y in itip_events if y.has_key('attendees')]]:
+ for list_attendees_raw in [x for x in [y['attendees'] for y in itip_events if y.has_key('attendees') and isinstance(y['attendees'], list)]]:
attendees_raw.extend(list_attendees_raw)
+ for list_attendees_raw in [y['attendees'] for y in itip_events if y.has_key('attendees') and isinstance(y['attendees'], basestring)]:
+ attendees_raw.append(list_attendees_raw)
+
log.debug(_("Raw set of attendees: %r") % (attendees_raw), level=9)
# TODO: Resources are actually not implemented in the format. We reset this
@@ -364,4 +500,18 @@ def resource_records_from_itip_events(itip_events):
log.debug(_("The following resources are being referred to in the iTip: %r") % (resource_records), level=8)
- return resource_records \ No newline at end of file
+ return resource_records
+
+def send_response(from_address, itip_events):
+ import smtplib
+ smtp = smtplib.SMTP("localhost", 10027)
+
+ if conf.debuglevel > 8:
+ smtp.set_debuglevel(True)
+
+ for itip_event in itip_events:
+ attendee = [a for a in itip_event['xml'].get_attendees() if a.get_email() == from_address][0]
+ participant_status = itip_event['xml'].get_attendee_participant_status(attendee)
+ message = itip_event['xml'].to_message_itip(from_address, method="REPLY", participant_status=participant_status)
+ smtp.sendmail(message['From'], message['To'], message.as_string())
+ smtp.quit()