diff --git a/addons/account_budget/account_budget.py b/addons/account_budget/account_budget.py index e6fc3a29668..753a5df79f4 100644 --- a/addons/account_budget/account_budget.py +++ b/addons/account_budget/account_budget.py @@ -162,7 +162,7 @@ class crossovered_budget_lines(osv.osv): elapsed = strToDate(date_to) - strToDate(date_to) if total.days: - theo_amt = float(elapsed.days / float(total.days)) * line.planned_amount + theo_amt = float((elapsed.days + 1) / float(total.days + 1)) * line.planned_amount else: theo_amt = line.planned_amount diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index e495f5c9f2f..ce80d0bf4fd 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -92,6 +92,8 @@ class DAVHandler(DAVRequestHandler, HttpOptions, FixSendError): self.client_address = client_address self.server = server self.setup() + if hasattr(self, '_init_buffer'): + self._init_buffer() def get_userinfo(self, user, pw): return False diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 0c5ea5ed65b..43920ad56ce 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -30,6 +30,7 @@ except ImportError: from lxml import etree import logging import pytz +import socket import time import xmlrpclib from email.message import Message @@ -869,25 +870,30 @@ class mail_thread(osv.AbstractModel): # 2. message is a reply to an existign thread (6.1 compatibility) ref_match = thread_references and tools.reference_re.search(thread_references) if ref_match: - thread_id = int(ref_match.group(1)) - model = ref_match.group(2) or fallback_model - if thread_id and model in self.pool: - model_obj = self.pool[model] - compat_mail_msg_ids = mail_msg_obj.search( - cr, uid, [ - ('message_id', '=', False), - ('model', '=', model), - ('res_id', '=', thread_id), - ], context=context) - if compat_mail_msg_ids and model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'): - _logger.info( - 'Routing mail from %s to %s with Message-Id %s: direct thread reply (compat-mode) to model: %s, thread_id: %s, custom_values: %s, uid: %s', - email_from, email_to, message_id, model, thread_id, custom_values, uid) - route = self.message_route_verify( - cr, uid, message, message_dict, - (model, thread_id, custom_values, uid, None), - update_author=True, assert_model=True, create_fallback=True, context=context) - return route and [route] or [] + reply_thread_id = int(ref_match.group(1)) + reply_model = ref_match.group(2) or fallback_model + reply_hostname = ref_match.group(3) + local_hostname = socket.gethostname() + # do not match forwarded emails from another OpenERP system (thread_id collision!) + if local_hostname == reply_hostname: + thread_id, model = reply_thread_id, reply_model + if thread_id and model in self.pool: + model_obj = self.pool[model] + compat_mail_msg_ids = mail_msg_obj.search( + cr, uid, [ + ('message_id', '=', False), + ('model', '=', model), + ('res_id', '=', thread_id), + ], context=context) + if compat_mail_msg_ids and model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'): + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: direct thread reply (compat-mode) to model: %s, thread_id: %s, custom_values: %s, uid: %s', + email_from, email_to, message_id, model, thread_id, custom_values, uid) + route = self.message_route_verify( + cr, uid, message, message_dict, + (model, thread_id, custom_values, uid, None), + update_author=True, assert_model=True, create_fallback=True, context=context) + return route and [route] or [] # 2. Reply to a private message if in_reply_to: diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py index cb735ec098e..3fbf3d40405 100644 --- a/addons/mail/tests/test_mail_gateway.py +++ b/addons/mail/tests/test_mail_gateway.py @@ -21,6 +21,7 @@ from openerp.addons.mail.tests.common import TestMail from openerp.tools import mute_logger +import socket MAIL_TEMPLATE = """Return-Path: To: {to} @@ -400,13 +401,15 @@ class TestMailgateway(TestMail): to='noone@example.com', subject='spam', extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>' % frog_group.id, msg_id='<1.1.JavaMail.new@agrolait.com>') - # There are 6.1 messages, activate compat mode + + # When 6.1 messages are present, compat mode is available + # Create a fake 6.1 message tmp_msg_id = self.mail_message.create(cr, uid, {'message_id': False, 'model': 'mail.group', 'res_id': frog_group.id}) # Do: compat mode accepts partial-matching emails frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other5@gmail.com', msg_id='<1.2.JavaMail.new@agrolait.com>', to='noone@example.com>', subject='spam', - extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>' % frog_group.id) + extra='In-Reply-To: <12321321-openerp-%d-mail.group@%s>' % (frog_group.id, socket.gethostname())) self.mail_message.unlink(cr, uid, [tmp_msg_id]) # Test: no group 'Re: news' created, still only 1 Frogs group self.assertEqual(len(frog_groups), 0, @@ -418,6 +421,17 @@ class TestMailgateway(TestMail): # Test: one new message self.assertEqual(len(frog_group.message_ids), 4, 'message_process: group should contain 4 messages after reply') + # 6.1 compat mode should not work if hostname does not match! + tmp_msg_id = self.mail_message.create(cr, uid, {'message_id': False, 'model': 'mail.group', 'res_id': frog_group.id}) + self.assertRaises(ValueError, + format_and_process, + MAIL_TEMPLATE, email_from='other5@gmail.com', + msg_id='<1.3.JavaMail.new@agrolait.com>', + to='noone@example.com>', subject='spam', + extra='In-Reply-To: <12321321-openerp-%d-mail.group@neighbor.com>' % frog_group.id) + self.mail_message.unlink(cr, uid, [tmp_msg_id]) + + # Do: due to some issue, same email goes back into the mailgateway frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com', msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>', @@ -445,7 +459,7 @@ class TestMailgateway(TestMail): # Do: post a new message, with a known partner -> duplicate emails -> partner format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik ', - to='erroneous@example.com>', subject='Re: news (2)', + subject='Re: news (2)', msg_id='<1198923581.41972151344608186760.JavaMail.new1@agrolait.com>', extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>') frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')]) @@ -456,10 +470,9 @@ class TestMailgateway(TestMail): # Do: post a new message, with a known partner -> duplicate emails -> user frog_group.message_unsubscribe([extra_partner_id]) - raoul_email = self.user_raoul.email self.res_users.write(cr, uid, self.user_raoul_id, {'email': 'test_raoul@email.com'}) format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik ', - to='erroneous@example.com>', subject='Re: news (3)', + to='groups@example.com', subject='Re: news (3)', msg_id='<1198923581.41972151344608186760.JavaMail.new2@agrolait.com>', extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>') frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')]) @@ -474,7 +487,7 @@ class TestMailgateway(TestMail): raoul_email = self.user_raoul.email self.res_users.write(cr, uid, self.user_raoul_id, {'email': 'test_raoul@email.com'}) format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik ', - to='erroneous@example.com>', subject='Re: news (3)', + to='groups@example.com', subject='Re: news (3)', msg_id='<1198923581.41972151344608186760.JavaMail.new3@agrolait.com>', extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>') frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])