You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
5.6 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. # -*- coding: utf-8 -*-
  2. ###################################################################################
  3. #
  4. # Copyright (C) 2017 MuK IT GmbH
  5. #
  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.
  10. #
  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.
  15. #
  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/>.
  18. #
  19. ###################################################################################
  20. import os
  21. import json
  22. import email
  23. import base64
  24. import urllib
  25. import logging
  26. import mimetypes
  27. import collections
  28. import werkzeug.exceptions
  29. from urllib import parse
  30. from odoo import _
  31. from odoo import tools
  32. from odoo import http
  33. from odoo.http import request
  34. from odoo.http import Response
  35. _logger = logging.getLogger(__name__)
  36. try:
  37. import requests
  38. except ImportError:
  39. _logger.warn('Cannot `import requests`.')
  40. try:
  41. from cachetools import TTLCache
  42. mail_cache = TTLCache(maxsize=50, ttl=600)
  43. except ImportError:
  44. _logger.warn('Cannot `import cachetools`.')
  45. class MailParserController(http.Controller):
  46. _Attachment = collections.namedtuple('Attachment', 'name mimetype extension url info')
  47. @http.route('/web/preview/converter/mail', auth="user", type='http')
  48. def parse_mail(self, url, attachment=None, force_compute=False, **kw):
  49. try:
  50. message = mail_cache[url] if not force_compute else None
  51. except KeyError:
  52. message = None
  53. if not message:
  54. if not bool(parse.urlparse(url).netloc):
  55. url_parts = url.split('?')
  56. path = url_parts[0]
  57. query_string = url_parts[1] if len(url_parts) > 1 else None
  58. router = request.httprequest.app.get_db_router(request.db).bind('')
  59. match = router.match(path, query_args=query_string)
  60. method = router.match(path, query_args=query_string)[0]
  61. params = dict(parse.parse_qsl(query_string))
  62. if len(match) > 1:
  63. params.update(match[1])
  64. response = method(**params)
  65. if not response.status_code == 200:
  66. return self._make_error_response(response.status_code,response.description if hasattr(response, 'description') else _("Unknown Error"))
  67. else:
  68. if response.headers['content-type'] == 'message/rfc822':
  69. message = request.env['mail.thread'].message_parse(response.data, False)
  70. else:
  71. return werkzeug.exceptions.UnsupportedMediaType(_("Unparsable message! The file has to be of type: message/rfc822"))
  72. else:
  73. try:
  74. response = requests.get(url)
  75. if response.headers['content-type'] == 'message/rfc822':
  76. message = request.env['mail.thread'].message_parse(response.content, False)
  77. else:
  78. return werkzeug.exceptions.UnsupportedMediaType(_("Unparsable message! The file has to be of type: message/rfc822"))
  79. except requests.exceptions.RequestException as exception:
  80. return self._make_error_response(exception.response.status_code, exception.response.reason or _("Unknown Error"))
  81. mail_cache[url] = message
  82. return self._make_parse_response(request.httprequest.url, message.copy(), attachment)
  83. def _set_query_parameter(self, url, param_name, param_value):
  84. scheme, netloc, path, query_string, fragment = parse.urlsplit(url)
  85. query_params = parse.parse_qs(query_string)
  86. query_params[param_name] = [param_value]
  87. new_query_string = urllib.parse.urlencode(query_params, doseq=True)
  88. return parse.urlunsplit((scheme, netloc, path, new_query_string, fragment))
  89. def _make_error_response(self, status, message):
  90. exception = werkzeug.exceptions.HTTPException()
  91. exception.code = status
  92. exception.description = message
  93. return exception
  94. def _make_attachment_response(self, file, filename):
  95. headers = [('Content-Type', mimetypes.guess_type(urllib.request.pathname2url(filename))[0]),
  96. ('Content-Disposition', 'attachment; filename="{}";'.format(filename)),
  97. ('Content-Length', len(file))]
  98. return request.make_response(file, headers)
  99. def _make_parse_response(self, url, message, attachment):
  100. if attachment:
  101. for file in message["attachments"]:
  102. if file.fname and file.fname == attachment:
  103. return self._make_attachment_response(file.content, file.fname)
  104. else:
  105. attachments = []
  106. for file in message["attachments"]:
  107. mimetype = mimetypes.guess_type(urllib.request.pathname2url(file.fname))[0]
  108. extension = os.path.splitext(file.fname)[1]
  109. link = self._set_query_parameter(url, "attachment", file.fname)
  110. attachments.append(self._Attachment(file.fname, mimetype, extension, link, file.info))
  111. message["attachments"] = attachments
  112. return Response(json.dumps(message), content_type='application/json;charset=utf-8', status=200)