diff --git a/pykolab/itip/__init__.py b/pykolab/itip/__init__.py --- a/pykolab/itip/__init__.py +++ b/pykolab/itip/__init__.py @@ -303,44 +303,118 @@ conf = pykolab.getConf() smtp = None + sl = pykolab.logger.StderrToLogger(log) + smtplib.stderr = sl + + smtp = smtplib.SMTP(timeout=15) + + if conf.debuglevel > 8: + smtp.set_debuglevel(1) + if isinstance(itip_events, dict): itip_events = [ itip_events ] for itip_event in itip_events: - attendee = itip_event['xml'].get_attendee_by_email(from_address) - participant_status = itip_event['xml'].get_ical_attendee_participant_status(attendee) + success = False + attempt = 1 + + while not success and attempt <= 5: + try: + attendee = itip_event['xml'].get_attendee_by_email(from_address) + participant_status = itip_event['xml'].get_ical_attendee_participant_status( + attendee + ) + + log.debug( + _("Send iTip reply %s for %s %r") % ( + participant_status, + itip_event['xml'].type, + itip_event['xml'].uid + ), + level=8 + ) + + event_summary = itip_event['xml'].get_summary() + + message_text = response_text % { + 'name': attendee.get_name(), + 'status': participant_status_label(participant_status), + 'summary': event_summary + } + + if subject is not None: + subject = subject % { + 'name': attendee.get_name(), + 'status': participant_status_label(participant_status), + 'summary': event_summary + } + + try: + message = itip_event['xml'].to_message_itip(from_address, + method="REPLY", + participant_status=participant_status, + message_text=message_text, + subject=subject + ) + + except Exception, errmsg: + log.error( + _("Failed to compose iTip reply message: %r: %s") % ( + errmsg, + traceback.format_exc() + ) + ) + + return + + + smtp.connect("localhost", 10026) + + _response = smtp.sendmail(message['From'], message['To'], message.as_string()) + + if len(_response) == 0: + log.debug(_("SMTP sendmail OK"), level=8) + else: + log.debug(_("SMTP sendmail returned: %r") % (_reponse), level=8) - log.debug(_("Send iTip reply %s for %s %r") % (participant_status, itip_event['xml'].type, itip_event['xml'].uid), level=8) + smtp.quit() - event_summary = itip_event['xml'].get_summary() - message_text = response_text % { 'summary':event_summary, 'status':participant_status_label(participant_status), 'name':attendee.get_name() } + success = True + break - if subject is not None: - subject = subject % { 'summary':event_summary, 'status':participant_status_label(participant_status), 'name':attendee.get_name() } + except smtplib.SMTPServerDisconnected, errmsg: + log.error("SMTP Server Disconnected Error, %r" % (errmsg)) - try: - message = itip_event['xml'].to_message_itip(from_address, - method="REPLY", - participant_status=participant_status, - message_text=message_text, - subject=subject - ) - except Exception, e: - log.error(_("Failed to compose iTip reply message: %r: %s") % (e, traceback.format_exc())) - return + except smtplib.SMTPConnectError, errmsg: + # DEFER + log.error("SMTP Connect Error, %r" % (errmsg)) + + except smtplib.SMTPDataError, errmsg: + # DEFER + log.error("SMTP Data Error, %r" % (errmsg)) - smtp = smtplib.SMTP("localhost", 10026) # replies go through wallace again + except smtplib.SMTPHeloError, errmsg: + # DEFER + log.error("SMTP HELO Error, %r" % (errmsg)) - if conf.debuglevel > 8: - smtp.set_debuglevel(True) + except smtplib.SMTPRecipientsRefused, errmsg: + # REJECT, send NDR + log.error("SMTP Recipient(s) Refused, %r" % (errmsg)) + + except smtplib.SMTPSenderRefused, errmsg: + # REJECT, send NDR + log.error("SMTP Sender Refused, %r" % (errmsg)) + + except Exception, errmsg: + log.exception(_("smtplib - Unknown error occurred: %r") % (errmsg)) try: - smtp.sendmail(message['From'], message['To'], message.as_string()) - except Exception, e: - log.error(_("SMTP sendmail error: %r") % (e)) + smtp.quit() + except Exception, errmsg: + log.error("smtplib quit() error - %r" % errmsg) - if smtp: - smtp.quit() + time.sleep(10) + attempt += 1 def send_request(to_address, itip_events, request_text, subject=None, direct=False): @@ -352,36 +426,98 @@ conf = pykolab.getConf() smtp = None + sl = pykolab.logger.StderrToLogger(log) + smtplib.stderr = sl + + smtp = smtplib.SMTP(timeout=15) + + if conf.debuglevel > 8: + smtp.set_debuglevel(1) + if isinstance(itip_events, dict): itip_events = [ itip_events ] for itip_event in itip_events: - event_summary = itip_event['xml'].get_summary() - message_text = request_text % { 'summary':event_summary } + success = False + attempt = 1 + + while not success and attempt <= 5: + try: + event_summary = itip_event['xml'].get_summary() + message_text = request_text % { + 'summary': event_summary + } + + if subject is not None: + subject = subject % { + 'summary': event_summary + } + + try: + message = itip_event['xml'].to_message_itip( + None, + method="REQUEST", + message_text=message_text, + subject=subject + ) + + except Exception, errmsg: + log.error( + _("Failed to compose iTip request message: %r: %s") % ( + errmsg, + traceback.format_exc() + ) + ) + + return + + if direct: + smtp.connect("localhost", 10027) + else: + smtp.connect("localhost", 10026) - if subject is not None: - subject = subject % { 'summary':event_summary } + _response = smtp.sendmail(message['From'], to_address, message.as_string()) - try: - message = itip_event['xml'].to_message_itip(None, - method="REQUEST", - message_text=message_text, - subject=subject - ) - except Exception, e: - log.error(_("Failed to compose iTip request message: %r") % (e)) - return + if len(_response) == 0: + log.debug(_("SMTP sendmail OK"), level=8) + else: + log.debug(_("SMTP sendmail returned: %r") % (_reponse), level=8) + + smtp.quit() + + success = True + break + + except smtplib.SMTPServerDisconnected, errmsg: + log.error("SMTP Server Disconnected Error, %r" % (errmsg)) + + except smtplib.SMTPConnectError, errmsg: + # DEFER + log.error("SMTP Connect Error, %r" % (errmsg)) + + except smtplib.SMTPDataError, errmsg: + # DEFER + log.error("SMTP Data Error, %r" % (errmsg)) + + except smtplib.SMTPHeloError, errmsg: + # DEFER + log.error("SMTP HELO Error, %r" % (errmsg)) + + except smtplib.SMTPRecipientsRefused, errmsg: + # REJECT, send NDR + log.error("SMTP Recipient(s) Refused, %r" % (errmsg)) - port = 10027 if direct else 10026 - smtp = smtplib.SMTP("localhost", port) + except smtplib.SMTPSenderRefused, errmsg: + # REJECT, send NDR + log.error("SMTP Sender Refused, %r" % (errmsg)) - if conf.debuglevel > 8: - smtp.set_debuglevel(True) + except Exception, errmsg: + log.exception(_("smtplib - Unknown error occurred: %r") % (errmsg)) try: - smtp.sendmail(message['From'], to_address, message.as_string()) - except Exception, e: - log.error(_("SMTP sendmail error: %r") % (e)) + smtp.quit() + except Exception, errmsg: + log.error("smtplib quit() error - %r" % errmsg) - if smtp: - smtp.quit() + time.sleep(10) + attempt += 1 diff --git a/wallace/module_resources.py b/wallace/module_resources.py --- a/wallace/module_resources.py +++ b/wallace/module_resources.py @@ -1273,7 +1273,11 @@ # Extra actions to take: send delegated reply if participant_status == "DELEGATED": - delegatee = [a for a in itip_event['xml'].get_attendees() if from_address in a.get_delegated_from(True)][0] + atts = itip_event['xml'].get_attendees() + + delegatee = [ + a for a in atts if from_address in a.get_delegated_from(True) + ][0] delegated_message_text = _(""" *** This is an automated response, please do not reply! *** @@ -1281,16 +1285,38 @@ Your reservation was delegated to "%s" which is available for the requested time. """) % (delegatee.get_name()) - pykolab.itip.send_reply(from_address, itip_event, delegated_message_text, - subject=subject_template) + seed = random.randint(0, 6) + alarm_after = (seed * 10) + 60 + log.debug(_("Set alarm to %s seconds") % (alarm_after), level=8) + signal.alarm(alarm_after) + + pykolab.itip.send_reply( + from_address, + itip_event, + delegated_message_text, + subject=subject_template + ) + + signal.alarm(0) # adjust some vars for the regular reply from the delegatee message_text = reservation_response_text(delegatee.get_participant_status(True), owner) from_address = delegatee.get_email() time.sleep(2) - pykolab.itip.send_reply(from_address, itip_event, message_text, - subject=subject_template) + seed = random.randint(0, 6) + alarm_after = (seed * 10) + 60 + log.debug(_("Set alarm to %s seconds") % (alarm_after), level=8) + signal.alarm(alarm_after) + + pykolab.itip.send_reply( + from_address, + itip_event, + message_text, + subject=subject_template + ) + + signal.alarm(0) def reservation_response_text(status, owner): @@ -1395,16 +1421,21 @@ def send_owner_confirmation(resource, owner, itip_event): """ - Send a reservation request to the resource owner for manual confirmation (ACCEPT or DECLINE) + Send a reservation request to the resource owner for manual confirmation (ACCEPT or + DECLINE) - This clones the given invtation with a new UID and setting the resource as organizer in order to - receive the reply from the owner. + This clones the given invtation with a new UID and setting the resource as organizer in + order to receive the reply from the owner. """ uid = itip_event['uid'] event = itip_event['xml'] organizer = event.get_organizer() - event_attendees = [a.get_displayname() for a in event.get_attendees() if not a.get_cutype() == kolabformat.CutypeResource] + + atts = event.get_attendees() + event_attendees = [ + a.get_displayname() for a in atts if not a.get_cutype() == kolabformat.CutypeResource + ] log.debug( _("Clone invitation for owner confirmation: %r from %r") % ( @@ -1421,7 +1452,13 @@ # add resource owner as (the sole) attendee event._attendees = [] - event.add_attendee(owner['mail'], owner['cn'], rsvp=True, role=kolabformat.Required, participant_status=kolabformat.PartNeedsAction) + event.add_attendee( + owner['mail'], + owner['cn'], + rsvp=True, + role=kolabformat.Required, + participant_status=kolabformat.PartNeedsAction + ) # flag this iTip message as confirmation type event.add_custom_property('X-Kolab-InvitationType', 'CONFIRMATION') @@ -1437,7 +1474,7 @@ Participants: %(attendees)s *** This is an automated message, please don't reply by email. *** - """)% { + """) % { 'resource': resource['cn'], 'orgname': organizer.name(), 'orgemail': organizer.email(), @@ -1446,8 +1483,19 @@ 'attendees': ",\n+ ".join(event_attendees) } - pykolab.itip.send_request(owner['mail'], itip_event, message_text, + seed = random.randint(0, 6) + alarm_after = (seed * 10) + 60 + log.debug(_("Set alarm to %s seconds") % (alarm_after), level=8) + signal.alarm(alarm_after) + + pykolab.itip.send_request( + owner['mail'], + itip_event, + message_text, subject=_('Booking request for %s requires confirmation') % (resource['cn']), - direct=True) + direct=True + ) + + signal.alarm(0)