moved to personal fennel config for awesome and added qutebrowser
This commit is contained in:
		
							parent
							
								
									403cb92b7d
								
							
						
					
					
						commit
						688748f8a6
					
				
					 502 changed files with 8576 additions and 9597 deletions
				
			
		
							
								
								
									
										10
									
								
								qutebrowser/autoconfig.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								qutebrowser/autoconfig.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
# If a config.py file exists, this file is ignored unless it's explicitly loaded
 | 
			
		||||
# via config.load_autoconfig(). For more information, see:
 | 
			
		||||
# https://github.com/qutebrowser/qutebrowser/blob/master/doc/help/configuring.asciidoc#loading-autoconfigyml
 | 
			
		||||
# DO NOT edit this file by hand, qutebrowser will overwrite it.
 | 
			
		||||
# Instead, create a config.py - see :help for details.
 | 
			
		||||
 | 
			
		||||
config_version: 2
 | 
			
		||||
settings:
 | 
			
		||||
  content.notifications:
 | 
			
		||||
    https://192.168.1.35: true
 | 
			
		||||
							
								
								
									
										302
									
								
								qutebrowser/bitwarden.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								qutebrowser/bitwarden.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,302 @@
 | 
			
		|||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com>
 | 
			
		||||
# Adapted for Bitwarden by Jonathan Haylett (JonnyHaystack) <jonathan@haylett.dev>
 | 
			
		||||
#
 | 
			
		||||
# This file is part of qutebrowser.
 | 
			
		||||
#
 | 
			
		||||
# qutebrowser is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published bjy
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# qutebrowser 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 qutebrowser.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Insert login information using Bitwarden CLI and a dmenu-compatible application
 | 
			
		||||
(e.g. dmenu, rofi -dmenu, ...).
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
USAGE = """The domain of the site has to be in the name of the Bitwarden entry, for example: "github.com/cryzed" or
 | 
			
		||||
"websites/github.com".  The login information is inserted by emulating key events using qutebrowser's fake-key command in this manner:
 | 
			
		||||
[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms.
 | 
			
		||||
 | 
			
		||||
If enabled, with the `--totp` flag, it will also move the TOTP code to the
 | 
			
		||||
clipboard, much like the Firefox add-on.
 | 
			
		||||
 | 
			
		||||
You must log into Bitwarden CLI using `bw login` prior to use of this script.
 | 
			
		||||
The session key will be stored using keyctl for the number of seconds passed to
 | 
			
		||||
the --auto-lock option.
 | 
			
		||||
 | 
			
		||||
To use in qutebrowser, run: `spawn --userscript bitwarden`
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
EPILOG = """Dependencies: tldextract (Python 3 module), pyperclip (optional
 | 
			
		||||
Python module, used for TOTP codes), Bitwarden CLI (1.7.4 is known to work
 | 
			
		||||
but older versions may well also work)
 | 
			
		||||
 | 
			
		||||
WARNING: The login details are viewable as plaintext in qutebrowser's debug log
 | 
			
		||||
(qute://log) and might be shared if you decide to submit a crash report!"""
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import enum
 | 
			
		||||
import functools
 | 
			
		||||
import os
 | 
			
		||||
import shlex
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import json
 | 
			
		||||
import tldextract
 | 
			
		||||
 | 
			
		||||
argument_parser = argparse.ArgumentParser(
 | 
			
		||||
    description=__doc__,
 | 
			
		||||
    usage=USAGE,
 | 
			
		||||
    epilog=EPILOG,
 | 
			
		||||
)
 | 
			
		||||
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
 | 
			
		||||
argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu -i -p Bitwarden',
 | 
			
		||||
                             help='Invocation used to execute a dmenu-provider')
 | 
			
		||||
argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false',
 | 
			
		||||
                             help="Don't automatically enter insert mode")
 | 
			
		||||
argument_parser.add_argument('--totp', '-t', action='store_true',
 | 
			
		||||
                             help="Copy TOTP key to clipboard")
 | 
			
		||||
argument_parser.add_argument('--io-encoding', '-i', default='UTF-8',
 | 
			
		||||
                             help='Encoding used to communicate with subprocesses')
 | 
			
		||||
argument_parser.add_argument('--merge-candidates', '-m', action='store_true',
 | 
			
		||||
                             help='Merge pass candidates for fully-qualified and registered domain name')
 | 
			
		||||
