1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
#!/usr/bin/env python3
"""
Syncthing-GTK - IDDialog
Dialog with Device ID and generated QR code
"""
import http.client
import logging
import os
import ssl
import tempfile
import urllib.request
from gi.repository import Gio, GLib
from syncthing_gtk.uibuilder import UIBuilder
from .tools import IS_WINDOWS
log = logging.getLogger("IDDialog")
class IDDialog(object):
""" Dialog with Device ID and generated QR code """
def __init__(self, app, device_id):
self.app = app
self.device_id = device_id
self.setup_widgets()
self.ssl_ctx = create_ssl_context()
self.load_data()
def __getitem__(self, name):
""" Convince method that allows widgets to be accessed via self["widget"] """
return self.builder.get_object(name)
def show(self, parent=None):
if parent is not None:
self["dialog"].set_transient_for(parent)
self["dialog"].show_all()
def close(self):
self["dialog"].hide()
self["dialog"].destroy()
def setup_widgets(self):
# Load ui file
self.builder = UIBuilder()
self.builder.add_from_file(os.path.join(
self.app.uipath, "device-id.ui"))
self.builder.connect_signals(self)
self["vID"].set_text(self.device_id)
def load_data(self):
""" Loads QR code from Syncthing daemon """
if IS_WINDOWS:
return self.load_data_urllib()
uri = "%s/qr/?text=%s" % (self.app.daemon.get_webui_url(),
self.device_id)
io = Gio.file_new_for_uri(uri)
io.load_contents_async(None, self.cb_syncthing_qr, ())
def load_data_urllib(self):
""" Loads QR code from Syncthing daemon """
uri = "%s/qr/?text=%s" % (self.app.daemon.get_webui_url(),
self.device_id)
api_key = self.app.daemon.get_api_key()
opener = urllib.request.build_opener(DummyHTTPSHandler(self.ssl_ctx))
if api_key is not None:
opener.addheaders = [("X-API-Key", api_key)]
a = opener.open(uri)
data = a.read()
tf = tempfile.NamedTemporaryFile("wb", suffix=".png", delete=False)
tf.write(data)
tf.close()
self["vQR"].set_from_file(tf.name)
os.unlink(tf.name)
def cb_btClose_clicked(self, *a):
self.close()
def cb_syncthing_qr(self, io, results, *a):
"""
Called when QR code is loaded or operation fails. Image is then
displayed in dialog, failure is silently ignored.
"""
try:
ok, contents, etag = io.load_contents_finish(results)
if ok:
# QR is loaded, save it to temp file and let GTK to handle
# rest
tf = tempfile.NamedTemporaryFile(
"wb", suffix=".png", delete=False)
tf.write(contents)
tf.close()
self["vQR"].set_from_file(tf.name)
os.unlink(tf.name)
except GLib.Error as e:
if e.code in [14, 15]:
# Unauthorized. Grab CSRF token from daemon and try again
log.warning(
"Failed to load image using glib. Retrying with urllib2.")
self.load_data_urllib()
except Exception as e:
log.exception(e)
return
finally:
del io
def create_ssl_context():
""" May return NULL if ssl is not available """
if hasattr(ssl, "create_default_context"):
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
else:
log.warning("SSL is not available, cannot verify server certificate.")
class DummyHTTPSHandler(urllib.request.HTTPSHandler):
"""
Dummy HTTPS handler that ignores certificate errors. This in unsafe,
but used ONLY for QR code images.
"""
def __init__(self, ctx):
urllib.request.HTTPSHandler.__init__(self)
self.ctx = ctx
def https_open(self, req):
return self.do_open(self.getConnection, req)
def getConnection(self, host, timeout=300):
if self.ctx is not None:
return http.client.HTTPSConnection(host, context=self.ctx)
return True
|