annotate src/plugins/plugin_sec_otr.py @ 1095:ef7b7dd5c5db

plugin OTR: various improvments: - otr_data is now saved between sessions - private key is encrypted before saving - state change is detected (unencrypted/started/refreshed/finished) and (un)trusted (trusting is not implemented yet) - user feedback - fixed unencrypted message in an encrypted context warning - fixed inject msg encoding - minor refactoring
author Goffi <goffi@goffi.org>
date Thu, 26 Jun 2014 14:58:25 +0200
parents abcac1ac27a7
children 8def4a3f55c2
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/python
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
3
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
4 # SAT plugin for OTR encryption
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
6
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # This program is free software: you can redistribute it and/or modify
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # it under the terms of the GNU Affero General Public License as published by
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # the Free Software Foundation, either version 3 of the License, or
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
10 # (at your option) any later version.
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
11
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # This program is distributed in the hope that it will be useful,
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
15 # GNU Affero General Public License for more details.
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
16
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # You should have received a copy of the GNU Affero General Public License
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
19
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
20 # XXX: thanks to Darrik L Mazey for his documentation (https://blog.darmasoft.net/2013/06/30/using-pure-python-otr.html)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
21 # this implentation is based on it
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
22
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
23 from sat.core.i18n import _
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
24 from sat.core.log import getLogger
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
25 from sat.core import exceptions
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
26 log = getLogger(__name__)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
27 from twisted.words.protocols.jabber import jid
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
28 from twisted.python import failure
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
29 from twisted.internet import defer
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
30 import potr
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
31 from sat.memory import persistent
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
32
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
33 NS_OTR = "otr_plugin"
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
34 PRIVATE_KEY = "PRIVATE KEY"
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
35
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
36 DEFAULT_POLICY_FLAGS = {
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
37 'ALLOW_V1':False,
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
38 'ALLOW_V2':True,
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
39 'REQUIRE_ENCRYPTION':True,
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
40 }
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
41
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
42 PLUGIN_INFO = {
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
43 "name": "OTR",
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
44 "import_name": "OTR",
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
45 "type": "SEC",
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
46 "protocols": [],
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
47 "dependencies": [],
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
48 "main": "OTR",
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
49 "handler": "no",
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
50 "description": _("""Implementation of OTR""")
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
51 }
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
52
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
53
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
54 class Context(potr.context.Context):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
55
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
56 def __init__(self, host, account, other_jid):
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
57 super(Context, self).__init__(account, other_jid)
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
58 self.host = host
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
59
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
60 def getPolicy(self, key):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
61 if key in DEFAULT_POLICY_FLAGS:
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
62 return DEFAULT_POLICY_FLAGS[key]
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
63 else:
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
64 return False
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
65
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
66 def inject(self, msg_str, appdata=None):
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
67 assert isinstance(self.peer, jid.JID)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
68 msg = msg_str.decode('utf-8')
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
69 client = self.user.client
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
70 log.debug(u'inject(%s, appdata=%s, to=%s)' % (msg, appdata, self.peer))
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
71 mess_data = {'message': msg,
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
72 'type': 'chat',
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
73 'from': client.jid,
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
74 'to': self.peer,
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
75 'subject': None,
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
76 }
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
77 self.host.generateMessageXML(mess_data)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
78 client.xmlstream.send(mess_data['xml'])
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
79
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
80 def setState(self, state):
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
81 old_state = self.state
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
82 super(Context, self).setState(state)
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
83 log.debug(u"setState: %s (old_state=%s) " % (state, old_state))
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
84
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
85 if state == potr.context.STATE_PLAINTEXT:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
86 feedback = _(u"/!\\ conversation with %(other_jid)s is now UNENCRYPTED") % {'other_jid': self.peer.full()}
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
87 elif state == potr.context.STATE_ENCRYPTED:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
88 try:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
89 fingerprint, trusted = self.getCurrentTrust()
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
90 except TypeError:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
91 trusted = False
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
92 trusted_str = _(u"trusted") if trusted else _(u"untrusted")
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
93
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
94 if old_state == potr.context.STATE_ENCRYPTED:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
95 feedback = _(u"%(trusted)s OTR conversation with %(other_jid)s REFRESHED") % {'trusted': trusted_str, 'other_jid': self.peer.full()}
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
96 else:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
97 feedback = _(u"%(trusted)s Encrypted OTR conversation started with %(other_jid)s") % {'trusted': trusted_str, 'other_jid': self.peer.full()}
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
98 elif state == potr.context.STATE_FINISHED:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
99 feedback = _(u"OTR conversation with %(other_jid)s is FINISHED") % {'other_jid': self.peer.full()}
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
100 else:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
101 log.error(_(u"Unknown OTR state"))
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
102 return
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
103
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
104 client = self.user.client
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
105 # FIXME: newMessage should manage system message, so they don't appear as coming from the contact
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
106 self.host.bridge.newMessage(client.jid.full(),
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
107 feedback,
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
108 mess_type="headline",
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
109 to_jid=self.peer.full(),
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
110 extra={},
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
111 profile=client.profile)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
112 # TODO: send signal to frontends
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
113
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
114
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
115 class Account(potr.context.Account):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
116
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
117 def __init__(self, host, client):
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
118 log.debug(u"new account: %s" % client.jid)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
119 super(Account, self).__init__(client.jid, "xmpp", 1024)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
120 self.host = host
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
121 self.client = client
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
122
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
123 def loadPrivkey(self):
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
124 log.debug(u"loadPrivkey")
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
125 return self.client.otr_priv_key
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
126
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
127 def savePrivkey(self):
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
128 log.debug(u"savePrivkey")
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
129 priv_key = self.getPrivkey().serializePrivateKey()
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
130 d = self.host.memory.encryptValue(priv_key, self.client.profile)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
131 def save_encrypted_key(encrypted_priv_key):
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
132 self.client.otr_data[PRIVATE_KEY] = encrypted_priv_key
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
133 d.addCallback(save_encrypted_key)
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
134
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
135
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
136 class ContextManager(object):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
137
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
138 def __init__(self, host, client):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
139 self.host = host
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
140 self.account = Account(host, client)
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
141 self.contexts = {}
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
142
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
143 def startContext(self, other_jid):
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
144 assert isinstance(other_jid, jid.JID)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
145 context = self.contexts.setdefault(other_jid, Context(self.host, self.account, other_jid))
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
146 return context
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
147
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
148 def getContextForUser(self, other):
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
149 log.debug(u"getContextForUser [%s]" % other)
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
150 return self.startContext(other)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
151
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
152
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
153 class OTR(object):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
154
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
155 def __init__(self, host):
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
156 log.info(_(u"OTR plugin initialization"))
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
157 self.host = host
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
158 self.context_managers = {}
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
159 host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=100000)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
160 host.trigger.add("sendMessage", self.sendMessageTrigger, priority=100000)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
161
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
162 @defer.inlineCallbacks
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
163 def profileConnected(self, profile):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
164 client = self.host.getClient(profile)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
165 self.context_managers[profile] = ContextManager(self.host, client)
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
166 client.otr_data = persistent.PersistentBinaryDict(NS_OTR, profile)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
167 yield client.otr_data.load()
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
168 encrypted_priv_key = client.otr_data.get(PRIVATE_KEY, None)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
169 if encrypted_priv_key is not None:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
170 priv_key = yield self.host.memory.decryptValue(encrypted_priv_key, profile)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
171 client.otr_priv_key = potr.crypt.PK.parsePrivateKey(priv_key)[0]
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
172 else:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
173 client.otr_priv_key = None
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
174
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
175 def _receivedTreatment(self, data, profile):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
176 from_jid = jid.JID(data['from'])
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
177 log.debug(u"_receivedTreatment [from_jid = %s]" % from_jid)
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
178 otrctx = self.context_managers[profile].getContextForUser(from_jid)
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
179 encrypted = True
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
180
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
181 try:
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
182 res = otrctx.receiveMessage(data['body'].encode('utf-8'))
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
183 except potr.context.UnencryptedMessage:
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
184 if otrctx.state == potr.context.STATE_ENCRYPTED:
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
185 log.warning(u"Received unencrypted message in an encrypted context (from %(jid)s)" % {'jid': from_jid.full()})
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
186 client = self.host.getClient(profile)
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
187 self.host.bridge.newMessage(from_jid.full(),
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
188 _(u"WARNING: received unencrypted data in a supposedly encrypted context"),
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
189 mess_type="headline", # FIXME: add message type for internal informations
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
190 to_jid=client.jid.full(),
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
191 extra={},
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
192 profile=client.profile)
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
193 encrypted = False
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
194
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
195 if not encrypted:
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
196 return data
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
197 else:
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
198 if res[0] != None:
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
199 # decrypted messages handling.
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
200 # receiveMessage() will return a tuple, the first part of which will be the decrypted message
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
201 data['body'] = res[0].decode('utf-8')
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
202 raise failure.Failure(exceptions.SkipHistory()) # we send the decrypted message to frontends, but we don't want it in history
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
203 else:
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
204 raise failure.Failure(exceptions.CancelError()) # no message at all (no history, no signal)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
205
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
206 def MessageReceivedTrigger(self, message, post_treat, profile):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
207 post_treat.addCallback(self._receivedTreatment, profile)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
208 return True
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
209
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
210 def sendMessageTrigger(self, mess_data, pre_xml_treatments, post_xml_treatments, profile):
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
211 to_jid = mess_data['to']
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
212 if mess_data['type'] != 'groupchat' and not to_jid.resource:
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
213 to_jid.resource = self.host.memory.getLastResource(to_jid, profile) # FIXME: it's dirty, but frontends don't manage resources correctly now, refactoring is planed
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
214 otrctx = self.context_managers[profile].getContextForUser(to_jid)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
215 if mess_data['type'] != 'groupchat' and otrctx.state == potr.context.STATE_ENCRYPTED:
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
216 log.debug(u"encrypting message")
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
217 otrctx.sendMessage(0, mess_data['message'].encode('utf-8'))
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
218 client = self.host.getClient(profile)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
219 self.host.sendMessageToBridge(mess_data, client)
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
220 return False
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
221 else:
1095
ef7b7dd5c5db plugin OTR: various improvments:
Goffi <goffi@goffi.org>
parents: 1055
diff changeset
222 log.debug(u"sending message unencrypted")
1055
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
223 return True
abcac1ac27a7 plugin otr: first draft
Goffi <goffi@goffi.org>
parents:
diff changeset
224