# mailboxes-lib.pl # Common functions for reading user mailboxes do '../web-lib.pl'; do '../ui-lib.pl'; do 'boxes-lib.pl'; do 'folders-lib.pl'; &init_config(); %access = &get_module_acl(); $config{'perpage'} ||= 20; $config{'column_count'} ||= 4; $gconfig{'logfiles'} = 0; # file change logging never needs to be done # Always detect the mail system if not set if ($config{'mail_system'} == 3) { $config{'mail_system'} = &detect_mail_system(); &save_module_config() if ($config{'mail_system'} != 3); } @ignore_users_list = &split_quoted_string($config{'ignore_users'}); # send_mail_program(from, to) # Returns the command for injecting email, based on the mail system in use sub send_mail_program { if ($config{'mail_system'} == 1 || $config{'mail_system'} == 0) { # Use sendmail executable, or postfix wrapper local %sconfig = &foreign_check("sendmail") && $config{'mail_system'} == 1 ? &foreign_config("sendmail") : ( ); local $cmd = &has_command($sconfig{'sendmail_path'} || "sendmail"); return "$cmd -t -f".quotemeta($_[0]) if ($cmd); } elsif ($config{'mail_system'} == 2) { # Use qmail injector local %qconfig = &foreign_check("qmailadmin") ? &foreign_config("qmailadmin") : ( ); local $cmd = ($qconfig{'qmail_dir'} || "/var/qmail"). "/bin/qmail-inject"; return $cmd if (-x $cmd); } else { # Fallback - use sendmail command local $cmd = &has_command("sendmail"); return "$cmd -t -f".quotemeta($_[0]) if ($cmd); } return undef; } # list_folders() # Returns a list of all mailboxes for all users sub list_folders { local (@rv, $uinfo); foreach $uinfo (&list_mail_users()) { push(@rv, &list_user_folders(@$uinfo)); } return @rv; } # list_user_folders(user, [other info]) # Returns a list of folders for mailboxes belonging to some user sub list_user_folders { if ($_[0] =~ /^\//) { # A path .. return a folder just for it return ( { 'name' => $_[0], 'file' => $_[0], 'type' => &folder_type($_[0]), 'mode' => 1, 'index' => 0 } ); } else { # Getting folders for a user local @uinfo = @_ > 1 ? @_ : &get_mail_user($_[0]); return ( ) if (!@uinfo); local ($dir, $style, $mailbox, $maildir) = &get_mail_style(); local @rv; # Check for user-specified mail store if ($uinfo[10]) { push(@rv, { 'name' => $uinfo[10], 'file' => $uinfo[10], 'type' => &folder_type($uinfo[10]), 'mode' => 0, 'index' => scalar(@rv) } ); } # Check for /var/mail/USERNAME file if ($dir) { local $mf = &mail_file_style($uinfo[0], $dir, $style); push(@rv, { 'type' => 0, 'name' => $mf, 'file' => $mf, 'user' => $uinfo[0], 'index' => scalar(@rv) }); } # Check for file in home dir if ($mailbox) { local $mf = "$uinfo[7]/$mailbox"; if (-r $mf || !@rv) { push(@rv, { 'type' => 0, 'name' => "~$uinfo[0]/$mailbox", 'file' => $mf, 'user' => $uinfo[0], 'index' => scalar(@rv) }); } } # Check for directory in home dir if ($maildir) { local $mf = "$uinfo[7]/$maildir"; if (-d $mf || !@rv) { push(@rv, { 'type' => 1, 'name' => "~$uinfo[0]/$maildir/", 'file' => $mf, 'user' => $uinfo[0], 'index' => scalar(@rv) }); } } # Add any ~/mail files if ($config{'mail_usermin'}) { local $folders_dir = "$uinfo[7]/$config{'mail_usermin'}"; foreach $p (&recursive_files($folders_dir, 1)) { local $f = $p; $f =~ s/^\Q$folders_dir\E\///; push(@rv, { 'file' => $p, 'name' => "~$uinfo[0]/$config{'mail_usermin'}/$f", 'type' => &folder_type($p), 'mode' => 0, 'sent' => $f eq "sentmail", 'index' => scalar(@rv) } ); # Work out if this is a spam folder if (lc($f) eq "spam" || lc($f) eq ".spam") { # Has spam in the name $rv[$#rv]->{'spam'} = 1; } elsif (&foreign_check("virtual-server")) { # Check if Virtualmin is defaulting to it local %vconfig = &foreign_config( "virtual-server"); local $sf = $vconfig{'spam_delivery'}; if ($sf) { $sf =~ s/\$HOME/$uinfo[7]/g; $sf =~ s/\~/$uinfo[7]/g; if ($sf !~ /^\//) { $sf = $uinfo[7]."/".$sf; } $sf =~ s/\/$//; if ($p eq $sf) { $rv[$#rv]->{'spam'} = 1; } } } } } # Add any Usermin external mail files if ($config{'mailbox_user'}) { local %userconfig; &read_file_cached("$uinfo[7]/$config{'mailbox_user'}/config", \%userconfig); local $o; foreach $o (split(/\t+/, $userconfig{'mailboxes'})) { $o =~ /\/([^\/]+)$/ || next; push(@rv, { 'name' => $o, 'file' => $o, 'type' => &folder_type($o), 'mode' => 1, 'index' => scalar(@rv) } ); } } foreach my $f (@rv) { $f->{'user'} = $_[0]; } return @rv; } } sub list_user_folders_sorted { return &list_user_folders(@_); } # get_mail_style() # Returns a list containing the mail base directory, directory style, # mail file in home dir, and maildir in home dir sub get_mail_style { if (!defined(@mail_style_cache)) { if ($config{'auto'}) { # Based on mail server if ($config{'mail_system'} == 1) { # Can get paths from Sendmail module config local %sconfig = &foreign_config("sendmail"); if ($sconfig{'mail_dir'}) { return ($sconfig{'mail_dir'}, $sconfig{'mail_style'}, undef, undef); } else { return (undef, $sconfig{'mail_style'}, $sconfig{'mail_file'}, undef); } } elsif ($config{'mail_system'} == 0) { # Need to query Postfix module for paths &foreign_require("postfix", "postfix-lib.pl"); local @s = &postfix::postfix_mail_system(); if ($s[0] == 0) { return ($s[1], 0, undef, undef); } elsif ($s[0] == 1) { return (undef, 0, $s[1], undef); } elsif ($s[0] == 2) { return (undef, 0, undef, $s[1]); } } elsif ($config{'mail_system'} == 2 || $config{'mail_system'} == 4) { # Need to check qmail module config for paths local %qconfig = &foreign_config("qmailadmin"); if ($qconfig{'mail_system'} == 1) { return (undef, 0, undef, $qconfig{'mail_dir_qmail'}); } elsif ($qconfig{'mail_dir'}) { return ($qconfig{'mail_dir'}, $qconfig{'mail_style'}, undef, undef); } else { return (undef, $qconfig{'mail_style'}, $qconfig{'mail_file'}, undef); } } elsif ($config{'mail_system'} == 5) { # vpopmail always uses ~/Maildir return ( undef, 0, undef, "Maildir" ); } else { # No mail server set yet! return (undef, undef, undef, undef); } } else { # Use config settings @mail_style_cache = ($config{'mail_dir'}, $config{'mail_style'}, $config{'mail_file'}, $config{'mail_sub'}); } } return @mail_style_cache; } # can_user(username, [other details]) sub can_user { if (!&is_user($_[0])) { # For external files, check if the file is under an allowed # directory, or owned by an allowed user. local @st = stat($_[0]); local @uinfo = &get_mail_uid($st[4]); return 1 if (@uinfo && &can_user(@uinfo)); local $dir = &allowed_directory(); return defined($dir) && &is_under_directory($dir, $_[0]); } local @u = @_ > 1 ? @_ : &get_mail_user($_[0]); return 1 if ($_[0] && $access{'sent'} eq $_[0]); return 1 if ($access{'mmode'} == 1); return 0 if (!@u); return 0 if ($_[0] =~ /\.\./); return 0 if ($access{'mmode'} == 0); local $u; if ($access{'mmode'} == 2) { # Is user in list of users? foreach $u (split(/\s+/, $access{'musers'})) { return 1 if ($u eq $_[0]); } return 0; } elsif ($access{'mmode'} == 4) { # Is user the current Webmin user? return 1 if ($_[0] eq $remote_user); } elsif ($access{'mmode'} == 5) { # Is the user's gid in the list of groups? local $gid; foreach $gid (split(/\s+/, $access{'musers'})) { return 1 if ($u[3] == $gid); if ($access{'msec'}) { # Check user's secondary groups too local @ginfo = getgrgid($gid); local @m = split(/\s+/, $ginfo[3]); return 1 if (&indexof($_[0], @m) >= 0); } } } elsif ($access{'mmode'} == 3) { # Is the user not in the list of denied users foreach $u (split(/\s+/, $access{'musers'})) { return 0 if ($u eq $_[0]); } return 1; } elsif ($access{'mmode'} == 6) { # Does the user match a regexp? return ($_[0] =~ /^$access{'musers'}$/); } elsif ($access{'mmode'} == 7) { # Is the user's UID within the allowed range? return (!$access{'musers'} || $u[2] >= $access{'musers'}) && (!$access{'musers2'} || $u[2] <= $access{'musers2'}); } return 0; # can't happen! } # movecopy_user_select(number, folders, folder, form-no) # Returns HTML for entering a username to copy mail to sub movecopy_user_select { local $rv; $rv .= ""; $rv .= ""; $rv .= &ui_user_textbox("mfolder$_[0]", undef, $_[3]); return $rv; } # need_delete_warn(&folder) sub need_delete_warn { return 1 if ($config{'delete_warn'} eq 'y'); return 0 if ($config{'delete_warn'} eq 'n'); local $mf; return $_[0]->{'type'} == 0 && ($mf = &folder_file($_[0])) && &disk_usage_kb($mf)*1024 > $config{'delete_warn'}; } @mail_system_modules = ( [ undef, 4, \&check_qmail_ldap, \&test_qmail_ldap ], [ undef, 5, \&check_vpopmail ], [ "qmailadmin", 2 ], [ "postfix", 0 ], [ "sendmail", 1 ], ); # detect_mail_system() # Works out which mail server is installed sub detect_mail_system { foreach $m (@mail_system_modules) { return $m->[1] if (&check_mail_system($m)); } return 3; } # check_mail_system(&mailsystem) sub check_mail_system { if ($_[0]->[0]) { # Just check module return &foreign_installed($_[0]->[0]); } else { # Call function local $func = $_[0]->[2]; return &$func($_[0]); } } # test_mail_system([&mailsystem]) # Returns an error message if the mail system is invalid sub test_mail_system { local $ms; if (!$ms) { ($ms) = grep { $_->[1] == $config{'mail_system'} } @mail_system_modules; } if ($ms->[3]) { local $func = $ms->[3]; return &$func(); } return undef; } # check_qmail_ldap() # Make sure Qmail with LDAP extensions is installed sub check_qmail_ldap { return 0 if (&foreign_installed("qmailadmin", 1) != 2); local %qconfig = &foreign_config("qmailadmin"); return 0 if (!-r "$qconfig{'qmail_dir'}/control/ldapserver"); return 1; } # check_vpopmail() # Make sure Qmail with VPopMail extensions is installed sub check_vpopmail { return 0 if (&foreign_installed("qmailadmin", 1) != 2); return -x "$config{'vpopmail_dir'}/bin/vadddomain"; } # test_qmail_ldap() # Returns undef the Qmail+LDAP database can be contacted OK, or an error message sub test_qmail_ldap { $config{'ldap_host'} || return $text{'ldap_ehost'}; $config{'ldap_port'} =~ /^\d+$/ || return $text{'ldap_eport'}; $config{'ldap_login'} || return $text{'ldap_euser'}; $config{'ldap_base'} || return $text{'ldap_ebase'}; local $err = &connect_qmail_ldap(1); return ref($err) ? undef : $err; } # show_users_table(&users-list, [only-with-mail]) # Outputs HTML for a table of users, with the appropriate sorting and mode sub show_users_table { local @users = @{$_[0]}; local ($u, %size, %incount, %sentcount, %foldercount); if ($config{'sort_mode'} == 2 || $config{'show_size'} > 0 || $_[1] || $config{'show_count'} || $config{'show_sent'}) { # Need to check folders foreach $u (@users) { next if ($config{'ignore_users_enabled'} == 1 && &indexof($u->[0], @ignore_users_list) >= 0); local @folders = &list_user_folders(@$u); $foldercount{$u->[0]} = scalar(@folders); if ($config{'sort_mode'} == 2 || $config{'show_size'} > 0 || $_[1]) { # Compute size of folders $size{$u->[0]} = $config{'size_mode'} ? &folder_size(@folders) : &folder_size($folders[0]); } if ($config{'show_count'}) { # Get number of mails in inbox $incount{$u->[0]} = &mailbox_folder_size($folders[0]); } if ($config{'show_sent'}) { # Count number of messages in sent mail local ($sent) = grep { $_->{'sent'} } @folders; $sentcount{$u->[0]} = &mailbox_folder_size($sent) if ($sent); } } } # Sort by chosen mode if ($config{'sort_mode'} == 2) { @users = sort { $size{$b->[0]} <=> $size{$a->[0]} } @users; } elsif ($config{'sort_mode'} == 1) { @users = sort { lc($a->[0]) cmp lc($b->[0]) } @users; } local @allusers = @users; if ($_[1]) { # Limit to those with mail @users = grep { $size{$_->[0]} } @users; } # Show table of users if (!@allusers) { print "$text{'index_nousers'}
\n"; } elsif (!@users) { print "$text{'index_nousersmail'}
\n"; } elsif ($config{'show_size'} == 2) { # Show full user details local %uconfig = &foreign_config("useradmin"); local @ccols; push(@ccols, $text{'find_incount'}) if ($config{'show_count'}); push(@ccols, $text{'find_sentcount'}) if ($config{'show_sent'}); push(@ccols, $text{'find_fcount'}) if (%foldercount); print &ui_columns_start( [ $text{'find_user'}, $text{'find_real'}, $text{'find_group'}, $text{'find_home'}, $text{'find_size'}, @ccols ], 100); foreach $u (@users) { local $g = getgrgid($u->[3]); next if ($config{'ignore_users_enabled'} == 1 && &indexof($u->[0], @ignore_users_list) >= 0); $u->[6] =~ s/,.*$// if ($uconfig{'extra_real'}); local $home = $u->[7]; if (length($home) > 30) { $home = "...".substr($home, -30); } local @ccols; if ($config{'show_count'}) { push(@ccols, int($incount{$u->[0]})) } if ($config{'show_sent'}) { push(@ccols, int($sentcount{$u->[0]})) } if (%foldercount) { push(@ccols, int($foldercount{$u->[0]})) } print &ui_columns_row( [ "$u->[0]", $u->[6], $g, $home, $size{$u->[0]} == 0 ? $text{'index_empty'} : &nice_size($size{$u->[0]}), @ccols ], [ undef, undef, undef, undef, "nowrap" ]); } print &ui_columns_end(); } else { # Just showing username (and maybe size) print &ui_table_start($text{'index_header'}, "width=100%", $config{'column_count'}); local $i = 0; foreach $u (@users) { next if ($config{'ignore_users_enabled'} == 1 && &indexof($u->[0], @ignore_users_list) >= 0); print "