source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Utils.pm @ 2008

Last change on this file since 2008 was 2008, checked in by nanardon, 7 years ago

Ignore empty line in object file

  • Property svn:keywords set to Id Rev
File size: 10.3 KB
Line 
1package LATMOS::Accounts::Utils;
2use 5.010000;
3use strict;
4use warnings;
5use Exporter ();
6use vars qw(@ISA @EXPORT_OK @EXPORT);
7use utf8;
8use LATMOS::Accounts::Log;
9use File::Temp qw(tempfile);
10use Crypt::Cracklib;
11use Text::CSV;
12
13our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
14
15=head1 NAME
16
17LATMOS::Accounts::Utils - Utils functions
18
19=head1 FUNCTIONS
20
21=cut
22
23@ISA = qw(Exporter);
24@EXPORT = qw(to_ascii exec_command switch_user run_via_sudo buildLogin loadCSV yesno);
25@EXPORT_OK = qw(to_ascii exec_command switch_user run_via_sudo buildLogin loadCSV yesno);
26
27=head2 to_ascii($text)
28
29Replace in C<$text> non ascii caracters from iso-8859-15 table to ascii
30equivalent caracter.
31
32=cut
33
34sub to_ascii {
35    my ($text) = @_;
36    return $text unless(defined($text));
37    utf8::decode($text) unless(utf8::is_utf8($text));
38    $text =~ s/œ/oe/g;
39    $text =~ s/Ê/ae/g;
40    $text =~ tr {uàâÀÂÄÀçéÚêëÉÈÊËïîÏÎÞöÎÖÔÌûÛÜć}
41                {uaaaAAAceeeeEEEEiiIIoooOOuuUUc};
42    $text =~ s/([^[:ascii:]])/_/g;
43    $text
44} 
45
46=head2 exec_command($command, $env)
47
48Execute C<$command> and redirect output to log system.
49
50C<$env> is a hashref containing environment variable to set, all variables are
51prefixed by 'LA_'.
52
53=cut
54
55sub exec_command {
56    my ($command, $env) = @_;
57    my $rout = undef;
58    $rout = \$_[2] if(@_ > 2);
59
60    my @exec = ref $command
61        ? (@$command)
62        : ($command);
63    la_log(LA_DEBUG, 'running command `%s\'', join(' ', @exec));
64
65    pipe(my $rh, my $wh);
66    my $pid = fork;
67    if (!defined($pid)) {
68        la_log(LA_ERR, "Can't launch script: cannot fork");
69    } elsif ($pid) {
70        # Father
71        close($wh);
72        my $header;
73        while (<$rh>) {
74            if ($rout) {
75                $$rout .= $_;
76            } else {
77                chomp;
78                if (!$header) {
79                    $header = 1;
80                    la_log(LA_NOTICE, "exec `%s'", join(' ', @exec));
81                }
82                la_log(LA_NOTICE, "output: %s", $_);
83            }
84        }
85        waitpid($pid, 0);
86        if (my $exitstatus = $?) {
87            la_log(LA_ERR, 'command %s exit with status %d',
88                join(' ', @exec), $exitstatus);
89            return;
90        } else {
91            return 1;
92        }
93    } else {
94        # Child
95        close($rh);
96        ( $ENV{LA_MODULE} ) = caller();
97        foreach (keys %{ $env || {} }) {
98            $ENV{"LA_$_"} = $env->{$_};
99        }
100        open(STDOUT, ">&=" . fileno($wh));
101        open(STDERR, ">&=" . fileno($wh));
102        exec(@exec);
103        exit($!);
104    }
105    1
106}
107
108=head2 parse_obj_file($handle)
109
110Read file content from C<$handle> and return hash containing parsed attributes
111
112=cut
113
114sub parse_obj_file {
115    my ($handle) = @_;
116
117    my %attributes;
118    while (my $line = <$handle>) {
119        $line =~ /^#/ and next;
120        # Empty line are skipped (or space only)
121        $line =~ /^\s*$/ and next;
122        chomp($line);
123        my ($attr, $value) = $line =~ /^\s*(\S+):\s*(.*)\s*$/ or
124            die "Malformed input file\n";
125        $value =~ s/\s*$//;
126        $value =~ s/\\n/\n/g;
127        if ($attributes{$attr}) {
128            if (ref $attributes{$attr}) {
129                push(@{ $attributes{$attr} }, $value);
130            } else {
131                my $temp = $attributes{$attr};
132                $attributes{$attr} = [ $temp, $value ];
133            }
134        } else {
135            $attributes{$attr} = $value eq '' ? undef : $value;
136            # Don't remember why this is here
137            #$attr eq 'exported' && !defined $attributes{$attr} and $attributes{$attr} = 1;
138        }
139    }
140    %attributes
141}
142
143=head2 dump_read_temp_file($writecb, $readcb)
144
145Create a temporary file, call C<$writecb()> function, run editor and if file get
146modified call C<$readcb>.
147
148=cut
149
150sub dump_read_temp_file {
151    my ($writecb, $readcb) = @_;
152
153    my ($fh, $filename) = tempfile(CLEANUP => 0);
154    $writecb->($fh) or return;
155    $fh = undef; # closing file
156    my $res;
157   
158    my @stat = stat($filename);
159    while (1) {
160        my $cmd = ($ENV{EDITOR} || 'vi') . " $filename";
161        warn "Running $cmd\n";
162        if (system($cmd) == -1 ) {
163            warn "Cannot run editor $!\n";
164            last;
165        }
166        if ((stat($filename))[9] == $stat[9]) {
167            warn "No change existing\n";
168            last;
169        }
170
171        open($fh, '<', $filename) or return;
172        $res = $readcb->($fh);
173        $fh = undef; # closing again
174        $res < 2 and last;
175    }
176    unlink($filename);
177    $res;
178}
179
180=head2 loadCSV($fh, $callback, $initcallback)
181
182Parse CVS files and return an array for each parsed line.
183
184C<%options> may contains
185
186=over 4
187
188=item C<initcb> An function call after parsing first line
189
190=item C<cb> A function call for each line
191
192=back
193
194=cut
195
196sub loadCSV {
197    my ($fh, %opt) = @_;
198
199    my $csv = Text::CSV->new({
200            blank_is_undef => 1,
201            binary => 1,
202    });
203
204    binmode($fh, ":encoding(UTF-8)");
205
206    # First line contains attribute
207    my $columns = $csv->getline( $fh );
208
209    $csv->column_names($columns);
210
211    if ($opt{initcb}) {
212        $opt{initcb}->($csv);
213    }
214
215    my $all = [];
216    my $linecount = 1;
217    while ( my $row = $csv->getline_hr( $fh ) ) {
218        $linecount++;
219        if ($opt{cb}) {
220            if (! $opt{cb}->($row, $linecount)) {
221                return;
222            }
223        }
224        push(@{ $all }, $row);
225    }
226    $csv->eof () or do {
227        return;
228    };
229
230    return $all;
231}
232
233=head2 check_oid_validity($name)
234
235Check C<$name> is suitable to be used as object identifier. Return the error
236text, undef if no error.
237
238=cut
239
240sub check_oid_validity {
241    my ($name) = @_;
242    return "leadind space" if ($name =~ /^\s/);
243    return "trailing space" if ($name =~ /\s$/);
244    return "containing space" if ($name =~ /\s/);
245
246    return;
247}
248
249=head2 check_ug_validity($name)
250
251Check C<$name> is suitable to used as user or group identifier.
252
253=cut
254
255sub check_ug_validity {
256    my ($name) = @_;
257    return "Empty name is not a valid name !"
258        if (!$name);
259    return "first caractere must be a-z"
260        if ($name !~ /^[a-z]/);
261    return "must contain only a-z,0-9"
262        if ($name !~ /^[a-z,0-9,_,\-,\.]+$/);
263
264    return check_oid_validity($name);
265}
266
267=head2 switch_user($runas)
268
269Switch effective id of the process to user named C<$runas>
270
271=cut
272
273sub switch_user {
274    my ($runas) = @_;
275
276    if ($< == 0 || $> == 0) {
277        my @info = getpwnam($runas) or do {
278            warn "Can find user $runas";
279            return;
280        };
281        $> = $info[3];
282        return;
283    } else {
284        warn "we are not root";
285    }
286}
287
288=head2 run_via_sudo($runas)
289
290Rerun current programme as C<$runas> user using sudo
291
292=cut
293
294sub run_via_sudo {
295    my ($runas) = @_;
296
297    my @info = getpwnam($runas) or do {
298        warn "Can find user $runas";
299        return;
300    }; 
301    if ($< != $info[3]) {
302        exec('sudo', '-u', $runas, $0, @ARGV) or "Can run $!";
303    }
304}
305
306=head2 genpassword(%options)
307
308Generate a random password, options are:
309
310=over 4
311
312=item length
313
314The minimum password length (default is 6)
315
316=item nonalpha
317
318Include non alpha-numeric caracters
319
320=item syllables
321
322Use a set of syllables instead letter
323
324=back
325
326=cut
327
328sub genpassword {
329    my (%options) = @_;
330    my @non_alpha = (qw$; : / ! ( ) [ ] { } + = @ - " ' % * & . ? < >$, ',', '$');
331    my @letters = ('a' .. 'z', 'A' .. 'Z', 0 .. 9);
332
333    my @consonants = qw(b d f g j k l m n r s t v x z ch);
334    my @vowels = qw(a e i o u ou oi io ia iu);
335
336    $options{length} ||= 8 + int(rand(3));
337
338    while (1) {
339        if ($options{syllables}) {
340            $options{length} = int($options{length} / 2);
341            $options{length} = 3 if ($options{length} < 3);
342        } else {
343            $options{length} = 6 if ($options{length} < 6);
344        }
345
346        my @chars;
347
348        if ($options{nonalpha}) {
349            for(0 .. (0 + int(rand(3)))) {
350                push(@chars, (@non_alpha[rand(scalar(@non_alpha))]));
351            }
352        }
353
354        foreach (1 .. ($options{length} - scalar(@chars))) {
355            if ($options{syllables}) {
356                my $c = @consonants[rand(scalar(@consonants))];
357                my $v = @vowels[rand(scalar(@vowels))];
358                push(@chars, "$c$v");
359            } else {
360                push(@chars, (@letters[rand(scalar(@letters))]));
361            }
362        }
363
364
365        my $pass = join('', sort { rand() <=> rand() } @chars);
366        if (length($pass) >= 6 && fascist_check($pass) eq 'ok') {
367            return $pass;
368        }
369    }
370}
371
372=head2 buildLogin([$cb, ] @names)
373
374Try to find a proper login from @names.
375
376Optionnal $cb is a callback to check the solution over exiting data. If return false, another solution is tried
377
378Example:
379
380    buildLogin(sub { $_[0] ? 1 : 0 }, "sn", "givenName")
381
382=cut
383
384sub buildLogin {
385    my (@names) = @_;
386    my $cb = undef;
387
388    if (ref $names[0]) {
389        $cb = shift(@names);
390    } else {
391        $cb = sub { 1 }; # always validating
392    }
393
394    # Cleaning names:
395    foreach (@names) {
396        $_ ||= '';
397        $_ = lc(to_ascii($_));
398        s/[^\w]//g;
399    }
400
401    @names = grep { $_ } @names;
402    my $base = shift(@names) or return;
403    my $rest = join('', grep { $_ } @names);
404
405    if ((my $l = length($base)) > 10) {
406        my $len = 8;
407        while ($len < $l) {
408            my $try  = substr($base, 0, $len);
409            if ($cb->($try)) {
410                return $try;
411            }
412            $len++;
413        }
414    } elsif ($cb->($base)) {
415        return $base;
416    }
417
418    my $try = $base;
419    while (my $len = length($try) - length($base) + 1) {
420        if ($len > length($rest)) {
421            last;
422        }
423        $try = $base . substr($rest, 0, $len);
424        if ($cb->($try)) {
425            return $try;
426        }
427    }
428    return;
429}
430
431=head2 yesno($value, $default)
432
433Check is a paramter mean yes or no
434
435=cut
436
437sub yesno {
438    my ($value, $default) = @_;
439
440    if ($value =~ /^(yes|1|true)$/i) {
441        return 1;
442    }
443    if ($value =~ /^(no|0|false)$/i) {
444        return 0;
445    }
446    if ($value =~ /^\d+$/ and $value != 0) {
447        return 1;
448    }
449    return $default || $value;
450}
451
4521;
453
454__END__
455
456=head1 SEE ALSO
457
458L<sudo>
459
460=head1 AUTHOR
461
462Olivier Thauvin, E<lt>olivier.thauvin@aerov.jussieu.frE<gt>
463
464=head1 COPYRIGHT AND LICENSE
465
466Copyright (C) 2008, 2009, 2010, 2011, 2012 CNRS SA/CETP/LATMOS
467
468This library is free software; you can redistribute it and/or modify
469it under the same terms as Perl itself, either Perl version 5.10.0 or,
470at your option, any later version of Perl 5 you may have available.
471
472=cut
Note: See TracBrowser for help on using the repository browser.