Changeset View
Changeset View
Standalone View
Standalone View
wallace/module_invitationpolicy.py
Show First 20 Lines • Show All 274 Lines • ▼ Show 20 Lines | def execute(*args, **kw): | ||||
recipient_email = None | recipient_email = None | ||||
recipient_emails = [] | recipient_emails = [] | ||||
recipient_user_dn = None | recipient_user_dn = None | ||||
# An iTip message may contain multiple events. Later on, test if the message | # An iTip message may contain multiple events. Later on, test if the message | ||||
# is an iTip message by checking the length of this list. | # is an iTip message by checking the length of this list. | ||||
try: | try: | ||||
itip_events = objects_from_message(message, ['VEVENT','VTODO'], ['REQUEST', 'REPLY', 'CANCEL']) | itip_events = objects_from_message(message, ['VEVENT','VTODO'], ['REQUEST', 'REPLY', 'CANCEL']) | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error(_("Failed to parse iTip objects from message: %r" % (errmsg))) | log.error(_("Failed to parse iTip objects from message: %r" % (errmsg))) | ||||
itip_events = [] | itip_events = [] | ||||
if not len(itip_events) > 0: | if not len(itip_events) > 0: | ||||
log.info(_("Message is not an iTip message or does not contain any (valid) iTip objects.")) | log.info(_("Message is not an iTip message or does not contain any (valid) iTip objects.")) | ||||
else: | else: | ||||
any_itips = True | any_itips = True | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | def process_itip_request(itip_event, policy, recipient_email, sender_email, receiving_user): | ||||
# if invitation policy is set to MANUAL, pass message along | # if invitation policy is set to MANUAL, pass message along | ||||
if policy & ACT_MANUAL: | if policy & ACT_MANUAL: | ||||
log.info(_("Pass invitation for manual processing")) | log.info(_("Pass invitation for manual processing")) | ||||
return MESSAGE_FORWARD | return MESSAGE_FORWARD | ||||
try: | try: | ||||
receiving_attendee = itip_event['xml'].get_attendee_by_email(recipient_email) | receiving_attendee = itip_event['xml'].get_attendee_by_email(recipient_email) | ||||
log.debug(_("Receiving attendee: %r") % (receiving_attendee.to_dict()), level=8) | log.debug(_("Receiving attendee: %r") % (receiving_attendee.to_dict()), level=8) | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error("Could not find envelope attendee: %r" % (errmsg)) | log.error("Could not find envelope attendee: %r" % (errmsg)) | ||||
return MESSAGE_FORWARD | return MESSAGE_FORWARD | ||||
# process request to participating attendees with RSVP=TRUE or PARTSTAT=NEEDS-ACTION | # process request to participating attendees with RSVP=TRUE or PARTSTAT=NEEDS-ACTION | ||||
is_task = itip_event['type'] == 'task' | is_task = itip_event['type'] == 'task' | ||||
nonpart = receiving_attendee.get_role() == kolabformat.NonParticipant | nonpart = receiving_attendee.get_role() == kolabformat.NonParticipant | ||||
partstat = receiving_attendee.get_participant_status() | partstat = receiving_attendee.get_participant_status() | ||||
save_object = not nonpart or not partstat == kolabformat.PartNeedsAction | save_object = not nonpart or not partstat == kolabformat.PartNeedsAction | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | if policy & ACT_MANUAL: | ||||
log.info(_("Pass reply for manual processing")) | log.info(_("Pass reply for manual processing")) | ||||
return MESSAGE_FORWARD | return MESSAGE_FORWARD | ||||
# auto-update is enabled for this user | # auto-update is enabled for this user | ||||
if policy & ACT_UPDATE: | if policy & ACT_UPDATE: | ||||
try: | try: | ||||
sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email) | sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email) | ||||
log.debug(_("Sender Attendee: %r") % (sender_attendee), level=8) | log.debug(_("Sender Attendee: %r") % (sender_attendee), level=8) | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error("Could not find envelope sender attendee: %r" % (errmsg)) | log.error("Could not find envelope sender attendee: %r" % (errmsg)) | ||||
return MESSAGE_FORWARD | return MESSAGE_FORWARD | ||||
# find existing event in user's calendar | # find existing event in user's calendar | ||||
# sets/checks lock to avoid concurrent wallace processes trying to update the same event simultaneously | # sets/checks lock to avoid concurrent wallace processes trying to update the same event simultaneously | ||||
(existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True) | (existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True) | ||||
if existing: | if existing: | ||||
# compare sequence number to avoid outdated replies? | # compare sequence number to avoid outdated replies? | ||||
if not itip_event['sequence'] == existing.get_sequence(): | if not itip_event['sequence'] == existing.get_sequence(): | ||||
log.info(_("The iTip reply sequence (%r) doesn't match the referred object version (%r). Forwarding to Inbox.") % ( | log.info(_("The iTip reply sequence (%r) doesn't match the referred object version (%r). Forwarding to Inbox.") % ( | ||||
itip_event['sequence'], existing.get_sequence() | itip_event['sequence'], existing.get_sequence() | ||||
)) | )) | ||||
remove_write_lock(existing._lock_key) | remove_write_lock(existing._lock_key) | ||||
return MESSAGE_FORWARD | return MESSAGE_FORWARD | ||||
log.debug(_("Auto-updating %s %r on iTip REPLY") % (existing.type, existing.uid), level=8) | log.debug(_("Auto-updating %s %r on iTip REPLY") % (existing.type, existing.uid), level=8) | ||||
updated_attendees = [] | updated_attendees = [] | ||||
try: | try: | ||||
existing.set_attendee_participant_status(sender_email, sender_attendee.get_participant_status(), rsvp=False) | existing.set_attendee_participant_status(sender_email, sender_attendee.get_participant_status(), rsvp=False) | ||||
existing_attendee = existing.get_attendee(sender_email) | existing_attendee = existing.get_attendee(sender_email) | ||||
updated_attendees.append(existing_attendee) | updated_attendees.append(existing_attendee) | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error("Could not find corresponding attende in organizer's copy: %r" % (errmsg)) | log.error("Could not find corresponding attende in organizer's copy: %r" % (errmsg)) | ||||
# append delegated-from attendee ? | # append delegated-from attendee ? | ||||
if len(sender_attendee.get_delegated_from()) > 0: | if len(sender_attendee.get_delegated_from()) > 0: | ||||
existing.add_attendee(sender_attendee) | existing.add_attendee(sender_attendee) | ||||
updated_attendees.append(sender_attendee) | updated_attendees.append(sender_attendee) | ||||
else: | else: | ||||
# TODO: accept new participant if ACT_ACCEPT ? | # TODO: accept new participant if ACT_ACCEPT ? | ||||
Show All 16 Lines | if policy & ACT_UPDATE: | ||||
updated_attendees.append(sender_delegatee) | updated_attendees.append(sender_delegatee) | ||||
# copy all parameters from replying attendee (e.g. delegated-to, role, etc.) | # copy all parameters from replying attendee (e.g. delegated-to, role, etc.) | ||||
existing_attendee.copy_from(sender_attendee) | existing_attendee.copy_from(sender_attendee) | ||||
existing.update_attendees([existing_attendee]) | existing.update_attendees([existing_attendee]) | ||||
log.debug(_("Update delegator: %r") % (existing_attendee.to_dict()), level=8) | log.debug(_("Update delegator: %r") % (existing_attendee.to_dict()), level=8) | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error("Could not find delegated-to attendee: %r" % (errmsg)) | log.error("Could not find delegated-to attendee: %r" % (errmsg)) | ||||
# update the organizer's copy of the object | # update the organizer's copy of the object | ||||
if update_object(existing, receiving_user, master): | if update_object(existing, receiving_user, master): | ||||
if policy & COND_NOTIFY: | if policy & COND_NOTIFY: | ||||
send_update_notification(existing, receiving_user, existing, True, | send_update_notification(existing, receiving_user, existing, True, | ||||
sender_attendee, itip_event['xml'].get_comment()) | sender_attendee, itip_event['xml'].get_comment()) | ||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | def imap_proxy_auth(user_rec): | ||||
backend = conf.get('kolab', 'imap_backend') | backend = conf.get('kolab', 'imap_backend') | ||||
admin_login = conf.get(backend, 'admin_login') | admin_login = conf.get(backend, 'admin_login') | ||||
admin_password = conf.get(backend, 'admin_password') | admin_password = conf.get(backend, 'admin_password') | ||||
try: | try: | ||||
imap.disconnect() | imap.disconnect() | ||||
imap.connect(login=False) | imap.connect(login=False) | ||||
imap.login_plain(admin_login, admin_password, user_rec[mail_attribute]) | imap.login_plain(admin_login, admin_password, user_rec[mail_attribute]) | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error(_("IMAP proxy authentication failed: %r") % (errmsg)) | log.error(_("IMAP proxy authentication failed: %r") % (errmsg)) | ||||
return False | return False | ||||
return True | return True | ||||
def list_user_folders(user_rec, _type): | def list_user_folders(user_rec, _type): | ||||
""" | """ | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | for folder in list_user_folders(user_rec, type): | ||||
imap.imap.m.select(imap.folder_utf7(folder)) | imap.imap.m.select(imap.folder_utf7(folder)) | ||||
res, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid)) | res, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid)) | ||||
for num in reversed(data[0].split()): | for num in reversed(data[0].split()): | ||||
res, data = imap.imap.m.fetch(num, '(UID RFC822)') | res, data = imap.imap.m.fetch(num, '(UID RFC822)') | ||||
try: | try: | ||||
msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1) | msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1) | ||||
except Exception, errmsg: | except Exception: | ||||
log.error(_("No UID found in IMAP response: %r") % (data[0][0])) | log.error(_("No UID found in IMAP response: %r") % (data[0][0])) | ||||
continue | continue | ||||
try: | try: | ||||
if type == 'task': | if type == 'task': | ||||
event = todo_from_message(message_from_string(data[0][1])) | event = todo_from_message(message_from_string(data[0][1])) | ||||
else: | else: | ||||
event = event_from_message(message_from_string(data[0][1])) | event = event_from_message(message_from_string(data[0][1])) | ||||
Show All 9 Lines | for folder in list_user_folders(user_rec, type): | ||||
if not event and master.uid == uid: | if not event and master.uid == uid: | ||||
return (event, master) | return (event, master) | ||||
if event is not None: | if event is not None: | ||||
setattr(event, '_imap_folder', folder) | setattr(event, '_imap_folder', folder) | ||||
setattr(event, '_lock_key', lock_key) | setattr(event, '_lock_key', lock_key) | ||||
setattr(event, '_msguid', msguid) | setattr(event, '_msguid', msguid) | ||||
except Exception, errmsg: | except Exception: | ||||
log.error(_("Failed to parse %s from message %s/%s: %s") % (type, folder, num, traceback.format_exc())) | log.error(_("Failed to parse %s from message %s/%s: %s") % (type, folder, num, traceback.format_exc())) | ||||
event = None | event = None | ||||
master = None | master = None | ||||
continue | continue | ||||
if event and event.uid == uid: | if event and event.uid == uid: | ||||
return (event, master) | return (event, master) | ||||
Show All 24 Lines | for folder in list_user_folders(receiving_user, 'event'): | ||||
num_messages += len(data[0].split()) | num_messages += len(data[0].split()) | ||||
for num in reversed(data[0].split()): | for num in reversed(data[0].split()): | ||||
event = None | event = None | ||||
res, data = imap.imap.m.fetch(num, '(RFC822)') | res, data = imap.imap.m.fetch(num, '(RFC822)') | ||||
try: | try: | ||||
event = event_from_message(message_from_string(data[0][1])) | event = event_from_message(message_from_string(data[0][1])) | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error(_("Failed to parse event from message %s/%s: %r") % (folder, num, errmsg)) | log.error(_("Failed to parse event from message %s/%s: %r") % (folder, num, errmsg)) | ||||
continue | continue | ||||
if event and event.uid: | if event and event.uid: | ||||
conflict = check_event_conflict(event, itip_event) | conflict = check_event_conflict(event, itip_event) | ||||
if conflict: | if conflict: | ||||
log.info(_("Existing event %r conflicts with invitation %r") % (event.uid, itip_event['uid'])) | log.info(_("Existing event %r conflicts with invitation %r") % (event.uid, itip_event['uid'])) | ||||
break | break | ||||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | try: | ||||
result = imap.imap.m.append( | result = imap.imap.m.append( | ||||
imap.folder_utf7(targetfolder), | imap.folder_utf7(targetfolder), | ||||
None, | None, | ||||
None, | None, | ||||
saveobj.to_message(creator="Kolab Server <wallace@localhost>").as_string() | saveobj.to_message(creator="Kolab Server <wallace@localhost>").as_string() | ||||
) | ) | ||||
return result | return result | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error(_("Failed to save %s to user folder at %r: %r") % ( | log.error(_("Failed to save %s to user folder at %r: %r") % ( | ||||
saveobj.type, targetfolder, errmsg | saveobj.type, targetfolder, errmsg | ||||
)) | )) | ||||
return False | return False | ||||
def delete_object(existing): | def delete_object(existing): | ||||
Show All 21 Lines | try: | ||||
), level=8) | ), level=8) | ||||
for num in data[0].split(): | for num in data[0].split(): | ||||
imap.imap.m.store(num, '+FLAGS', '(\\Deleted)') | imap.imap.m.store(num, '+FLAGS', '(\\Deleted)') | ||||
imap.imap.m.expunge() | imap.imap.m.expunge() | ||||
return True | return True | ||||
except Exception, errmsg: | except Exception as errmsg: | ||||
log.error(_("Failed to delete %s from folder %r: %r") % ( | log.error(_("Failed to delete %s from folder %r: %r") % ( | ||||
existing.type, targetfolder, errmsg | existing.type, targetfolder, errmsg | ||||
)) | )) | ||||
return False | return False | ||||
def send_update_notification(object, receiving_user, old=None, reply=True, sender=None, comment=None): | def send_update_notification(object, receiving_user, old=None, reply=True, sender=None, comment=None): | ||||
▲ Show 20 Lines • Show All 309 Lines • Show Last 20 Lines |