Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
Bohdan Horbeshko | 7bbff365ad | ||
Bohdan Horbeshko | b538fa106e | ||
Bohdan Horbeshko | d6b7575c4c | ||
Bohdan Horbeshko | 7c6d87080c | ||
Bohdan Horbeshko | 75cc62153c | ||
Bohdan Horbeshko | c745cb4b60 | ||
Bohdan Horbeshko | f011f1c5ab |
39
otr.py
39
otr.py
|
@ -20,10 +20,11 @@ import string
|
|||
import random
|
||||
import itertools
|
||||
import logging
|
||||
from inspect import signature
|
||||
from gajim.common import const, app, helpers, configpaths
|
||||
from gajim.common.const import EncryptionData
|
||||
from gajim.common.structs import OutgoingMessage
|
||||
from nbxmpp.protocol import Message, JID
|
||||
from nbxmpp.simplexml import Node
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
@ -51,8 +52,8 @@ class OTRChannel(context.Context):
|
|||
|
||||
# print some text to chat window
|
||||
def printl(self,line):
|
||||
println = self.user.getControl(self.peer) and self.user.getControl(self.peer).conv_textview.print_conversation_line
|
||||
println and println("OTR: "+line,kind='status',name='',tim='',**('jid' in signature(println).parameters and {'jid':None} or {}))
|
||||
control = app.window.get_control()
|
||||
control and control.add_info_message("OTR: "+line)
|
||||
|
||||
@staticmethod
|
||||
def getPolicy(policy): return OTR.DEFAULT_POLICY.get(policy)
|
||||
|
@ -98,8 +99,7 @@ class OTR(context.Account):
|
|||
|
||||
# get chat control
|
||||
def getControl(self,peer):
|
||||
ctl = app.interface.msg_win_mgr.get_control(peer.getStripped(),self.account)
|
||||
return ctl
|
||||
return app.window.get_control()
|
||||
|
||||
# get OTR context (encrypted dialog between Alice and Bob)
|
||||
def getContext(self,peer):
|
||||
|
@ -107,6 +107,18 @@ class OTR(context.Account):
|
|||
self.ctxs[peer] = self.ctxs.get(peer) or OTRChannel(self,peer)
|
||||
return self.ctxs[peer]
|
||||
|
||||
# factory for Gajim 1.4+
|
||||
def makeOutgoingMessage(self,message,control,peer):
|
||||
contact = control.client.get_module('Contacts').get_contact(peer, groupchat=False)
|
||||
chatstate = control.client.get_module('Chatstate').get_active_chatstate(contact)
|
||||
return OutgoingMessage(account=self.account,
|
||||
contact=contact,
|
||||
message=message,
|
||||
type_='chat',
|
||||
chatstate=chatstate,
|
||||
label=None,
|
||||
correct_id=None)
|
||||
|
||||
# load my private key
|
||||
def loadPrivkey(self):
|
||||
my = self.keystore.load(jid=str(self.jid))
|
||||
|
@ -127,7 +139,8 @@ class OTR(context.Account):
|
|||
|
||||
# decrypt message
|
||||
def decrypt(self,stanza,properties):
|
||||
peer = stanza.getFrom().new_as_bare()
|
||||
sFrom = stanza.getFrom()
|
||||
peer = sFrom.new_as_bare()
|
||||
msgtxt = stanza.getBody()
|
||||
channel, ctl = self.getContext(peer), self.getControl(peer)
|
||||
try:
|
||||
|
@ -136,16 +149,21 @@ class OTR(context.Account):
|
|||
self.log.error("** got exception while decrypting message: %s" % e)
|
||||
channel.printl(OTR.STATUS[e].format(msg=msgtxt,err=e.args[0].error))
|
||||
else:
|
||||
channel.resource = sFrom.resource
|
||||
stanza.setBody(text and text.decode() or "")
|
||||
properties.encrypted = EncryptionData({'name': OTR.ENCRYPTION_NAME})
|
||||
finally:
|
||||
if channel.mayRetransmit and channel.state and ctl: channel.mayRetransmit = ctl.send_message(channel.lastMessage.decode())
|
||||
if channel.mayRetransmit and channel.state and ctl: channel.mayRetransmit = ctl.client.send_message(self.makeOutgoingMessage(channel.lastMessage.decode(), ctl, peer))
|
||||
|
||||
# encrypt message
|
||||
def encrypt(self,event,callback):
|
||||
peer = event.msg_iq.getTo().new_as_bare()
|
||||
channel, ctl = self.getContext(peer), event.control
|
||||
if event.xhtml: return ctl.send_message(event.message) # skip xhtml messages
|
||||
channel, ctl = self.getContext(peer), self.getControl(peer)
|
||||
if not hasattr(channel, 'resource'):
|
||||
channel.resource = ""
|
||||
if channel.resource:
|
||||
peer = peer.new_with(resource=channel.resource)
|
||||
if event.xhtml: return ctl.client.send_message(self.makeOutgoingMessage(event.message, ctl, peer)) # skip xhtml messages
|
||||
try:
|
||||
encrypted = channel.sendMessage(context.FRAGMENT_SEND_ALL_BUT_LAST,event.message.encode(),appdata=event.msg_iq.getThread()) or b''
|
||||
message = (encrypted != self.getDefaultQueryMessage(OTR.DEFAULT_POLICY.get)) and event.message or ""
|
||||
|
@ -156,4 +174,7 @@ class OTR(context.Account):
|
|||
event.msg_iq.setBody(encrypted.decode()) # encrypted data goes here
|
||||
event.message = message # message that will be displayed in our chat goes here
|
||||
event.encrypted, event.additional_data["encrypted"] = OTR.ENCRYPTION_NAME, {"name":OTR.ENCRYPTION_NAME} # some mandatory encryption flags
|
||||
if channel.resource:
|
||||
event.stanza.addChild('no-copy', namespace='urn:xmpp:hints') # don't send carbons
|
||||
event.stanza.addChild('no-store', namespace='urn:xmpp:hints') # don't store in MAM
|
||||
callback(event)
|
||||
|
|
21
plugin-manifest.json
Normal file
21
plugin-manifest.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"authors": [
|
||||
"Pavel R <pd@narayana.im>",
|
||||
"Bohdan Horbeshko <bodqhrohro@gmail.com>"
|
||||
],
|
||||
"config_dialog": false,
|
||||
"description": "Off-the-Record encryption",
|
||||
"homepage": "https://dev.narayana.im/gajim-otrplugin",
|
||||
"name": "otrplugin",
|
||||
"platforms": [
|
||||
"others",
|
||||
"linux",
|
||||
"darwin",
|
||||
"win32"
|
||||
],
|
||||
"requirements": [
|
||||
"gajim>=1.6,<1.9"
|
||||
],
|
||||
"short_name": "otrplugin",
|
||||
"version": "0.5.2"
|
||||
}
|
45
plugin.py
45
plugin.py
|
@ -22,10 +22,14 @@ ERROR = None
|
|||
import logging
|
||||
from gajim.plugins import GajimPlugin
|
||||
from gajim.common import app
|
||||
from gajim.gtk.util import make_menu_item
|
||||
from gi.repository import Gtk, Gio
|
||||
|
||||
log = logging.getLogger('gajim.p.otr')
|
||||
|
||||
try: from Cryptodome import *
|
||||
except ImportError as e:
|
||||
try: from Crypto import *
|
||||
except ImportError as e:
|
||||
log.error(e)
|
||||
ERROR = 'Cryptodome is missing'
|
||||
|
@ -52,7 +56,26 @@ class OTRPlugin(GajimPlugin):
|
|||
self.modules = [module]
|
||||
self.gui_extension_points = {
|
||||
'encrypt' + self.encryption_name: (self._encrypt_message, None),
|
||||
'message_actions_box': (self._message_actions_box_activate, self._message_actions_box_deactivate),
|
||||
}
|
||||
self._menuitem = None
|
||||
|
||||
@staticmethod
|
||||
def _get_grid():
|
||||
return app.window.get_chat_stack().get_message_action_box()
|
||||
|
||||
@staticmethod
|
||||
def _get_model(grid):
|
||||
return grid._ui.encryption_menu_button.get_menu_model()
|
||||
|
||||
def activate(self):
|
||||
if app.window is not None and self._menuitem is None:
|
||||
grid = self._get_grid()
|
||||
self._actions_hack_activate(grid)
|
||||
|
||||
def deactivate(self):
|
||||
grid = self._get_grid()
|
||||
self._actions_hack_deactivate(grid)
|
||||
|
||||
@staticmethod
|
||||
def get_otr(account):
|
||||
|
@ -73,3 +96,25 @@ class OTRPlugin(GajimPlugin):
|
|||
if not event.message or event.type_ != 'chat': return # drop empty and non-chat messages
|
||||
otr = self.get_otr(event.account)
|
||||
otr.otr.encrypt(event,callback)
|
||||
|
||||
def _message_actions_box_activate(self, grid, box):
|
||||
if self._menuitem is None:
|
||||
self._actions_hack_activate(grid)
|
||||
|
||||
def _message_actions_box_deactivate(self, grid, box):
|
||||
if self._menuitem is not None:
|
||||
self._actions_hack_deactivate(grid)
|
||||
|
||||
def _actions_hack_activate(self, grid):
|
||||
model = grid._ui.encryption_menu_button.get_menu_model()
|
||||
menuitem = make_menu_item('OTR', 'win.set-encryption', 'OTR')
|
||||
self._menuitem = menuitem.get_attribute_value(Gio.MENU_ATTRIBUTE_LABEL)
|
||||
model.append_item(menuitem)
|
||||
|
||||
def _actions_hack_deactivate(self, grid):
|
||||
model = self._get_model(grid)
|
||||
for i in range(model.get_n_items()):
|
||||
if model.get_item_attribute_value(i, Gio.MENU_ATTRIBUTE_LABEL, None) == self._menuitem:
|
||||
model.remove(i)
|
||||
self._menuitem = None
|
||||
break
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
try:
|
||||
from Cryptodome import Cipher
|
||||
from Cryptodome.Hash import HMAC as _HMAC
|
||||
from Cryptodome.Hash import SHA256 as _SHA256
|
||||
|
@ -25,6 +26,15 @@ from Cryptodome.PublicKey import DSA
|
|||
from Cryptodome.Random import random
|
||||
from Cryptodome.Signature import DSS
|
||||
from Cryptodome.Util import Counter
|
||||
except ImportError as e:
|
||||
from Crypto import Cipher
|
||||
from Crypto.Hash import HMAC as _HMAC
|
||||
from Crypto.Hash import SHA256 as _SHA256
|
||||
from Crypto.Hash import SHA as _SHA1
|
||||
from Crypto.PublicKey import DSA
|
||||
from Crypto.Random import random
|
||||
from Crypto.Signature import DSS
|
||||
from Crypto.Util import Counter
|
||||
|
||||
from numbers import Number
|
||||
|
||||
|
|
Loading…
Reference in a new issue