chiark / gitweb /
srcdump_vcsscript: make into a single hash, not a bevy of separate settings
[cgi-auth-flexible.git] / cgi-auth-flexible.pm
index 8c3cb49..4b4823a 100644 (file)
@@ -50,6 +50,7 @@ use Data::Dumper;
 use File::Copy;
 use Cwd qw/realpath/;
 
+
 #---------- public utilities ----------
 
 sub flatten_params ($) {
@@ -76,10 +77,11 @@ sub has_a_param ($$) {
 
 sub get_params ($) {
     my ($r) = @_;
-    my %p;
     my $c = $r->{Cgi};
-    foreach my $name ($c->param()) {
-       $p{$name} = [ $c->param($name) ];
+    my $vars = $c->Vars();
+    my %p;
+    foreach my $name (keys %$vars) {
+       $p{$name} = [ split "\0", $vars->{$name} ];
     }
     return \%p;
 }
@@ -178,8 +180,9 @@ sub gen_plain_login_link ($$) {
 sub gen_srcdump_link_html ($$$$) {
     my ($c,$r,$anchor,$specval) = @_;
     my %params = ($r->{S}{srcdump_param_name} => [ $specval ]);
-    return '<a href="'.escapeHTML($r->url_with_query_params(\%params)).'">'.
-       $anchor."</a>";
+    return '<a href="'.
+       escapeHTML($r->url_with_query_params(\%params,'SRCDUMP')).
+       '">'.$anchor."</a>";
 }
 sub gen_plain_licence_link_html ($$) {
     my ($c,$r) = @_;
@@ -236,6 +239,7 @@ sub srcdump_process_item ($$$$$$) {
     my $upwards = $item;
     for (;;) {
        $upwards =~ s#/+$##;
+       $upwards =~ s#/+\.$##;
        last unless $upwards =~ m#[^/]#;
        foreach my $try (@{ $v->{S}{srcdump_vcs_dirs} }) {
 #print STDERR "TRY $item $upwards $try\n";
@@ -244,13 +248,16 @@ sub srcdump_process_item ($$$$$$) {
                next;
            }
 #print STDERR "VCS $item $upwards $try\n";
-           return if $dirsdone->{$upwards}++;
+            if ($dirsdone->{$upwards}++) {
+               $outfn->("srcdump_process_item: did $upwards,".
+                        " skipping $item");
+               return;
+           }
 #print STDERR "VCS $item $upwards $try GO\n";
            $try =~ m/\w+/ or die;
-           return $v->_ch(('srcdump_byvcs_'.lc $&),
-                          $dumpdir, $upwards, $outfn);
+           return $v->_ch('srcdump_byvcs', $dumpdir, $upwards, $outfn, lc $&);
        }
-       $upwards =~ s#/*[^/]+##;
+       $upwards =~ s#/*[^/]+$##;
     }
     return $v->_ch('srcdump_novcs', $dumpdir, $item, $outfn);
 }
@@ -264,7 +271,7 @@ sub srcdump_novcs ($$$$$) {
            $script .= " \\! -name '$excl'";
        }
        $script .= " -print0";
-       return srcdump_dir_cpio($c,$v,$dumpdir,$item,$outfn,$script);
+       return srcdump_dir_cpio($c,$v,$dumpdir,$item,$outfn,'novcs',$script);
     } elsif (-f _) {
        return srcdump_file($c,$v,$dumpdir,$item,$outfn);
     } else {
@@ -272,14 +279,12 @@ sub srcdump_novcs ($$$$$) {
     }
 }
 
-sub srcdump_byvcs_git ($$$$$) {
-    my ($c, $v, $dumpdir, $dir, $outfn) = @_;
+sub srcdump_byvcs ($$$$$$) {
+    my ($c, $v, $dumpdir, $dir, $outfn, $vcs) = @_;
 #print STDERR "BYVCS GIT $dir\n";
-    return srcdump_dir_cpio($c,$v,$dumpdir,$dir,$outfn,"
-                 git ls-files -z
-                 git ls-files -z --others --exclude-from=.gitignore
-                 find .git -print0
-                            ");
+    my $script = $v->{S}{"srcdump_vcsscript"}{$vcs};
+    die "no script for vcs $vcs" unless defined $script;
+    return srcdump_dir_cpio($c,$v,$dumpdir,$dir,$outfn,$vcs,$script);
 }
 
 sub srcdump_file ($$$$) {
@@ -288,9 +293,9 @@ sub srcdump_file ($$$$) {
     copy($file,$outfile) or die "$file $outfile $!";
 }
 
-sub srcdump_dir_cpio ($$$$$) {
-    my ($c,$v,$dumpdir,$dir,$outfn,$script) = @_;
-    my $outfile = $outfn->("srcdump_dir_cpio saved $dir", "tar");
+sub srcdump_dir_cpio ($$$$$$$) {
+    my ($c,$v,$dumpdir,$dir,$outfn,$how,$script) = @_;
+    my $outfile = $outfn->("srcdump_dir_cpio $how saved $dir", "tar");
 #print STDERR "CPIO $dir >$script<\n";
     my $pid = fork();
     defined $pid or die $!;
@@ -313,7 +318,7 @@ sub srcdump_dir_cpio ($$$$$) {
        die $!;
     }
     $!=0; (waitpid $pid, 0) == $pid or die "$!";
-    die "$dir ($script) $outfile $?" if $?;
+    die "$dir ($how $script) $outfile $?" if $?;
 }
 
 sub srcdump_dirscan_prepare ($$) {
@@ -359,14 +364,14 @@ sub srcdump_dirscan_prepare ($$) {
        }
        if (!lstat "$item") {
            die "stat $item $!" unless $!==&ENOENT;
-           $outfn->("srcdump_dirscan_prepare stat ENOENT: skipping $item");
+           $outfn->("srcdump_dirscan_prepare stat ENOENT, skipping $item");
            next;
        };
        if (-l _) {
            $item = realpath($item);
            if (!defined $item) {
                die "realpath $item $!" unless $!==&ENOENT;
-               $outfn->("srcdump_dirscan_prepare realpath ENOENT:".
+               $outfn->("srcdump_dirscan_prepare realpath ENOENT,".
                         " skipping $item");
            }
        }
@@ -381,6 +386,7 @@ sub srcdump_dirscan_prepare ($$) {
        $dirsdone{$item}++;
     }
     close $reportfh or die $!;
+    srcdump_install($c,$v, $dumpdir, 'licence', 'text/plain');
     $!=0;
     my @cmd = (qw(tar -zvvcf), "$dumpdir/source.tmp",
               "-C", $dumpdir, qw(  --), @srcfiles);
@@ -390,7 +396,6 @@ sub srcdump_dirscan_prepare ($$) {
        die "tar failed";
     }
     die "licence file not found" unless defined $needlicence;
-    srcdump_install($c,$v, $dumpdir, 'licence', 'text/plain');
     srcdump_install($c,$v, $dumpdir, 'source', 'application/octet-stream');
     close $lockf or die $!;
 }
@@ -443,14 +448,13 @@ sub new_verifier {
            get_path_info => sub { $_[0]->path_info() },
            get_cookie => sub { $_[0]->cookie($_[1]->{S}{cookie_name}) },
            get_method => sub { $_[0]->request_method() },
-           check_https => sub { !!$_[0]->https() },
+           is_https => sub { !!$_[0]->https() },
            get_url => sub { $_[0]->url(); },
             is_login => sub { defined $_[1]->_rp('password_param_name') },
             login_ok => \&login_ok_password,
             username_password_error => sub { die },
            is_logout => sub { $_[1]->has_a_param('logout_param_names') },
            is_loggedout => sub { $_[1]->has_a_param('loggedout_param_names') },
-           is_page => sub { return 1 },
            handle_divert => sub { return 0 },
            do_redirect => \&do_redirect_cgi, # this hook is allowed to throw
            cookie_path => "/",
@@ -474,11 +478,13 @@ sub new_verifier {
                $_[2] =~ m#^/etc/|^/usr/(?!local/)(?!lib/cgi)#;
            },
            srcdump_process_item => \&srcdump_process_item,
-           srcdump_vcs_dirs => [qw(.git .hg .svn CVS)],
-           srcdump_byvcs_git => \&srcdump_byvcs_git,
-           srcdump_byvcs_hg => \&srcdump_byvcs_hg,
-           srcdump_byvcs_svn => \&srcdump_byvcs_svn,
-           srcdump_byvcs_cvs => \&srcdump_byvcs_cvs,
+           srcdump_vcs_dirs => [qw(.git .hg .bzr .svn)],
+           srcdump_vcsscript => [git => "
+                 git ls-files -z
+                 git ls-files -z --others --exclude-from=.gitignore
+                 find .git -print0
+                            "],
+           srcdump_byvcs => \&srcdump_byvcs,
            srcdump_novcs => \&srcdump_novcs,
            srcdump_excludes => [qw(*~ *.bak *.tmp), '#*#'],
            dump => \&dump_plain,
@@ -490,7 +496,9 @@ sub new_verifier {
     };
     my ($k,$v);
     while (($k,$v,@_) = @_) {
-       die "unknown setting $k" unless exists $verifier->{S}{$k};
+       die "unknown setting $k" unless
+           $k =~ m/^promise_/ or
+           exists $verifier->{S}{$k};
        $verifier->{S}{$k} = $v;
     }
     bless $verifier, $class;
@@ -656,9 +664,9 @@ my @ca = (-name => $r->{S}{cookie_name},
 
 # pages/param-sets are
 #   n normal non-mutating page
-#   r retrieval of information for JS, non-mutating
+#   r retrieval of information for JS etc., non-mutating
 #   m mutating page
-#   u update of information by JS, mutating
+#   u update of information by JS etc., mutating
 #   i login
 #   o logout
 #   O "you have just logged out" page load
@@ -778,7 +786,7 @@ sub _check_divert_core ($) {
 
     my $cooks = $r->_ch('get_cookie');
 
-    if ($r->{S}{encrypted_only} && !$r->_ch('check_https')) {
+    if ($r->{S}{encrypted_only} && !$r->_ch('is_https')) {
         return ({ Kind => 'REDIRECT-HTTPS',
                   Message => $r->_gt("Redirecting to secure server..."),
                   CookieSecret => undef,
@@ -895,6 +903,7 @@ sub _check_divert_core ($) {
         die unless $parmt eq 'y';
         die unless $cookh eq $parmh;
     }
+    $r->{ParmT} = $parmt;
     $r->{AssocSecret} = $cooks;
     $r->{UserOK} = $cooku;
 #print STDERR "C-D-C OK\n";
@@ -1024,12 +1033,16 @@ sub get_username ($) {
     return $r->{UserOK};
 }
 
-sub url_with_query_params ($$) {
-    my ($r, $params) = @_;
+sub url_with_query_params ($$;$) {
+    my ($r, $params, $nonpagetype) = @_;
 #print STDERR "PARAMS ",Dumper($params);
     my $uri = URI->new($r->_ch('get_url'));
     $uri->path($uri->path() . $params->{''}[0]) if $params->{''};
-    $uri->query_form(flatten_params($params));
+    my @flatparams = flatten_params($params);
+    if (defined $nonpagetype && $r->need_add_hidden('GET',$nonpagetype)) {
+       push @flatparams, $r->{S}{assoc_param_name}, $r->secret_hidden_val();
+    }
+    $uri->query_form(@flatparams);
     return $uri->as_string();
 }
 
@@ -1061,7 +1074,7 @@ sub check_ok ($) {
     }
 
     if ($kind =~ m/^REDIRECT-/) {
-       # for redirects, we honour stored NextParams and SetCookie,
+       # for redirects, we honour stored Params and Cookie,
        # as we would for non-divert
        if ($kind eq 'REDIRECT-LOGGEDOUT') {
            $params->{$r->{S}{loggedout_param_names}[0]} = [ 1 ];
@@ -1269,11 +1282,39 @@ sub check_mutate ($) {
     $r->_must_be_post();
 }
 
-sub mutate_ok ($) {
-    my ($r) = @_;
+our %_resource_get_needs_secret_hidden =
+    (map { $_ => 0 } qw(PAGE FRAME IFRAME SRCDUMP STYLESHEET FAVICON ROBOTS),
+     map { $_ => 1 } qw(IMAGE SCRIPT AJAX-XML AJAX-JSON AJAX-OTHER));
+
+sub update_get_need_add_hidden ($$;$) {
+    my ($r, $reqtype, $value, $force) = @_;
+    my $hash = ref $r
+       ? ($r->{GetNeedsSecretHidden} ||= { })
+       : \%_resource_get_needs_secret_hidden;
+    return if !$force &&
+       (exists $_resource_get_needs_secret_hidden{$reqtype} ||
+        exists $hash->{$reqtype});
+    $hash->{$reqtype} = $value;
+}
+
+sub need_add_hidden ($$) {
+    my ($r, $method, $reqtype) = @_;
+    return 1 if $method ne 'GET';
+    if (ref $r) {
+       my $ent = $r->{GetNeedsSecretHidden}{$reqtype};
+       return $ent if defined $ent;
+    }
+    my $ent = $_resource_get_needs_secret_hidden{$reqtype};
+    return $ent if defined $ent;
+    die "unsupported nonpage GET type $reqtype";
+}
+
+sub check_nonpage ($$) {
+    my ($r, $reqtype) = @_;
     $r->_assert_checked();
-    die if $r->{Divert};
-    return $r->_is_post();
+    return unless $r->resource_get_needs_secret_hidden($nonpagetype);
+    return if $r->{ParmT};
+    die "missing hidden secret parameter on nonpage request $nonpagetype";
 }
 
 #---------- output ----------
@@ -1307,41 +1348,3 @@ sub secret_cookie ($) {
 }
 
 1;
-
-__END__
-
-=head1 NAME
-
-CGI::Auth::Flexible - web authentication optionally using cookies
-
-=head1 SYNOPSYS
-
- my $verifier = CGI::Auth::Flexible->new_verifier(setting => value,...);
- my $authreq = $verifier->new_request($cgi_request_object);
-
- my $authreq = CGI::Auth::Flexible->new_request($cgi_request_object,
-                                              setting => value,...);
-
-=head1 USAGE PATTERN FOR SIMPLE APPLICATIONS
-
- $authreq->check_ok() or return;
-
- blah blah blah
- $authreq->check_mutate();
- blah blah blah
-
-=head1 USAGE PATTERN FOR FANCY APPLICATIONS
-
- my $divert_kind = $authreq->check_divert();
- if ($divert_kind) {
-     if ($divert_kind eq 'LOGGEDOUT') {
-        print "goodbye you are now logged out" and quit
-     } elsif ($divert_kind eq 'NOCOOKIES') {
-        print "you need cookies" and quit
-     ... etc.
-     }
- }
-
- blah blah blah
- $authreq->check_mutate();
- blah blah blah