File Coverage

blib/lib/Mail/vpopmail.pm
Criterion Covered Total %
statement 3 159 1.8
branch 0 126 0.0
condition 0 21 0.0
subroutine 1 8 12.5
pod 4 5 80.0
total 8 319 2.5


line stmt bran cond sub pod time code
1             # Mail::vpopmail.pm
2             # $Id: vpopmail.pm,v 0.60b3 2007/04/16 00:32:24 jkister Exp $
3             # Copyright (c) 2004-2007 Jeremy Kister.
4             # Released under Perl's Artistic License.
5              
6             $Mail::vpopmail::VERSION = "0.60b3";
7              
8             =head1 NAME
9              
10             Mail::vpopmail - Utility to get information about vpopmail managed email addresses
11              
12             =head1 SYNOPSIS
13              
14             use Mail::vpopmail;
15              
16             my $vchkpw = Mail::vpopmail->new();
17              
18             my $vchkpw = Mail::vpopmail->new(cache => 1,
19             debug => 0,
20             auth_module => 'cdb',
21             dsn => 'DBI:mysql:host=localhost;database=vpopmail',
22             dbun => 'vpopmailuser',
23             dbpw => 'vpoppasswd',
24             );
25              
26            
27             =head1 DESCRIPTION
28              
29             C provides serveral functions for interacting with
30             vpopmail. This module can be useful especially when hashing is turned
31             on, as you can not predict the location of the domain's nor the
32             mailbox's directories.
33              
34             =head1 CONSTRUCTOR
35              
36             =over 4
37              
38             =item new( [OPTIONS] );
39              
40             C are passed in a hash like fashion, using key and value
41             pairs. Possible options are:
42              
43             B - Cache results of queries (0=Off, 1=On). Default=On.
44              
45             B - Print debugging info to STDERR (0=Off, 1=On). Default=On.
46              
47             B - cdb or sql. Default=cdb, but
48             Default=sql if ~vpopmail/etc/vpopmail.mysql exists.
49              
50             B - SQL DSN. Default='DBI:mysql:host=localhost;database=vpopmail'
51             Autogenerated if ~vpopmail/etc/vpopmail.mysql exists.
52              
53             B - SQL Username. Default=vpopmailuser.
54             Autogenerated if ~vpopmail/etc/vpopmail.mysql exists.
55              
56             B - SQL Password. Default=vpoppasswd.
57             Autogenerated if ~vpopmail/etc/vpopmail.mysql exists.
58              
59             =item userinfo( email => $email, field => );
60              
61             B - the email address to get properties on
62              
63             B - the field(s) to be returned (may be comma separated):
64              
65             dir - return this domain's vpopmail domains directory
66              
67             crypt - return the encrypted password
68              
69             uid - return the uid
70              
71             gid - return the gid
72              
73             comment - return the comment, if available
74              
75             maildir - return this user's maildir
76            
77             quota - return the quota (you have to parse this yourself)
78              
79             plain - return the plain text password, if available
80              
81             =item domaininfo( domain => $domain, field => );
82              
83             B - the domain to get properties on
84              
85             B - the field to be returned:
86              
87             dir - return the vpopmail domain directory
88              
89             mailboxes - return an array reference containing all the mailboxes
90              
91             all - return an array ref of hash refs of all data for the domain
92            
93             =item alldomains( field => );
94              
95             B - the field to be returned:
96              
97             name - returns an array reference of the names of all domains
98              
99             dir - returns an array refrence of all domain directories
100              
101             map - returns a hash reference of domain name -> domain directory
102              
103              
104             =head1 EXAMPLES
105              
106             use strict;
107             use Mail::vpopmail;
108              
109             my $vchkpw = Mail::vpopmail->new(cache=>1, debug=>0);
110              
111              
112             # find all domains
113             my $domains_aref = $vchkpw->alldomains(field => 'name');
114             foreach my $domain (@${domains_aref}){
115             print "$domain\n";
116             }
117              
118             # find all domains directories
119             my $dirlist_aref = $vchkpw->alldomains(field => 'dir');
120             foreach my $dir (@${dirlist_aref}){
121             print "$dir\n";
122             }
123              
124             # find all domains and their directories
125             my $alllist_aref = $vchkpw->alldomains(field => 'map');
126             foreach my $href (@${alllist_aref}){
127             print "$href->{name} => $href->{dir}\n";
128             }
129              
130             my $domain = shift;
131             unless(defined($domain)){
132             print "enter domain: ";
133             chop($domain=);
134             }
135              
136              
137             # find all mailboxes in a given domain
138             my $mailboxes_aref = $vchkpw->domaininfo(domain => $domain, field => 'mailboxes');
139             foreach my $mailbox (@{$mailboxes_aref}){
140             print "found mailbox: $mailbox for domain: $domain\n";
141             }
142              
143             # find all properties for a given domain
144             my $alldata_aref = $vchkpw->domaininfo(domain => $domain, field => 'all');
145             foreach my $href (@{$alldata_aref}){
146             print "found data for $domain:\n";
147             while(my($key,$value) = each %{$href}){
148             print " found $key => $value\n";
149             }
150             }
151              
152             # individual user stuff
153             my $email = shift;
154             unless(defined($email)){
155             print "email address: ";
156             chop($email=);
157             }
158              
159             my $dir = $vchkpw->userinfo(email => $email, field => 'dir');
160             print "dir: $dir\n";
161             my ($crypt,$uid,$gid) = $vchkpw->userinfo(email => $email, field => 'crypt,uid,gid');
162             print "crypt/uid/gid: $crypt/$uid/$gid\n";
163             my $comment = $vchkpw->userinfo(email => $email, field => 'comment');
164             print "comment: $comment\n";
165             my $maildir = $vchkpw->userinfo(email => $email, field => 'maildir');
166             print "maildir: $maildir\n";
167             my $quota = $vchkpw->userinfo(email => $email, field => 'quota');
168             print "quota: $quota\n";
169             my $plain = $vchkpw->userinfo(email => $email, field => 'plain');
170             print "plain: $plain\n";
171              
172             =head1 CAVEATS
173              
174             This version is the first that supports SQL auth modules. It is not
175             tested and should be used with caution. Feedback needed.
176              
177              
178             =head1 AUTHOR
179              
180             Jeremy Kister - http://jeremy.kister.net/
181              
182             =cut
183              
184             package Mail::vpopmail;
185              
186 1     1   605 use strict;
  1         2  
  1         2391  
