text: #  Licensed under the Apache License, Version 2.0 (the "License"); you may
  #  not use this file except in compliance with the License. You may obtain
  #  a copy of the License at
  #
  #       http://www.apache.org/licenses/LICENSE-2.0
  #
  #  Unless required by applicable law or agreed to in writing, software
  #  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  #  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  #  License for the specific language governing permissions and limitations
  #  under the License.
  
  from __future__ import print_function
  
  import argparse
  import base64
  import configparser
  import hashlib
  import sys
  from os import path
  from os import urandom
  
  import pyotp
  
  import pyperclip
  
  __version__ = '0.0.2'
  
  CONFIG_FILE = '.softtoken.conf'
  
  
  def load_config():
      dir_path = path.join(path.expanduser('~'), CONFIG_FILE)
      cfg = configparser.SafeConfigParser()
      cfg.read(dir_path)
      return cfg
  
  
  def save_config(cfg):
      dir_path = path.join(path.expanduser('~'), CONFIG_FILE)
      try:
          with open(dir_path, 'w+') as configfile:
              cfg.write(configfile)
      except Exception:
          print('ERROR: Cannot write config file')
          sys.exit(2)
  
  
  def create_token(name, hash_function='sha256', digits=6, seed_length=20):
      cfg = load_config()
      if cfg.has_section(name):
          print('Token %s already exists. Delete it first' % name)
          sys.exit(2)
  
      seed = urandom(seed_length)
  
      cfg.add_section(name)
      cfg.set(name, 'hash', hash_function)
      cfg.set(name, 'digits', str(digits))
      cfg.set(name, 'seed', base64.b32encode(seed))
  
      save_config(cfg)
  
      print('\nNew Token created:\n\n%s\n-------------' % name)
      print('Seed (hex): %s' % seed.encode('hex'))
      print('Seed (b32): %s\n' % base64.b32encode(seed))
  
  
  def delete_token(name):
      cfg = load_config()
      if not cfg.has_section(name):
          print('Token %s does not exist' % name)
          sys.exit(2)
      cfg.remove_section(name)
      save_config(cfg)
      print("Token %s successfully deleted" % name)
  
  
  def print_tokens():
      cfg = load_config()
      for section in cfg.sections():
          print("[*] %s" % section)
  
  
  def main():
  
      parser = argparse.ArgumentParser(version=__version__)
      parser.add_argument('--new', action='store_true', default=False,
                          dest='new_token', help='Generate a new Soft Token')
      parser.add_argument('--delete', action='store_true', default=False,
                          dest='delete_token', help='Delete a Soft Token')
      parser.add_argument('--list', action='store_true', default=False,
                          dest='list_tokens', help='List configured tokens')
      parser.add_argument('--token', '-t', required=False, dest='token_name',
                          help='Soft Token name')
      parser.add_argument('--hash', default='sha256', dest='hash_function',
                          choices=('sha1', 'sha256', 'sha512'), help='Hash '
                          'function to use (default is sha256)')
      parser.add_argument('--digits', '-d', type=int, default=6, dest='digits',
                          help='OTP Length (default is 6)')
      parser.add_argument('--length', '-l', type=int, default=20,
                          dest='seed_length', help='Seed length in bytes '
                          '(default is 20)')
      parser.add_argument('-X', action='store_true', default=False,
                          dest='print_focus', help='Output the OTP where '
                          'the current focus is')
      parser.add_argument('-C', action='store_true', default=False,
                          dest='copy_clipboard', help='Copy OTP to clipboard')
  
      args = parser.parse_args()
  
      if args.list_tokens:
          print_tokens()
          sys.exit(0)
  
      if args.token_name is None:
          print("A Token name is required for this action")
          parser.print_help()
          sys.exit(-1)
  
      if args.new_token:
          create_token(args.token_name,
                       args.hash_function,
                       args.digits,
                       args.seed_length)
          sys.exit(0)
  
      if args.delete_token:
          delete_token(args.token_name)
          sys.exit(0)
  
      if args.list_tokens:
          print_tokens()
          sys.exit(0)
  
      # Generate new OTP if the token exists
      cfg = load_config()
      if not cfg.has_section(args.token_name):
          print('Token %s does not exist' % args.token_name)
          sys.exit(2)
  
      if args.hash_function == 'sha1':
          hf = hashlib.sha1
      elif args.hash_function == 'sha256':
          hf = hashlib.sha256
      elif args.hash_function == 'sha512':
          hf = hashlib.sha512
  
      seed = cfg.get(args.token_name, 'seed')
      totp = pyotp.TOTP(seed, digest=hf, digits=args.digits)
  
      otp = totp.now()
  
      if args.print_focus:
          from pykeyboard import PyKeyboard
          k = PyKeyboard()
          k.type_string(otp)
      elif args.copy_clipboard:
          pyperclip.copy(otp.encode('ascii'))
      else:
          print(otp)
  
  
  if __name__ == "__main__":
      main()
  