您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

177 行
5.0 KiB

  1. # Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. #
  3. # MIT License (MIT)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the "Software"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #
  22. from __future__ import unicode_literals
  23. from webnotes.utils import extract_email_id
  24. class IncomingMail:
  25. """
  26. Single incoming email object. Extracts, text / html and attachments from the email
  27. """
  28. def __init__(self, content):
  29. """
  30. Parse the incoming mail content
  31. """
  32. import email
  33. from email.utils import parseaddr
  34. self.mail = email.message_from_string(content)
  35. self.text_content = ''
  36. self.html_content = ''
  37. self.attachments = []
  38. self.parse()
  39. self.set_content_and_type()
  40. self.from_email = extract_email_id(self.mail["From"])
  41. self.from_real_name = parseaddr(self.mail["From"])[0]
  42. def parse(self):
  43. for part in self.mail.walk():
  44. self.process_part(part)
  45. def set_content_and_type(self):
  46. self.content, self.content_type = '[Blank Email]', 'text/plain'
  47. if self.text_content:
  48. self.content, self.content_type = self.text_content, 'text/plain'
  49. else:
  50. self.content, self.content_type = self.html_content, 'text/html'
  51. def process_part(self, part):
  52. content_type = part.get_content_type()
  53. charset = part.get_content_charset()
  54. if not charset: charset = self.get_charset(part)
  55. if content_type == 'text/plain':
  56. self.text_content += self.get_payload(part, charset)
  57. if content_type == 'text/html':
  58. self.html_content += self.get_payload(part, charset)
  59. if part.get_filename():
  60. self.get_attachment(part, charset)
  61. def get_text_content(self):
  62. return self.text_content or self.html_content
  63. def get_charset(self, part):
  64. charset = part.get_content_charset()
  65. if not charset:
  66. import chardet
  67. charset = chardet.detect(str(part))['encoding']
  68. return charset
  69. def get_payload(self, part, charset):
  70. try:
  71. return unicode(part.get_payload(decode=True),str(charset),"ignore")
  72. except LookupError, e:
  73. return part.get_payload()
  74. def get_attachment(self, part, charset):
  75. self.attachments.append({
  76. 'content-type': part.get_content_type(),
  77. 'filename': part.get_filename(),
  78. 'content': part.get_payload(decode=True),
  79. })
  80. def save_attachments_in_doc(self, doc):
  81. from webnotes.utils.file_manager import save_file, add_file_list
  82. for attachment in self.attachments:
  83. fid = save_file(attachment['filename'], attachment['content'])
  84. status = add_file_list(doc.doctype, doc.name, fid, fid)
  85. def get_thread_id(self):
  86. import re
  87. subject = self.mail.get('Subject', '')
  88. l= re.findall('(?<=\[)[\w/-]+', subject)
  89. return l and l[0] or None
  90. class POP3Mailbox:
  91. def __init__(self):
  92. self.setup()
  93. self.get_messages()
  94. def setup(self):
  95. # overrride
  96. self.settings = webnotes._dict()
  97. def check_mails(self):
  98. # overrride
  99. return True
  100. def process_message(self, mail):
  101. # overrride
  102. pass
  103. def connect(self):
  104. import poplib
  105. if self.settings.use_ssl:
  106. self.pop = poplib.POP3_SSL(self.settings.host)
  107. else:
  108. self.pop = poplib.POP3(self.settings.host)
  109. self.pop.user(self.settings.username)
  110. self.pop.pass_(self.settings.password)
  111. def get_messages(self):
  112. import webnotes
  113. if not self.check_mails():
  114. return # nothing to do
  115. webnotes.conn.commit()
  116. self.connect()
  117. num = num_copy = len(self.pop.list()[1])
  118. # track if errors arised
  119. errors = False
  120. # WARNING: Hard coded max no. of messages to be popped
  121. if num > 20: num = 20
  122. for m in xrange(1, num+1):
  123. msg = self.pop.retr(m)
  124. self.pop.dele(m)
  125. try:
  126. incoming_mail = IncomingMail(b'\n'.join(msg[1]))
  127. webnotes.conn.begin()
  128. self.process_message(incoming_mail)
  129. webnotes.conn.commit()
  130. except:
  131. from webnotes.utils.scheduler import log
  132. # log performs rollback and logs error in scheduler log
  133. log("receive.get_messages")
  134. errors = True
  135. webnotes.conn.rollback()
  136. # WARNING: Delete message number 101 onwards from the pop list
  137. # This is to avoid having too many messages entering the system
  138. num = num_copy
  139. if num > 100 and not errors:
  140. for m in xrange(101, num+1):
  141. self.pop.dele(m)
  142. self.pop.quit()
  143. webnotes.conn.begin()