Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
G
gajim-plugins
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Evert Mouw
gajim-plugins
Commits
8a876284
Commit
8a876284
authored
7 years ago
by
Philipp Hörist
Browse files
Options
Downloads
Patches
Plain Diff
[omemo] Add file decryption
parent
573c88a3
No related branches found
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
omemo/file_decryption.py
+286
-0
286 additions, 0 deletions
omemo/file_decryption.py
omemo/omemoplugin.py
+2
-0
2 additions, 0 deletions
omemo/omemoplugin.py
omemo/upload_progress_dialog.ui
+108
-0
108 additions, 0 deletions
omemo/upload_progress_dialog.ui
with
396 additions
and
0 deletions
omemo/file_decryption.py
0 → 100644
+
286
−
0
View file @
8a876284
# -*- coding: utf-8 -*-
#
# Copyright 2017 Philipp Hörist <philipp@hoerist.com>
#
# This file is part of Gajim-OMEMO plugin.
#
# The Gajim-OMEMO plugin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# Gajim-OMEMO is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
#
import
os
import
hashlib
import
logging
import
urllib2
import
socket
import
threading
import
platform
import
subprocess
import
binascii
from
io
import
BufferedWriter
,
FileIO
,
BytesIO
from
urlparse
import
urlparse
,
urldefrag
import
gtk
import
gobject
import
gtkgui_helpers
from
common
import
configpaths
from
dialogs
import
ErrorDialog
,
YesNoDialog
from
cryptography.hazmat.primitives.ciphers
import
Cipher
from
cryptography.hazmat.primitives.ciphers
import
algorithms
from
cryptography.hazmat.primitives.ciphers.modes
import
GCM
from
cryptography.hazmat.backends
import
default_backend
log
=
logging
.
getLogger
(
'
gajim.plugin_system.omemo.filedecryption
'
)
DIRECTORY
=
os
.
path
.
join
(
configpaths
.
gajimpaths
[
'
MY_DATA
'
],
'
downloads
'
)
try
:
if
not
os
.
path
.
exists
(
DIRECTORY
):
os
.
makedirs
(
DIRECTORY
)
ERROR
=
False
except
Exception
:
ERROR
=
True
log
.
exception
(
'
Error
'
)
class
File
:
def
__init__
(
self
,
url
):
self
.
url
,
self
.
fragment
=
urldefrag
(
url
)
self
.
key
=
None
self
.
iv
=
None
self
.
filepath
=
None
self
.
filename
=
None
class
FileDecryption
:
def
__init__
(
self
,
plugin
):
self
.
plugin
=
plugin
self
.
chat_control
=
None
self
.
orig_handler
=
None
self
.
tv
=
None
self
.
progress_windows
=
{}
def
activate
(
self
,
chat_control
):
if
ERROR
:
return
self
.
tv
=
chat_control
.
conv_textview
.
tv
self
.
chat_control
=
chat_control
self
.
orig_handler
=
self
.
tv
.
hyperlink_handler
self
.
tv
.
hyperlink_handler
=
self
.
hyperlink_handler
def
deactivate
(
self
):
self
.
tv
.
hyperlink_handler
=
self
.
orig_handler
def
hyperlink_handler
(
self
,
texttag
,
widget
,
event
,
iter_
,
kind
):
if
event
.
type
!=
gtk
.
gdk
.
BUTTON_PRESS
:
return
begin_iter
=
iter_
.
copy
()
# we get the begining of the tag
while
not
begin_iter
.
begins_tag
(
texttag
):
begin_iter
.
backward_char
()
end_iter
=
iter_
.
copy
()
# we get the end of the tag
while
not
end_iter
.
ends_tag
(
texttag
):
end_iter
.
forward_char
()
url
=
self
.
tv
.
get_buffer
().
get_text
(
begin_iter
,
end_iter
,
True
)
urlparts
=
urlparse
(
url
)
file
=
File
(
urlparts
.
geturl
())
if
urlparts
.
scheme
not
in
[
"
https
"
]
or
not
urlparts
.
netloc
:
log
.
info
(
"
Not accepting URL for decryption: %s
"
,
url
)
self
.
orig_handler
(
texttag
,
widget
,
event
,
iter_
,
kind
)
return
if
not
self
.
is_encrypted
(
file
):
log
.
info
(
'
Url not encrypted: %s
'
,
url
)
self
.
orig_handler
(
texttag
,
widget
,
event
,
iter_
,
kind
)
return
self
.
create_paths
(
file
)
if
os
.
path
.
exists
(
file
.
filepath
):
self
.
finished
(
file
)
return
event
=
threading
.
Event
()
progressbar
=
ProgressWindow
(
self
.
plugin
,
self
.
chat_control
,
event
)
thread
=
threading
.
Thread
(
target
=
Download
,
args
=
(
file
,
progressbar
,
self
.
chat_control
,
event
,
self
))
thread
.
daemon
=
True
thread
.
start
()
def
is_encrypted
(
self
,
file
):
if
file
.
fragment
:
try
:
fragment
=
binascii
.
unhexlify
(
file
.
fragment
)
file
.
key
=
fragment
[
16
:]
file
.
iv
=
fragment
[:
16
]
if
len
(
file
.
key
)
==
32
and
len
(
file
.
iv
)
==
16
:
return
True
except
:
return
False
return
False
def
create_paths
(
self
,
file
):
file
.
filename
=
os
.
path
.
basename
(
file
.
url
)
ext
=
os
.
path
.
splitext
(
file
.
filename
)[
1
]
name
=
os
.
path
.
splitext
(
file
.
filename
)[
0
]
urlhash
=
hashlib
.
sha1
(
file
.
url
).
hexdigest
()
newfilename
=
name
+
'
_
'
+
urlhash
[:
10
]
+
ext
file
.
filepath
=
os
.
path
.
join
(
DIRECTORY
,
newfilename
)
def
finished
(
self
,
file
):
question
=
'
Do you want to open %s
'
%
file
.
filename
YesNoDialog
(
'
Open File
'
,
question
,
transient_for
=
self
.
chat_control
.
parent_win
.
window
,
on_response_yes
=
(
self
.
open_file
,
file
.
filepath
))
return
False
def
open_file
(
self
,
checked
,
path
):
if
platform
.
system
()
==
"
Windows
"
:
os
.
startfile
(
path
)
elif
platform
.
system
()
==
"
Darwin
"
:
subprocess
.
Popen
([
"
open
"
,
path
])
else
:
subprocess
.
Popen
([
"
xdg-open
"
,
path
])
class
Download
:
def
__init__
(
self
,
file
,
progressbar
,
chat_control
,
event
,
base
):
self
.
file
=
file
self
.
progressbar
=
progressbar
self
.
chat_control
=
chat_control
self
.
event
=
event
self
.
base
=
base
self
.
download
()
def
download
(
self
):
gobject
.
idle_add
(
self
.
progressbar
.
set_text
,
'
Downloading...
'
)
data
=
self
.
load_url
()
if
isinstance
(
data
,
str
):
gobject
.
idle_add
(
self
.
progressbar
.
close_dialog
)
gobject
.
idle_add
(
self
.
error
,
data
)
return
gobject
.
idle_add
(
self
.
progressbar
.
set_text
,
'
Decrypting...
'
)
decrypted_data
=
self
.
aes_decrypt
(
data
)
gobject
.
idle_add
(
self
.
progressbar
.
set_text
,
'
Writing file to harddisk...
'
)
self
.
write_file
(
decrypted_data
)
gobject
.
idle_add
(
self
.
progressbar
.
close_dialog
)
gobject
.
idle_add
(
self
.
base
.
finished
,
self
.
file
)
def
load_url
(
self
):
try
:
get_request
=
urllib2
.
urlopen
(
self
.
file
.
url
,
timeout
=
30
)
size
=
get_request
.
info
().
getheader
(
'
Content-Length
'
).
strip
()
if
not
size
:
errormsg
=
'
Content-Length not found in header
'
log
.
error
(
errormsg
)
return
errormsg
stream
=
BytesIO
()
while
True
:
try
:
if
self
.
event
.
isSet
():
raise
UploadAbortedException
temp
=
get_request
.
read
(
10000
)
gobject
.
idle_add
(
self
.
progressbar
.
update_progress
,
len
(
temp
),
size
)
except
socket
.
timeout
:
errormsg
=
'
Request timeout
'
log
.
error
(
errormsg
)
return
errormsg
if
temp
:
stream
.
write
(
temp
)
else
:
return
stream
except
UploadAbortedException
as
error
:
log
.
info
(
'
Upload Aborted
'
)
errormsg
=
error
except
urllib2
.
URLError
as
error
:
log
.
exception
(
'
URLError
'
)
errormsg
=
error
.
reason
except
Exception
as
error
:
log
.
exception
(
'
Error
'
)
errormsg
=
error
stream
.
close
()
return
str
(
errormsg
)
def
aes_decrypt
(
self
,
payload
):
# Use AES128 GCM with the given key and iv to decrypt the payload.
payload
=
payload
.
getvalue
()
data
=
payload
[:
-
16
]
tag
=
payload
[
-
16
:]
decryptor
=
Cipher
(
algorithms
.
AES
(
self
.
file
.
key
),
GCM
(
self
.
file
.
iv
,
tag
=
tag
),
backend
=
default_backend
()).
decryptor
()
return
decryptor
.
update
(
data
)
+
decryptor
.
finalize
()
def
write_file
(
self
,
data
):
log
.
info
(
'
Writing data to %s
'
,
self
.
file
.
filepath
)
try
:
with
BufferedWriter
(
FileIO
(
self
.
file
.
filepath
,
"
wb
"
))
as
output
:
output
.
write
(
data
)
output
.
close
()
except
Exception
:
log
.
exception
(
'
Failed to write file
'
)
def
error
(
self
,
error
):
ErrorDialog
(
_
(
'
Error
'
),
error
,
transient_for
=
self
.
chat_control
.
parent_win
.
window
)
return
False
class
ProgressWindow
:
def
__init__
(
self
,
plugin
,
chat_control
,
event
):
self
.
plugin
=
plugin
self
.
event
=
event
self
.
xml
=
gtkgui_helpers
.
get_gtk_builder
(
self
.
plugin
.
local_file_path
(
'
upload_progress_dialog.ui
'
))
self
.
dialog
=
self
.
xml
.
get_object
(
'
progress_dialog
'
)
self
.
dialog
.
set_transient_for
(
chat_control
.
parent_win
.
window
)
self
.
label
=
self
.
xml
.
get_object
(
'
label
'
)
self
.
progressbar
=
self
.
xml
.
get_object
(
'
progressbar
'
)
self
.
progressbar
.
set_text
(
""
)
self
.
dialog
.
show_all
()
self
.
xml
.
connect_signals
(
self
)
self
.
seen
=
0
def
set_text
(
self
,
text
):
self
.
label
.
set_markup
(
'
<big>%s</big>
'
%
text
)
return
False
def
update_progress
(
self
,
seen
,
total
):
self
.
seen
+=
seen
pct
=
(
self
.
seen
/
float
(
total
))
*
100.0
self
.
progressbar
.
set_fraction
(
self
.
seen
/
float
(
total
))
self
.
progressbar
.
set_text
(
str
(
int
(
pct
))
+
"
%
"
)
return
False
def
close_dialog
(
self
,
*
args
):
self
.
dialog
.
destroy
()
return
False
def
on_destroy
(
self
,
*
args
):
self
.
event
.
set
()
class
UploadAbortedException
(
Exception
):
def
__str__
(
self
):
return
_
(
'
Upload Aborted
'
)
This diff is collapsed.
Click to expand it.
omemo/omemoplugin.py
+
2
−
0
View file @
8a876284
...
...
@@ -37,6 +37,7 @@ from .xmpp import (
DevicelistPEP
,
OmemoMessage
,
successful
,
unpack_device_bundle
,
unpack_device_list_update
,
unpack_encrypted
)
from
.file_decryption
import
FileDecryption
from
common
import
demandimport
demandimport
.
enable
()
demandimport
.
ignore
+=
[
'
_imp
'
,
'
_thread
'
,
'
axolotl
'
,
'
PIL
'
,
...
...
@@ -832,6 +833,7 @@ class OmemoPlugin(GajimPlugin):
chat_control : ChatControl
Gajim ChatControl object
"""
FileDecryption
(
self
).
activate
(
chat_control
)
account
=
chat_control
.
contact
.
account
.
name
if
account
in
self
.
disabled_accounts
:
return
...
...
This diff is collapsed.
Click to expand it.
omemo/upload_progress_dialog.ui
0 → 100644
+
108
−
0
View file @
8a876284
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires
lib=
"gtk+"
version=
"2.16"
/>
<!-- interface-naming-policy toplevel-contextual -->
<object
class=
"GtkDialog"
id=
"progress_dialog"
>
<property
name=
"can_focus"
>
True
</property>
<property
name=
"title"
translatable=
"yes"
>
Download
</property>
<property
name=
"resizable"
>
False
</property>
<property
name=
"window_position"
>
center-on-parent
</property>
<property
name=
"destroy_with_parent"
>
True
</property>
<property
name=
"icon_name"
>
go-down
</property>
<property
name=
"type_hint"
>
dialog
</property>
<child
internal-child=
"vbox"
>
<object
class=
"GtkVBox"
id=
"dialog-vbox"
>
<property
name=
"width_request"
>
250
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"spacing"
>
6
</property>
<child
internal-child=
"action_area"
>
<object
class=
"GtkHButtonBox"
id=
"dialog-action_area11"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"layout_style"
>
end
</property>
<child>
<object
class=
"GtkAlignment"
id=
"alignment3"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"top_padding"
>
2
</property>
<property
name=
"bottom_padding"
>
4
</property>
<property
name=
"right_padding"
>
3
</property>
<child>
<object
class=
"GtkButton"
id=
"close_button"
>
<property
name=
"label"
>
gtk-cancel
</property>
<property
name=
"use_action_appearance"
>
False
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
True
</property>
<property
name=
"can_default"
>
True
</property>
<property
name=
"receives_default"
>
False
</property>
<property
name=
"use_stock"
>
True
</property>
<signal
name=
"destroy"
handler=
"on_destroy"
swapped=
"no"
/>
<signal
name=
"clicked"
handler=
"close_dialog"
swapped=
"no"
/>
</object>
</child>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"fill"
>
True
</property>
<property
name=
"position"
>
2
</property>
</packing>
</child>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"fill"
>
False
</property>
<property
name=
"pack_type"
>
end
</property>
<property
name=
"position"
>
0
</property>
</packing>
</child>
<child>
<object
class=
"GtkAlignment"
id=
"alignment1"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"top_padding"
>
8
</property>
<property
name=
"bottom_padding"
>
4
</property>
<property
name=
"left_padding"
>
8
</property>
<property
name=
"right_padding"
>
8
</property>
<child>
<object
class=
"GtkLabel"
id=
"label"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"xalign"
>
0
</property>
<property
name=
"use_markup"
>
True
</property>
</object>
</child>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"fill"
>
True
</property>
<property
name=
"position"
>
1
</property>
</packing>
</child>
<child>
<object
class=
"GtkAlignment"
id=
"alignment2"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"top_padding"
>
4
</property>
<property
name=
"bottom_padding"
>
4
</property>
<property
name=
"left_padding"
>
8
</property>
<property
name=
"right_padding"
>
8
</property>
<child>
<object
class=
"GtkProgressBar"
id=
"progressbar"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"show_text"
>
True
</property>
<property
name=
"pulse_step"
>
0.10000000149
</property>
</object>
</child>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"fill"
>
True
</property>
<property
name=
"position"
>
2
</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment