From 2f51b3efd01fbd49b212595cfddff9975f763aba Mon Sep 17 00:00:00 2001 From: Gangadhar Kadam Date: Tue, 24 Nov 2015 18:11:42 +0530 Subject: [PATCH] Added imap with pop protocal for email retrive --- .../doctype/email_account/email_account.js | 34 +++++++++ .../doctype/email_account/email_account.json | 27 ++++++- .../doctype/email_account/email_account.py | 21 ++++-- frappe/email/receive.py | 74 ++++++++++++++++--- 4 files changed, 136 insertions(+), 20 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.js b/frappe/email/doctype/email_account/email_account.js index 8781ce6a0c..fc11586094 100644 --- a/frappe/email/doctype/email_account/email_account.js +++ b/frappe/email/doctype/email_account/email_account.js @@ -33,11 +33,45 @@ email_defaults = { }, }; +email_defaults_imap = { + "GMail": { + "pop3_server": "imap.gmail.com" + }, + "Outlook.com": { + "pop3_server": "imap.live.com" + }, + "Yahoo Mail": { + "pop3_server": "imap.mail.yahoo.com" + }, + "Yandex.Mail": { + "pop3_server": "imap.yandex.com" + }, + +}; + frappe.ui.form.on("Email Account", { service: function(frm) { $.each(email_defaults[frm.doc.service], function(key, value) { frm.set_value(key, value); }) + if (frm.doc.use_imap) { + $.each(email_defaults_imap[frm.doc.service], function(key, value) { + frm.set_value(key, value); + }) + } + }, + use_imap: function(frm) { + console.log("in use imap"); + if (frm.doc.use_imap) { + $.each(email_defaults_imap[frm.doc.service], function(key, value) { + frm.set_value(key, value); + }) + } + else{ + $.each(email_defaults[frm.doc.service], function(key, value) { + frm.set_value(key, value); + }) + } }, email_id: function(frm) { if(!frm.doc.email_account_name) { diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json index 1239a257d6..90dc9915a7 100644 --- a/frappe/email/doctype/email_account/email_account.json +++ b/frappe/email/doctype/email_account/email_account.json @@ -222,6 +222,30 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "", + "fieldname": "use_imap", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Use Imap", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -808,13 +832,14 @@ "hide_heading": 0, "hide_toolbar": 0, "icon": "icon-inbox", + "idx": 0, "in_create": 0, "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2015-11-16 06:29:45.876335", + "modified": "2015-11-24 17:45:31.001113", "modified_by": "Administrator", "module": "Email", "name": "Email Account", diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 7eb3e48793..785a2f64e0 100644 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -9,13 +9,15 @@ from frappe.utils import validate_email_add, cint, get_datetime, DATE_FORMAT, st from frappe.utils.user import is_system_user from frappe.utils.jinja import render_template from frappe.email.smtp import SMTPServer -from frappe.email.receive import POP3Server, Email +from frappe.email.receive import POP3Server,Email from poplib import error_proto -import re +import imaplib +import markdown2, re from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta -class SentEmailInInbox(Exception): pass +class SentEmailInInbox(Exception): pass # imap +class error(Exception): pass # imap class EmailAccount(Document): def autoname(self): @@ -30,7 +32,7 @@ class EmailAccount(Document): self.name = self.email_account_name def validate(self): - """Validate email id and check POP3 and SMTP connections is enabled.""" + """Validate email id and check POP3/IMAP and SMTP connections is enabled.""" if self.email_id: validate_email_add(self.email_id, True) @@ -102,7 +104,8 @@ class EmailAccount(Document): "host": self.pop3_server, "use_ssl": self.use_ssl, "username": getattr(self, "login_id", None) or self.email_id, - "password": self.password + "password": self.password, + "use_imap":self.use_imap } if not self.pop3_server: @@ -111,7 +114,7 @@ class EmailAccount(Document): pop3 = POP3Server(frappe._dict(args)) try: pop3.connect() - except error_proto, e: + except (error_proto,imaplib.IMAP4.error) , e: frappe.throw(e.message) return pop3 @@ -315,14 +318,18 @@ def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len if not txt: txt = "" return [[d] for d in frappe.get_hooks("email_append_to") if txt in d] -def pull(now=False): +def pull(now=True): """Will be called via scheduler, pull emails from all enabled POP3 email accounts.""" import frappe.tasks + print "in pull method " for email_account in frappe.get_list("Email Account", filters={"enable_incoming": 1}): + print email_account #frappe.tasks.pull_from_email_account(frappe.local.site, email_account.name) if now: + print "in now" frappe.tasks.pull_from_email_account(frappe.local.site, email_account.name) else: + print "not now" frappe.tasks.pull_from_email_account.delay(frappe.local.site, email_account.name) def notify_unreplied(): diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 1a65b8c74f..502eee3018 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,imaplib import frappe from frappe import _ from frappe.utils import extract_email_id, convert_utc_to_user_timezone, now, cint, cstr, strip @@ -36,6 +36,31 @@ class POP3Server: def connect(self): """Connect to **Email Account**.""" + responce = self.connect_imap() if cint(self.settings.use_imap) else self.connect_pop() + return responce + + def connect_imap(self): + #this method return imap connection + try: + if cint(self.settings.use_ssl): + self.imap = Timed_IMAP4_SSL(self.settings.host, timeout=frappe.conf.get("pop_timeout")) + else: + self.imap = Timed_IMAP4(self.settings.host, timeout=frappe.conf.get("pop_timeout")) + self.imap.login(self.settings.username,self.settings.password) + # connection established! + return True + + except _socket.error: + # Invalid mail server -- due to refusing connection + frappe.msgprint(_('Invalid Mail Server. Please rectify and try again.')) + raise + + except : + frappe.msgprint(_('Invalid User Name or Support Password. Please rectify and try again.')) + raise + + def connect_pop(self): + #this method return pop connection try: if cint(self.settings.use_ssl): self.pop = Timed_POP3_SSL(self.settings.host, timeout=frappe.conf.get("pop_timeout")) @@ -75,8 +100,14 @@ class POP3Server: # track if errors arised self.errors = False self.latest_messages = [] - pop_list = self.pop.list()[1] - num = num_copy = len(pop_list) + # if section code below is for imap and else part is for pop3 + if cint(self.settings.use_imap): + self.imap.select("Inbox") + responce, message = self.imap.uid('search',None, "UNSEEN") # search and return Uids + email_list = message[0].split() + else: + email_list = self.pop.list()[1] + num = num_copy = len(email_list) # WARNING: Hard coded max no. of messages to be popped if num > 20: num = 20 @@ -86,20 +117,24 @@ class POP3Server: self.max_email_size = cint(frappe.local.conf.get("max_email_size")) self.max_total_size = 5 * self.max_email_size - for i, pop_meta in enumerate(pop_list): + for i, pop_meta in enumerate(email_list): # do not pull more than NUM emails if (i+1) > num: break try: - self.retrieve_message(pop_meta, i+1) + # if section code below is for imap and else part is for pop3 + if cint(self.settings.use_imap): + self.retrieve_message(pop_meta) + else: + self.retrieve_message(pop_meta, i+1) except (TotalSizeExceededError, EmailTimeoutError, LoginLimitExceeded): break # WARNING: Mark as read - message number 101 onwards from the pop list # This is to avoid having too many messages entering the system num = num_copy - if num > 100 and not self.errors: + if num > 100 and not self.errors and not cint(self.settings.use_imap): for m in xrange(101, num+1): self.pop.dele(m) @@ -112,17 +147,25 @@ class POP3Server: finally: # no matter the exception, pop should quit if connected - self.pop.quit() + if cint(self.settings.use_imap): + self.imap.logout() + else: + self.pop.quit() return self.latest_messages - def retrieve_message(self, pop_meta, msg_num): + def retrieve_message(self, pop_meta,msg_num=None): incoming_mail = None try: self.validate_pop(pop_meta) - msg = self.pop.retr(msg_num) + # if section code below is for imap and else part is for pop3 + if cint(self.settings.use_imap): + status,message = self.imap.uid('fetch', pop_meta, '(RFC822)') + self.latest_messages.append(message[0][1]) + else: + msg = self.pop.retr(msg_num) - self.latest_messages.append(b'\n'.join(msg[1])) + self.latest_messages.append(b'\n'.join(msg[1])) except (TotalSizeExceededError, EmailTimeoutError): # propagate this error to break the loop @@ -140,9 +183,11 @@ class POP3Server: self.errors = True frappe.db.rollback() - self.pop.dele(msg_num) + if not cint(self.settings.use_imap): + self.pop.dele(msg_num) else: - self.pop.dele(msg_num) + if not cint(self.settings.use_imap): + self.pop.dele(msg_num) def has_login_limit_exceeded(self, e): return "-ERR Exceeded the login limit" in strip(cstr(e.message)) @@ -355,3 +400,8 @@ class Timed_POP3(TimerMixin, poplib.POP3): class Timed_POP3_SSL(TimerMixin, poplib.POP3_SSL): _super = poplib.POP3_SSL +class Timed_IMAP4(TimerMixin, imaplib.IMAP4): + _super = imaplib.IMAP4 + +class Timed_IMAP4_SSL(TimerMixin, imaplib.IMAP4_SSL): + _super = imaplib.IMAP4_SSL