summaryrefslogtreecommitdiff
path: root/files/arts/software/Code/elektropost/mkvalidrcptto
blob: 5b03fbbf46fcba9404d64b86fda5cb2ef4d5d480 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
#!/usr/bin/perl -w
#
# mkvalidrcptto
# John Simpson <jms1@jms1.net> 2005-04-20
#
# reads qmail control files and builds a list of all valid email addresses
# on the system.
#
# 2005-05-04 jms1 - cleaned up the code a little bit. holding the output in
#   memory until the whole thing is done so that partial output doesn't
#   become an issue. it also makes it possible to create a cdb file directly,
#   but i don't think i'm going to do that- i like the "chaining" approach
#   better, piping this script's output through "cdbmake-12" to produce the
#   cdb file.
#
# 2005-05-09 jms1 - changing the vpopmail user info process. instead of 
#   reading vpasswd.cdb, using "vuserinfo -D {domain}" and parsing that
#   output. this should include mysql user information for people who are
#   using vpopmail with mysql. Thanks to Roman Volf on the qmailrocks
#   mailing list for pointing this out.
#
# 2005-06-09 jms1 - adding support for "fastforward" aliases.
#
# 2005-06-18 jms1 - finishing support for .qmail-* files in local user home
#   directories... i don't ever use local users for mailboxes myself, so it
#   wasn't an issue for me, but somebody out there may be doing it, so...
#
# 2005-06-23 jms1 - changed the search pattern when reading "vuserinfo -D"
#   to get a vpopmail domain's mailbox list, instead of recognizing \w+
#   it now uses \S+ which should allow "." in mailbox names
#
# 2005-06-29 jms1 - translating ":" to "." in .qmail-* filenames... i forgot
#   that qmail-local does this. somebody emailed me to remind me about this,
#   but i can't find the email so i don't know who to thank for reminding me
#   about this...
#
# 2005-06-30 jms1 - when vpopmail stores everything in a mysql database,
#   aliases are apparently not represented by .qmail files at all. it looks
#   like we have no choice but to use the vpopmail command-line tools to
#   get the lists of mailboxes and aliases in the domain. thanks to Rob Pitt
#   for telling me that this wasn't working (i don't use vpopmail with mysql
#   so i had no way to know that this would be a problem.)
#
# 2005-07-17 jms1 - domains listed in smtproutes are currently listed as
#   just "@domain", meaning the entire domain is accepted without any checks
#   done for individual userid's. adding code so that you can create a
#   directory full of files named for the domains, containing userid's
#   which exist in that domain. thanks to roman volf for the suggestion.
#
# 2005-08-03 jms1 - turns out if vpopmail is compiled with support for mysql,
#   the "valias -s" command doesn't list aliases which exist by virtue of
#   .qmail-blah files... which rather sucks, because this is how ezmlm sets
#   up the aliases it needs, by creating .qmail-blah files. thanks again to
#   Roman Volf for pointing this out.
#
# 2005-10-24 jms1 - adding a "-n" switch to generate a list without the 
#   system accounts (i.e. no "locals" or "me" domains will be printed.)
#   note that "fastforward" aliases are considered local, since they are
#   processed through the local delivery mechanism.
#
# 2005-11-29 jms1 - now treats missing vpopmail directory as a warning 
#   rather than a fatal error.
#
# 2005-12-07 jms1 - after reviewing qmail-send.c and controls.c, it 
#   turns out that the "me" becoming part of "locals" only happens if 
#   the "control/locals" file does not exist... otherwise an empty 
#   "control/locals" file means that there are no locals. updating the
#   script to duplicate this logic.
#   also replacing ":" with "." in .qmail filenames.
#   thanks to jeff hedlund for pointing me towards the "me" problem,
#   and for pointing out my oversight with the ":" thing.
#
# 2005-12-29 jms1 - adding an array of numeric uid's (empty by default)
#   which will be ignored when system uid's are scanned. thanks to
#   roman volf for the suggestion.
#
# 2006-01-08 jms1 - fixed a typo, thanks to "marlowe" for pointing it out.
#
# 2006-01-11 jms1 - vpopmail has a "--enable-qmail-ext" option which 
#   changes how mailbox names are handled. if vpopmail is running WITH
#   this option, every vpopmail mailbox has an implied "-default" alias
#   whether there's a .qmail-user-default file there or not. thanks to
#   robin bowes for pointing this out.
#
# 2006-01-12 jms1 - making the script work correctly in the unlikely 
#   case that the user-ext separator character was changed, either by
#   changing conf-break before compiling qmail, or specifying a custom
#   value in the users/cdb file.
#
# 2006-02-05 jms1 - adding an "exclude" list, for addresses which you 
#   may not want to include in the output (i.e. private mailing list
#   aliases and things like that.) also fixed a bug in the code which
#   handles the user-ext separator character.
#
# 2006-03-26 jms1 - fixed a minor typo in the text of an error message
#   (which does not affect how the script works.) Thanks to Robin Bowes
#   for pointing it out.
#
# 2006-11-29 jms1 - adding logic to work around the case where users/cdb
#   just plain doesn't exist (which can happen on systems which don't use
#   vpopmail etc.) thanks to "Eric d'Alibut" on djb's qmail mailing list
#   for pointing it out.
#
###############################################################################
#
# Copyright (C) 2005-2006 John Simpson.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
# or visit http://www.gnu.org/licenses/gpl.txt
#
###############################################################################

