Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

receive.py 5.8 KiB

il y a 13 ans
il y a 13 ans
il y a 13 ans
il y a 13 ans
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. import webnotes
  24. from webnotes.utils import extract_email_id, convert_utc_to_user_timezone
  25. class IncomingMail:
  26. """
  27. Single incoming email object. Extracts, text / html and attachments from the email
  28. """
  29. def __init__(self, content):
  30. import email, email.utils, email.header
  31. import datetime
  32. self.mail = email.message_from_string(content)
  33. self.text_content = ''
  34. self.html_content = ''
  35. self.attachments = []
  36. self.parse()
  37. self.set_content_and_type()
  38. self.set_subject()
  39. self.from_email = extract_email_id(self.mail["From"])
  40. self.from_real_name = email.utils.parseaddr(self.mail["From"])[0]
  41. utc = email.utils.mktime_tz(email.utils.parsedate_tz(self.mail["Date"]))
  42. utc_dt = datetime.datetime.utcfromtimestamp(utc)
  43. self.date = convert_utc_to_user_timezone(utc_dt).strftime('%Y-%m-%d %H:%M:%S')
  44. def parse(self):
  45. for part in self.mail.walk():
  46. self.process_part(part)
  47. def set_subject(self):
  48. _subject = email.header.decode_header(self.mail.get("Subject", "No Subject"))
  49. self.subject = _subject[0][0]
  50. if _subject[0][1]:
  51. self.subject = _subject[0][0].decode(_subject[0][1])
  52. def set_content_and_type(self):
  53. self.content, self.content_type = '[Blank Email]', 'text/plain'
  54. if self.text_content:
  55. self.content, self.content_type = self.text_content, 'text/plain'
  56. else:
  57. self.content, self.content_type = self.html_content, 'text/html'
  58. def process_part(self, part):
  59. content_type = part.get_content_type()
  60. charset = part.get_content_charset()
  61. if not charset: charset = self.get_charset(part)
  62. if content_type == 'text/plain':
  63. self.text_content += self.get_payload(part, charset)
  64. if content_type == 'text/html':
  65. self.html_content += self.get_payload(part, charset)
  66. if part.get_filename():
  67. self.get_attachment(part, charset)
  68. def get_text_content(self):
  69. return self.text_content or self.html_content
  70. def get_charset(self, part):
  71. charset = part.get_content_charset()
  72. if not charset:
  73. import chardet
  74. charset = chardet.detect(str(part))['encoding']
  75. return charset
  76. def get_payload(self, part, charset):
  77. try:
  78. return unicode(part.get_payload(decode=True),str(charset),"ignore")
  79. except LookupError, e:
  80. return part.get_payload()
  81. def get_attachment(self, part, charset):
  82. self.attachments.append({
  83. 'content-type': part.get_content_type(),
  84. 'filename': part.get_filename(),
  85. 'content': part.get_payload(decode=True),
  86. })
  87. def save_attachments_in_doc(self, doc):
  88. from webnotes.utils.file_manager import save_file, MaxFileSizeReachedError
  89. for attachment in self.attachments:
  90. try:
  91. fid = save_file(attachment['filename'], attachment['content'],
  92. doc.doctype, doc.name)
  93. except MaxFileSizeReachedError:
  94. # bypass max file size exception
  95. pass
  96. except webnotes.DuplicateEntryError:
  97. # same file attached twice??
  98. pass
  99. def get_thread_id(self):
  100. import re
  101. l = re.findall('(?<=\[)[\w/-]+', self.subject)
  102. return l and l[0] or None
  103. class POP3Mailbox:
  104. def __init__(self, args=None):
  105. self.setup(args)
  106. self.get_messages()
  107. def setup(self, args=None):
  108. # overrride
  109. import webnotes
  110. self.settings = args or webnotes._dict()
  111. def check_mails(self):
  112. # overrride
  113. return True
  114. def process_message(self, mail):
  115. # overrride
  116. pass
  117. def connect(self):
  118. import poplib
  119. if self.settings.use_ssl:
  120. self.pop = poplib.POP3_SSL(self.settings.host)
  121. else:
  122. self.pop = poplib.POP3(self.settings.host)
  123. self.pop.user(self.settings.username)
  124. self.pop.pass_(self.settings.password)
  125. def get_messages(self):
  126. import webnotes
  127. if not self.check_mails():
  128. return # nothing to do
  129. webnotes.conn.commit()
  130. self.connect()
  131. num = num_copy = len(self.pop.list()[1])
  132. # track if errors arised
  133. errors = False
  134. # WARNING: Hard coded max no. of messages to be popped
  135. if num > 20: num = 20
  136. for m in xrange(1, num+1):
  137. msg = self.pop.retr(m)
  138. # added back dele, as most pop3 servers seem to require msg to be deleted
  139. # else it will again be fetched in self.pop.list()
  140. self.pop.dele(m)
  141. try:
  142. incoming_mail = IncomingMail(b'\n'.join(msg[1]))
  143. webnotes.conn.begin()
  144. self.process_message(incoming_mail)
  145. webnotes.conn.commit()
  146. except:
  147. from webnotes.utils.scheduler import log
  148. # log performs rollback and logs error in scheduler log
  149. log("receive.get_messages")
  150. errors = True
  151. webnotes.conn.rollback()
  152. # WARNING: Mark as read - message number 101 onwards from the pop list
  153. # This is to avoid having too many messages entering the system
  154. num = num_copy
  155. if num > 100 and not errors:
  156. for m in xrange(101, num+1):
  157. self.pop.dele(m)
  158. self.pop.quit()
  159. webnotes.conn.begin()