Changeset View
Changeset View
Standalone View
Standalone View
wallace/module_invitationpolicy.py
Show First 20 Lines • Show All 178 Lines • ▼ Show 20 Line(s) | |||||
exec('modules.cb_action_REJECT(%r, %r)' % ('invitationpolicy',filepath)) | exec('modules.cb_action_REJECT(%r, %r)' % ('invitationpolicy',filepath)) | ||||
def description(): | def description(): | ||||
return """Invitation policy execution module.""" | return """Invitation policy execution module.""" | ||||
def cleanup(): | def cleanup(): | ||||
global auth, imap, write_locks | global auth, imap, write_locks | ||||
log.debug("cleanup(): %r, %r" % (auth, imap), level=9) | log.debug("cleanup(): %r, %r" % (auth, imap), level=8) | ||||
auth.disconnect() | auth.disconnect() | ||||
del auth | del auth | ||||
# Disconnect IMAP or we lock the mailbox almost constantly | # Disconnect IMAP or we lock the mailbox almost constantly | ||||
imap.disconnect() | imap.disconnect() | ||||
del imap | del imap | ||||
Show All 9 Lines | |||||
if not os.path.isdir(mybasepath): | if not os.path.isdir(mybasepath): | ||||
os.makedirs(mybasepath) | os.makedirs(mybasepath) | ||||
for stage in ['incoming', 'ACCEPT', 'REJECT', 'HOLD', 'DEFER', 'locks']: | for stage in ['incoming', 'ACCEPT', 'REJECT', 'HOLD', 'DEFER', 'locks']: | ||||
if not os.path.isdir(os.path.join(mybasepath, stage)): | if not os.path.isdir(os.path.join(mybasepath, stage)): | ||||
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=9) | log.debug(_("Invitation policy called for %r, %r") % (args, kw), level=8) | ||||
auth = Auth() | auth = Auth() | ||||
imap = IMAP() | imap = IMAP() | ||||
filepath = args[0] | filepath = args[0] | ||||
# 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 kw.has_key('stage') and kw['stage'] == 'locks': | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Line(s) | |||||
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 | ||||
log.debug(_("iTip objects attached to this message contain the following information: %r") % (itip_events), level=9) | 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 x.has_key('attendees') or x.has_key('organizer')]) > 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] | ||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Line(s) | |||||
# 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), level=9) | log.debug(_("Receiving Attendee: %r") % (receiving_attendee), level=8) | ||||
except Exception, errmsg: | except Exception, 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() | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Line(s) | |||||
if policy & ACT_MANUAL: | 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=9) | log.debug(_("Sender Attendee: %r") % (sender_attendee), level=8) | ||||
except Exception, errmsg: | except Exception, 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) | ||||
Show All 28 Lines | |||||
if len(sender_attendee.get_delegated_to()) > 0: | if len(sender_attendee.get_delegated_to()) > 0: | ||||
try: | try: | ||||
delegatee_email = sender_attendee.get_delegated_to(True)[0] | delegatee_email = sender_attendee.get_delegated_to(True)[0] | ||||
sender_delegatee = itip_event['xml'].get_attendee_by_email(delegatee_email) | sender_delegatee = itip_event['xml'].get_attendee_by_email(delegatee_email) | ||||
existing_delegatee = existing.find_attendee(delegatee_email) | existing_delegatee = existing.find_attendee(delegatee_email) | ||||
if not existing_delegatee: | if not existing_delegatee: | ||||
existing.add_attendee(sender_delegatee) | existing.add_attendee(sender_delegatee) | ||||
log.debug(_("Add delegatee: %r") % (sender_delegatee.to_dict()), level=9) | log.debug(_("Add delegatee: %r") % (sender_delegatee.to_dict()), level=8) | ||||
else: | else: | ||||
existing_delegatee.copy_from(sender_delegatee) | existing_delegatee.copy_from(sender_delegatee) | ||||
log.debug(_("Update existing delegatee: %r") % (existing_delegatee.to_dict()), level=9) | log.debug(_("Update existing delegatee: %r") % (existing_delegatee.to_dict()), level=8) | ||||
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=9) | log.debug(_("Update delegator: %r") % (existing_attendee.to_dict()), level=8) | ||||
except Exception, errmsg: | except Exception, 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, | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Line(s) | |||||
log.debug(_("Checking if email address %r belongs to a local user") % (email_address), level=8) | log.debug(_("Checking if email address %r belongs to a local user") % (email_address), level=8) | ||||
user_dn = auth.find_user_dn(email_address, True) | user_dn = auth.find_user_dn(email_address, True) | ||||
if isinstance(user_dn, basestring): | if isinstance(user_dn, basestring): | ||||
log.debug(_("User DN: %r") % (user_dn), level=8) | log.debug(_("User DN: %r") % (user_dn), level=8) | ||||
else: | else: | ||||
log.debug(_("No user record(s) found for %r") % (email_address), level=9) | log.debug(_("No user record(s) found for %r") % (email_address), level=8) | ||||
# remember this lookup | # remember this lookup | ||||
user_dn_from_email_address.cache[email_address] = user_dn | user_dn_from_email_address.cache[email_address] = user_dn | ||||
return user_dn | return user_dn | ||||
user_dn_from_email_address.cache = {} | user_dn_from_email_address.cache = {} | ||||
▲ Show 20 Lines • Show All 262 Lines • ▼ Show 20 Line(s) | |||||
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 | ||||
if conflict: | if conflict: | ||||
break | break | ||||
end = time.time() | end = time.time() | ||||
log.debug(_("start: %r, end: %r, total: %r, messages: %d") % (start, end, (end-start), num_messages), level=9) | log.debug(_("start: %r, end: %r, total: %r, messages: %d") % (start, end, (end-start), num_messages), level=8) | ||||
# remember the result of this check for further iterations | # remember the result of this check for further iterations | ||||
itip_event['_conflicts'] = conflict | itip_event['_conflicts'] = conflict | ||||
return not conflict | return not conflict | ||||
def set_write_lock(key, wait=True): | def set_write_lock(key, wait=True): | ||||
Show All 11 Lines | |||||
if os.path.isfile(filename): | if os.path.isfile(filename): | ||||
locktime = os.path.getmtime(filename) | locktime = os.path.getmtime(filename) | ||||
# wait if file lock is in place | # wait if file lock is in place | ||||
while time.time() < locktime + 300: | while time.time() < locktime + 300: | ||||
if not wait: | if not wait: | ||||
return False | return False | ||||
log.debug(_("%r is locked, waiting...") % (key), level=9) | log.debug(_("%r is locked, waiting...") % (key), level=8) | ||||
time.sleep(0.5) | time.sleep(0.5) | ||||
locktime = os.path.getmtime(filename) if os.path.isfile(filename) else 0 | locktime = os.path.getmtime(filename) if os.path.isfile(filename) else 0 | ||||
# touch the file | # touch the file | ||||
if os.path.isfile(filename): | if os.path.isfile(filename): | ||||
os.utime(filename, None) | os.utime(filename, None) | ||||
else: | else: | ||||
open(filename, 'w').close() | open(filename, 'w').close() | ||||
▲ Show 20 Lines • Show All 261 Lines • ▼ Show 20 Line(s) | |||||
msg = MIMEText(utils.stripped_message(message_text), _charset='utf-8') | msg = MIMEText(utils.stripped_message(message_text), _charset='utf-8') | ||||
msg['To'] = receiving_user['mail'] | msg['To'] = receiving_user['mail'] | ||||
msg['Date'] = formatdate(localtime=True) | msg['Date'] = formatdate(localtime=True) | ||||
msg['Subject'] = utils.str2unicode(_('"%s" has been updated') % (object.get_summary())) | msg['Subject'] = utils.str2unicode(_('"%s" has been updated') % (object.get_summary())) | ||||
msg['From'] = Header(utils.str2unicode('%s' % orgname) if orgname else '') | msg['From'] = Header(utils.str2unicode('%s' % orgname) if orgname else '') | ||||
msg['From'].append("<%s>" % orgemail) | msg['From'].append("<%s>" % orgemail) | ||||
smtp = smtplib.SMTP("localhost", 10027) | modules._sendmail(orgemail, receiving_user['mail'], msg.as_string()) | ||||
if conf.debuglevel > 8: | |||||
smtp.set_debuglevel(True) | |||||
success = False | |||||
retries = 5 | |||||
while not success and retries > 0: | |||||
try: | |||||
success = smtp.sendmail(orgemail, receiving_user['mail'], msg.as_string()) | |||||
log.debug(_("Sent update notification to %r: %r") % (receiving_user['mail'], success), level=8) | log.debug(_("Sent update notification to %r: %r") % (receiving_user['mail'], success), level=8) | ||||
smtp.quit() | |||||
break | |||||
except Exception, errmsg: | |||||
log.error(_("SMTP sendmail error: %r") % (errmsg)) | |||||
time.sleep(10) | |||||
retries -= 1 | |||||
return success | |||||
def send_cancel_notification(object, receiving_user, deleted=False, sender=None, comment=None): | def send_cancel_notification(object, receiving_user, deleted=False, sender=None, comment=None): | ||||
""" | """ | ||||
Send a notification about event/task cancellation | Send a notification about event/task cancellation | ||||
""" | """ | ||||
import smtplib | import smtplib | ||||
from email.MIMEText import MIMEText | from email.MIMEText import MIMEText | ||||
from email.Utils import formatdate | from email.Utils import formatdate | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Line(s) | |||||
msg = MIMEText(utils.stripped_message(message_text), _charset='utf-8') | msg = MIMEText(utils.stripped_message(message_text), _charset='utf-8') | ||||
msg['To'] = receiving_user['mail'] | msg['To'] = receiving_user['mail'] | ||||
msg['Date'] = formatdate(localtime=True) | msg['Date'] = formatdate(localtime=True) | ||||
msg['Subject'] = utils.str2unicode(_('"%s" has been cancelled') % (object.get_summary())) | msg['Subject'] = utils.str2unicode(_('"%s" has been cancelled') % (object.get_summary())) | ||||
msg['From'] = Header(utils.str2unicode('%s' % orgname) if orgname else '') | msg['From'] = Header(utils.str2unicode('%s' % orgname) if orgname else '') | ||||
msg['From'].append("<%s>" % orgemail) | msg['From'].append("<%s>" % orgemail) | ||||
smtp = smtplib.SMTP("localhost", 10027) | modules._sendmail(orgemail, receiving_user['mail'], msg.as_string()) | ||||
log.debug(_("Sent cancel notification to %r: %r") % (receiving_user['mail'], success), level=8) | |||||
machniak: I'd put this log.debug() after modules._sendmail(). | |||||
if conf.debuglevel > 8: | |||||
smtp.set_debuglevel(True) | |||||
try: | |||||
smtp.sendmail(orgemail, receiving_user['mail'], msg.as_string()) | |||||
except Exception, errmsg: | |||||
log.error(_("SMTP sendmail error: %r") % (errmsg)) | |||||
smtp.quit() | |||||
def is_auto_reply(user, sender_email, type): | def is_auto_reply(user, sender_email, type): | ||||
accept_available = False | accept_available = False | ||||
accept_conflicts = False | accept_conflicts = False | ||||
for policy in get_matching_invitation_policies(user, sender_email, object_type_conditons.get(type, COND_TYPE_EVENT)): | for policy in get_matching_invitation_policies(user, sender_email, object_type_conditons.get(type, COND_TYPE_EVENT)): | ||||
if policy & (ACT_ACCEPT | ACT_REJECT | ACT_DELEGATE): | if policy & (ACT_ACCEPT | ACT_REJECT | ACT_DELEGATE): | ||||
if check_policy_condition(policy, True): | if check_policy_condition(policy, True): | ||||
accept_available = True | accept_available = True | ||||
▲ Show 20 Lines • Show All 93 Lines • Show Last 20 Lines |
I'd put this log.debug() after modules._sendmail().