require 5.003 ;
use strict ;

use CDB_File ;

###############################################################################
#
# configuration

my $vq		= "/var/qmail" ;
my $vuser	= "vpopmail" ;		# vpopmail userid

# any numeric uid's on the system which are lower than this will be ignored
# this way we don't create entries for root, bin, daemon, lp, news, uucp,
# and other non-used system users.
my $uid_min	= 500 ;		# ignore uid's lower than this
my $uid_max	= 65000 ;	# ignore uid's higher than this
my @uid_ignore	= qw ( ) ;	# ignore any uid's listed in this array

# any entries listed in this array will NOT be included in the output
my @exclude	= qw
(
	sample1@domain.xyz
	sample2@domain.xyz
) ;

# if you have text files containing lists of valid mailboxes for smtproutes
# domains, put them all into a single directory and put that directory in
# the variable below.
#
# each line of each files should contain a mailbox userid- anything on the
# line which comes after "#" or "@" is deleted, so you can use "#" for
# comments if you like, and you can use a symlink (or hard link) to cause
# one file to be effective for multiple domains.
#
# note that these files are only consulted for domains listed in the
# smtproutes file. if this variable is blank, or it points to a directory
# which doesn't exist, all smtproutes domains will be printed as "@domain",
# which tells qmail-smtpd to accept any mailbox userid in that domain.

my $smtpr_dir	= "" ;

###############################################################################
#
# global variables

my ( %alldom , %ldom , %vdom , %sdom , %adom , %lusr , %ausr ,
	%home , %MRH , %UCDB , @output , $ffl , %ACDB ) ;

my $err		= 0 ;
my $lfound	= 0 ;
my $afound	= 0 ;
my $vfound	= 0 ;
my $showlocal	= 1 ;
my $need_untie	= 0 ;

my $vhome	= "" ;
my $vbin	= "" ;
my $vinc	= "" ;
my $vqext	= "?" ;
my $dash	= "" ;
my $gdash	= "" ;

###############################################################################
#
# debugging function

my $show_debug	= 0 ;

sub debug($)
{
	$show_debug && print $_[0] ;
}

###############################################################################
#
# function to read /var/qmail/alias/.qmail-default

sub find_ffl($)
{
	my $file = shift ;

	my $flagdeliver = 1 ;
	my $flagpassthrough = 0 ;
	my $flagdefault = 0 ;

	if ( open ( DQD , "<$file" ) )
	{
		while ( my $line = <DQD> )
		{
			chomp $line ;
			next unless ( $line =~ /^\|.*fastforward/ ) ;

			$line =~ s/^.*fastforward\s+// ;
			my @dw = split ( /\s+/ , $line ) ;
			while ( my $zz = shift @dw )
			{
				next if ( $zz =~ /^\-/ ) ;
				$ffl = $zz ;
				last ;
			}

			last if ( $ffl ) ;
		}
		close DQD ;
	}
}

