[REM] mail: remove debugging statement
[odoo/odoo.git] / addons / mail / email_smtp_server.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2011 Tiny SPRL (<http://tiny.be>)
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>
19 #
20 ##############################################################################
21
22 from osv import osv
23 from osv import fields
24 from tools.translate import _
25 import tools
26
27 #import time
28 #import binascii
29 #import email
30 #from email.header import decode_header
31 #from email.utils import parsedate
32 #import base64
33 #import re
34 #import logging
35 #import xmlrpclib
36
37 #_logger = logging.getLogger('mailgate')
38
39 #import re
40 import smtplib
41 #import base64
42 #from email import Encoders
43 #from email.mime.base import MIMEBase
44 #from email.mime.multipart import MIMEMultipart
45 #from email.mime.text import MIMEText
46 #from email.header import decode_header, Header
47 #from email.utils import formatdate
48 #import netsvc
49 #import datetime
50 #import tools
51 #import logging
52
53 #EMAIL_PATTERN = re.compile(r'([^()\[\] ,<:\\>@";]+@[^()\[\] ,<:\\>@";]+)') # See RFC822
54 #def extract_emails(emails_str):
55 #    """
56 #    Returns a list of email addresses recognized in a string, ignoring the rest of the string.
57 #    extract_emails('a@b.com,c@bcom, "John Doe" <d@b.com> , e@b.com') -> ['a@b.com','c@bcom', 'd@b.com', 'e@b.com']"
58 #    """
59 #    return EMAIL_PATTERN.findall(emails_str)
60 #
61 #
62 #def extract_emails_from_dict(addresses={}):
63 #    """
64 #    Extracts email addresses from a dictionary with comma-separated address string values, handling
65 #    separately the To, CC, BCC and Reply-To addresses.
66 #
67 #    :param addresses: a dictionary of addresses in the form {'To': 'a@b.com,c@bcom; d@b.com;e@b.com' , 'CC': 'e@b.com;f@b.com', ... }
68 #    :return: a dictionary with a list of separate addresses for each header (To, CC, BCC), with an additional key 'all-recipients'
69 #             containing all addresses for the 'To', 'CC', 'BCC' entries.
70 #    """
71 #    result = {'all-recipients':[]}
72 #    keys = ['To', 'CC', 'BCC', 'Reply-To']
73 #    for each in keys:
74 #        emails = extract_emails(addresses.get(each, u''))
75 #        while u'' in emails:
76 #            emails.remove(u'')
77 #        result[each] = emails
78 #        if each != 'Reply-To':
79 #            result['all-recipients'].extend(emails)
80 #    return result
81
82 class email_smtp_server(osv.osv):
83     """
84     SMTP Server
85     """
86     _name = "email.smtp_server"
87
88     _columns = {
89         'name': fields.char('Name',
90                         size=64, required=True,
91                         select=True,
92                         help="The Name is used as the Sender name along with the provided From Email, \
93 unless it is already specified in the From Email, e.g: John Doe <john@doe.com>",
94                         ),
95         'email_id': fields.char('From Email',
96                         size=120, required=True,
97                         help="eg: 'john@doe.com' or 'John Doe <john@doe.com>'"),
98         'smtpserver': fields.char('Server',
99                         size=120, required=True,
100                         help="Enter name of outgoing server, eg: smtp.yourdomain.com"),
101         'smtpport': fields.integer('SMTP Port',
102                         size=64, required=True,
103                         help="Enter port number, eg: 25 or 587"),
104         'smtpuname': fields.char('User Name',
105                         size=120, required=False,
106                         help="Specify the username if your SMTP server requires authentication, "
107                         "otherwise leave it empty."),
108         'smtppass': fields.char('Password',
109                         size=120, 
110                         required=False),
111         'smtptls':fields.boolean('TLS'),
112         'smtpssl':fields.boolean('SSL/TLS'),
113         'default': fields.boolean('Default', help="Only one account can be default at a time"),
114     }
115
116     _defaults = {
117          'name':lambda self, cursor, user, context:self.pool.get( 'res.users'
118                                                 ).read(cursor, user, user, ['name'], context)['name'],
119          'smtpport': tools.config.get('smtp_port',25),
120          'smtpserver': tools.config.get('smtp_server','localhost'),
121          'smtpssl': tools.config.get('smtp_ssl',False),
122          'smtptls': True,
123      }
124
125     _sql_constraints = [
126         (
127          'email_uniq',
128          'unique (email_id)',
129          'Another setting already exists with this email ID !')
130     ]
131
132     def _constraint_unique(self, cr, uid, ids, context=None):
133         default_ids = self.search(cr, uid, [('default','=',True)])
134         if len(default_ids) > 1:
135             return False
136         
137         return True
138
139     _constraints = [
140         (_constraint_unique,
141          'Error: You must be define one default smtp server account !.',
142          [])
143     ]
144
145     def name_get(self, cr, uid, ids, context=None):
146         return [(a["id"], "%s (%s)" % (a['email_id'], a['name'])) for a in self.read(cr, uid, ids, ['name', 'email_id'], context=context)]
147
148     
149
150     def test_smtp_connection(self, cr, uid, ids, context=None):
151         """
152         Test SMTP connection works
153         """
154         try:
155             for smtp_server in self.browse(cr, uid, ids, context=context):
156                 smtp = tools.connect_smtp_server(smtp_server.smtpserver, smtp_server.smtpport,  user_name=smtp_server.smtpuname, 
157                                 user_password=smtp_server.smtppass, ssl=smtp_server.smtpssl, tls=smtp_server.smtptls)
158                 try:
159                     smtp.quit()
160                 except Exception:
161                     # ignored, just a consequence of the previous exception
162                     pass
163         except Exception, error:
164             raise osv.except_osv(
165                                  _("SMTP Connection: Test failed"),
166                                  _("Reason: %s") % error
167                                  )
168
169         raise osv.except_osv(_("SMTP Connection: Test Successfully!"), '')
170
171 #    def do_approval(self, cr, uid, ids, context=None):
172 #        #TODO: Check if user has rights
173 #        self.write(cr, uid, ids, {'state':'approved'}, context=context)
174 ##        wf_service = netsvc.LocalService("workflow")
175 #
176 #    def smtp_connection(self, cursor, user, id, context=None):
177 #        """
178 #        This method should now wrap smtp_connection
179 #        """
180 #        #This function returns a SMTP server object
181 #        logger = netsvc.Logger()
182 #        core_obj = self.browse(cursor, user, id, context=context)
183 #        if core_obj.smtpserver and core_obj.smtpport and core_obj.state == 'approved':
184 #            try:
185 #                serv = self.get_outgoing_server(cursor, user, id, context)
186 #            except Exception, error:
187 #                logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed on login. Probable Reason:Could not login to server\nError: %s") % (id, error))
188 #                return False
189 #            #Everything is complete, now return the connection
190 #            return serv
191 #        else:
192 #            logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id)
193 #            return False
194 #
195 ##**************************** MAIL SENDING FEATURES ***********************#
196 #    def send_email(self, cr, uid, ids, addresses, subject='', body=None, payload=None, message_id=None, context=None):
197 #        #TODO: Replace all this with a single email object
198 #        if body is None:
199 #            body = {}
200 #        if payload is None:
201 #            payload = {}
202 #        if context is None:
203 #            context = {}
204 #        logger = netsvc.Logger()
205 #        for id in ids:
206 #            core_obj = self.browse(cr, uid, id, context)
207 #            serv = self.smtp_connection(cr, uid, id)
208 #            if serv:
209 #                try:
210 #                    # Need a multipart/mixed wrapper for attachments if content is alternative
211 #                    if payload:
212 #                        payload_part = MIMEMultipart(_subtype='mixed')
213 #                        text_part = MIMEMultipart(_subtype='mixed')
214 #                        payload_part.attach(text_part)
215 #                    else:
216 #                        # otherwise a single multipart/mixed will do the whole job
217 #                        payload_part = text_part = MIMEMultipart(_subtype='mixed')
218 #
219 #                    if subject:
220 #                        payload_part['Subject'] = subject
221 #                    from_email = core_obj.email_id
222 #                    if '<' in from_email:
223 #                        # We have a structured email address, keep it untouched
224 #                        payload_part['From'] = Header(core_obj.email_id, 'utf-8').encode()
225 #                    else:
226 #                        # Plain email address, construct a structured one based on the name:
227 #                        sender_name = Header(core_obj.name, 'utf-8').encode()
228 #                        payload_part['From'] = sender_name + " <" + core_obj.email_id + ">"
229 #                    payload_part['Organization'] = tools.ustr(core_obj.user.company_id.name)
230 #                    payload_part['Date'] = formatdate()
231 #                    addresses_l = extract_emails_from_dict(addresses)
232 #                    if addresses_l['To']:
233 #                        payload_part['To'] = u','.join(addresses_l['To'])
234 #                    if addresses_l['CC']:
235 #                        payload_part['CC'] = u','.join(addresses_l['CC'])
236 #                    if addresses_l['Reply-To']:
237 #                        payload_part['Reply-To'] = addresses_l['Reply-To'][0]
238 #                    if message_id:
239 #                        payload_part['Message-ID'] = message_id
240 #                    if body.get('text', False):
241 #                        temp_body_text = body.get('text', '')
242 #                        l = len(temp_body_text.replace(' ', '').replace('\r', '').replace('\n', ''))
243 #                        if l == 0:
244 #                            body['text'] = u'No Mail Message'
245 #                    # Attach parts into message container.
246 #                    # According to RFC 2046, the last part of a multipart message, in this case
247 #                    # the HTML message, is best and preferred.
248 ##                    if core_obj.send_pref in ('text', 'mixed', 'alternative'):
249 ##                        body_text = body.get('text', u'<Empty Message>')
250 ##                        body_text = tools.ustr(body_text)
251 ##                        text_part.attach(MIMEText(body_text.encode("utf-8"), _charset='UTF-8'))
252 ##                    if core_obj.send_pref in ('html', 'mixed', 'alternative'):
253 #                    html_body = body.get('html', u'')
254 #                    if len(html_body) == 0 or html_body == u'':
255 #                        html_body = body.get('text', u'<p>&lt;Empty Message&gt;</p>').replace('\n', '<br/>').replace('\r', '<br/>')
256 #                    html_body = tools.ustr(html_body)
257 #                    text_part.attach(MIMEText(html_body.encode("utf-8"), _subtype='html', _charset='UTF-8'))
258 #
259 #                    #Now add attachments if any, wrapping into a container multipart/mixed if needed
260 #                    if payload:
261 #                        for file in payload:
262 #                            part = MIMEBase('application', "octet-stream")
263 #                            part.set_payload(base64.decodestring(payload[file]))
264 #                            part.add_header('Content-Disposition', 'attachment; filename="%s"' % file)
265 #                            Encoders.encode_base64(part)
266 #                            payload_part.attach(part)
267 #                except Exception, error:
268 #                    logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:MIME Error\nDescription: %s") % (id, error))
269 #                    return {'error_msg': _("Server Send Error\nDescription: %s")%error}
270 #                try:
271 #                    serv.sendmail(payload_part['From'], addresses_l['all-recipients'], payload_part.as_string())
272 #                except Exception, error:
273 #                    logging.getLogger('email_template').error(_("Mail from Account %s failed. Probable Reason: Server Send Error\n Description: %s"), id, error, exc_info=True)
274 #                    return {'error_msg': _("Server Send Error\nDescription: %s")%error}
275 #                #The mail sending is complete
276 #                serv.close()
277 #                logger.notifyChannel(_("Email Template"), netsvc.LOG_INFO, _("Mail from Account %s successfully Sent.") % (id))
278 #                return True
279 #            else:
280 #                logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id)
281 #                return {'nodestroy':True,'error_msg': _("Mail from Account %s failed. Probable Reason:Account not approved")% id}
282 #
283 #    def extracttime(self, time_as_string):
284 #        """
285 #        TODO: DOC THis
286 #        """
287 #        logger = netsvc.Logger()
288 #        #The standard email dates are of format similar to:
289 #        #Thu, 8 Oct 2009 09:35:42 +0200
290 #        date_as_date = False
291 #        convertor = {'+':1, '-':-1}
292 #        try:
293 #            time_as_string = time_as_string.replace(',', '')
294 #            date_list = time_as_string.split(' ')
295 #            date_temp_str = ' '.join(date_list[1:5])
296 #            if len(date_list) >= 6:
297 #                sign = convertor.get(date_list[5][0], False)
298 #            else:
299 #                sign = False
300 #            try:
301 #                dt = datetime.datetime.strptime(
302 #                                            date_temp_str,
303 #                                            "%d %b %Y %H:%M:%S")
304 #            except:
305 #                try:
306 #                    dt = datetime.datetime.strptime(
307 #                                            date_temp_str,
308 #                                            "%d %b %Y %H:%M")
309 #                except:
310 #                    return False
311 #            if sign:
312 #                try:
313 #                    offset = datetime.timedelta(
314 #                                hours=sign * int(
315 #                                             date_list[5][1:3]
316 #                                                ),
317 #                                             minutes=sign * int(
318 #                                                            date_list[5][3:5]
319 #                                                                )
320 #                                                )
321 #                except Exception, e2:
322 #                    """Looks like UT or GMT, just forget decoding"""
323 #                    return False
324 #            else:
325 #                offset = datetime.timedelta(hours=0)
326 #            dt = dt + offset
327 #            date_as_date = dt.strftime('%Y-%m-%d %H:%M:%S')
328 #        except Exception, e:
329 #            logger.notifyChannel(
330 #                    _("Email Template"),
331 #                    netsvc.LOG_WARNING,
332 #                    _(
333 #                      "Datetime Extraction failed.Date:%s \
334 #                      \tError:%s") % (
335 #                                    time_as_string,
336 #                                    e)
337 #                      )
338 #        return date_as_date
339 #
340 #    def send_receive(self, cr, uid, ids, context=None):
341 #        for id in ids:
342 #            ctx = context.copy()
343 #            ctx['filters'] = [('account_id', '=', id)]
344 #            self.pool.get('email.message').send_all_mail(cr, uid, [], context=ctx)
345 #        return True
346 #
347 #    def decode_header_text(self, text):
348 #        """ Decode internationalized headers RFC2822.
349 #            To, CC, BCC, Subject fields can contain
350 #            text slices with different encodes, like:
351 #                =?iso-8859-1?Q?Enric_Mart=ED?= <enricmarti@company.com>,
352 #                =?Windows-1252?Q?David_G=F3mez?= <david@company.com>
353 #            Sometimes they include extra " character at the beginning/
354 #            end of the contact name, like:
355 #                "=?iso-8859-1?Q?Enric_Mart=ED?=" <enricmarti@company.com>
356 #            and decode_header() does not work well, so we use regular
357 #            expressions (?=   ? ?   ?=) to split the text slices
358 #        """
359 #        if not text:
360 #            return text
361 #        p = re.compile("(=\?.*?\?.\?.*?\?=)")
362 #        text2 = ''
363 #        try:
364 #            for t2 in p.split(text):
365 #                text2 += ''.join(
366 #                            [s.decode(
367 #                                      t or 'ascii'
368 #                                    ) for (s, t) in decode_header(t2)]
369 #                                ).encode('utf-8')
370 #        except:
371 #            return text
372 #        return text2
373
374 email_smtp_server()
375
376 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: