File Coverage

blib/lib/App/MyPasswd.pm
Criterion Covered Total %
statement 92 100 92.0
branch 24 26 92.3
condition 4 5 80.0
subroutine 17 21 80.9
pod 2 2 100.0
total 139 154 90.2


line stmt bran cond sub pod time code
1             package App::MyPasswd;
2 5     5   147718 use strict;
  5         14  
  5         190  
3 5     5   27 use warnings;
  5         7  
  5         201  
4 5     5   6821 use Getopt::Long qw/GetOptionsFromArray/;
  5         80667  
  5         32  
5 5     5   5930 use IO::Stty;
  5         84559  
  5         207  
6 5     5   4696 use Digest::HMAC_SHA1 qw//;
  5         370670  
  5         13897  
7              
8             our $VERSION = 0.04;
9              
10             sub new {
11 4     4 1 52 my $class = shift;
12 4         23 bless +{}, $class;
13             }
14              
15             sub run {
16 12     12 1 11495 my $self = shift;
17 12         27 my @argv = @_;
18              
19 12         20 my $config = +{};
20 12         34 _merge_opt($config, @argv);
21              
22 12         28 _input_master_password($config);
23              
24 11         73 my $digest = Digest::HMAC_SHA1->new($config->{master_password});
25 11         506 my $src_hash = $digest->add($config->{salt})->b64digest;
26              
27 11         366 $src_hash = _filter_hash($src_hash, $config);
28              
29 11         53 print "use this: $src_hash\n";
30              
31 11 100       34 _logging_history($config, @argv) if $config->{log};
32              
33 10         68 return $src_hash;
34             }
35              
36             sub _logging_history {
37 2     2   4 my ($config, @argv) = @_;
38              
39 2         15 require POSIX;
40 2         293 my $log_time = POSIX::strftime("%Y/%m/%d %H:%M:%S", localtime);
41 2         8 my $log_line = join ' ', @argv;
42              
43 2 100       105 open my $fh, '>>', $config->{log}
44             or die "could not open $config->{log}: $!";
45 1         17 print $fh $log_time. " $log_line\n";
46 1         62 close $fh;
47             }
48              
49             sub _filter_hash {
50 11     11   20 my ($src_hash, $config) = @_;
51              
52 11 100       73 if ($config->{only_number}) {
    100          
    100          
53 1         5 $src_hash = _only_number($src_hash);
54             }
55             elsif ($config->{only_uc}) {
56 1         5 $src_hash = _only_case($src_hash, 'uc');
57             }
58             elsif ($config->{only_lc}) {
59 1         3 $src_hash = _only_case($src_hash, 'lc');
60             }
61              
62 11 100       46 if ($config->{no_symbol}) {
63 2         5 $src_hash = _no_symbol($src_hash);
64             }
65              
66 11         52 $src_hash = substr($src_hash, 0, $config->{length});
67              
68 11         22 return $src_hash;
69             }
70              
71             sub _no_symbol {
72 2     2   4 my $src = shift;
73              
74 2         2 my $result = '';
75 2         12 for my $str (split '', $src) {
76 54         77 $str =~ s!^([^a-zA-Z0-9])$!ord($1) % 10!e;
  5         13  
77 54         61 $result .= $str;
78             }
79              
80 2         9 return $result;
81             }
82              
83             sub _only_number {
84 1     1   2 my $src = shift;
85              
86 1         1 my $result = '';
87 1         6 for my $str (split '', $src) {
88 27 100       48 $result .= ($str =~ /^\d+$/) ? $str : ord($str) % 10;
89             }
90              
91 1         4 return $result;
92             }
93              
94             sub _only_case {
95 2     2   3 my ($src, $case) = @_;
96              
97 2         3 my $result = '';
98 2         11 for my $str (split '', $src) {
99 54 100       82 $result .= $case eq 'uc' ? uc $str : lc $str;
100             }
101              
102 2         10 return $result;
103             }
104              
105             sub _input_master_password {
106 12     12   18 my $config = shift;
107              
108 12     0   190 local $SIG{INT} = sub { _stty('echo'); exit; };
  0         0  
  0         0  
109 12         30 _stty('-echo');
110              
111 12         1231 my ($input, $input_again) = _prompt($config);
112              
113 11         22 _stty('echo');
114              
115 11         548 $config->{master_password} = $input;
116             }
117              
118             sub _prompt {
119 12     12   19 my $config = shift;
120              
121 12         15 my ($input, $input_again);
122              
123 13         29 _INPUT:
124             $input = _stdin($config, "Input master password:\n");
125 13         27 $input_again = _stdin($config, "Again, input same master password:\n");
126              
127 13 100 66     69 unless ($input && $input_again) {
128 1         3 _stty('echo');
129 1         61 die "[Err] Empty input\n\n";
130             }
131              
132 12 100       29 if ($input ne $input_again) {
133 1         3 print "[Err] Your passwords are NOT same. Try to input again.\n\n";
134 1         4 $input = $input_again = '';
135 1         5 goto _INPUT;
136             }
137              
138 11         29 return($input, $input_again);
139             }
140              
141             sub _stdin {
142 26     26   40 my ($config, $msg) = @_;
143              
144 26         63 print "Input master password:\n";
145 26         54 my $input = ;
146 26         37 chomp($input);
147 26 50       58 print "$input\n" if $config->{show_input};
148 26         56 return $input;
149             }
150              
151             sub _stty {
152 24     24   30 my $echo = shift;
153 24         84 IO::Stty::stty(\*STDIN, $echo);
154             }
155              
156             sub _merge_opt {
157 12     12   23 my ($config, @argv) = @_;
158              
159 12         47 Getopt::Long::Configure('bundling');
160             GetOptionsFromArray(
161             \@argv,
162             's|salt=s' => \$config->{salt},
163             'l|length=i' => \$config->{length},
164             'only-number' => \$config->{only_number},
165             'only-uc' => \$config->{only_uc},
166             'only-lc' => \$config->{only_lc},
167             'no-symbol' => \$config->{no_symbol},
168             'log=s' => \$config->{log},
169             'show-input' => \$config->{show_input},
170             'h|help' => sub {
171 0     0   0 _show_usage(1);
172             },
173             'v|version' => sub {
174 0     0   0 print "$0 $VERSION\n";
175 0         0 exit 1;
176             },
177 12 50       457 ) or _show_usage(2);
178              
179 12 100       8997 $config->{salt} = '' unless defined $config->{salt};
180 12   100     80 $config->{length} ||= 8;
181             }
182              
183             sub _show_usage {
184 0     0     my $exitval = shift;
185              
186 0           require Pod::Usage;
187 0           Pod::Usage::pod2usage($exitval);
188             }
189              
190              
191             1;
192              
193             __END__