1 ##############################################################################
3 # OpenERP, Open Source Management Solution
4 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as
8 # published by the Free Software Foundation, either version 3 of the
9 # License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 ##############################################################################
26 from datetime import datetime
27 from datetime import timedelta
39 from email import Encoders
40 from optparse import OptionParser
41 from email.Message import Message
42 from email.MIMEBase import MIMEBase
43 from email.MIMEText import MIMEText
44 from email.MIMEMultipart import MIMEMultipart
45 from email.Utils import COMMASPACE, formatdate
51 from osv import fields
53 from tools.translate import _
56 'not_active' : "Please activate Email Server, without activating you can not send Email(s).",
57 'server_stop' : 'Please start Email Server, without starting you can not send Email(s).',
58 'server_not_confirm' : 'Please Verify Email Server, without verifying you can not send Email(s).'
61 logger = netsvc.Logger()
63 class smtpclient(osv.osv):
65 _name = 'email.smtpclient'
66 _description = 'Email Client'
69 'name' : fields.char('Server Name', size=256, required=True),
70 'from_email' : fields.char('Email From', size=256),
71 'email' : fields.char('Email Address', size=256, required=True, readonly=True, states={'new':[('readonly',False)]}),
72 'user' : fields.char('User Name', size=256, readonly=True, states={'new':[('readonly',False)]}),
73 'password' : fields.char('Password', size=1024, invisible=True, readonly=True, states={'new':[('readonly',False)]}),
74 'server' : fields.char('SMTP Server', size=256, required=True, readonly=True, states={'new':[('readonly',False)]}),
75 'auth' : fields.boolean("Use Auth", readonly=True, states={'new':[('readonly',False)]}),
76 'port' : fields.char('SMTP Port', size=256, required=True, readonly=True, states={'new':[('readonly',False)]}),
77 'ssl' : fields.boolean("Use SSL?", readonly=True, states={'new':[('readonly',False)]}),
78 'users_id': fields.many2many('res.users', 'res_smtpserver_group_rel', 'sid', 'uid', 'Users Allowed'),
79 'state': fields.selection([
80 ('new','Not Verified'),
81 ('waiting','Waiting for Verification'),
82 ('confirm','Verified'),
83 ],'Server Status', select=True, readonly=True),
84 'auth_type':fields.selection([('gmail','Google Server'), ('yahoo','Yahoo!!! Server'), ('unknown','Other Mail Servers')], string="Server Type", readonly=True, states={'new':[('readonly',False)]}),
85 'active' : fields.boolean("Active"),
86 'date_create': fields.date('Date Create', required=True, readonly=True),
87 'test_email' : fields.text('Test Message', translate=True),
88 'body' : fields.text('Message', translate=True, help="The message text that will be send along with the email which is send through this server"),
89 'verify_email' : fields.text('Verify Message', translate=True, readonly=True, states={'new':[('readonly',False)]}),
90 'code' : fields.char('Verification Code', size=1024),
91 'type' : fields.selection([("default", "Default"),("account", "Account"),("sale","Sale"),("stock","Stock")], "Server Type",required=True),
92 'history_line': fields.one2many('email.smtpclient.history', 'server_id', 'History'),
93 'server_statistics': fields.one2many('report.smtp.server', 'server_id', 'Statistics'),
94 'delete_queue': fields.selection([
95 ('never','Never Delete Message'),
96 ('content','Delete Content After'),
97 ('all','Clear All After'),
98 ('after_send','Delete when Email Sent'),
99 ],'Queue Option', select=True),
100 'priority': fields.integer('Server Priority', readonly=True, states={'new':[('readonly',False)]}, help="Priority between 0 to 10, will be used to define the MTA process priotiry"),
101 'header_ids':fields.one2many('email.headers', 'server_id', 'Default Headers'),
102 'disclaimers': fields.text('Disclaimers'),
103 'process_id': fields.many2one('ir.cron', 'MTA Process', readonly=True, help="Mail Transport Agent Process"),
104 'pstate': fields.selection([
105 ('running','Running'),
107 ],'Server Statue', select=True, readonly=True),
108 'delete_queue_period': fields.integer('Delete after', help="delete emails/contents from email queue after specified no of days"),
111 def _get_users(self, cr, uid, context={}):
112 return self.pool.get('res.users').search(cr, uid, [])
115 'date_create': lambda *a: time.strftime('%Y-%m-%d'),
116 'state': lambda *a: 'new',
117 'type': lambda *a: 'default',
118 'port': lambda *a: '25',
119 'pstate':lambda *a: 'stop',
120 'priority': lambda *a: 5,
121 'delete_queue_period': lambda *a: 30,
122 'auth': lambda *a: True,
123 'active': lambda *a: True,
124 'delete_queue': lambda *a: 'never',
125 'users_id': _get_users,
126 'verify_email': lambda *a: _("Verification Message. This is the code\n\n__code__\n\nyou must copy in the OpenERP Email Server (Verify Server wizard).\n\nCreated by user __user__"),
132 def create(self, cr, user, vals, context={}):
133 if vals.get('password', False) != False:
134 vals['password'] = base64.b64encode(vals.get('password'))
136 res_id = super(smtpclient, self).create(cr, user, vals, context)
139 def write(self, cr, user, ids, vals, context=None):
141 if vals.get('password', False) != False:
142 for pass_char in vals.get('password'):
148 vals['password'] = base64.b64encode(vals.get('password'))
152 res = super(smtpclient, self).write(cr, user, ids, vals, context)
155 def read(self,cr, uid, ids, fields=None, context=None, load='_classic_read'):
156 def override_password(o):
159 if field == 'password':
160 o[0][field] = '********'
163 result = super(smtpclient, self).read(cr, uid, ids, fields, context, load)
164 result = override_password(result)
167 def change_servertype(self, cr, uid, ids, server):
168 if server == 'gmail':
169 return {'value':{'server':'smtp.gmail.com', 'port':'25', 'ssl':True, 'auth':True}}
170 elif server== 'yahoo':
171 return {'value':{'server':'smtp.mail.yahoo.co.in', 'ssl':False, 'port':'587', 'auth':True}}
173 return {'value':{'server':'localhost', 'port':'25', 'ssl':False, 'auth':False}}
175 def change_email(self, cr, uid, ids, email):
176 email_from = self.pool.get('res.users').browse(cr, uid, uid).name
177 if len(email) > 0 and email.find('@') > -1 and email.index('@') > 0:
178 user = email[0:email.index('@')]
179 return {'value':{'user':user, 'from_email':email_from+' <'+email+'>'}}
181 return {'value':{'user':email, 'from_email':email_from+' <'+email+'>'}}
183 def check_permissions(self, cr, uid, ids):
186 cr.execute('select * from res_smtpserver_group_rel where sid=%s and uid=%s' % (ids[0], uid))
193 def gen_private_key(self, cr, uid, ids):
195 for i in time.strftime('%Y-%m-%d %H:%M:%S'):
197 if ky in (' ', '-', ':'):
198 keys = random.random()
199 key = str(keys).split('.')[1]
204 key = ''.join(new_key)
208 def _set_error(self, cr, uid, server_id, context={}):
209 server_obj = self.browse(cr, uid, server_id)
210 if not server_obj.active:
212 if server_obj.pstate == 'stop' :
214 if server_obj.state != 'confirm':
215 return 'server_not_confirm'
218 def test_verify_email(self, cr, uid, ids, toemail, test=False, code=False):
221 self.open_connection(cr, uid, ids, serverid)
224 if test and self.server[serverid]['state'] == 'confirm':
225 body = self.server[serverid]['test_email'] or ''
227 body = self.server[serverid]['verify_email'] or ''
229 key = self.gen_private_key(cr, uid, ids)
230 #md5(time.strftime('%Y-%m-%d %H:%M:%S') + toemail).hexdigest();
232 body = body.replace("__code__", key)
234 user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
235 body = body.replace("__user__", user.name)
237 if len(body.strip()) <= 0:
238 raise osv.except_osv(_('Message Error!'), _('Please configure Email Server Messages [Verification / Test]'))
241 msg = MIMEText(body.encode('utf8') or '',_subtype='plain',_charset='utf-8')
243 msg = MIMEText(body or '',_subtype='plain',_charset='utf-8')
245 if not test and not self.server[serverid]['state'] == 'confirm':
246 msg['Subject'] = _('OpenERP SMTP server Email Registration Code!')
248 msg['Subject'] = _('OpenERP Test Email!')
251 msg['From'] = tools.ustr(self.server[serverid]['from_email'])
253 message = msg.as_string()
255 if self.server[serverid]['disclaimers']:
256 body = body + "\n" + self.server[serverid]['disclaimers']
258 queue = pooler.get_pool(cr.dbname).get('email.smtpclient.queue')
259 queue.create(cr, uid, {
261 'server_id':serverid,
262 'name':msg['Subject'],
264 'serialized_message':message,
269 if self.server[serverid]['state'] != 'confirm':
270 self.write(cr, uid, ids, {'state':'waiting', 'code':key})
274 def getpassword(self, cr, uid, ids):
276 cr.execute("select * from email_smtpclient where id = %s" , (str(ids[0]),))
277 data = cr.dictfetchall()
280 def open_connection(self, cr, uid, ids, serverid=False, permission=True):
282 self.server[serverid] = self.getpassword(cr, uid, [serverid])[0]
284 raise osv.except_osv(_('Read Error!'), _('Unable to read Server Settings'))
287 if not self.check_permissions(cr, uid, [serverid]):
288 raise osv.except_osv(_('Permission Error!'), _('You have no permission to access SMTP Server : %s ') % (self.server[serverid]['name'],) )
290 if self.server[serverid]:
292 self.smtpServer[serverid] = smtplib.SMTP()
293 self.smtpServer[serverid].debuglevel = 0
294 self.smtpServer[serverid].connect(str(self.server[serverid]['server']),str(self.server[serverid]['port']))
296 if self.server[serverid]['ssl']:
297 self.smtpServer[serverid].ehlo()
298 self.smtpServer[serverid].starttls()
299 self.smtpServer[serverid].ehlo()
301 if self.server[serverid]['auth']:
302 password = self.server[serverid]['password']
303 password = base64.b64decode(password)
304 self.smtpServer[serverid].login(str(self.server[serverid]['user']), password)
307 logger.notifyChannel('imap', netsvc.LOG_WARNING, e)
311 def selectAddress(self, cr, uid, partner=None, contact=None, ):
312 email = 'none@none.com'
313 if partner is None and contact is None:
314 return 'none@none.com'
316 if partner is not None and contact is None:
317 pool = self.pool.get('res.partner')
318 data = pool.read(cr, uid, [partner])[0]
320 contact = data['address']
322 if contact is not None:
323 pool = self.pool.get('res.partner.address')
324 data = pool.read(cr, uid, contact)[0]
325 email = data['email']
329 def select(self, cr, uid, type):
330 pool = self.pool.get('email.smtpclient')
331 ids = pool.search(cr, uid, [('type','=',type)], context=False)
333 ids = pool.search(cr, uid, [('type','=','default')], context=False)
340 # Reports is a list of tuples,where first arguement of tuple is the name of the report,second is the list of ids of the object
341 def send_email(self, cr, uid, server_id, emailto, subject, body='', attachments=[], reports=[], ir_attach=[], charset='utf-8', headers={}, context={}):
344 raise osv.except_osv(_('SMTP Data Error !'), _('Email TO Address not Defined !'))
346 def createReport(cr, uid, report, ids, name=False):
350 service = netsvc.LocalService(report)
351 (result, format) = service.create(cr, uid, [id], {}, {})
353 report_file = '/tmp/reports'+ str(id) + '.pdf'
357 fp = open(report_file,'wb+')
360 files += [report_file]
365 smtp_server = self.browse(cr, uid, server_id)
366 if smtp_server.state != 'confirm':
367 raise osv.except_osv(_('SMTP Server Error !'), _('Server is not Verified, Please Verify the Server !'))
370 subject = "OpenERP Email: [Unknown Subject]"
373 subject = subject.encode(charset)
375 subject = subject.decode()
377 #attachment from Reports
380 rpt_file = createReport(cr, uid, rpt[0], rpt[1], rpt[2])
382 rpt_file = createReport(cr, uid, rpt[0], rpt[1])
383 attachments += rpt_file
385 if isinstance(emailto, str) or isinstance(emailto, unicode):
388 ir_pool = self.pool.get('ir.attachment')
391 msg = MIMEMultipart()
392 msg['Subject'] = tools.ustr(subject)
394 msg['From'] = context.get('email_from', smtp_server.from_email)
399 if smtp_server.disclaimers:
400 body = body + "\n" + smtp_server.disclaimers
403 msg.attach(MIMEText(body.encode(charset) or '', _charset=charset, _subtype="html"))
405 msg.attach(MIMEText(body or '', _charset=charset, _subtype="html"))
407 #add custom headers to email
408 for hk in headers.keys():
409 msg[hk] = headers[hk]
411 for hk in smtp_server.header_ids:
412 msg[hk.key] = hk.value
414 context_headers = context.get('headers', [])
415 for hk in context_headers:
416 msg[hk] = context_headers[hk]
418 # Add OpenERP Server information
419 msg['X-Generated-By'] = 'OpenERP (http://www.openerp.com)'
420 msg['X-OpenERP-Server-Host'] = socket.gethostname()
421 msg['X-OpenERP-Server-Version'] = release.version
422 msg['Message-Id'] = "<%s-openerp-@%s>" % (time.time(), socket.gethostname())
424 #attach files from disc
425 for file in attachments:
426 part = MIMEBase('application', "octet-stream")
427 part.set_payload(open(file,"rb").read())
428 Encoders.encode_base64(part)
429 part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file))
432 #attach files from ir_attachments
433 for ath in ir_pool.browse(cr, uid, ir_attach):
434 part = MIMEBase('application', "octet-stream")
435 datas = base64.decodestring(ath.datas)
436 part.set_payload(datas)
437 Encoders.encode_base64(part)
438 part.add_header('Content-Disposition', 'attachment; filename="%s"' %(ath.name))
441 message = msg.as_string()
444 'server_id':server_id,
449 'serialized_message':message,
450 'priority':smtp_server.priority,
452 self.create_queue_enrty(cr, uid, data, context)
456 def create_queue_enrty(self, cr, uid, data, context={}):
457 queue = pooler.get_pool(cr.dbname).get('email.smtpclient.queue')
458 return queue.create(cr, uid, data, context)
460 def _check_history(self, cr, uid, ids=False, context={}):
462 server = self.pool.get('email.smtpclient')
463 queue = self.pool.get('email.smtpclient.queue')
464 sids = self.search(cr, uid, [])
465 for server in self.browse(cr, uid, sids):
466 if server.delete_queue == 'never':
469 now = datetime.today()
470 days = timedelta(days=server.delete_queue_period)
472 kday = day.__str__().split(' ')[0]
474 if server.delete_queue == 'content':
475 qids = queue.search(cr, uid, [('server_id','=',server.id), ('date_create','<=',kday)])
476 queue.write(cr, uid, qids, {'serialized_message':False})
479 if server.delete_queue == 'all':
480 qids = queue.search(cr, uid, [('server_id','=',server.id), ('date_create','<=',kday)])
481 queue.unlink(cr, uid, qids)
485 def _send_emails(self, cr, uid, ids, context={}):
486 queue = self.pool.get('email.smtpclient.queue')
487 history = self.pool.get('email.smtpclient.history')
488 queue.write(cr, uid, ids, {'state':'sending'})
495 for email in queue.browse(cr, uid, ids):
497 if not email.server_id.id in open_server:
498 open_server.append(email.server_id.id)
499 self.open_connection(cr, uid, ids, email.server_id.id)
502 self.smtpServer[email.server_id.id].sendmail(email.server_id.email, email.to, tools.ustr(email.serialized_message))
503 message = "message sent successfully to %s from %s server" % (email.to, email.server_id.name)
504 logger.notifyChannel('smtp', netsvc.LOG_INFO, message)
506 queue.write(cr, uid, [email.id], {'error':e, 'state':'error'})
509 history.create(cr, uid, {
512 'server_id': email.server_id.id,
515 if email.server_id.delete_queue == 'after_send':
516 remove.append(email.id)
518 sent.append(email.id)
520 queue.unlink(cr, uid, remove)
521 queue.write(cr, uid, sent, {'state':'send'})
524 def _check_queue(self, cr, uid, ids=False):
525 queue = self.pool.get('email.smtpclient.queue')
528 sids = queue.search(cr, uid, [('state','not in',['send','sending']), ('type','=','system')], order="priority", limit=30)
531 sids = queue.search(cr, uid, [('state','not in',['send','sending']), ('server_id','in',ids)], order="priority", limit=30)
535 message = "sending %s emails from message queuq !" % (len(ids))
536 logger.notifyChannel('smtp', netsvc.LOG_INFO, message)
538 result = self. _send_emails(cr, uid, sids, {})
541 def set_to_draft(self, cr, uid, ids, context={}):
542 self.write(cr, uid, ids, {'state':'new', 'code':False})
545 def create_process(self, cr, uid, ids, context={}):
546 svr = self.browse(cr, uid, ids[0])
547 if not svr.process_id:
549 'name':'Process : ' + svr.name,
550 'model':'email.smtpclient',
552 'function':'_check_queue',
555 'interval_type':'minutes',
560 id = self.pool.get('ir.cron').create(cr, uid, res)
561 self.write(cr, uid, ids, {'process_id':id})
564 def start_process(self, cr, uid, ids, context={}):
565 process = self.browse(cr, uid, ids[0], context)
566 if not process.process_id:
567 raise osv.except_osv(_('SMTP Server Error !'), _('Server is not Verified, Please Verify the Server !'))
569 pid = process.process_id.id
570 self.pool.get('ir.cron').write(cr, uid, [pid], {'active':True})
571 self.write(cr, uid, ids, {'pstate':'running'})
574 def stop_process(self, cr, uid, ids, context={}):
575 pid = self.browse(cr, uid, ids[0], context).process_id.id
576 self.pool.get('ir.cron').write(cr, uid, [pid], {'active':False})
577 self.write(cr, uid, ids, {'pstate':'stop'})
582 class email_headers(osv.osv):
583 _name = 'email.headers'
584 _description = 'Email Headers'
586 'server_id':fields.many2one('email.smtpclient', 'SMTP Server'),
587 'key':fields.char('Header', size=64, required=True),
588 'value':fields.char('Value', size=1024, required=False),
592 class email_history(osv.osv):
593 _name = 'email.smtpclient.history'
594 _description = 'Email Client History'
598 'name' : fields.text('Description',required=True, readonly=True),
599 'date_create': fields.datetime('Date',readonly=True),
600 'user_id':fields.many2one('res.users', 'Username', readonly=True, select=True),
601 'server_id' : fields.many2one('email.smtpclient', 'Smtp Server', ondelete='set null', readonly=True, required=True),
602 'model':fields.many2one('ir.model', 'Model', readonly=True, select=True),
603 'resource_id':fields.integer('Resource ID', readonly=True),
604 'email':fields.char('Email',size=64,readonly=True),
608 'date_create': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
609 'user_id': lambda obj, cr, uid, context: uid,
612 def create(self, cr, uid, vals, context=None):
613 super(email_history,self).create(cr, uid, vals, context)
617 class message_queue(osv.osv):
618 _name = 'email.smtpclient.queue'
619 _description = 'Email Queue'
622 'to' : fields.char('Mail to', size=1024, readonly=True, states={'draft':[('readonly',False)], 'error':[('readonly',False)]}),
623 'server_id':fields.many2one('email.smtpclient', 'SMTP Server', readonly=True, states={'draft':[('readonly',False)]}),
624 'cc' : fields.char('CC to', size=1024, readonly=True, states={'draft':[('readonly',False)]}),
625 'bcc' : fields.char('BCC to', size=1024, readonly=True, states={'draft':[('readonly',False)]}),
626 'name' : fields.char('Subject', size=1024, readonly=True, states={'draft':[('readonly',False)]}),
627 'body' : fields.text('Email Text', readonly=True, states={'draft':[('readonly',False)]}),
628 'serialized_message':fields.text('Message', readonly=True, states={'draft':[('readonly',False)]}),
629 'state':fields.selection([
631 ('sending','Waiting'),
634 ],'Message Status', select=True, readonly=True),
635 'type':fields.selection([
636 ('default','Default Message'),
637 ('system','System Message'),
638 ],'Message Type', select=True, readonly=True),
639 'error':fields.text('Last Error', size=256, readonly=True, states={'draft':[('readonly',False)]}),
640 'date_create': fields.datetime('Date', readonly=True),
641 'priority':fields.integer('Message Priority', readonly=True),
644 'date_create': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
645 'state': lambda *a: 'draft',
646 'priority': lambda *a: '10',
647 'type': lambda *a: 'default',
651 class report_smtp_server(osv.osv):
652 _name = "report.smtp.server"
653 _description = "Server Statistics"
656 'server_id':fields.many2one('email.smtpclient','Server ID',readonly=True),
657 'name': fields.char('Server',size=64,readonly=True),
658 'history':fields.char('History',size=64, readonly=True),
659 'no':fields.integer('Total No.',readonly=True),
663 create or replace view report_smtp_server as (
664 select min(h.id) as id, c.id as server_id, h.name as history, h.name as name, count(h.name) as no from email_smtpclient c inner join email_smtpclient_history h on c.id=h.server_id group by h.name, c.id
670 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: