1#! @PERL@
2#
3# Copyright 1998 - 2020 Double Precision, Inc.  See COPYING for
4# distribution information.
5
6use Fcntl ':flock';
7
8$prefix="@prefix@";
9$exec_prefix="@exec_prefix@";
10$userdb="@userdb@";
11
12eval {
13	die "SYMLINK\n" if -l $userdb;
14};
15
16die "ERROR: Wrong userdb command.\n       ($userdb is a symbolic link)\n"
17	if $@ eq "SYMLINK\n";
18
19sub usage {
20	print "Usage: $0 [path/.../ | -f file ]name set field=value field=value...\n";
21	print "       $0 [path/.../ | -f file ]name unset field field...\n";
22	print "       $0 [path/.../ | -f file ]name del\n";
23	print "       $0 -show [path/... | -f file ] [name]\n";
24	exit 1;
25}
26
27$name=shift @ARGV;
28$doshow=0;
29
30if ($name eq "-show")
31{
32	$doshow=1;
33	$name=shift @ARGV;
34}
35
36if ($name eq "-f")
37{
38	$userdb=shift @ARGV;
39	$name=shift @ARGV;
40}
41elsif ( $name =~ /^(.*)\/([^\/]*)$/ )
42{
43	$userdb="$userdb/$1";
44	$name=$2;
45}
46
47
48if ($doshow)
49{
50	&usage unless $userdb =~ /./;
51}
52else
53{
54	$verb=shift @ARGV;
55
56	&usage unless $verb =~ /./ && $name =~ /./ && $userdb =~ /./;
57}
58
59while (defined ($link= &safe_readlink($userdb)))
60{
61	$userdb .= "/";
62	$userdb = "" if $link =~ /^\//;
63	$userdb .= $link;
64}
65
66$tmpuserdb= $userdb =~ /^(.*)\/([^\/]*)$/ ? "$1/.tmp.$2":".tmp.$userdb";
67$lockuserdb= $userdb =~ /^(.*)\/([^\/]*)$/ ? "$1/.lock.$2":".lock.$userdb";
68
69grep( (/[\|\n]/ && die "Invalid field or value.\n"), @ARGV);
70
71umask(066);
72
73open(LOCK, ">$lockuserdb") or die "Can't open $lockuserdb: $!";
74flock(LOCK,LOCK_EX) || die "Can't lock $lockuserdb: $!";
75
76if ( $doshow )
77{
78	if (open (OLDFILE, $userdb))
79	{
80		stat(OLDFILE);
81		die "$userdb: not a file.\n" unless -f _;
82
83		while ( defined($_=<OLDFILE>) )
84		{
85			chop if /\n$/;
86			next if /^#/;
87			next unless /^([^\t]+)(\t(.*))?$/;
88			($addr,$vals)=($1,$3);
89			if (defined $name)
90			{
91				if ($name eq $addr)
92				{
93					$vals =~ s/\|/\n/g;
94					print "$vals\n";
95					last;
96				}
97			}
98			else
99			{
100				print "$addr\n";
101			}
102		}
103	}
104	close (OLDFILE);
105}
106elsif ( $verb eq "set" )
107{
108	$isatty=1;
109
110	eval {
111		$isatty=0 unless -t STDIN;
112	} ;
113
114	&doadd;
115	$mode= (stat $userdb)[2];
116	chmod ($mode & 0777,$tmpuserdb ) if defined $mode;
117	rename $tmpuserdb,$userdb;
118}
119elsif ( $verb eq "unset" )
120{
121	if ($#ARGV >= 0 && &dodel)
122	{
123		$mode= (stat $userdb)[2];
124		chmod ($mode & 0777 ,$tmpuserdb) if defined $mode;
125		rename ($tmpuserdb,$userdb)
126	}
127}
128elsif ( $verb eq "del" )
129{
130	&usage unless $#ARGV < 0;
131	if (&dodel)
132	{
133		$mode= (stat $userdb)[2];
134		chmod ($mode & 0777 ,$tmpuserdb) if defined $mode;
135		rename ($tmpuserdb,$userdb)
136	}
137}
138else
139{
140	&usage;
141}
142exit 0;
143
144sub doadd {
145my (%FIELDS);
146my ($key, $in);
147
148	foreach $key (@ARGV)
149	{
150		next if $key =~ /=/;
151		print "$name.$key: " if $isatty;
152		exit 1 unless defined ($in=<STDIN>);
153		chop $in if $in =~ /\n$/;
154		die "Invalid value.\n" if $in =~ /[\|\n]/;
155		$key = "$key=$in";
156	}
157
158	open (NEWFILE, ">$tmpuserdb") || die "$!\n";
159	if (open (OLDFILE, $userdb))
160	{
161		stat(OLDFILE);
162		die "$userdb: not a file.\n" unless -f _;
163		while ( defined($_=<OLDFILE>) )
164		{
165			chop if /\n$/;
166			if ( /^([^\t]+)(\t(.*))?$/ && ($1 eq $name))
167			{
168				grep( (/^([^=]*)(=.*)?$/,
169					$FIELDS{$1}="$2"), split(/\|/, $3));
170				while ( defined ($key=shift @ARGV))
171				{
172					$key =~ /^([^=]*)(=.*)?$/;
173					$FIELDS{$1}="$2";
174				}
175				$name="$name\t";
176				grep ( $name="$name$_$FIELDS{$_}|",
177					keys %FIELDS);
178				chop $name;
179				print NEWFILE "$name\n" || die "$!\n";
180				while (<OLDFILE>)
181				{
182					print NEWFILE || die "$!\n";
183				}
184				close (OLDFILE);
185				close (NEWFILE) || die "$!\n";
186				return;
187			}
188			print NEWFILE "$_\n" || die "$!\n";
189		}
190		close (OLDFILE);
191	}
192
193	$name="$name\t";
194	grep ( $name="$name$_|", @ARGV );
195	chop $name;
196	print NEWFILE "$name\n" || die "$!\n";
197	close (NEWFILE) || die "$!\n";
198}
199
200sub dodel {
201my (%FIELDS);
202
203	open (NEWFILE, ">$tmpuserdb") || die "$!\n";
204	if (open (OLDFILE, $userdb))
205	{
206		stat(OLDFILE);
207		die "$userdb: not a file.\n" unless -f _;
208		while ( defined($_=<OLDFILE>) )
209		{
210			chop if /\n$/;
211			if ( /^([^\t]+)(\t(.*))?$/ && ($1 eq $name))
212			{
213				if ($#ARGV >= 0)
214				{
215					grep( (/^([^=]*)(=.*)?$/,
216						$FIELDS{$1}=$2),
217							split(/\|/, $3));
218					grep( delete $FIELDS{$_}, @ARGV);
219
220					$name="$name\t";
221					grep ( $name="$name$_$FIELDS{$_}|",
222						keys %FIELDS);
223					chop $name;
224					$name="$name\n";
225					print NEWFILE "$name" || die "$!\n";
226				}
227				while (<OLDFILE>)
228				{
229					print NEWFILE || die "$!\n";
230				}
231				close (OLDFILE);
232				close (NEWFILE) || die "$!\n";
233				return (1);
234			}
235			print NEWFILE "$_\n" || die "$!\n";
236		}
237		close (OLDFILE);
238	}
239	unlink "$tmpuserdb";
240	return (0);
241}
242
243sub safe_readlink {
244my ($l)=@_;
245my ($err,$link);
246
247	eval {
248
249		$link=readlink($l);
250	} ;
251
252	$link=undef if $@;
253	return $link;
254}
255