require "option_parser" require "yaml" require "colorize" # password serializer class Password include YAML::Serializable @[YAML::Field(key: "url")] property url : String @[YAML::Field(key: "email")] property email : String @[YAML::Field(key: "login")] property login : String @[YAML::Field(key: "password")] property password : String @[YAML::Field(key: "desc")] property desc : String @[YAML::Field(key: "profile_url")] property profile_url : String @[YAML::Field(key: "update")] property update : Int32 end VERSION = "0.2.4" PASSWORD_FILE_PATH = "#{ENV["HOME"]}/.pwd.yml" ASCII_LOGO = " ██████╗ ███╗ ███╗███╗ ██╗ ██████╗ ██╔══██╗████╗ ████║████╗ ██║██╔════╝ ██████╔╝██╔████╔██║██╔██╗ ██║██║ ███╗ ██╔═══╝ ██║╚██╔╝██║██║╚██╗██║██║ ██║ ██║ ██║ ╚═╝ ██║██║ ╚████║╚██████╔╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ " # program options begin OptionParser.parse do |parser| parser.banner = "The very simple password manager for humans\n" parser.on "-v", "--version", "Show version" do puts ASCII_LOGO.colorize(:yellow) puts "The very simple password manager for humans.".colorize(:yellow) print "Version ".colorize(:yellow) puts VERSION.colorize(:red).mode(:bold) print "\nURL to full change log: ".colorize(:yellow) puts "https://git.a2s.su/iiiypuk/pmng/raw/branch/master/HISTORY.md".colorize(:green).mode(:bold) exit(0) end parser.on "-h", "--help", "Show help" do puts parser exit(0) end parser.on "-g", "--generate-password", "Generate password" do puts Random::Secure.urlsafe_base64(16, padding: false).colorize(:black).back(:white) puts Random::Secure.urlsafe_base64(16, padding: false).colorize(:white).back(:blue) puts Random::Secure.urlsafe_base64(16, padding: false).colorize(:white).back(:red) exit(0) end parser.on "-t", "--unixtime", "Return local timestamp" do puts Time.local.to_unix.colorize(:yellow).mode(:bold) exit(0) end end rescue ex puts ex.message, "" end # check password file exists if File.exists?(PASSWORD_FILE_PATH) yaml = File.open(PASSWORD_FILE_PATH) do |file| YAML.parse(file) end else print "~/.pwd.yml".colorize(:red).mode(:bold) puts ": No such file" exit(1) end # check file ppermissions password_file_permissions = File.info(PASSWORD_FILE_PATH).permissions.to_s if /\d{3}/.match(password_file_permissions).try &.[0] != "600" puts "Password file permissions is not RW for you.".colorize(:red) print "Execute: ".colorize(:yellow) print "(chmod 600 ~/.pwd.yml) ".colorize(:green).mode(:bold) puts "for fix.".colorize(:yellow) exit(1) end # fill passwords array passwords_array = [] of Password count = 0 while count < yaml.size passwords_array << Password.from_yaml(yaml[count].to_yaml) count += 1 end # pmng title system "clear" puts ASCII_LOGO.colorize(:yellow) puts "The very simple password manager for humans".colorize(:yellow).mode(:bold) puts "-------------------------------------------".colorize(:yellow).mode(:bold) # main loop loop = true while loop # shell prompt print "Enter URL (".colorize(:white).mode(:bold) print ":h".colorize(:red).mode(:bold) print " for help or ".colorize(:white).mode(:bold) print ":q".colorize(:red).mode(:bold) print " for exit)\n".colorize(:white).mode(:bold) print "> ".colorize(:green).mode(:bold) password_string = gets if password_string.to_s == ":q" # if ':q' to close program loop = false elsif password_string.to_s.size == 0 # if puts empty, retry prompt puts elsif password_string.to_s == ":h" # if ':h' to view help system "clear" puts "Help\n----".colorize(:yellow).mode(:bold) print ":s".colorize(:red).mode(:bold) puts " - Return stats" elsif password_string.to_s == ":s" # if ':s' to view Statistics system "clear" puts "Statistics\n----------".colorize(:yellow).mode(:bold) print "All elements: ".colorize(:yellow).mode(:bold) puts passwords_array.size print "Passwords outdated: ".colorize(:red).mode(:bold) outdated_count = 0 current_time = Time.local.to_unix a = [] of String passwords_array.each do |item| if item.update + (2629743 * 3) < current_time # 2629743 * 3 -- 3 month outdated_count += 1 a << item.url end end puts outdated_count else # list search password system "clear" passwords_finded_array = 0 passwords_array.each do |item| if item.url.includes?(password_string.to_s) print "🌐 " puts item.url.colorize(:magenta).mode(:bold).mode(:underline) if !item.email.blank? print "📧 " puts item.email.colorize(:red) end if !item.login.blank? print "🗿 " puts item.login.colorize(:yellow) end if !item.password.blank? print "🔐 " puts item.password.colorize(:red).back(:red) end if !item.desc.blank? print "📄 " puts item.desc.colorize(:cyan) end if !item.profile_url.blank? print "👦 " puts item.profile_url.colorize(:green) end puts "-----".colorize(:dark_gray).mode(:bold) passwords_finded_array += 1 end end puts print "Finded ".colorize(:yellow).mode(:bold) print passwords_finded_array.colorize(:red).mode(:bold) puts " records".colorize(:yellow).mode(:bold) puts "-----".colorize(:dark_gray).mode(:bold) end puts end system "clear" puts "Bye! 👋"