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

307 行
8.2 KiB

  1. """
  2. Sends email via outgoing server specified in "Control Panel"
  3. Allows easy adding of Attachments of "File" objects
  4. """
  5. import webnotes
  6. import webnotes.defs
  7. from webnotes import msgprint
  8. import email
  9. class EMail:
  10. """
  11. Wrapper on the email module. Email object represents emails to be sent to the client.
  12. Also provides a clean way to add binary `FileData` attachments
  13. Also sets all messages as multipart/alternative for cleaner reading in text-only clients
  14. """
  15. def __init__(self, sender='', recipients=[], subject='', from_defs=0, alternative=0, reply_to=None):
  16. from email.mime.multipart import MIMEMultipart
  17. from email import Charset
  18. Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')
  19. if isinstance(recipients, basestring):
  20. recipients = recipients.replace(';', ',')
  21. recipients = recipients.split(',')
  22. self.from_defs = from_defs
  23. self.sender = sender
  24. self.reply_to = reply_to or sender
  25. self.recipients = recipients
  26. self.subject = subject
  27. self.msg_root = MIMEMultipart('mixed')
  28. self.msg_multipart = MIMEMultipart('alternative')
  29. self.msg_root.attach(self.msg_multipart)
  30. self.cc = []
  31. def set_text(self, message):
  32. """
  33. Attach message in the text portion of multipart/alternative
  34. """
  35. from email.mime.text import MIMEText
  36. msg = unicode(message, 'utf-8')
  37. part = MIMEText(msg.encode('utf-8'), 'plain', 'UTF-8')
  38. self.msg_multipart.attach(part)
  39. def set_html(self, message):
  40. """
  41. Attach message in the html portion of multipart/alternative
  42. """
  43. from email.mime.text import MIMEText
  44. part = MIMEText(message, 'html')
  45. self.msg_multipart.attach(part)
  46. def set_message(self, message, mime_type='text/html', as_attachment=0, filename='attachment.html'):
  47. """
  48. Append the message with MIME content to the root node (as attachment)
  49. """
  50. from email.mime.text import MIMEText
  51. maintype, subtype = mime_type.split('/')
  52. part = MIMEText(message, _subtype = subtype)
  53. if as_attachment:
  54. part.add_header('Content-Disposition', 'attachment', filename=filename)
  55. self.msg_root.attach(part)
  56. def attach_file(self, n):
  57. """
  58. attach a file from the `FileData` table
  59. """
  60. from webnotes.utils.file_manager import get_file
  61. res = get_file(n)
  62. if not res:
  63. return
  64. self.add_attachment(res[0], res[1])
  65. def add_attachment(self, fname, fcontent, content_type=None):
  66. from email.mime.audio import MIMEAudio
  67. from email.mime.base import MIMEBase
  68. from email.mime.image import MIMEImage
  69. from email.mime.text import MIMEText
  70. import mimetypes
  71. if not content_type:
  72. content_type, encoding = mimetypes.guess_type(fname)
  73. if content_type is None:
  74. # No guess could be made, or the file is encoded (compressed), so
  75. # use a generic bag-of-bits type.
  76. content_type = 'application/octet-stream'
  77. maintype, subtype = content_type.split('/', 1)
  78. if maintype == 'text':
  79. # Note: we should handle calculating the charset
  80. part = MIMEText(fcontent, _subtype=subtype)
  81. elif maintype == 'image':
  82. part = MIMEImage(fcontent, _subtype=subtype)
  83. elif maintype == 'audio':
  84. part = MIMEAudio(fcontent, _subtype=subtype)
  85. else:
  86. part = MIMEBase(maintype, subtype)
  87. part.set_payload(fcontent)
  88. # Encode the payload using Base64
  89. from email import encoders
  90. encoders.encode_base64(part)
  91. # Set the filename parameter
  92. if fname:
  93. part.add_header('Content-Disposition', 'attachment', filename=fname)
  94. self.msg_root.attach(part)
  95. def validate(self):
  96. """
  97. validate the email ids
  98. """
  99. if not self.sender:
  100. self.sender = webnotes.conn.get_value('Control Panel',None,'auto_email_id')
  101. from webnotes.utils import validate_email_add
  102. # validate ids
  103. if self.sender and (not validate_email_add(self.sender)):
  104. webnotes.msgprint("%s is not a valid email id" % self.sender, raise_exception = 1)
  105. if self.reply_to and (not validate_email_add(self.reply_to)):
  106. webnotes.msgprint("%s is not a valid email id" % self.reply_to, raise_exception = 1)
  107. for e in self.recipients:
  108. if not validate_email_add(e):
  109. webnotes.msgprint("%s is not a valid email id" % e, raise_exception = 1)
  110. def setup(self):
  111. """
  112. setup the SMTP (outgoing) server from `Control Panel` or defs.py
  113. """
  114. if self.from_defs:
  115. import webnotes
  116. self.server = getattr(webnotes.defs,'mail_server','')
  117. self.login = getattr(webnotes.defs,'mail_login','')
  118. self.port = getattr(webnotes.defs,'mail_port',None)
  119. self.password = getattr(webnotes.defs,'mail_password','')
  120. self.use_ssl = getattr(webnotes.defs,'use_ssl',0)
  121. else:
  122. import webnotes.model.doc
  123. from webnotes.utils import cint
  124. # get defaults from control panel
  125. cp = webnotes.model.doc.Document('Control Panel','Control Panel')
  126. self.server = cp.outgoing_mail_server or getattr(webnotes.defs,'mail_server','')
  127. self.login = cp.mail_login or getattr(webnotes.defs,'mail_login','')
  128. self.port = cp.mail_port or getattr(webnotes.defs,'mail_port',None)
  129. self.password = cp.mail_password or getattr(webnotes.defs,'mail_password','')
  130. self.use_ssl = cint(cp.use_ssl)
  131. def make_msg(self):
  132. self.msg_root['Subject'] = self.subject
  133. self.msg_root['From'] = self.sender
  134. self.msg_root['To'] = ', '.join([r.strip() for r in self.recipients])
  135. if self.reply_to and self.reply_to != self.sender:
  136. self.msg_root['Reply-To'] = self.reply_to
  137. if self.cc:
  138. self.msg_root['CC'] = ', '.join([r.strip() for r in self.cc])
  139. def add_to_queue(self):
  140. # write to a file called "email_queue" or as specified in email
  141. q = EmailQueue()
  142. q.push({
  143. 'server': self.server,
  144. 'port': self.port,
  145. 'use_ssl': self.use_ssl,
  146. 'login': self.login,
  147. 'password': self.password,
  148. 'sender': self.sender,
  149. 'recipients': self.recipients,
  150. 'msg': self.msg_root.as_string()
  151. })
  152. q.close()
  153. def send(self, send_now = 0):
  154. """
  155. send the message
  156. """
  157. from webnotes.utils import cint
  158. self.setup()
  159. self.validate()
  160. self.make_msg()
  161. if (not send_now) and getattr(webnotes.defs, 'batch_emails', 0):
  162. self.add_to_queue()
  163. return
  164. sess = self.smtp_connect()
  165. sess.sendmail(self.sender, self.recipients, self.msg_root.as_string())
  166. try:
  167. sess.quit()
  168. except:
  169. pass
  170. def smtp_connect(self):
  171. """
  172. Gets a smtp connection
  173. """
  174. from webnotes.utils import cint
  175. import smtplib
  176. sess = smtplib.SMTP(self.server.encode('utf-8'), cint(self.port) or None)
  177. if self.use_ssl:
  178. sess.ehlo()
  179. sess.starttls()
  180. sess.ehlo()
  181. ret = sess.login(self.login.encode('utf-8'), self.password.encode('utf-8'))
  182. # check if logged correctly
  183. if ret[0]!=235:
  184. msgprint(ret[1])
  185. raise Exception
  186. return sess
  187. # ===========================================
  188. # Email Queue
  189. # Maintains a list of emails in a file
  190. # Flushes them when called from cron
  191. # Defs settings:
  192. # email_queue: (filename) [default: email_queue.py]
  193. #
  194. # From the scheduler, call: flush(qty)
  195. # ===========================================
  196. class EmailQueue:
  197. def __init__(self):
  198. self.server = self.login = self.sess = None
  199. self.filename = getattr(webnotes.defs, 'email_queue', 'email_queue.py')
  200. try:
  201. f = open(self.filename, 'r')
  202. self.queue = eval(f.read() or '[]')
  203. f.close()
  204. except IOError, e:
  205. if e.args[0]==2:
  206. self.queue = []
  207. else:
  208. raise e
  209. def push(self, email):
  210. self.queue.append(email)
  211. def close(self):
  212. f = open(self.filename, 'w')
  213. f.write(str(self.queue))
  214. f.close()
  215. def get_smtp_session(self, e):
  216. if self.server==e['server'] and self.login==e['login'] and self.sess:
  217. return self.sess
  218. webnotes.msgprint('getting server')
  219. import smtplib
  220. sess = smtplib.SMTP(e['server'], e['port'] or None)
  221. if self.use_ssl:
  222. sess.ehlo()
  223. sess.starttls()
  224. sess.ehlo()
  225. ret = sess.login(e['login'], e['password'])
  226. # check if logged correctly
  227. if ret[0]!=235:
  228. webnotes.msgprint(ret[1])
  229. raise Exception
  230. self.sess = sess
  231. self.server, self.login = e['server'], e['login']
  232. return sess
  233. def flush(self, qty = 100):
  234. f = open(self.filename, 'r')
  235. self.queue = eval(f.read() or '[]')
  236. if len(self.queue) < 100:
  237. qty = len(self.queue)
  238. for i in range(qty):
  239. e = self.queue[i]
  240. sess = self.get_smtp_session(e)
  241. sess.sendmail(e['sender'], e['recipients'], e['msg'])
  242. self.queue = self.queue[:(len(self.queue) - qty)]
  243. self.close()