]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/Config.pm
drop dependency on File::Path::Expand
[public-inbox.git] / lib / PublicInbox / Config.pm
1 # Copyright (C) 2014-2015 all contributors <meta@public-inbox.org>
2 # License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
3 #
4 # Used throughout the project for reading configuration
5 package PublicInbox::Config;
6 use strict;
7 use warnings;
8 require PublicInbox::Inbox;
9 use PublicInbox::Spawn qw(popen_rd);
10
11 # returns key-value pairs of config directives in a hash
12 # if keys may be multi-value, the value is an array ref containing all values
13 sub new {
14         my ($class, $file) = @_;
15         $file = default_file() unless defined($file);
16         $file = ref $file ? $file : git_config_dump($file);
17         my $self = bless $file, $class;
18
19         # caches
20         $self->{-by_addr} ||= {};
21         $self->{-by_name} ||= {};
22         $self->{-by_newsgroup} ||= {};
23         $self;
24 }
25
26 sub lookup {
27         my ($self, $recipient) = @_;
28         my $addr = lc($recipient);
29         my $inbox = $self->{-by_addr}->{$addr};
30         return $inbox if $inbox;
31
32         my $pfx;
33
34         foreach my $k (keys %$self) {
35                 $k =~ /\A(publicinbox\.[\w-]+)\.address\z/ or next;
36                 my $v = $self->{$k};
37                 if (ref($v) eq "ARRAY") {
38                         foreach my $alias (@$v) {
39                                 (lc($alias) eq $addr) or next;
40                                 $pfx = $1;
41                                 last;
42                         }
43                 } else {
44                         (lc($v) eq $addr) or next;
45                         $pfx = $1;
46                         last;
47                 }
48         }
49         defined $pfx or return;
50         _fill($self, $pfx);
51 }
52
53 sub lookup_name {
54         my ($self, $name) = @_;
55         my $rv = $self->{-by_name}->{$name};
56         return $rv if $rv;
57         $rv = _fill($self, "publicinbox.$name") or return;
58 }
59
60 sub lookup_newsgroup {
61         my ($self, $ng) = @_;
62         $ng = lc($ng);
63         my $rv = $self->{-by_newsgroup}->{$ng};
64         return $rv if $rv;
65
66         foreach my $k (keys %$self) {
67                 $k =~ /\A(publicinbox\.[\w-]+)\.newsgroup\z/ or next;
68                 my $v = $self->{$k};
69                 my $pfx = $1;
70                 if ($v eq $ng) {
71                         $rv = _fill($self, $pfx);
72                         return $rv;
73                 }
74         }
75         undef;
76 }
77
78 sub get {
79         my ($self, $inbox, $key) = @_;
80
81         $self->{"publicinbox.$inbox.$key"};
82 }
83
84 sub config_dir { $ENV{PI_DIR} || "$ENV{HOME}/.public-inbox" }
85
86 sub default_file {
87         my $f = $ENV{PI_CONFIG};
88         return $f if defined $f;
89         config_dir() . '/config';
90 }
91
92 sub git_config_dump {
93         my ($file) = @_;
94         my ($in, $out);
95         my @cmd = (qw/git config/, "--file=$file", '-l');
96         my $cmd = join(' ', @cmd);
97         my $fh = popen_rd(\@cmd);
98         my %rv;
99         local $/ = "\n";
100         foreach my $line (<$fh>) {
101                 chomp $line;
102                 my ($k, $v) = split(/=/, $line, 2);
103                 my $cur = $rv{$k};
104
105                 if (defined $cur) {
106                         if (ref($cur) eq "ARRAY") {
107                                 push @$cur, $v;
108                         } else {
109                                 $rv{$k} = [ $cur, $v ];
110                         }
111                 } else {
112                         $rv{$k} = $v;
113                 }
114         }
115         close $fh or die "failed to close ($cmd) pipe: $?";
116         \%rv;
117 }
118
119 sub _fill {
120         my ($self, $pfx) = @_;
121         my $rv = {};
122
123         foreach my $k (qw(mainrepo address filter url newsgroup)) {
124                 my $v = $self->{"$pfx.$k"};
125                 $rv->{$k} = $v if defined $v;
126         }
127         return unless $rv->{mainrepo};
128         my $name = $pfx;
129         $name =~ s/\Apublicinbox\.//;
130         $rv->{name} = $name;
131         my $v = $rv->{address} ||= 'public-inbox@example.com';
132         my $p = $rv->{-primary_address} = ref($v) eq 'ARRAY' ? $v->[0] : $v;
133         $rv->{domain} = ($p =~ /\@(\S+)\z/) ? $1 : 'localhost';
134         $rv = PublicInbox::Inbox->new($rv);
135         if (ref($v) eq 'ARRAY') {
136                 $self->{-by_addr}->{lc($_)} = $rv foreach @$v;
137         } else {
138                 $self->{-by_addr}->{lc($v)} = $rv;
139         }
140         if (my $ng = $rv->{newsgroup}) {
141                 $self->{-by_newsgroup}->{$ng} = $rv;
142         }
143         $self->{-by_name}->{$name} = $rv;
144 }
145
146 1;