187              
188             my $HAVE_DBI;
189             eval{ require DBI; $HAVE_DBI=1; };
190              
191             my (%_cache,%_arg);
192              
193             sub new {
194 0     0 1   my $class = shift;
195 0           %_arg = @_;
196              
197 0 0         $_arg{cache} = 1 unless(defined($_arg{cache}));
198 0 0         $_arg{debug} = 1 unless(defined($_arg{debug}));
199            
200 0           my $vpopdir = (getpwnam('vpopmail'))[7];
201 0 0         die "vpopmail home directory ($vpopdir) not found.\n" unless(-d $vpopdir);
202              
203 0 0         if(open(MYSQL, "${vpopdir}/etc/vpopmail.mysql")){
    0          
204 0           chop(my $input=);
205 0           my ($hostname,$dbport,$dbun,$dbpw,$dbname) = split(/\|/, $input);
206 0           close MYSQL;
207              
208 0           my $dsn = "DBI:mysql:hostname=${hostname};database=${dbname}";
209 0 0         $dsn .= ";port=$dbport" if($dbport);
210 0           $_arg{dsn} = $dsn;
211 0           $_arg{dbname} = $dbname;
212 0           $_arg{dbun} = $dbun;
213 0           $_arg{dbpw} = $dbpw;
214 0           $_arg{auth_module} = 'sql';
215             }elsif($_arg{auth_module} eq 'sql'){
216 0 0         $_arg{dsn} = 'DBI:ldap:host=localhost;database=vpopmail' unless(defined($_arg{dsn}));
217 0           ($_arg{dbname}) = $_arg{dsn} =~ /database=([^\=\;\:\s]+)/;
218 0 0         $_arg{dbun} = 'vpopmailuser' unless(defined($_arg{dbun}));
219 0 0         $_arg{dbpw} = 'vpoppasswd' unless(defined($_arg{dbpw}));
220             }else{
221 0           $_arg{auth_module} = 'cdb';
222             }
223            
224 0 0         if($_arg{auth_module} eq 'sql'){
225 0 0         unless($HAVE_DBI){
226 0           warn "You're trying to use SQL support, but do not have DBI in \@INC. (\@INC contains: )";
227 0           foreach(@INC){
228 0           print "$_ ";
229             }
230 0           die "\nnew() failed-- ";
231             }
232             }
233            
234 0           return(bless({},$class));
235             }
236              
237 0     0 0   sub Version { $Mail::vpopmail::VERSION }
238              
239             sub _handle_dbh {
240 0 0   0     my $dbh = ($_cache{dbh}) ? $_cache{dbh} : DBI->connect($_arg{dsn}, $_arg{dbun}, $_arg{dbpw}, {RaiseError => 1});
241              
242 0 0         unless($dbh){
243 0           die "Connect to database failed: $DBI::errstr ";
244             }
245 0 0         if($_arg{cache}){
246 0 0         $_cache{dbh} = $dbh unless($_cache{dbh});
247             }
248 0           return($dbh);
249             }
250              
251             sub _dir {
252 0     0     my $class = shift;
253 0 0         if(my $domain = shift){
254 0 0         return($_cache{$domain}{dir}) if($_cache{$domain}{dir});
255              
256             # assign is still authoritative when sql in use
257 0 0         if(open(ASSIGN, '/var/qmail/users/assign')){
258 0           my $dir;
259 0           while(){
260 0 0         if(/^\+${domain}\-:[^:]+:\d+:\d+:([^:]+):-:/){
261 0           $dir = $1;
262 0           last;
263             }
264             }
265 0           close ASSIGN;
266              
267 0 0         if(defined($dir)){
268 0 0         $_cache{$domain}{dir} = $dir if($_arg{cache});
269 0           return($dir); # this dir is not verified, it's just what vpopmail thinks
270             }else{
271 0 0         warn "could not find directory for domain: $domain\n" if($_arg{debug});
272             }
273             }else{
274 0 0         warn "could not open /var/qmail/users/assign: $!\n" if($_arg{debug});
275             }
276             }else{
277 0 0         warn "domain not supplied correctly\n" if($_arg{debug});
278             }
279 0           return();
280             }
281              
282             sub userinfo {
283 0     0 1   my $class = shift;
284 0           my %arg = @_;
285 0 0 0       unless(exists($arg{email}) && exists($arg{field})){
286 0 0         if($_arg{debug}){
287 0           warn "syntax error: email: $arg{email} field: $arg{field}\n";
288             }
289 0           return();
290             }
291 0           my ($user,$domain) = split(/\@/, $arg{email}); # no routing data supported
292 0 0         warn "arg{email}: $arg{email} - user: $user - domain: $domain\n" if($_arg{debug});
293              
294 0 0 0       if(defined($user) && defined($domain)){
295 0           my @return;
296 0           my $dir = Mail::vpopmail->_dir($domain);
297              
298 0 0         if($arg{field} eq 'dir'){
299 0           push @return, $dir;
300             }else{
301 0 0         if(exists($_cache{$arg{email}}{crypt})){
302 0 0         warn "cache found for $arg{email}\n" if($_arg{debug});
303 0           foreach my $field (split(/,/, $arg{field})){
304 0           push @return, $_cache{$arg{email}}{$field};
305             }
306             }else{
307 0           my (%uhash,$found);
308 0 0         if($_arg{auth_module} eq 'cdb'){
309 0 0         if(open(VPASSWD, "${dir}/vpasswd")){
310 0           while(){
311 0           chomp;
312 0 0         if(/^${user}:([^:]+):(\d+):(\d+):([^:]*):([^:]+):([^:]+)(:([^:]+))?/){
313 0           %uhash = (crypt => $1, uid => $2, gid => $3, comment => $4,
314             maildir => $5, quota => $6, plain => $8, dir => $dir);
315 0           $found=1;
316 0           last;
317             }
318             }
319 0           close VPASSWD;
320             }else{
321 0 0         warn "cannot open ${dir}/vpasswd: $!\n" if($_arg{debug});
322             }
323             }else{
324             # sql
325 0           my $dbh = _handle_dbh();
326 0           my $sql = "SELECT pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell,pw_clear_passwd FROM $_arg{dbname}";
327 0           $sql .= ' WHERE pw_name = ' . $dbh->quote($user) . ' AND pw_domain = ' . $dbh->quote($domain);
328 0           my $sth = $dbh->prepare($sql);
329 0           $sth->execute;
330 0           my $row = $sth->fetchrow_arrayref;
331 0           %uhash = (crypt => $row->[0], uid => $row->[1], gid => $row->[2], comment => $row->[3],
332             maildir => $row->[4], quota => $row->[5], plain => $row->[6], dir => ${dir});
333 0 0         $found=1 if(exists($uhash{crypt}));
334             }
335 0 0         if($found){
336 0 0         if($_arg{cache}){
337 0           while(my($key,$value) = each %uhash){
338 0           $_cache{$arg{email}}{$key} = $value;
339             }
340             }
341            
342 0           foreach my $field (split(/,/, $arg{field})){
343 0           push @return, $uhash{$field};
344             }
345             }else{
346 0 0         warn "cannot find ${user} in ${domain}\n" if($_arg{debug});
347             }
348             }
349             }
350 0 0         return (@return == 1) ? $return[0] : @return;
351             }else{
352 0 0         warn "email not supplied correctly\n" if($_arg{'debug'});
353             }
354 0           return();
355             }
356              
357             sub alldomains {
358 0     0 1   my $class = shift;
359 0           my %arg = @_;
360 0 0 0       unless($arg{field} eq 'name' || $arg{field} eq 'dir' || $arg{field} eq 'map'){
      0        
361 0 0         if($_arg{debug}){
362 0           warn "syntax error: field: $arg{field}\n";
363             }
364 0           return();
365             }
366              
367             # assign is still authoritative when sql in use
368 0 0         if(open(ASSIGN, '/var/qmail/users/assign')){
369 0           my @array;
370 0           while(){
371 0 0         if(/^\+([^:]+)\-:[^:]+:\d+:\d+:([^:]+):-:/){
372 0 0         if($arg{field} eq 'map'){
    0          
373 0           push @array, { name => $1, dir => $2 };
374             }elsif($arg{field} eq 'dir'){
375 0           push @array, $2;
376             }else{
377 0           push @array, $1;
378             }
379             }
380             }
381 0           close ASSIGN;
382 0           return(\@array);
383             }else{
384 0 0         warn "could not open /var/qmail/users/assign: $!\n" if($_arg{debug});
385             }
386 0           return();
387             }
388              
389             sub domaininfo {
390 0     0 1   my $class = shift;
391 0           my %arg = @_;
392              
393 0 0 0       if(exists($arg{domain}) && exists($arg{field})){
394 0 0 0       unless($arg{field} eq 'mailboxes' || $arg{field} eq 'all' || $arg{field} eq 'dir'){
      0        
395 0 0         warn "syntax error: domain field type may be 'mailboxes' or 'all'\n" if($_arg{debug});
396 0           return();
397             }
398             }else{
399 0 0         if($_arg{debug}){
400 0           warn "syntax error: domain: $arg{domain} - field: $arg{field}\n";
401             }
402 0           return();
403             }
404              
405 0 0         my %hash = ( dir => (exists($_cache{$arg{domain}}{dir})) ? $_cache{$arg{domain}}{dir} : Mail::vpopmail->_dir($arg{domain}) );
406 0 0         warn "hash{dir}: $hash{dir}\n" if($_arg{debug});
407              
408 0 0         if($arg{field} eq 'dir'){
409 0           return($hash{dir});
410             }
411              
412 0           my @return;
413 0 0         if($_arg{auth_module} eq 'cdb'){
414 0 0         if(open(VPASSWD, "$hash{dir}/vpasswd")){
415 0           while(){
416 0           chomp;
417 0 0         if(/^([^:]+):([^:]+):(\d+):(\d+):([^:]*):([^:]+):([^:]+)(:([^:]+))?/){
418 0           %hash = (mailbox => $1, crypt => $2, uid => $3, gid => $4,
419             comment => $5, maildir => $6, quota => $7, plain => $9, dir => $hash{dir});
420            
421 0 0         if($arg{field} eq 'mailboxes'){
422 0           push @return, $hash{mailbox};
423             }else{
424 0           push @return, \%hash;
425             }
426            
427 0 0         if($_arg{cache}){
428 0           while(my($key,$value) = each %hash){
429 0           $_cache{$hash{mailbox}}{$key} = $value;
430             }
431             }
432             }
433             }
434 0           close VPASSWD;
435            
436             }else{
437 0 0         warn "cannot open $hash{dir}/vpasswd: $!\n" if($_arg{debug});
438             }
439             }else{
440             #sql;
441 0           my $dbh = _handle_dbh();
442 0           my $sql = 'SELECT pw_name';
443 0 0         $sql .= ',pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell,pw_clear_passwd' if($arg{field} eq 'all');
444 0           $sql .= " FROM $_arg{dbname} WHERE pw_domain = " . $dbh->quote($arg{domain});
445 0           my $sth = $dbh->prepare($sql);
446 0           $sth->execute;
447 0           while(my $row = $sth->fetchrow_arrayref){
448 0 0         if($arg{field} eq 'mailboxes'){
449 0           push @return, $row->[0];
450             }else{
451 0           push @return, { mailbox => $row->[0], crypt => $row->[1], uid => $row->[2], gid => $row->[3],
452             comment => $row->[4], maildir => $row->[5], quota => $row->[6],
453             plain => $row->[7], dir => $hash{dir} };
454             }
455             }
456             }
457 0           return(\@return);
458             }
459              
460             1;