electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

qrscanner.py (5178B)


      1 #!/usr/bin/env python
      2 #
      3 # Electrum - lightweight Bitcoin client
      4 # Copyright (C) 2015 Thomas Voegtlin
      5 #
      6 # Permission is hereby granted, free of charge, to any person
      7 # obtaining a copy of this software and associated documentation files
      8 # (the "Software"), to deal in the Software without restriction,
      9 # including without limitation the rights to use, copy, modify, merge,
     10 # publish, distribute, sublicense, and/or sell copies of the Software,
     11 # and to permit persons to whom the Software is furnished to do so,
     12 # subject to the following conditions:
     13 #
     14 # The above copyright notice and this permission notice shall be
     15 # included in all copies or substantial portions of the Software.
     16 #
     17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     24 # SOFTWARE.
     25 
     26 import os
     27 import sys
     28 import ctypes
     29 
     30 from .util import UserFacingException
     31 from .i18n import _
     32 from .logging import get_logger
     33 
     34 
     35 _logger = get_logger(__name__)
     36 
     37 
     38 if sys.platform == 'darwin':
     39     name = 'libzbar.dylib'
     40 elif sys.platform in ('windows', 'win32'):
     41     name = 'libzbar-0.dll'
     42 else:
     43     name = 'libzbar.so.0'
     44 
     45 try:
     46     libzbar = ctypes.cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), name))
     47 except BaseException as e1:
     48     try:
     49         libzbar = ctypes.cdll.LoadLibrary(name)
     50     except BaseException as e2:
     51         libzbar = None
     52         if sys.platform != 'darwin':
     53             _logger.error(f"failed to load zbar. exceptions: {[e1,e2]!r}")
     54 
     55 
     56 def scan_barcode_ctypes(device='', timeout=-1, display=True, threaded=False):
     57     if libzbar is None:
     58         raise UserFacingException("Cannot start QR scanner: zbar not available.")
     59     libzbar.zbar_symbol_get_data.restype = ctypes.c_char_p
     60     libzbar.zbar_processor_create.restype = ctypes.POINTER(ctypes.c_int)
     61     libzbar.zbar_processor_get_results.restype = ctypes.POINTER(ctypes.c_int)
     62     libzbar.zbar_symbol_set_first_symbol.restype = ctypes.POINTER(ctypes.c_int)
     63     # libzbar.zbar_set_verbosity(100)  # verbose logs for debugging
     64     proc = libzbar.zbar_processor_create(threaded)
     65     libzbar.zbar_processor_request_size(proc, 640, 480)
     66     if libzbar.zbar_processor_init(proc, device.encode('utf-8'), display) != 0:
     67         raise UserFacingException(
     68             _("Cannot start QR scanner: initialization failed.") + "\n" +
     69             _("Make sure you have a camera connected and enabled."))
     70     libzbar.zbar_processor_set_visible(proc)
     71     if libzbar.zbar_process_one(proc, timeout):
     72         symbols = libzbar.zbar_processor_get_results(proc)
     73     else:
     74         symbols = None
     75     libzbar.zbar_processor_destroy(proc)
     76     if symbols is None:
     77         return
     78     if not libzbar.zbar_symbol_set_get_size(symbols):
     79         return
     80     symbol = libzbar.zbar_symbol_set_first_symbol(symbols)
     81     data = libzbar.zbar_symbol_get_data(symbol)
     82     return data.decode('utf8')
     83 
     84 def scan_barcode_osx(*args_ignored, **kwargs_ignored):
     85     import subprocess
     86     # NOTE: This code needs to be modified if the positions of this file changes with respect to the helper app!
     87     # This assumes the built macOS .app bundle which ends up putting the helper app in
     88     # .app/contrib/osx/CalinsQRReader/build/Release/CalinsQRReader.app.
     89     root_ec_dir = os.path.abspath(os.path.dirname(__file__) + "/../")
     90     prog = root_ec_dir + "/" + "contrib/osx/CalinsQRReader/build/Release/CalinsQRReader.app/Contents/MacOS/CalinsQRReader"
     91     if not os.path.exists(prog):
     92         raise UserFacingException("Cannot start QR scanner: helper app not found.")
     93     data = ''
     94     try:
     95         # This will run the "CalinsQRReader" helper app (which also gets bundled with the built .app)
     96         # Just like the zbar implementation -- the main app will hang until the QR window returns a QR code
     97         # (or is closed). Communication with the subprocess is done via stdout.
     98         # See contrib/CalinsQRReader for the helper app source code.
     99         with subprocess.Popen([prog], stdout=subprocess.PIPE) as p:
    100             data = p.stdout.read().decode('utf-8').strip()
    101         return data
    102     except OSError as e:
    103         raise UserFacingException("Cannot start camera helper app: {}".format(e.strerror))
    104 
    105 scan_barcode = scan_barcode_osx if sys.platform == 'darwin' else scan_barcode_ctypes
    106 
    107 def _find_system_cameras():
    108     device_root = "/sys/class/video4linux"
    109     devices = {} # Name -> device
    110     if os.path.exists(device_root):
    111         for device in os.listdir(device_root):
    112             path = os.path.join(device_root, device, 'name')
    113             try:
    114                 with open(path, encoding='utf-8') as f:
    115                     name = f.read()
    116             except Exception:
    117                 continue
    118             name = name.strip('\n')
    119             devices[name] = os.path.join("/dev", device)
    120     return devices
    121 
    122 
    123 if __name__ == "__main__":
    124     print(scan_barcode())