--- /dev/null
+Download and decrypt encrypted files uploaded by the Conversations XMPP client
+using the mechanism from XEP-0363: HTTP Upload:
+(http://xmpp.org/extensions/xep-0363.html)
+
+This is basically a translation in python of ImageDownlaoder:
+https://github.com/iNPUTmice/ImageDownloader
+
+However there are some differences:
+ - The file is saved on local storage instead of being written on the
+ standard output, this way xdg-open can be used to open it.
+ - The wrapper script uses xdg-open, this is a more generic approach which
+ works with different file types.
+ - The wrapper script downloads the file to a temporary directory, this way
+ it can be invoked multiple times without the risk of overwriting existing
+ files.
+
+Some snippets were taken from python-omemo:
+https://github.com/omemo/python-omemo/blob/HEAD/src/omemo/aes_gcm_native.py
+
+Example of use:
+
+ ./conversations_http_downloader.py http://host.tld/path/to/file.jpg#theivandkey
+
+The open_wrapper.sh script can be used in gajim as a browser command to
+download, decrypt, and open the files automatically.
--- /dev/null
+#!/usr/bin/env python3
+#
+# conversations_http_downloader - download files uploaded by Conversations
+#
+# Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
+#
+# This program 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.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+import urllib.error
+import urllib.parse
+import urllib.request
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import algorithms
+from cryptography.hazmat.primitives.ciphers import Cipher
+from cryptography.hazmat.primitives.ciphers.modes import GCM
+
+
+# Taken verbatim from python-omemo
+def aes_decrypt(key, iv, payload):
+ """ Use AES128 GCM with the given key and iv to decrypt the payload. """
+ data = payload[:-16]
+ tag = payload[-16:]
+ backend = default_backend()
+ decryptor = Cipher(
+ algorithms.AES(key),
+ GCM(iv, tag=tag),
+ backend=backend).decryptor()
+ return decryptor.update(data) + decryptor.finalize()
+
+
+# Inspired by https://github.com/iNPUTmice/ImageDownloader
+def decrypt(data, anchor):
+ """ Use the iv an key that Conversations put into the URL anchor. """
+ key_and_iv = bytes.fromhex(anchor)
+ iv = key_and_iv[0:16]
+ key = key_and_iv[16:48]
+ return aes_decrypt(key, iv, data)
+
+
+def usage(name):
+ print("usage: %s <URL>" % name)
+
+
+def main():
+ if len(sys.argv) != 2:
+ usage(sys.argv[0])
+ return 1
+
+ url = sys.argv[1]
+
+ parsed_url = urllib.parse.urlparse(url)
+ anchor = parsed_url.fragment
+
+ try:
+ response = urllib.request.urlopen(url)
+ data = response.read()
+ except (urllib.error.URLError, urllib.error.HTTPError) as error:
+ sys.stderr.write("Error: %s\n" % error)
+ return 1
+
+ filename = os.path.basename(parsed_url.path)
+ with open(filename, "xb") as output_file:
+ if len(anchor) == 96:
+ output_file.write(decrypt(data, anchor))
+ else:
+ output_file.write(data)
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
--- /dev/null
+#!/bin/sh
+#
+# open_warapper.sh - wrapper script for conversations_http_downloader.py
+#
+# Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
+#
+# This program is free software. It comes without any warranty, to
+# the extent permitted by applicable law. You can redistribute it
+# and/or modify it under the terms of the Do What The Fuck You Want
+# To Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/wtfpl/COPYING for more details.
+#
+# This script is based on
+# https://github.com/iNPUTmice/ImageDownloader/blob/master/openbrowser.sh
+
+set -e
+
+DOWNLOADER="/home/ao2/Proj/conversations_http_downloader/conversations_http_downloader.py"
+URL="$1"
+
+decrypted_file()
+{
+ URL="$1"
+
+ TEMPDIR=$(mktemp -d)
+
+ cd $TEMPDIR
+ $DOWNLOADER "$URL"
+ cd $OLDPWD
+
+ FILENAME=$(basename ${URL%#*})
+ echo "${TEMPDIR}/${FILENAME}"
+}
+
+if [ ${URL: -97:1} = "#" ];
+then
+ DESTINATION=$(decrypted_file "$URL")
+else
+ DESTINATION="$URL"
+fi
+
+exec xdg-open "$DESTINATION"