]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Config.pm
cleanup: use '$ibx' consistently when referring to Inbox refs
[public-inbox.git] / lib / PublicInbox / Config.pm
index 2ff266f184499d2660f755ffc75e8be01d80f9d2..27222c033c330aed99a4e10af1fb51920b63cc5f 100644 (file)
@@ -30,6 +30,7 @@ sub new {
        $self->{-no_obfuscate} ||= {};
        $self->{-limiters} ||= {};
        $self->{-code_repos} ||= {}; # nick => PublicInbox::Git object
+       $self->{-cgitrc_unparsed} = $self->{'publicinbox.cgitrc'};
 
        if (my $no = delete $self->{'publicinbox.noobfuscate'}) {
                $no = _array($no);
@@ -59,8 +60,8 @@ sub new {
 sub lookup {
        my ($self, $recipient) = @_;
        my $addr = lc($recipient);
-       my $inbox = $self->{-by_addr}->{$addr};
-       return $inbox if $inbox;
+       my $ibx = $self->{-by_addr}->{$addr};
+       return $ibx if $ibx;
 
        my $pfx;
 
@@ -131,8 +132,10 @@ sub limiter {
        my ($self, $name) = @_;
        $self->{-limiters}->{$name} ||= do {
                require PublicInbox::Qspawn;
-               my $max = $self->{"publicinboxlimiter.$name.max"};
-               PublicInbox::Qspawn::Limiter->new($max);
+               my $max = $self->{"publicinboxlimiter.$name.max"} || 1;
+               my $limiter = PublicInbox::Qspawn::Limiter->new($max);
+               $limiter->setup_rlimit($name, $self);
+               $limiter;
        };
 }
 
@@ -197,15 +200,148 @@ sub valid_inbox_name ($) {
        1;
 }
 
+# XXX needs testing for cgit compatibility
+# cf. cgit/scan-tree.c::add_repo
+sub cgit_repo_merge ($$$) {
+       my ($self, $base, $repo) = @_;
+       my $path = $repo->{dir};
+       if (defined(my $se = $self->{-cgit_strict_export})) {
+               return unless -e "$path/$se";
+       }
+       return if -e "$path/noweb";
+       # $repo = { url => 'foo.git', dir => '/path/to/foo.git' }
+       my $rel = $repo->{url};
+       unless (defined $rel) {
+               my $off = index($path, $base, 0);
+               if ($off != 0) {
+                       $rel = $path;
+               } else {
+                       $rel = substr($path, length($base) + 1);
+               }
+
+               $rel =~ s!/\.git\z!! or
+                       $rel =~ s!/+\z!!;
+
+               $self->{-cgit_remove_suffix} and
+                       $rel =~ s!/?\.git\z!!;
+       }
+       $self->{"coderepo.$rel.dir"} ||= $path;
+       $self->{"coderepo.$rel.cgiturl"} ||= $rel;
+}
+
+sub is_git_dir ($) {
+       my ($git_dir) = @_;
+       -d "$git_dir/objects" && -f "$git_dir/HEAD";
+}
+
+# XXX needs testing for cgit compatibility
+sub scan_path_coderepo {
+       my ($self, $base, $path) = @_;
+       opendir(my $dh, $path) or do {
+               warn "error opening directory: $path\n";
+               return
+       };
+       my $git_dir = $path;
+       if (is_git_dir($git_dir) || is_git_dir($git_dir .= '/.git')) {
+               my $repo = { dir => $git_dir };
+               cgit_repo_merge($self, $base, $repo);
+               return;
+       }
+       while (defined(my $dn = readdir $dh)) {
+               next if $dn eq '.' || $dn eq '..';
+               if (index($dn, '.') == 0 && !$self->{-cgit_scan_hidden_path}) {
+                       next;
+               }
+               my $dir = "$path/$dn";
+               scan_path_coderepo($self, $base, $dir) if -d $dir;
+       }
+}
+
+sub scan_tree_coderepo ($$) {
+       my ($self, $path) = @_;
+       scan_path_coderepo($self, $path, $path);
+}
+
+sub scan_projects_coderepo ($$$) {
+       my ($self, $list, $path) = @_;
+       open my $fh, '<', $list or do {
+               warn "failed to open cgit projectlist=$list: $!\n";
+               return;
+       };
+       foreach (<$fh>) {
+               chomp;
+               scan_path_coderepo($self, $path, "$path/$_");
+       }
+}
+
+sub parse_cgitrc {
+       my ($self, $cgitrc, $nesting) = @_;
+       if ($nesting == 0) {
+               # defaults:
+               my %s = map { $_ => 1 } qw(/cgit.css /cgit.png
+                                               /favicon.ico /robots.txt);
+               $self->{-cgit_static} = \%s;
+       }
+
+       # same limit as cgit/configfile.c::parse_configfile
+       return if $nesting > 8;
+
+       open my $fh, '<', $cgitrc or do {
+               warn "failed to open cgitrc=$cgitrc: $!\n";
+               return;
+       };
+
+       # FIXME: this doesn't support macro expansion via $VARS, yet
+       my $repo;
+       foreach (<$fh>) {
+               chomp;
+               if (m!\Arepo\.url=(.+?)/*\z!) {
+                       my $nick = $1;
+                       cgit_repo_merge($self, $repo->{dir}, $repo) if $repo;
+                       $repo = { url => $nick };
+               } elsif (m!\Arepo\.path=(.+)\z!) {
+                       if (defined $repo) {
+                               $repo->{dir} = $1;
+                       } else {
+                               warn "$_ without repo.url\n";
+                       }
+               } elsif (m!\Ainclude=(.+)\z!) {
+                       parse_cgitrc($self, $1, $nesting + 1);
+               } elsif (m!\A(scan-hidden-path|remove-suffix)=(\d+)\z!) {
+                       my ($k, $v) = ($1, $2);
+                       $k =~ tr/-/_/;
+                       $self->{"-cgit_$k"} = $v;
+               } elsif (m!\A(project-list|strict-export)=(.+)\z!) {
+                       my ($k, $v) = ($1, $2);
+                       $k =~ tr/-/_/;
+                       $self->{"-cgit_$k"} = $v;
+               } elsif (m!\Ascan-path=(.+)\z!) {
+                       if (defined(my $list = $self->{-cgit_project_list})) {
+                               scan_projects_coderepo($self, $list, $1);
+                       } else {
+                               scan_tree_coderepo($self, $1);
+                       }
+               } elsif (m!\A(?:css|favicon|logo|repo\.logo)=(/.+)\z!) {
+                       # absolute paths for static files via PublicInbox::Cgit
+                       $self->{-cgit_static}->{$1} = 1;
+               }
+       }
+       cgit_repo_merge($self, $repo->{dir}, $repo) if $repo;
+}
+
 # parse a code repo
 # Only git is supported at the moment, but SVN and Hg are possibilities
 sub _fill_code_repo {
        my ($self, $nick) = @_;
        my $pfx = "coderepo.$nick";
 
+       # TODO: support gitweb and other repository viewers?
+       if (defined(my $cgitrc = delete $self->{-cgitrc_unparsed})) {
+               parse_cgitrc($self, $cgitrc, 0);
+       }
        my $dir = $self->{"$pfx.dir"}; # aka "GIT_DIR"
        unless (defined $dir) {
-               warn "$pfx.repodir unset";
+               warn "$pfx.dir unset\n";
                return;
        }
 
@@ -226,8 +362,6 @@ sub _fill_code_repo {
                        } @$cgits;
                }
        }
-       # TODO: support gitweb and other repository viewers?
-       # TODO: parse cgitrc
 
        $git;
 }
@@ -292,7 +426,11 @@ sub _fill {
                my $code_repos = $self->{-code_repos};
                my $repo_objs = $rv->{-repo_objs} = [];
                foreach my $nick (@$ibx_code_repos) {
-                       valid_inbox_name($nick) or next;
+                       my @parts = split(m!/!, $nick);
+                       my $valid = 0;
+                       $valid += valid_inbox_name($_) foreach (@parts);
+                       $valid == scalar(@parts) or next;
+
                        my $repo = $code_repos->{$nick} ||=
                                                _fill_code_repo($self, $nick);
                        push @$repo_objs, $repo if $repo;