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.

131 lines
5.9 KiB

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