argument_parser.add_argument('--auto-lock', type=int, default=900,
 | 
			
		||||
                             help='Automatically lock the vault after this many seconds')
 | 
			
		||||
group = argument_parser.add_mutually_exclusive_group()
 | 
			
		||||
group.add_argument('--username-only', '-e',
 | 
			
		||||
                   action='store_true', help='Only insert username')
 | 
			
		||||
group.add_argument('--password-only', '-w',
 | 
			
		||||
                   action='store_true', help='Only insert password')
 | 
			
		||||
group.add_argument('--totp-only', '-T',
 | 
			
		||||
                   action='store_true', help='Only insert totp code')
 | 
			
		||||
 | 
			
		||||
stderr = functools.partial(print, file=sys.stderr)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExitCodes(enum.IntEnum):
 | 
			
		||||
    SUCCESS = 0
 | 
			
		||||
    FAILURE = 1
 | 
			
		||||
    # 1 is automatically used if Python throws an exception
 | 
			
		||||
    NO_PASS_CANDIDATES = 2
 | 
			
		||||
    COULD_NOT_MATCH_USERNAME = 3
 | 
			
		||||
    COULD_NOT_MATCH_PASSWORD = 4
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def qute_command(command):
 | 
			
		||||
    with open(os.environ['QUTE_FIFO'], 'w') as fifo:
 | 
			
		||||
        fifo.write(command + '\n')
 | 
			
		||||
        fifo.flush()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ask_password():
 | 
			
		||||
    process = subprocess.run([
 | 
			
		||||
        'rofi',
 | 
			
		||||
        '-dmenu',
 | 
			
		||||
        '-p',
 | 
			
		||||
        'Master Password',
 | 
			
		||||
        '-password',
 | 
			
		||||
        '-lines',
 | 
			
		||||
        '0',
 | 
			
		||||
    ], universal_newlines=True, stdout=subprocess.PIPE)
 | 
			
		||||
    if process.returncode > 0:
 | 
			
		||||
        raise Exception('Could not unlock vault')
 | 
			
		||||
    master_pass = process.stdout.strip()
 | 
			
		||||
    return subprocess.check_output(
 | 
			
		||||
        ['bw', 'unlock', '--raw', master_pass],
 | 
			
		||||
        universal_newlines=True,
 | 
			
		||||
    ).strip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_session_key(auto_lock):
 | 
			
		||||
    if auto_lock == 0:
 | 
			
		||||
        subprocess.call(['keyctl', 'purge', 'user', 'bw_session'])
 | 
			
		||||
        return ask_password()
 | 
			
		||||
    else:
 | 
			
		||||
        process = subprocess.run(
 | 
			
		||||
            ['keyctl', 'request', 'user', 'bw_session'],
 | 
			
		||||
            universal_newlines=True,
 | 
			
		||||
            stdout=subprocess.PIPE,
 | 
			
		||||
        )
 | 
			
		||||
        key_id = process.stdout.strip()
 | 
			
		||||
        if process.returncode > 0:
 | 
			
		||||
            session = ask_password()
 | 
			
		||||
            if not session:
 | 
			
		||||
                raise Exception('Could not unlock vault')
 | 
			
		||||
            key_id = subprocess.check_output(
 | 
			
		||||
                ['keyctl', 'add', 'user', 'bw_session', session, '@u'],
 | 
			
		||||
                universal_newlines=True,
 | 
			
		||||
            ).strip()
 | 
			
		||||
 | 
			
		||||
        if auto_lock > 0:
 | 
			
		||||
            subprocess.call(['keyctl', 'timeout', str(key_id), str(auto_lock)])
 | 
			
		||||
        return subprocess.check_output(
 | 
			
		||||
            ['keyctl', 'pipe', str(key_id)],
 | 
			
		||||
            universal_newlines=True,
 | 
			
		||||
        ).strip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pass_(domain, encoding, auto_lock):
 | 
			
		||||
    session_key = get_session_key(auto_lock)
 | 
			
		||||
    process = subprocess.run(
 | 
			
		||||
        ['bw', 'list', 'items', '--session', session_key, '--url', domain],
 | 
			
		||||
        stdout=subprocess.PIPE,
 | 
			
		||||
        stderr=subprocess.PIPE,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    err = process.stderr.decode(encoding).strip()
 | 
			
		||||
    if err:
 | 
			
		||||
        msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain, err)
 | 
			
		||||
        stderr(msg)
 | 
			
		||||
        return '[]'
 | 
			
		||||
 | 
			
		||||
    out = process.stdout.decode(encoding).strip()
 | 
			
		||||
 | 
			
		||||
    return out
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_totp_code(selection_id, domain_name, encoding, auto_lock):
 | 
			
		||||
    session_key = get_session_key(auto_lock)
 | 
			
		||||
    process = subprocess.run(
 | 
			
		||||
        ['bw', 'get', 'totp', '--session', session_key, selection_id],
 | 
			
		||||
        stdout=subprocess.PIPE,
 | 
			
		||||
        stderr=subprocess.PIPE,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    err = process.stderr.decode(encoding).strip()
 | 
			
		||||
    if err:
 | 
			
		||||
        # domain_name instead of selection_id to make it more user-friendly
 | 
			
		||||
        msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain_name, err)
 | 
			
		||||
        stderr(msg)
 | 
			
		||||
        return '[]'
 | 
			
		||||
 | 
			
		||||
    out = process.stdout.decode(encoding).strip()
 | 
			
		||||
 | 
			
		||||
    return out
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def dmenu(items, invocation, encoding):
 | 
			
		||||
    command = shlex.split(invocation)
 | 
			
		||||
    process = subprocess.run(command, input='\n'.join(
 | 
			
		||||
        items).encode(encoding), stdout=subprocess.PIPE)
 | 
			
		||||
    return process.stdout.decode(encoding).strip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fake_key_raw(text):
 | 
			
		||||
    for character in text:
 | 
			
		||||
        # Escape all characters by default, space requires special handling
 | 
			
		||||
        sequence = '" "' if character == ' ' else '\{}'.format(character)
 | 
			
		||||
        qute_command('fake-key {}'.format(sequence))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(arguments):
 | 
			
		||||
    if not arguments.url:
 | 
			
		||||
        argument_parser.print_help()
 | 
			
		||||
        return ExitCodes.FAILURE
 | 
			
		||||
 | 
			
		||||
    extract_result = tldextract.extract(arguments.url)
 | 
			
		||||
 | 
			
		||||
    # Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains),
 | 
			
		||||
    # the registered domain name and finally: the IPv4 address if that's what
 | 
			
		||||
    # the URL represents
 | 
			
		||||
    candidates = []
 | 
			
		||||
    for target in filter(None, [
 | 
			
		||||
                extract_result.fqdn,
 | 
			
		||||
                extract_result.registered_domain,
 | 
			
		||||
                extract_result.subdomain + extract_result.domain,
 | 
			
		||||
                extract_result.domain,
 | 
			
		||||
                extract_result.ipv4]):
 | 
			
		||||
        target_candidates = json.loads(
 | 
			
		||||
            pass_(
 | 
			
		||||
                target,
 | 
			
		||||
                arguments.io_encoding,
 | 
			
		||||
                arguments.auto_lock,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        if not target_candidates:
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        candidates = candidates + target_candidates
 | 
			
		||||
        if not arguments.merge_candidates:
 | 
			
		||||
            break
 | 
			
		||||
    else:
 | 
			
		||||
        if not candidates:
 | 
			
		||||
            stderr('No pass candidates for URL {!r} found!'.format(
 | 
			
		||||
                arguments.url))
 | 
			
		||||
            return ExitCodes.NO_PASS_CANDIDATES
 | 
			
		||||
 | 
			
		||||
    if len(candidates) == 1:
 | 
			
		||||
        selection = candidates.pop()
 | 
			
		||||
    else:
 | 
			
		||||
        choices = ['{:s} | {:s}'.format(c['name'], c['login']['username']) for c in candidates]
 | 
			
		||||
        choice = dmenu(choices, arguments.dmenu_invocation, arguments.io_encoding)
 | 
			
		||||
        choice_tokens = choice.split('|')
 | 
			
		||||
        choice_name = choice_tokens[0].strip()
 | 
			
		||||
        choice_username = choice_tokens[1].strip()
 | 
			
		||||
        selection = next((c for (i, c) in enumerate(candidates)
 | 
			
		||||
                          if c['name'] == choice_name
 | 
			
		||||
                          and c['login']['username'] == choice_username),
 | 
			
		||||
                         None)
 | 
			
		||||
 | 
			
		||||
    # Nothing was selected, simply return
 | 
			
		||||
    if not selection:
 | 
			
		||||
        return ExitCodes.SUCCESS
 | 
			
		||||
 | 
			
		||||
    username = selection['login']['username']
 | 
			
		||||
    password = selection['login']['password']
 | 
			
		||||
    totp = selection['login']['totp']
 | 
			
		||||
 | 
			
		||||
    if arguments.username_only:
 | 
			
		||||
        fake_key_raw(username)
 | 
			
		||||
    elif arguments.password_only:
 | 
			
		||||
        fake_key_raw(password)
 | 
			
		||||
    elif arguments.totp_only:
 | 
			
		||||
        # No point in moving it to the clipboard in this case
 | 
			
		||||
        fake_key_raw(
 | 
			
		||||
            get_totp_code(
 | 
			
		||||
                selection['id'],
 | 
			
		||||
                selection['name'],
 | 
			
		||||
                arguments.io_encoding,
 | 
			
		||||
                arguments.auto_lock
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
        # Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch
 | 
			
		||||
        # back into insert-mode, so the form can be directly submitted by
 | 
			
		||||
        # hitting enter afterwards
 | 
			
		||||
        fake_key_raw(username)
 | 
			
		||||
        qute_command('fake-key <Tab>')
 | 
			
		||||
        fake_key_raw(password)
 | 
			
		||||
 | 
			
		||||
    if arguments.insert_mode:
 | 
			
		||||
        qute_command('enter-mode insert')
 | 
			
		||||
 | 
			
		||||
    # If it finds a TOTP code, it copies it to the clipboard,
 | 
			
		||||
    # which is the same behavior as the Firefox add-on.
 | 
			
		||||
    if not arguments.totp_only and totp and arguments.totp:
 | 
			
		||||
        # The import is done here, to make pyperclip an optional dependency
 | 
			
		||||
        import pyperclip
 | 
			
		||||
        pyperclip.copy(
 | 
			
		||||
            get_totp_code(
 | 
			
		||||
                selection['id'],
 | 
			
		||||
                selection['name'],
 | 
			
		||||
                arguments.io_encoding,
 | 
			
		||||
                arguments.auto_lock
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return ExitCodes.SUCCESS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    arguments = argument_parser.parse_args()
 | 
			
		||||
    sys.exit(main(arguments))
 | 
			
		||||
							
								
								
									
										2
									
								
								qutebrowser/bookmarks/urls
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								qutebrowser/bookmarks/urls
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
https://duckduckgo.com/?q=yout&ia=about yout at DuckDuckGo
 | 
			
		||||
https://yewtu.be/feed/subscriptions Subscriptions - YewTube
 | 
			
		||||
							
								
								
									
										2432
									
								
								qutebrowser/config.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2432
									
								
								qutebrowser/config.py
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										3313
									
								
								qutebrowser/greasemonkey/HTML5视频播放器增强脚本.user.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3313
									
								
								qutebrowser/greasemonkey/HTML5视频播放器增强脚本.user.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										8
									
								
								qutebrowser/qsettings/QtProject.conf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								qutebrowser/qsettings/QtProject.conf
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
[FileDialog]
 | 
			
		||||
history=file:///home/chris, file:///home/chris/Downloads
 | 
			
		||||
lastVisited=file:///home/chris/Downloads
 | 
			
		||||
qtVersion=5.15.0
 | 
			
		||||
shortcuts=file:, file:///home/chris
 | 
			
		||||
sidebarWidth=116
 | 
			
		||||
treeViewHeader=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x1\0\0\0\0\0\0\0\0\x1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2L\0\0\0\x4\x1\x1\0\0\0\0\0\0\0\0\0\0\0\0\0\0_\xff\xff\xff\xff\0\0\0\x81\0\0\0\0\0\0\0\x4\0\0\0\xff\0\0\0\x1\0\0\0\0\0\0\0X\0\0\0\x1\0\0\0\0\0\0\0X\0\0\0\x1\0\0\0\0\0\0\0\x9d\0\0\0\x1\0\0\0\0\0\0\x3\xe8\0\xff\xff\xff\xff)
 | 
			
		||||
viewMode=Detail
 | 
			
		||||
							
								
								
									
										1
									
								
								qutebrowser/quickmarks
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								qutebrowser/quickmarks
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
yt https://yewtu.be/feed/subscriptions
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue