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.

278 lines
11 KiB

  1. # -*- encoding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # OpenERP, Open Source Management Solution
  5. # This module copyright (C) 2013 Therp BV (<http://therp.nl>)
  6. # All Rights Reserved
  7. #
  8. # This program is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU Affero General Public License as
  10. # published by the Free Software Foundation, either version 3 of the
  11. # License, or (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU Affero General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Affero General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. #
  21. ##############################################################################
  22. import base64
  23. import simplejson
  24. from lxml import etree
  25. from openerp.osv.orm import Model, except_orm
  26. from openerp.tools.translate import _
  27. from openerp.osv import fields
  28. from openerp.addons.fetchmail.fetchmail import _logger as logger
  29. from openerp.tools.misc import UnquoteEvalContext
  30. class fetchmail_server(Model):
  31. _inherit = 'fetchmail.server'
  32. _columns = {
  33. 'folder_ids': fields.one2many(
  34. 'fetchmail.server.folder', 'server_id', 'Folders'),
  35. }
  36. _defaults = {
  37. 'type': 'imap',
  38. }
  39. def __init__(self, pool, cr):
  40. self._columns['object_id'].required = False
  41. return super(fetchmail_server, self).__init__(pool, cr)
  42. def onchange_server_type(
  43. self, cr, uid, ids, server_type=False, ssl=False,
  44. object_id=False):
  45. retval = super(
  46. fetchmail_server, self).onchange_server_type(cr, uid,
  47. ids, server_type, ssl,
  48. object_id)
  49. retval['value']['state'] = 'draft'
  50. return retval
  51. def fetch_mail(self, cr, uid, ids, context=None):
  52. if context is None:
  53. context = {}
  54. check_original = []
  55. for this in self.browse(cr, uid, ids, context):
  56. if this.object_id:
  57. check_original.append(this.id)
  58. context.update(
  59. {
  60. 'fetchmail_server_id': this.id,
  61. 'server_type': this.type
  62. })
  63. connection = this.connect()
  64. for folder in this.folder_ids:
  65. this.handle_folder(connection, folder)
  66. connection.close()
  67. return super(fetchmail_server, self).fetch_mail(
  68. cr, uid, check_original, context)
  69. def handle_folder(self, cr, uid, ids, connection, folder, context=None):
  70. '''Return ids of objects matched'''
  71. matched_object_ids = []
  72. for this in self.browse(cr, uid, ids, context=context):
  73. logger.info('start checking for emails in %s server %s',
  74. folder.path, this.name)
  75. match_algorithm = folder.get_algorithm()
  76. if connection.select(folder.path)[0] != 'OK':
  77. logger.error(
  78. 'Could not open mailbox %s on %s' % (folder.path, this.server))
  79. connection.select()
  80. continue
  81. result, msgids = this.get_msgids(connection)
  82. if result != 'OK':
  83. logger.error(
  84. 'Could not search mailbox %s on %s' % (
  85. folder.path, this.server))
  86. continue
  87. for msgid in msgids[0].split():
  88. matched_object_ids += this.apply_matching(
  89. connection, folder, msgid, match_algorithm)
  90. logger.info('finished checking for emails in %s server %s',
  91. folder.path, this.name)
  92. return matched_object_ids
  93. def get_msgids(self, cr, uid, ids, connection, context=None):
  94. '''Return imap ids of messages to process'''
  95. return connection.search(None, 'UNDELETED')
  96. def apply_matching(self, cr, uid, ids, connection, folder, msgid,
  97. match_algorithm, context=None):
  98. '''Return ids of objects matched'''
  99. matched_object_ids = []
  100. for this in self.browse(cr, uid, ids, context=context):
  101. result, msgdata = connection.fetch(msgid, '(RFC822)')
  102. if result != 'OK':
  103. logger.error(
  104. 'Could not fetch %s in %s on %s' % (msgid, folder.path, this.server))
  105. continue
  106. mail_message = self.pool.get('mail.thread').message_parse(
  107. cr, uid, msgdata[0][1], save_original=this.original,
  108. context=context)
  109. if self.pool.get('mail.message').search(
  110. cr, uid, [
  111. ('message_id', '=', mail_message['message_id'])]):
  112. continue
  113. found_ids = match_algorithm.search_matches(
  114. cr, uid, folder,
  115. mail_message, msgdata[0][1])
  116. if found_ids and (len(found_ids) == 1 or
  117. folder.match_first):
  118. try:
  119. cr.execute('savepoint apply_matching')
  120. match_algorithm.handle_match(
  121. cr, uid, connection,
  122. found_ids[0], folder, mail_message,
  123. msgdata[0][1], msgid, context)
  124. cr.execute('release savepoint apply_matching')
  125. matched_object_ids += found_ids[:1]
  126. except Exception:
  127. cr.execute('rollback to savepoint apply_matching')
  128. logger.exception(
  129. "Failed to fetch mail %s from %s",
  130. msgid, this.name)
  131. elif folder.flag_nonmatching:
  132. connection.store(msgid, '+FLAGS', '\\FLAGGED')
  133. return matched_object_ids
  134. def attach_mail(
  135. self, cr, uid, ids, connection, object_id, folder,
  136. mail_message, msgid, context=None):
  137. '''Return ids of messages created'''
  138. mail_message_ids = []
  139. for this in self.browse(cr, uid, ids, context):
  140. partner_id = None
  141. if folder.model_id.model == 'res.partner':
  142. partner_id = object_id
  143. if 'partner_id' in self.pool.get(folder.model_id.model)._columns:
  144. partner_id = self.pool.get(
  145. folder.model_id.model).browse(
  146. cr, uid, object_id, context
  147. ).partner_id.id
  148. attachments = []
  149. if this.attach and mail_message.get('attachments'):
  150. for attachment in mail_message['attachments']:
  151. fname, fcontent = attachment
  152. if isinstance(fcontent, unicode):
  153. fcontent = fcontent.encode('utf-8')
  154. data_attach = {
  155. 'name': fname,
  156. 'datas': base64.b64encode(str(fcontent)),
  157. 'datas_fname': fname,
  158. 'description': _('Mail attachment'),
  159. 'res_model': folder.model_id.model,
  160. 'res_id': object_id,
  161. }
  162. attachments.append(
  163. self.pool.get('ir.attachment').create(
  164. cr, uid, data_attach, context=context))
  165. mail_message_ids.append(
  166. self.pool.get('mail.message').create(
  167. cr, uid,
  168. {
  169. 'author_id': partner_id,
  170. 'model': folder.model_id.model,
  171. 'res_id': object_id,
  172. 'type': 'email',
  173. 'body': mail_message.get('body'),
  174. 'subject': mail_message.get('subject'),
  175. 'email_from': mail_message.get('from'),
  176. 'date': mail_message.get('date'),
  177. 'message_id': mail_message.get('message_id'),
  178. 'attachment_ids': [(6, 0, attachments)],
  179. },
  180. context))
  181. if folder.delete_matching:
  182. connection.store(msgid, '+FLAGS', '\\DELETED')
  183. return mail_message_ids
  184. def button_confirm_login(self, cr, uid, ids, context=None):
  185. retval = super(fetchmail_server, self).button_confirm_login(cr, uid,
  186. ids,
  187. context)
  188. for this in self.browse(cr, uid, ids, context):
  189. this.write({'state': 'draft'})
  190. connection = this.connect()
  191. connection.select()
  192. for folder in this.folder_ids:
  193. if connection.select(folder.path)[0] != 'OK':
  194. raise except_orm(
  195. _('Error'), _('Mailbox %s not found!') %
  196. folder.path)
  197. connection.close()
  198. this.write({'state': 'done'})
  199. return retval
  200. def fields_view_get(self, cr, user, view_id=None, view_type='form',
  201. context=None, toolbar=False, submenu=False):
  202. result = super(fetchmail_server, self).fields_view_get(
  203. cr, user, view_id, view_type, context, toolbar, submenu)
  204. if view_type == 'form':
  205. view = etree.fromstring(
  206. result['fields']['folder_ids']['views']['form']['arch'])
  207. modifiers = {}
  208. docstr = ''
  209. for algorithm in self.pool.get('fetchmail.server.folder')\
  210. ._get_match_algorithms().itervalues():
  211. for modifier in ['required', 'readonly']:
  212. for field in getattr(algorithm, modifier + '_fields'):
  213. modifiers.setdefault(field, {})
  214. modifiers[field].setdefault(modifier, [])
  215. if modifiers[field][modifier]:
  216. modifiers[field][modifier].insert(0, '|')
  217. modifiers[field][modifier].append(
  218. ("match_algorithm", "==", algorithm.__name__))
  219. docstr += _(algorithm.name) + '\n' + _(algorithm.__doc__) + \
  220. '\n\n'
  221. for field in view:
  222. if field.tag == 'field' and field.get('name') in modifiers:
  223. field.set('modifiers', simplejson.dumps(
  224. dict(
  225. eval(field.attrib['modifiers'],
  226. UnquoteEvalContext({})),
  227. **modifiers[field.attrib['name']])))
  228. if (field.tag == 'field' and
  229. field.get('name') == 'match_algorithm'):
  230. field.set('help', docstr)
  231. result['fields']['folder_ids']['views']['form']['arch'] = \
  232. etree.tostring(view)
  233. return result