###############################################################################
###############################################################################
###############################################################################

my $arg = ( shift || "" ) ;
if ( "-n" eq $arg )
{
	$showlocal = 0 ;
}

###############################################################################
#
# only interested in domains for which we legitimately receive mail

open ( I , "<$vq/control/rcpthosts" )
	or die "$vq/control/rcpthosts: $!\n" ;
while ( my $line = <I> )
{
	chomp $line ;
	$alldom{$line} = 1 ;
}
close I ;

open ( I , "<$vq/control/morercpthosts" )
	or die "$vq/control/morercpthosts: $!\n" ;
while ( my $line = <I> )
{
	chomp $line ;
	$alldom{$line} = 1 ;
}
close I ;

if ( -f "$vq/control/morercpthosts.cdb" )
{
	tie ( %MRH , "CDB_File" , "$vq/control/morercpthosts.cdb" )
		or die "$vq/control/morercpthosts: $!\n" ;
	map { $alldom{$_} = 1 } keys %MRH ;
	untie %MRH ;
}

###############################################################################
#
# classify each one as local, virtual, or pass-thru
#
# note that if the control/locals file does not exist, the name listed
# in the control/me file is used as if control/locals contained the 
# data.

if ( -f "$vq/control/locals" )
{
	open ( I , "<$vq/control/locals" )
		or die "$vq/control/locals: $!\n" ;
	while ( my $line = <I> )
	{
		chomp $line ;

		########################################
		# ignore any that we don't actually receive mail for

		next unless ( exists $alldom{$line} ) ;
		delete $alldom{$line} ;

		########################################
		# mark this one as local

		$ldom{$line} = 1 ;
		$lfound ++ ;
		$afound ++ ;
	}
	close I ;
}
elsif ( -f "$vq/control/me" )
{
	open ( I , "<$vq/control/me" )
		or die "$vq/control/me: $!\n" ;
	while ( my $line = <I> )
	{
		chomp $line ;

		########################################
		# ignore any that we don't actually receive mail for

		next unless ( exists $alldom{$line} ) ;
		delete $alldom{$line} ;

		########################################
		# mark this one as local

		$ldom{$line} = 1 ;
		$lfound ++ ;
		$afound ++ ;
	}
	close I ;
}

if ( -f "$vq/control/virtualdomains" )
{
	open ( I , "<$vq/control/virtualdomains" )
		or die "$vq/control/virtualdomains: $!\n" ;
	while ( my $line = <I> )
	{
		chomp $line ;

		########################################
		# extract the domain name

		my ( $dom , $zu ) = split ( /\s*\:\s*/ , $line ) ;
		$dom || die "error in $vq/control/virtualdomains\n$line\n" ;

		########################################
		# ignore any that we don't actually receive mail for

		next unless ( exists $alldom{$dom} ) ;
		delete $alldom{$dom} ;

		########################################
		# check the userid

		if ( $zu eq "alias" )
		{
			########################################
			# if the domain is handled by the qmail "alias"
			# user, then it needs alias processing

			$adom{$dom} = 1 ;
			$afound ++ ;
		}
		else
		{
			########################################
			# mark this one as a virtual domain
			# and remember the full line, we will need it later

			$vdom{$dom} = $line ;
			$vfound ++ ;
		}
	}
	close I ;
}

if ( -f "$vq/control/smtproutes" )
{
	open ( I , "<$vq/control/smtproutes" )
		or die "$vq/control/smtproutes: $!\n" ;
	while ( my $line = <I> )
	{
		chomp $line ;

		########################################
		# extract the domain name

		$line =~ s/\:.*// ;

		########################################
		# ignore lines with no domain (default instruction)

		next unless $line ;

		########################################
		# ignore any that we don't actually receive mail for

		next unless ( exists $alldom{$line} ) ;
		delete $alldom{$line} ;

		########################################
		# mark this one as an smtproutes domain

		$sdom{$line} = 1 ;
	}
	close I ;
}

