<commit_before>require 'lastpass'
require 'active_support/core_ext/numeric/time'
require 'timers'

module LastPassIndicator
  class Main
    def initialize
      @timers = Timers::Group.new
      @config = Config.new
      @menu = Menu.new(@config)
      @menu.on_account do |selected_account|
        puts "Asked for #{selected_account}"
        with_vault do |vault|
          accounts = vault.accounts.select { |account| account.id == selected_account[:id] }
          if accounts.any?
            account = accounts.first
            PasswordOutput.write_password account
          else
            error_dialog "Unknown account '#{selected_account[:name]}'\nID #{selected_account[:id]} not found"
          end
        end
      end
      @menu.on_other do
        with_vault do |vault|
          account_window = AccountWindow.new(vault)
          account_window.on_select do |account|
            PasswordOutput.write_password account
          end
        end
      end
      @menu.on_configure { puts 'TODO' }
    end

    def run
      Gtk.main
    end

    private

    # Obtain the decrypted LastPass vault
    #
    # Prompts for username (if required) and password - asynchronously with a GTK dialog
    # Retrieves the encrypted blob from LastPass (if required) - asynchronously on a non-GTK thread
    # Yields the decrypted vault to the given block - back in the GTK main loop
    def with_vault(&block)
      # Short circuit all of this if the vault has been kept open
      return block.call(@vault) if @vault

      login_window = LoginWindow.new(@config, reprompt: !@blob.nil?)
      login_window.on_login do |username, password, remember|
        Thread.start do
          begin
            @blob ||= LastPass::Vault.fetch_blob username, password
            vault = LastPass::Vault.open @blob, username, password

            if remember
              @vault = vault
              @timers.after(5.minutes) { @vault = nil }
            end

            idle do
              login_window.finished(true)
              block.call(vault)
            end
          rescue LastPass::Error => e
            # TODO: handle multifactor prompts (maybe handle duo magic)
            error_dialog "Error logging into LastPass: #{e.message}"<commit_msg>Handle password errors during reprompts<commit_after>require 'lastpass'
require 'active_support/core_ext/numeric/time'
require 'timers'

module LastPassIndicator
  class Main
    def initialize
      @timers = Timers::Group.new
      @config = Config.new
      @menu = Menu.new(@config)
      @menu.on_account do |selected_account|
        puts "Asked for #{selected_account}"
        with_vault do |vault|
          accounts = vault.accounts.select { |account| account.id == selected_account[:id] }
          if accounts.any?
            account = accounts.first
            PasswordOutput.write_password account
          else
            error_dialog "Unknown account '#{selected_account[:name]}'\nID #{selected_account[:id]} not found"
          end
        end
      end
      @menu.on_other do
        with_vault do |vault|
          account_window = AccountWindow.new(vault)
          account_window.on_select do |account|
            PasswordOutput.write_password account
          end
        end
      end
      @menu.on_configure { puts 'TODO' }
    end

    def run
      Gtk.main
    end

    private

    # Obtain the decrypted LastPass vault
    #
    # Prompts for username (if required) and password - asynchronously with a GTK dialog
    # Retrieves the encrypted blob from LastPass (if required) - asynchronously on a non-GTK thread
    # Yields the decrypted vault to the given block - back in the GTK main loop
    def with_vault(&block)
      # Short circuit all of this if the vault has been kept open
      return block.call(@vault) if @vault

      login_window = LoginWindow.new(@config, reprompt: !@blob.nil?)
      login_window.on_login do |username, password, remember|
        Thread.start do
          begin
            @blob ||= LastPass::Vault.fetch_blob username, password
            vault = LastPass::Vault.open @blob, username, password

            if remember
              @vault = vault
              @timers.after(5.minutes) { @vault = nil }
            end

            idle do
              login_window.finished(true)
              block.call(vault)
            end
          rescue LastPass::Error, OpenSSL::OpenSSLError => e
            # TODO: handle multifactor prompts (maybe handle duo magic)
            error_dialog "Error logging into LastPass: #{e.message}"
{'fraction_non_alphanumeric': 0.0780327868852459, 'fraction_numerical': 0.0004371584699453552, 'mean_word_length': 2.164591977869986, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '99965', 'n_tokens_mistral': 1268, 'n_tokens_neox': 1246, 'n_words': 378}