Changeset View
Changeset View
Standalone View
Standalone View
wallace/module_invitationpolicy.py
Show First 20 Lines • Show All 221 Lines • ▼ Show 20 Lines | for stage in ['incoming', 'ACCEPT', 'REJECT', 'HOLD', 'DEFER', 'locks']: | ||||
os.makedirs(os.path.join(mybasepath, stage)) | os.makedirs(os.path.join(mybasepath, stage)) | ||||
log.debug(_("Invitation policy called for %r, %r") % (args, kw), level=8) | log.debug(_("Invitation policy called for %r, %r") % (args, kw), level=8) | ||||
auth = Auth() | auth = Auth() | ||||
imap = IMAP() | imap = IMAP() | ||||
# ignore calls on lock files | # ignore calls on lock files | ||||
if '/locks/' in filepath or kw.has_key('stage') and kw['stage'] == 'locks': | if '/locks/' in filepath or 'stage' in kw and kw['stage'] == 'locks': | ||||
return False | return False | ||||
log.debug("Invitation policy executing for %r, %r" % (filepath, '/locks/' in filepath), level=8) | log.debug("Invitation policy executing for %r, %r" % (filepath, '/locks/' in filepath), level=8) | ||||
if kw.has_key('stage'): | if 'stage' in kw: | ||||
log.debug(_("Issuing callback after processing to stage %s") % (kw['stage']), level=8) | log.debug(_("Issuing callback after processing to stage %s") % (kw['stage']), level=8) | ||||
log.debug(_("Testing cb_action_%s()") % (kw['stage']), level=8) | log.debug(_("Testing cb_action_%s()") % (kw['stage']), level=8) | ||||
if hasattr(modules, 'cb_action_%s' % (kw['stage'])): | if hasattr(modules, 'cb_action_%s' % (kw['stage'])): | ||||
log.debug(_("Attempting to execute cb_action_%s()") % (kw['stage']), level=8) | log.debug(_("Attempting to execute cb_action_%s()") % (kw['stage']), level=8) | ||||
exec( | exec( | ||||
'modules.cb_action_%s(%r, %r)' % ( | 'modules.cb_action_%s(%r, %r)' % ( | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | def execute(*args, **kw): | ||||
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 | ||||
log.debug(_("iTip objects attached to this message contain the following information: %r") % (itip_events), level=8) | log.debug(_("iTip objects attached to this message contain the following information: %r") % (itip_events), level=8) | ||||
# See if any iTip actually allocates a user. | # See if any iTip actually allocates a user. | ||||
if any_itips and len([x['uid'] for x in itip_events if x.has_key('attendees') or x.has_key('organizer')]) > 0: | if any_itips and len([x['uid'] for x in itip_events if 'attendees' in x or 'organizer' in x]) > 0: | ||||
auth.connect() | auth.connect() | ||||
# we're looking at the first itip object | # we're looking at the first itip object | ||||
itip_event = itip_events[0] | itip_event = itip_events[0] | ||||
for recipient in recipients: | for recipient in recipients: | ||||
recipient_user_dn = user_dn_from_email_address(recipient) | recipient_user_dn = user_dn_from_email_address(recipient) | ||||
if recipient_user_dn: | if recipient_user_dn: | ||||
Show All 20 Lines | if not any_itips: | ||||
return filepath | return filepath | ||||
elif recipient_email is None: | elif recipient_email is None: | ||||
log.debug(_("iTips, but no users, pass along %r") % (filepath), level=5) | log.debug(_("iTips, but no users, pass along %r") % (filepath), level=5) | ||||
return filepath | return filepath | ||||
# for replies, the organizer is the recipient | # for replies, the organizer is the recipient | ||||
if itip_event['method'] == 'REPLY': | if itip_event['method'] == 'REPLY': | ||||
# Outlook can send iTip replies without an organizer property | # Outlook can send iTip replies without an organizer property | ||||
if itip_event.has_key('organizer'): | if 'organizer' in itip_event: | ||||
organizer_mailto = str(itip_event['organizer']).split(':')[-1] | organizer_mailto = str(itip_event['organizer']).split(':')[-1] | ||||
user_attendees = [organizer_mailto] if organizer_mailto in recipient_emails else [] | user_attendees = [organizer_mailto] if organizer_mailto in recipient_emails else [] | ||||
else: | else: | ||||
user_attendees = [recipient_email] | user_attendees = [recipient_email] | ||||
else: | else: | ||||
# Limit the attendees to the one that is actually invited with the current message. | # Limit the attendees to the one that is actually invited with the current message. | ||||
attendees = [str(a).split(':')[-1] for a in (itip_event['attendees'] if itip_event.has_key('attendees') else [])] | attendees = [str(a).split(':')[-1] for a in (itip_event['attendees'] if 'attendees' in itip_event else [])] | ||||
user_attendees = [a for a in attendees if a in recipient_emails] | user_attendees = [a for a in attendees if a in recipient_emails] | ||||
if itip_event.has_key('organizer'): | if 'organizer' in itip_event: | ||||
sender_email = itip_event['xml'].get_organizer().email() | sender_email = itip_event['xml'].get_organizer().email() | ||||
# abort if no attendee matches the envelope recipient | # abort if no attendee matches the envelope recipient | ||||
if len(user_attendees) == 0: | if len(user_attendees) == 0: | ||||
log.info(_("No user attendee matching envelope recipient %s, skip message") % (recipient_email)) | log.info(_("No user attendee matching envelope recipient %s, skip message") % (recipient_email)) | ||||
return filepath | return filepath | ||||
log.debug(_("Receiving user: %r") % (receiving_user), level=8) | log.debug(_("Receiving user: %r") % (receiving_user), level=8) | ||||
# set recipient_email to the matching attendee mailto: address | # set recipient_email to the matching attendee mailto: address | ||||
recipient_email = user_attendees[0] | recipient_email = user_attendees[0] | ||||
# change gettext language to the preferredlanguage setting of the receiving user | # change gettext language to the preferredlanguage setting of the receiving user | ||||
if receiving_user.has_key('preferredlanguage'): | if 'preferredlanguage' in receiving_user: | ||||
pykolab.translate.setUserLanguage(receiving_user['preferredlanguage']) | pykolab.translate.setUserLanguage(receiving_user['preferredlanguage']) | ||||
# find user's kolabInvitationPolicy settings and the matching policy values | # find user's kolabInvitationPolicy settings and the matching policy values | ||||
type_condition = object_type_conditons.get(itip_event['type'], COND_TYPE_ALL) | type_condition = object_type_conditons.get(itip_event['type'], COND_TYPE_ALL) | ||||
policies = get_matching_invitation_policies(receiving_user, sender_email, type_condition) | policies = get_matching_invitation_policies(receiving_user, sender_email, type_condition) | ||||
# select a processing function according to the iTip request method | # select a processing function according to the iTip request method | ||||
method_processing_map = { | method_processing_map = { | ||||
'REQUEST': process_itip_request, | 'REQUEST': process_itip_request, | ||||
'REPLY': process_itip_reply, | 'REPLY': process_itip_reply, | ||||
'CANCEL': process_itip_cancel | 'CANCEL': process_itip_cancel | ||||
} | } | ||||
done = None | done = None | ||||
if method_processing_map.has_key(itip_event['method']): | if itip_event['method'] in method_processing_map: | ||||
processor_func = method_processing_map[itip_event['method']] | processor_func = method_processing_map[itip_event['method']] | ||||
# connect as cyrus-admin | # connect as cyrus-admin | ||||
imap.connect() | imap.connect() | ||||
for policy in policies: | for policy in policies: | ||||
log.debug(_("Apply invitation policy %r for sender %r") % (policy_value_map[policy], sender_email), level=8) | log.debug(_("Apply invitation policy %r for sender %r") % (policy_value_map[policy], sender_email), level=8) | ||||
done = processor_func(itip_event, policy, recipient_email, sender_email, receiving_user) | done = processor_func(itip_event, policy, recipient_email, sender_email, receiving_user) | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | elif policy & ACT_UPDATE and existing: | ||||
sender = itip_event['xml'].get_organizer() | sender = itip_event['xml'].get_organizer() | ||||
comment = itip_event['xml'].get_comment() | comment = itip_event['xml'].get_comment() | ||||
send_update_notification(itip_event['xml'], receiving_user, existing, False, | send_update_notification(itip_event['xml'], receiving_user, existing, False, | ||||
sender, comment) | sender, comment) | ||||
# if RSVP, send an iTip REPLY | # if RSVP, send an iTip REPLY | ||||
if rsvp or scheduling_required: | if rsvp or scheduling_required: | ||||
# set attendee's CN from LDAP record if yet missing | # set attendee's CN from LDAP record if yet missing | ||||
if not receiving_attendee.get_name() and receiving_user.has_key('cn'): | if not receiving_attendee.get_name() and 'cn' in receiving_user: | ||||
receiving_attendee.set_name(receiving_user['cn']) | receiving_attendee.set_name(receiving_user['cn']) | ||||
# send iTip reply | # send iTip reply | ||||
if respond_with is not None and not respond_with == 'NEEDS-ACTION': | if respond_with is not None and not respond_with == 'NEEDS-ACTION': | ||||
receiving_attendee.set_participant_status(respond_with) | receiving_attendee.set_participant_status(respond_with) | ||||
send_reply(recipient_email, itip_event, invitation_response_text(itip_event['type']), | send_reply(recipient_email, itip_event, invitation_response_text(itip_event['type']), | ||||
subject=_('"%(summary)s" has been %(status)s')) | subject=_('"%(summary)s" has been %(status)s')) | ||||
▲ Show 20 Lines • Show All 190 Lines • ▼ Show 20 Lines | def user_dn_from_email_address(email_address): | ||||
""" | """ | ||||
global auth | global auth | ||||
if not auth: | if not auth: | ||||
auth = Auth() | auth = Auth() | ||||
auth.connect() | auth.connect() | ||||
# return cached value | # return cached value | ||||
if user_dn_from_email_address.cache.has_key(email_address): | if email_address in user_dn_from_email_address.cache: | ||||
return user_dn_from_email_address.cache[email_address] | return user_dn_from_email_address.cache[email_address] | ||||
local_domains = auth.list_domains() | local_domains = auth.list_domains() | ||||
if local_domains is not None: | if local_domains is not None: | ||||
local_domains = list(set(local_domains.keys())) | local_domains = list(set(local_domains.keys())) | ||||
if not email_address.split('@')[1] in local_domains: | if not email_address.split('@')[1] in local_domains: | ||||
Show All 14 Lines | def user_dn_from_email_address(email_address): | ||||
return user_dn | return user_dn | ||||
user_dn_from_email_address.cache = {} | user_dn_from_email_address.cache = {} | ||||
def get_matching_invitation_policies(receiving_user, sender_email, type_condition=COND_TYPE_ALL): | def get_matching_invitation_policies(receiving_user, sender_email, type_condition=COND_TYPE_ALL): | ||||
# get user's kolabInvitationPolicy settings | # get user's kolabInvitationPolicy settings | ||||
policies = receiving_user['kolabinvitationpolicy'] if receiving_user.has_key('kolabinvitationpolicy') else [] | policies = receiving_user['kolabinvitationpolicy'] if 'kolabinvitationpolicy' in receiving_user else [] | ||||
if policies and not isinstance(policies, list): | if policies and not isinstance(policies, list): | ||||
policies = [policies] | policies = [policies] | ||||
if len(policies) == 0: | if len(policies) == 0: | ||||
policies = conf.get_list('wallace', 'kolab_invitation_policy') | policies = conf.get_list('wallace', 'kolab_invitation_policy') | ||||
# match policies agains the given sender_email | # match policies agains the given sender_email | ||||
matches = [] | matches = [] | ||||
for p in policies: | for p in policies: | ||||
if ':' in p: | if ':' in p: | ||||
(value, domain) = p.split(':', 1) | (value, domain) = p.split(':', 1) | ||||
else: | else: | ||||
value = p | value = p | ||||
domain = '' | domain = '' | ||||
if domain == '' or domain == '*' or str(sender_email).endswith(domain): | if domain == '' or domain == '*' or str(sender_email).endswith(domain): | ||||
value = value.upper() | value = value.upper() | ||||
if policy_name_map.has_key(value): | if value in policy_name_map: | ||||
val = policy_name_map[value] | val = policy_name_map[value] | ||||
# append if type condition matches | # append if type condition matches | ||||
if val & type_condition: | if val & type_condition: | ||||
matches.append(val &~ COND_TYPE_ALL) | matches.append(val &~ COND_TYPE_ALL) | ||||
# add manual as default action | # add manual as default action | ||||
if len(matches) == 0: | if len(matches) == 0: | ||||
matches.append(ACT_MANUAL) | matches.append(ACT_MANUAL) | ||||
return matches | return matches | ||||
def imap_proxy_auth(user_rec): | def imap_proxy_auth(user_rec): | ||||
""" | """ | ||||
Perform IMAP login using proxy authentication with admin credentials | Perform IMAP login using proxy authentication with admin credentials | ||||
""" | """ | ||||
global imap | global imap | ||||
mail_attribute = conf.get('cyrus-sasl', 'result_attribute') | mail_attribute = conf.get('cyrus-sasl', 'result_attribute') | ||||
if mail_attribute is None: | if mail_attribute is None: | ||||
mail_attribute = 'mail' | mail_attribute = 'mail' | ||||
mail_attribute = mail_attribute.lower() | mail_attribute = mail_attribute.lower() | ||||
if not user_rec.has_key(mail_attribute): | if mail_attribute not in user_rec: | ||||
log.error(_("User record doesn't have the mailbox attribute %r set" % (mail_attribute))) | log.error(_("User record doesn't have the mailbox attribute %r set" % (mail_attribute))) | ||||
return False | return False | ||||
# do IMAP prox auth with the given user | # do IMAP prox auth with the given user | ||||
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') | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | """ | ||||
For the receiving user, determine if the event in question is in conflict. | For the receiving user, determine if the event in question is in conflict. | ||||
""" | """ | ||||
start = time.time() | start = time.time() | ||||
num_messages = 0 | num_messages = 0 | ||||
conflict = False | conflict = False | ||||
# return previously detected conflict | # return previously detected conflict | ||||
if itip_event.has_key('_conflicts'): | if '_conflicts' in itip_event: | ||||
return not itip_event['_conflicts'] | return not itip_event['_conflicts'] | ||||
for folder in list_user_folders(receiving_user, 'event'): | for folder in list_user_folders(receiving_user, 'event'): | ||||
log.debug(_("Listing events from folder %r") % (folder), level=8) | log.debug(_("Listing events from folder %r") % (folder), level=8) | ||||
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 X-Kolab-Type "application/x-vnd.kolab.event")') | res, data = imap.imap.m.search(None, '(UNDELETED HEADER X-Kolab-Type "application/x-vnd.kolab.event")') | ||||
num_messages += len(data[0].split()) | num_messages += len(data[0].split()) | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | def store_object(object, user_rec, targetfolder=None, master=None): | ||||
""" | """ | ||||
# find calendar folder to save object to if not specified | # find calendar folder to save object to if not specified | ||||
if targetfolder is None: | if targetfolder is None: | ||||
targetfolders = list_user_folders(user_rec, object.type) | targetfolders = list_user_folders(user_rec, object.type) | ||||
oc = object.get_classification() | oc = object.get_classification() | ||||
# use *.confidential/private folder for confidential/private invitations | # use *.confidential/private folder for confidential/private invitations | ||||
if oc == kolabformat.ClassConfidential and user_rec.has_key('_confidential_folder'): | if oc == kolabformat.ClassConfidential and '_confidential_folder' in user_rec: | ||||
targetfolder = user_rec['_confidential_folder'] | targetfolder = user_rec['_confidential_folder'] | ||||
elif oc == kolabformat.ClassPrivate and user_rec.has_key('_private_folder'): | elif oc == kolabformat.ClassPrivate and '_private_folder' in user_rec: | ||||
targetfolder = user_rec['_private_folder'] | targetfolder = user_rec['_private_folder'] | ||||
# use *.default folder if exists | # use *.default folder if exists | ||||
elif user_rec.has_key('_default_folder'): | elif '_default_folder' in user_rec: | ||||
targetfolder = user_rec['_default_folder'] | targetfolder = user_rec['_default_folder'] | ||||
# fallback to any existing folder of specified type | # fallback to any existing folder of specified type | ||||
elif targetfolders is not None and len(targetfolders) > 0: | elif targetfolders is not None and len(targetfolders) > 0: | ||||
targetfolder = targetfolders[0] | targetfolder = targetfolders[0] | ||||
if targetfolder is None: | if targetfolder is None: | ||||
log.error(_("Failed to save %s: no target folder found for user %r") % (object.type, user_rec['mail'])) | log.error(_("Failed to save %s: no target folder found for user %r") % (object.type, user_rec['mail'])) | ||||
return False | return False | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | if reply: | ||||
auto_replies_expected = 0 | auto_replies_expected = 0 | ||||
auto_replies_received = 0 | auto_replies_received = 0 | ||||
is_manual_reply = True | is_manual_reply = True | ||||
partstats = {'ACCEPTED': [], 'TENTATIVE': [], 'DECLINED': [], 'DELEGATED': [], 'IN-PROCESS': [], 'COMPLETED': [], 'PENDING': []} | partstats = {'ACCEPTED': [], 'TENTATIVE': [], 'DECLINED': [], 'DELEGATED': [], 'IN-PROCESS': [], 'COMPLETED': [], 'PENDING': []} | ||||
for attendee in object.get_attendees(): | for attendee in object.get_attendees(): | ||||
parstat = attendee.get_participant_status(True) | parstat = attendee.get_participant_status(True) | ||||
if partstats.has_key(parstat): | if parstat in partstats: | ||||
partstats[parstat].append(attendee.get_displayname()) | partstats[parstat].append(attendee.get_displayname()) | ||||
else: | else: | ||||
partstats['PENDING'].append(attendee.get_displayname()) | partstats['PENDING'].append(attendee.get_displayname()) | ||||
# look-up kolabinvitationpolicy for this attendee | # look-up kolabinvitationpolicy for this attendee | ||||
if attendee.get_cutype() == kolabformat.CutypeResource: | if attendee.get_cutype() == kolabformat.CutypeResource: | ||||
resource_dns = auth.find_resource(attendee.get_email()) | resource_dns = auth.find_resource(attendee.get_email()) | ||||
if isinstance(resource_dns, list): | if isinstance(resource_dns, list): | ||||
▲ Show 20 Lines • Show All 264 Lines • Show Last 20 Lines |