###############################################################################
#
# catch leftovers - domains which come into the machine but are not handled

for my $d ( sort keys %alldom )
{
	print "ERROR: $d is listed in rcpthosts/morercpthosts.cdb"
		. " but is not handled by the server.\n" ;
	$err ++ ;
}

$err && die "Cannot continue.\n" ;

###############################################################################
#
# start generating output.
#
# smtproutes domains - if a directory was specified, and it exists, and a
# file for the domain exists, read userid's from the file and generate
# "userid@domain" lines... otherwise just generate a single "@domain" line.

for my $d ( sort keys %sdom )
{
	if ( $smtpr_dir && ( -d $smtpr_dir ) && ( -f "$smtpr_dir/$d" ) )
	{
		open ( I , "<$smtpr_dir/$d" )
			or die "Can\'t read $smtpr_dir/$d: $!\n" ;
		while ( my $line = <I> )
		{
			chomp $line ;
			$line =~ s/#.*// ;
			$line =~ s/\@.*// ;
			$line =~ s/^\s+// ;
			$line =~ s/\s+$// ;
			next unless ( $line ) ;
			push ( @output , "$line\@$d" ) ;
		}
		close I ;
	}
	else
	{
		push ( @output, "\@$d" ) ;
	}
}

###############################################################################
#
# local domains - all system accounts and aliases, in each local domain

if ( $lfound || $afound )
{
	########################################
	# need the global "dash" character

	unless ( $gdash )
	{
		open ( GD , "$vq/bin/qmail-showctl |" )
			or die "Can\'t run qmail-showctl: $!\n" ;
		while ( my $gdline = <GD> )
		{
			if ( $gdline =~ /user\-ext delimiter\: (.)/ )
			{
				$gdash = $1 ;
				last ;
			}
		}
		close GD ;
	}
}

if ( $lfound )
{
	########################################
	# turn array into hash for speed

	my %ig = () ;
	map { $ig{$_} = "" } @uid_ignore ;

	########################################
	# grab a list of system accounts

	while ( my @pw = getpwent() )
	{
		next if ( $pw[2] < $uid_min )  ; # ignore system accounts
		next if ( $pw[2] > $uid_max )  ; # ignore "nobody" accounts
		next if ( exists $ig{$pw[2]} ) ; # ignore special accounts
		next unless ( $pw[2] ) ;         # no deliveries to root
		$lusr{$pw[0]} = 1 ;

		if ( opendir ( D , $pw[7] ) )
		{
			while ( my $f = readdir D )
			{
				next unless ( $f =~ /^\.qmail${gdash}(.+)/ ) ;
				my $zu = $1 ;
				$zu =~ s/\:/./g ;
				$lusr{"$pw[0]${gdash}$zu"} = 1 ;
			}
			closedir D ;
		}
	}
}

if ( $showlocal && ( $lfound || $afound ) )
{
	########################################
	# grab a list of aliases

	opendir ( D , "$vq/alias" )
		or die "$vq/alias: $!\n" ;
	while ( my $f = readdir ( D ) )
	{
		next unless ( $f =~ /^\.qmail${gdash}(.*)/ ) ;
		my $u = $1 ;

		if ( $u eq "default" )
		{
			find_ffl ( "$vq/alias/.qmail${gdash}default" ) ;
		}
		else
		{
			$u =~ s/\:/./g ;
			$ausr{$u} = 1 ;
		}
	}
	closedir D ;

	########################################
	# if we found a fastforward file, grab those aliases as well

	if ( $ffl )
	{
		tie ( %ACDB , "CDB_File" , $ffl )
			or die "$ffl: $!\n" ;

		for my $k ( keys %ACDB )
		{
			next unless ( $k =~ /^\:(.*)\@(.*)$/ ) ;
			my ( $au , $ad ) = ( $1 , $2 ) ;

			if ( $ad )
			{
				next unless (    exists ( $adom{$ad} )
					      || exists ( $ldom{$ad} ) ) ;
				push ( @output , "$au\@$ad" ) ;
			}
			else
			{
				$ausr{$au} = 1 ;
			}
		}

		untie %ACDB ;
	}

	########################################
	# generate output.
	# local domains get every system user AND every alias user

	for my $dd ( sort keys %ldom )
	{
		map { push ( @output , "$_\@$dd" ) } sort keys %lusr ;
		map { push ( @output , "$_\@$dd" ) } sort keys %ausr ;
	}

	########################################
	# alias domains get every alias user

	for my $dd ( sort keys %adom )
	{
		map { push ( @output , "$_\@$dd" ) } sort keys %ausr ;
	}
}

