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.

289 lines
11 KiB

10 years ago
  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.tools.safe_eval import safe_eval
  28. from openerp.osv import fields
  29. from openerp.addons.fetchmail.fetchmail import _logger as logger
  30. from openerp.tools.misc import UnquoteEvalContext
  31. class fetchmail_server(Model):
  32. _inherit = 'fetchmail.server'
  33. _columns = {
  34. 'folder_ids': fields.one2many(
  35. 'fetchmail.server.folder', 'server_id', 'Folders'),
  36. }
  37. _defaults = {
  38. 'type': 'imap',
  39. }
  40. def __init__(self, pool, cr):
  41. self._columns['object_id'].required = False
  42. super(fetchmail_server, self).__init__(pool, cr)
  43. def onchange_server_type(
  44. self, cr, uid, ids, server_type=False, ssl=False,
  45. object_id=False):
  46. retval = super(
  47. fetchmail_server, self).onchange_server_type(cr, uid,
  48. ids, server_type, ssl,
  49. object_id)
  50. retval['value']['state'] = 'draft'
  51. return retval
  52. def fetch_mail(self, cr, uid, ids, context=None):
  53. if context is None:
  54. context = {}
  55. check_original = []
  56. for this in self.browse(cr, uid, ids, context):
  57. if this.object_id:
  58. check_original.append(this.id)
  59. context.update(
  60. {
  61. 'fetchmail_server_id': this.id,
  62. 'server_type': this.type
  63. })
  64. connection = this.connect()
  65. for folder in this.folder_ids:
  66. this.handle_folder(connection, folder)
  67. connection.close()
  68. return super(fetchmail_server, self).fetch_mail(
  69. cr, uid, check_original, context)
  70. def handle_folder(self, cr, uid, ids, connection, folder, context=None):
  71. '''Return ids of objects matched'''
  72. matched_object_ids = []
  73. for this in self.browse(cr, uid, ids, context=context):
  74. logger.info('start checking for emails in %s server %s',
  75. folder.path, this.name)
  76. match_algorithm = folder.get_algorithm()
  77. if connection.select(folder.path)[0] != 'OK':
  78. logger.error('Could not open mailbox %s on %s',
  79. folder.path,
  80. this.server)
  81. connection.select()
  82. continue
  83. result, msgids = this.get_msgids(connection)
  84. if result != 'OK':
  85. logger.error('Could not search mailbox %s on %s',
  86. folder.path,
  87. this.server)
  88. continue
  89. for msgid in msgids[0].split():
  90. matched_object_ids += this.apply_matching(
  91. connection, folder, msgid, match_algorithm)
  92. logger.info('finished checking for emails in %s server %s',
  93. folder.path, this.name)
  94. return matched_object_ids
  95. def get_msgids(self, cr, uid, ids, connection, context=None):
  96. '''Return imap ids of messages to process'''
  97. return connection.search(None, 'UNDELETED')
  98. def apply_matching(self, cr, uid, ids, connection, folder, msgid,
  99. match_algorithm, context=None):
  100. '''Return ids of objects matched'''
  101. matched_object_ids = []
  102. for this in self.browse(cr, uid, ids, context=context):
  103. result, msgdata = connection.fetch(msgid, '(RFC822)')
  104. if result != 'OK':
  105. logger.error('Could not fetch %s in %s on %s',
  106. msgid,
  107. folder.path,
  108. this.server)
  109. continue
  110. mail_message = self.pool.get('mail.thread').message_parse(
  111. cr, uid, msgdata[0][1], save_original=this.original,
  112. context=context)
  113. if self.pool.get('mail.message').search(
  114. cr, uid, [
  115. ('message_id', '=', mail_message['message_id'])]):
  116. continue
  117. found_ids = match_algorithm.search_matches(
  118. cr, uid, folder,
  119. mail_message, msgdata[0][1])
  120. if found_ids and (len(found_ids) == 1 or
  121. folder.match_first):
  122. try:
  123. cr.execute('savepoint apply_matching')
  124. match_algorithm.handle_match(
  125. cr, uid, connection,
  126. found_ids[0], folder, mail_message,
  127. msgdata[0][1], msgid, context)
  128. cr.execute('release savepoint apply_matching')
  129. matched_object_ids += found_ids[:1]
  130. except Exception:
  131. cr.execute('rollback to savepoint apply_matching')
  132. logger.exception(
  133. "Failed to fetch mail %s from %s",
  134. msgid, this.name)
  135. elif folder.flag_nonmatching:
  136. connection.store(msgid, '+FLAGS', '\\FLAGGED')
  137. return matched_object_ids
  138. def attach_mail(
  139. self, cr, uid, ids, connection, object_id, folder,
  140. mail_message, msgid, context=None):
  141. '''Return ids of messages created'''
  142. mail_message_ids = []
  143. for this in self.browse(cr, uid, ids, context):
  144. partner_id = None
  145. if folder.model_id.model == 'res.partner':
  146. partner_id = object_id
  147. if 'partner_id' in self.pool.get(folder.model_id.model)._columns:
  148. partner_id = self.pool.get(
  149. folder.model_id.model).browse(
  150. cr, uid, object_id, context
  151. ).partner_id.id
  152. attachments = []
  153. if this.attach and mail_message.get('attachments'):
  154. for attachment in mail_message['attachments']:
  155. fname, fcontent = attachment
  156. if isinstance(fcontent, unicode):
  157. fcontent = fcontent.encode('utf-8')
  158. data_attach = {
  159. 'name': fname,
  160. 'datas': base64.b64encode(str(fcontent)),
  161. 'datas_fname': fname,
  162. 'description': _('Mail attachment'),
  163. 'res_model': folder.model_id.model,
  164. 'res_id': object_id,
  165. }
  166. attachments.append(
  167. self.pool.get('ir.attachment').create(
  168. cr, uid, data_attach, context=context))
  169. mail_message_ids.append(
  170. self.pool.get('mail.message').create(
  171. cr, uid,
  172. {
  173. 'author_id': partner_id,
  174. 'model': folder.model_id.model,
  175. 'res_id': object_id,
  176. 'type': 'email',
  177. 'body': mail_message.get('body'),
  178. 'subject': mail_message.get('subject'),
  179. 'email_from': mail_message.get('from'),
  180. 'date': mail_message.get('date'),
  181. 'message_id': mail_message.get('message_id'),
  182. 'attachment_ids': [(6, 0, attachments)],
  183. },
  184. context))
  185. if folder.delete_matching:
  186. connection.store(msgid, '+FLAGS', '\\DELETED')
  187. return mail_message_ids
  188. def button_confirm_login(self, cr, uid, ids, context=None):
  189. retval = super(fetchmail_server, self).button_confirm_login(cr, uid,
  190. ids,
  191. context)
  192. for this in self.browse(cr, uid, ids, context):
  193. this.write({'state': 'draft'})
  194. connection = this.connect()
  195. connection.select()
  196. for folder in this.folder_ids:
  197. if connection.select(folder.path)[0] != 'OK':
  198. raise except_orm(
  199. _('Error'), _('Mailbox %s not found!') %
  200. folder.path)
  201. connection.close()
  202. this.write({'state': 'done'})
  203. return retval
  204. def fields_view_get(self, cr, user, view_id=None, view_type='form',
  205. context=None, toolbar=False, submenu=False):
  206. result = super(fetchmail_server, self).fields_view_get(
  207. cr, user, view_id, view_type, context, toolbar, submenu)
  208. if view_type == 'form':
  209. view = etree.fromstring(
  210. result['fields']['folder_ids']['views']['form']['arch'])
  211. modifiers = {}
  212. docstr = ''
  213. for algorithm in self.pool.get('fetchmail.server.folder')\
  214. ._get_match_algorithms().itervalues():
  215. for modifier in ['required', 'readonly']:
  216. for field in getattr(algorithm, modifier + '_fields'):
  217. modifiers.setdefault(field, {})
  218. modifiers[field].setdefault(modifier, [])
  219. if modifiers[field][modifier]:
  220. modifiers[field][modifier].insert(0, '|')
  221. modifiers[field][modifier].append(
  222. ("match_algorithm", "==", algorithm.__name__))
  223. docstr += _(algorithm.name) + '\n' + _(algorithm.__doc__) + \
  224. '\n\n'
  225. for field in view:
  226. if field.tag == 'field' and field.get('name') in modifiers:
  227. field.set(
  228. 'modifiers',
  229. simplejson.dumps(
  230. dict(
  231. safe_eval(
  232. field.attrib['modifiers'],
  233. UnquoteEvalContext({})
  234. ),
  235. **modifiers[field.attrib['name']]
  236. )
  237. ),
  238. )
  239. if (field.tag == 'field' and
  240. field.get('name') == 'match_algorithm'):
  241. field.set('help', docstr)
  242. result['fields']['folder_ids']['views']['form']['arch'] = \
  243. etree.tostring(view)
  244. return result