pdb

Simple password manager
Log | Files | Refs | README

main.lua (3665B)


      1 local lfs = require( "lfs" )
      2 local symmetric = require( "symmetric" )
      3 
      4 local actions = require( "actions" )
      5 local paths = require( "paths" )
      6 
      7 function io.readfile( path )
      8 	local file, err = io.open( path, "r" )
      9 	if not file then
     10 		return nil, err
     11 	end
     12 
     13 	local contents, err = file:read( "*all" )
     14 	file:close()
     15 	return contents, err
     16 end
     17 
     18 function io.writefile( path, contents )
     19 	local file, err = io.open( path, "w" )
     20 	if not file then
     21 		return nil, err
     22 	end
     23 
     24 	local ok, err = file:write( contents )
     25 	if not ok then
     26 		file:close()
     27 		return nil, err
     28 	end
     29 
     30 	local ok, err = file:close()
     31 	if not ok then
     32 		return nil, err
     33 	end
     34 
     35 	return true
     36 end
     37 
     38 table.unpack = table.unpack or unpack
     39 
     40 local default_length = 32
     41 local default_pattern = "[%w%p ]"
     42 
     43 local function eprintf( form, ... )
     44 	io.stderr:write( form:format( ... ) .. "\n" )
     45 end
     46 
     47 local help =
     48 	"Usage: " .. arg[ 0 ] .. " <command>\n"
     49 	.. "where <command> is one of the following:\n"
     50 	.. "\n"
     51 	.. "init          - generate a new key\n"
     52 	.. "add <name>    - prompt you to enter a password for <name>\n"
     53 	.. "get <name>    - print the password stored under <name>\n"
     54 	.. "delete <name> - delete the password stored under <name>\n"
     55 	.. "list          - list stored passwords\n"
     56 	.. "gen <name> [length] [pattern] - generate a password for <name>\n"
     57 	.. "[pattern] is a Lua pattern. Some examples:\n"
     58 	.. "    gen test       - 32 characters, alphanumeric/puntuation/spaces\n"
     59 	.. "    gen test 16    - 16 characters, alphanumeric/puntuation/spaces\n"
     60 	.. "    gen test 10 %d - 10 characters, numbers only\n"
     61 	.. "    gen test \"%l \" - 32 characters, lowercase/spaces"
     62 
     63 local function load_key()
     64 	local key, err = io.readfile( paths.key )
     65 	if not key then
     66 		eprintf( "Unable to read key file: %s", err )
     67 		eprintf( "You might need to generate one with `pdb init`." )
     68 		return os.exit( 1 )
     69 	end
     70 
     71 	return key
     72 end
     73 
     74 local function write_new_key()
     75 	local key = symmetric.key()
     76 	return io.writefile( paths.key, key )
     77 end
     78 
     79 local commands = {
     80 	add = {
     81 		args = 1,
     82 		syntax = "<name>",
     83 	},
     84 	get = {
     85 		args = 1,
     86 	},
     87 	delete = {
     88 		args = 1,
     89 		syntax = "<name>",
     90 	},
     91 	list = {
     92 		args = 0,
     93 	},
     94 	gen = {
     95 		args = 3,
     96 		syntax = "<name> [length] [pattern]",
     97 	},
     98 }
     99 
    100 local cmd = arg[ 1 ]
    101 table.remove( arg, 1 )
    102 
    103 if not cmd then
    104 	print( help )
    105 	
    106 	return os.exit( 0 )
    107 end
    108 
    109 if cmd == "init" then
    110 	if io.open( paths.key, "r" ) then
    111 		eprintf( "Your key file already exists. Remove it and run init again if you're sure about this." )
    112 		return os.exit( 1 )
    113 	end
    114 
    115 	lfs.mkdir( paths.path )
    116 	lfs.mkdir( paths.db )
    117 
    118 	local ok, err = write_new_key()
    119 	if not ok then
    120 		eprintf( "Unable to write key file: %s", err )
    121 		return os.exit( 1 )
    122 	end
    123 
    124 	print( "You should chmod 600 " .. paths.key )
    125 
    126 	return os.exit( 0 )
    127 end
    128 
    129 local key = load_key()
    130 
    131 if cmd == "gen" and #arg > 0 then
    132 	local length
    133 	local pattern
    134 
    135 	if #arg == 1 then
    136 		length = default_length
    137 		pattern = default_pattern
    138 	elseif #arg == 3 then
    139 		length = tonumber( arg[ 2 ] )
    140 		pattern = "[" .. arg[ 3 ] .. "]"
    141 	else
    142 		length = tonumber( arg[ 2 ] ) or default_length
    143 		pattern = tonumber( arg[ 2 ] ) and default_pattern or arg[ 2 ]
    144 	end
    145 
    146 	arg[ 2 ] = length
    147 	arg[ 3 ] = pattern
    148 end
    149 
    150 if not actions[ cmd ] then
    151 	eprintf( "%s", help )
    152 	return os.exit( 1 )
    153 end
    154 
    155 if commands[ cmd ].args > 0 then
    156 	if arg[ 1 ]:find( "/" ) then
    157 		eprintf( "Password name can't contain slashes." )
    158 		return os.exit( 1 )
    159 	end
    160 
    161 	arg[ 1 ] = paths.db .. arg[ 1 ]
    162 end
    163 
    164 if commands[ cmd ].args ~= #arg then
    165 	eprintf( "Usage: %s %s %s", arg[ 0 ], cmd, commands[ cmd ].syntax or "" )
    166 	return os.exit( 1 )
    167 end
    168 
    169 local err = actions[ cmd ]( key, table.unpack( arg ) )
    170 
    171 if err then
    172 	eprintf( "%s", err )
    173 	return os.exit( 1 )
    174 end