###############################################################################
#
# virtual domains - a little more complicated.

if ( $vfound )
{
	#######################################################################
	#
	# the virtualdomains file contains a mapping from the domain name to a
	# userid, which may be a system account and may be a virtual userid
	# defined in the $vq/users/assign file.
	#
	# vpopmail normally uses the domain name as the virtual userid for
	# this purpose, but we want to be flexible enough to handle other
	# cases as well.
	#
	# in order to deal with this extra layer of indirection, we need to
	# read the users/cdb file. and because it's a cdb, we don't even need
	# to read the whole thing- we just need to open it so that we can
	# search it.

	if ( -f "$vq/users/cdb" )
	{
		tie ( %UCDB , "CDB_File" , "$vq/users/cdb" )
			or die "$vq/users/cdb: $!\n" ;
		$need_untie = 1 ;
	}
	else
	{
		%UCDB = () ;
	}

	my $wc = ( $UCDB{""} || "" ) ;

	#######################################################################
	#
	# now we have the list of users/assign virtual users (if any), we need
	# to identify the home directory, real or virutal, for the user.

	for my $dom ( sort keys %vdom )
	{
		$vdom{$dom} =~ /\:(.*)$/ ;
		my $usr = $1 ;

		my %vusr = () ;
		my $dir = "" ;
		my $vpopmail = 0 ;

		########################################
		# note that in cases where a given "userid" exists as both a
		# system userid and a virtual userid, the virtual userid takes
		# precedence (according to the qmail-lspawn man page.)
		# this is why we saved the home directories above.

		if ( exists $UCDB{"!$usr$wc"} )
		{
			my @w = split ( /\0/ , $UCDB{"!$usr$wc"} ) ;
			$dir = ( $w[3] || die "mis-formed users/cdb data:"
				. " $usr\n" ) ;
			$dash = ( $w[4] || "" ) ;
		}
		else
		{
			if ( my @pw = getpwnam ( $usr ) )
			{
				$dir = $pw[7] ;
			}
		}

		die "ERROR: virtual user \"$usr\" not found"
				. " (for virtualdomain \"$dom\")\n"
			unless ( $dir ) ;

		########################################
		# now we know which directory to look in. check for a
		# ".qmail-default" file. if it contains "vdelievermail", we
		# know that vpopmail is in control here... and if the 
		# vdelivermail line also has "bounce-no-mailbox", we need 
		# the list of individual users. otherwise we can just 
		# blindly accept the entire domain.

		unless ( -r $dir )
		{
			print STDERR "Can\'t read directory $dir"
				. " (for vpopmail domain \"$dom\")\n" ;
			next ;
		}

		if ( -f "$dir/.qmail${dash}default" )
		{
			open ( V , "<$dir/.qmail${dash}default" )
				or die "$dir/.qmail${dash}default: $!\n" ;

			while ( my $line = <V> )
			{
				if ( $line =~ /vdelivermail.*(bounce\-no\-mailbox|delete)/ )
				{
					$vpopmail = 1 ;
				}
			}
			close V ;
		}

		########################################
		# if we need the users...

		if ( $vpopmail )
		{
			########################################
			# if we don't already know where it is,
			# find the vpopmail user's "/bin" directory.

			unless ( $vhome )
			{
				my @pw = getpwnam ( $vuser )
					or die "getpwnam($vuser): $!\n" ;
				$vhome = $pw[7] ;
				$vbin = "$vhome/bin" ;
				$vinc = "$vhome/include" ;

				die "Can\'t find $vbin/vuserinfo: $!\n"
					unless ( -e "$vbin/vuserinfo" ) ;
			}

			########################################
			# if we don't already know, find out if
			# vpopmail was built with --enable-qmail-ext

			if ( $vqext eq "?" )
			{
				$vqext = "no" ;
				open ( VCH , "<$vinc/vpopmail_config.h" )
					or die ( "Can\'t read "
					. "$vinc/vpopmail_config.h: $!\n" ) ;

				while ( my $vcl = <VCH> )
				{
					next unless ( $vcl =~ /^#define QMAIL_EXT 1/ ) ;
					$vqext = "yes" ;
					last ;
				}
				close VCH ;
				debug "vqext=$vqext\n" ;
			}

			########################################
			# run "vuserinfo -D {domain}" to get a list of
			# actual mailboxes within the domain.

			debug "/----- $vbin/vuserinfo -D $dom\n" ;
			open ( VP , "$vbin/vuserinfo -D $dom |" )
				or die "Can\'t execute $vbin/vuserinfo: $!\n" ;
			while ( my $line = <VP> )
			{
				debug $line ;
				next unless ( $line =~ /^name\:\s+(\S+)/ ) ;
				my $u = $1 ;
				$vusr{$u} = $u ;
				debug "\t[$u]" ;
				if ( $vqext eq "yes" )
				{
					$vusr{"$u${dash}default"} =
						"$u${dash}default" ;
					debug " [$u${dash}default]" ;
				}
				debug "\n" ;
			}
			close VP ;
			debug "\\-----\n" ;

			########################################
			# run "valias -s {domain}" to get a list of
			# aliases within the domain.

			debug "/----- $vbin/valias -s $dom\n" ;
			open ( VP , "$vbin/valias -s $dom |" )
				or die "Can\'t execute $vbin/valias: $!\n" ;
			while ( my $line = <VP> )
			{
				debug $line ;
				next unless ( $line =~ /^(.+?)\@/ ) ;
				my $u = $1 ;
				$vusr{$u} = $u ;
				debug "\t[$u]\n" ;
			}
			close VP ;
			debug "\\-----\n" ;

			########################################
			# read the directory itself. any .qmail-___ files are
			# also valid aliases within the domain, even if
			# "valias" doesn't seem to know about them.

			opendir ( D , $dir )
				or die "$dir: $!\n" ;
			while ( my $f = readdir ( D ) )
			{
				if ( $f =~ /^\.qmail${dash}(.*)/ )
				{
					my $u = $1 ;
					next if ( $u eq "default" ) ;
					$u =~ s/\:/./g ;
					$vusr{$u} = $u ;
				}
			}
			closedir D ;

			########################################
			# now %vusr contains a list of all valid email
			# addresses within the domain.

			map { push ( @output , "$_\@$dom" ) ;
				debug "{$_\@$dom}\n" } sort keys %vusr ;
		}
		else
		{
			########################################
			# virtual domain, but either it's not handled by
			# vpopmail, or there is something going on with 
			# itother than "bounce-no-mailbox", which means
			# we don't need the full list of mailboxes.

			push ( @output , "\@$dom" ) ;
		}
	}

	if ( $need_untie )
	{
		untie %UCDB ;
		$need_untie = 0 ;
	}
}

###############################################################################
#
# if we make it this far, we have no errors and can print the list.
# we need to filter out any "exclude" entries

my %ex = () ;
map { $ex{lc $_} = "" } @exclude ;

for my $k ( @output )
{
	$k = lc $k ;
	unless ( exists $ex{$k} )
	{
		print "$k\n" ;
	}
}