From 6177d09b187293164c8ea7624d660d761f550305 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 23 Nov 2015 15:44:08 +0530 Subject: [PATCH] [fix] In pop3 receive, catch authentication and timeout errors while connecting, disable incoming emails and assign the email account to system managers --- frappe/desk/form/assign_to.py | 28 +++++------ .../doctype/email_account/email_account.py | 47 +++++++++++++++++-- .../email_account/test_email_account.py | 8 ++++ frappe/email/receive.py | 4 +- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py index c20483919b..543a1c1d48 100644 --- a/frappe/desk/form/assign_to.py +++ b/frappe/desk/form/assign_to.py @@ -38,13 +38,13 @@ def add(args=None): and owner=%(assign_to)s""", args): frappe.msgprint(_("Already in user's To Do list"), raise_exception=True) return - + else: from frappe.utils import nowdate - + if args.get("re_assign"): remove_from_todo_if_already_assigned(args['doctype'], args['name']) - + d = frappe.get_doc({ "doctype":"ToDo", "owner": args['assign_to'], @@ -56,39 +56,38 @@ def add(args=None): "date": args.get('date', nowdate()), "assigned_by": args.get('assigned_by', frappe.session.user), }).insert(ignore_permissions=True) - + # set assigned_to if field exists if frappe.get_meta(args['doctype']).get_field("assigned_to"): frappe.db.set_value(args['doctype'], args['name'], "assigned_to", args['assign_to']) # notify - if not args.get("no_notification"): - notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\ - description=args.get("description"), notify=args.get('notify')) - + notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\ + description=args.get("description"), notify=args.get('notify')) + if not args.get("bulk_assign"): return get(args) else: return {} - + @frappe.whitelist() def add_multiple(args=None): import json - + if not args: args = frappe.local.form_dict - + docname_list = json.loads(args['name']) - + for docname in docname_list: args.update({"name": docname}) add(args) - + def remove_from_todo_if_already_assigned(doctype, docname): owner = frappe.db.get_value("ToDo", {"reference_type": doctype, "reference_name": docname, "status":"Open"}, "owner") if owner: remove(doctype, docname, owner) - + @frappe.whitelist() def remove(doctype, name, assign_to): """remove from todo""" @@ -156,5 +155,6 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE', } arg["parenttype"] = "Assignment" + from frappe.desk.page.messages import messages messages.post(**arg) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 7eb3e48793..0c7583874b 100644 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -14,6 +14,9 @@ from poplib import error_proto import re from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta +from frappe.desk.form import assign_to +from frappe.utils.user import get_system_managers +import socket class SentEmailInInbox(Exception): pass @@ -96,7 +99,7 @@ class EmailAccount(Document): ) server.sess - def get_pop3(self): + def get_pop3(self, in_receive=False): """Returns logged in POP3 connection object.""" args = { "host": self.pop3_server, @@ -111,18 +114,56 @@ class EmailAccount(Document): pop3 = POP3Server(frappe._dict(args)) try: pop3.connect() + except error_proto, e: - frappe.throw(e.message) + if in_receive and e.message=="-ERR authentication failed": + # if called via self.receive and it leads to authentication error, disable incoming + # and send email to system manager + self.handle_incoming_connect_error( + description=_('Authentication failed while receiving emails from Email Account {0}'.format(self.name)) + ) + + return None + + else: + frappe.throw(e.message) + + except socket.error: + if in_receive: + # timeout while connecting, see receive.py connect method + description = frappe.message_log.pop() if frappe.message_log else "Socket Error" + self.handle_incoming_connect_error(description=description) + + return None + + else: + raise return pop3 + def handle_incoming_connect_error(self, description): + self.db_set("enable_incoming", 0) + + for user in get_system_managers(only_name=True): + assign_to.add({ + 'assign_to': user, + 'doctype': self.doctype, + 'name': self.name, + 'description': description, + 'priority': 'High', + 'notify': 1 + }) + def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3.""" if self.enable_incoming: if frappe.local.flags.in_test: incoming_mails = test_mails else: - pop3 = self.get_pop3() + pop3 = self.get_pop3(in_receive=True) + if not pop3: + return + incoming_mails = pop3.get_messages() exceptions = [] diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index 94cc1f6156..87627bb69c 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -13,6 +13,14 @@ from frappe.email.doctype.email_account.email_account import notify_unreplied from datetime import datetime, timedelta class TestEmailAccount(unittest.TestCase): + def setUp(self): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 1) + + def tearDown(self): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 0) + def test_incoming(self): frappe.db.sql("delete from tabCommunication where sender='test_sender@example.com'") diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 1a65b8c74f..7b5711d17a 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import time -import _socket, poplib +import socket, poplib import frappe from frappe import _ from frappe.utils import extract_email_id, convert_utc_to_user_timezone, now, cint, cstr, strip @@ -48,7 +48,7 @@ class POP3Server: # connection established! return True - except _socket.error: + except socket.error: # Invalid mail server -- due to refusing connection frappe.msgprint(_('Invalid Mail Server. Please rectify and try again.')) raise