2 # Common functions for reading user mailboxes
4 BEGIN { push(@INC, ".."); };
7 do "$module_root_directory/boxes-lib.pl";
8 do "$module_root_directory/folders-lib.pl";
9 %access = &get_module_acl();
10 $config{'perpage'} ||= 20;
11 $config{'column_count'} ||= 4;
12 $no_log_file_changes = 1; # Turn off file change logging for this module
14 @mail_system_modules = (
15 [ undef, 4, \&check_qmail_ldap, \&test_qmail_ldap ],
16 [ undef, 5, \&check_vpopmail ],
20 [undef, 6, \&check_exim ]
23 @ignore_users_list = &split_quoted_string($config{'ignore_users'});
25 # Always detect the mail system if not set
26 if ($config{'mail_system'} == 3) {
27 $config{'mail_system'} = &detect_mail_system();
28 &save_module_config() if ($config{'mail_system'} != 3);
31 # send_mail_program(from, &dests)
32 # Returns the command for injecting email, based on the mail system in use
35 my ($from, $dests) = @_;
36 my $qdests = join(" ", map { quotemeta($_) } @$dests);
38 if ($config{'mail_system'} == 1 || $config{'mail_system'} == 0) {
39 # Use sendmail executable, or postfix wrapper
40 local %sconfig = &foreign_check("sendmail") &&
41 $config{'mail_system'} == 1 ?
42 &foreign_config("sendmail") : ( );
43 local $cmd = &has_command($sconfig{'sendmail_path'} || "sendmail");
44 return "$cmd -f".quotemeta($_[0])." ".$qdests if ($cmd);
46 elsif ($config{'mail_system'} == 2) {
48 local %qconfig = &foreign_check("qmailadmin") ?
49 &foreign_config("qmailadmin") : ( );
50 local $cmd = ($qconfig{'qmail_dir'} || "/var/qmail").
52 return $cmd if (-x $cmd);
55 # Fallback - use sendmail command
56 local $cmd = &has_command("sendmail");
57 return "$cmd -f".quotemeta($_[0])." ".$qdests if ($cmd);
63 # Returns a list of all mailboxes for all users
67 foreach $uinfo (&list_mail_users()) {
68 push(@rv, &list_user_folders(@$uinfo));
73 # list_user_folders(user, [other info])
74 # Returns a list of folders for mailboxes belonging to some user
78 # A path .. return a folder just for it
79 return ( { 'name' => $_[0],
81 'type' => &folder_type($_[0]),
87 # Getting folders for a user
88 local @uinfo = @_ > 1 ? @_ : &get_mail_user($_[0]);
89 return ( ) if (!@uinfo);
90 local ($dir, $style, $mailbox, $maildir) = &get_mail_style();
93 # Check for user-specified mail store
95 push(@rv, { 'name' => $uinfo[10],
97 'type' => &folder_type($uinfo[10]),
99 'index' => scalar(@rv) } );
102 # Check for /var/mail/USERNAME file
104 local $mf = &mail_file_style($uinfo[0], $dir, $style);
105 push(@rv, { 'type' => 0,
109 'index' => scalar(@rv) });
112 # Check for file in home dir
114 local $mf = "$uinfo[7]/$mailbox";
115 if (-r $mf || !@rv) {
116 push(@rv, { 'type' => 0,
117 'name' => "~$uinfo[0]/$mailbox",
120 'index' => scalar(@rv) });
124 # Check for directory in home dir
126 local $mf = "$uinfo[7]/$maildir";
127 if (-d $mf || !@rv) {
128 push(@rv, { 'type' => 1,
129 'name' => "~$uinfo[0]/$maildir/",
132 'index' => scalar(@rv) });
136 # Add any ~/mail files
137 if ($config{'mail_usermin'}) {
138 local $folders_dir = "$uinfo[7]/$config{'mail_usermin'}";
139 foreach $p (&recursive_files($folders_dir, 1)) {
141 $f =~ s/^\Q$folders_dir\E\///;
142 push(@rv, { 'file' => $p,
143 'name' => "~$uinfo[0]/$config{'mail_usermin'}/$f",
144 'type' => &folder_type($p),
146 'sent' => $f eq "sentmail",
147 'index' => scalar(@rv) } );
149 # Work out if this is a spam folder
150 if (lc($f) eq "spam" || lc($f) eq ".spam") {
151 # Has spam in the name
152 $rv[$#rv]->{'spam'} = 1;
154 elsif (&foreign_check("virtual-server")) {
155 # Check if Virtualmin is defaulting to it
156 local %vconfig = &foreign_config(
158 local $sf = $vconfig{'spam_delivery'};
160 $sf =~ s/\$HOME/$uinfo[7]/g;
161 $sf =~ s/\~/$uinfo[7]/g;
163 $sf = $uinfo[7]."/".$sf;
167 $rv[$#rv]->{'spam'} = 1;
174 # Add any Usermin external mail files
175 if ($config{'mailbox_user'}) {
177 &read_file_cached("$uinfo[7]/$config{'mailbox_user'}/config",
180 foreach $o (split(/\t+/, $userconfig{'mailboxes'})) {
181 $o =~ /\/([^\/]+)$/ || next;
182 push(@rv, { 'name' => $o,
184 'type' => &folder_type($o),
186 'index' => scalar(@rv) } );
190 foreach my $f (@rv) {
191 $f->{'user'} = $_[0];
197 sub list_user_folders_sorted
199 return &list_user_folders(@_);
203 # Returns a list containing the mail base directory, directory style,
204 # mail file in home dir, and maildir in home dir
207 if (!scalar(@mail_style_cache)) {
208 if ($config{'auto'}) {
209 # Based on mail server
210 if ($config{'mail_system'} == 1) {
211 # Can get paths from Sendmail module config
212 local %sconfig = &foreign_config("sendmail");
213 if ($sconfig{'mail_dir'}) {
214 # File under /var/mail
215 return ($sconfig{'mail_dir'},
216 $sconfig{'mail_style'}, undef, undef);
218 elsif ($sconfig{'mail_type'}) {
219 # Maildir under home directory
220 return (undef, $sconfig{'mail_style'},
221 undef, $sconfig{'mail_file'});
224 # mbox under home directory
225 return (undef, $sconfig{'mail_style'},
226 $sconfig{'mail_file'}, undef);
229 elsif ($config{'mail_system'} == 0) {
230 # Need to query Postfix module for paths
231 &foreign_require("postfix", "postfix-lib.pl");
232 local @s = &postfix::postfix_mail_system();
234 return ($s[1], 0, undef, undef);
237 return (undef, 0, $s[1], undef);
240 return (undef, 0, undef, $s[1]);
243 elsif ($config{'mail_system'} == 2 ||
244 $config{'mail_system'} == 4) {
245 # Need to check qmail module config for paths
246 local %qconfig = &foreign_config("qmailadmin");
247 if ($qconfig{'mail_system'} == 1) {
248 return (undef, 0, undef,
249 $qconfig{'mail_dir_qmail'});
251 elsif ($qconfig{'mail_dir'}) {
252 return ($qconfig{'mail_dir'}, $qconfig{'mail_style'}, undef, undef);
255 return (undef, $qconfig{'mail_style'}, $qconfig{'mail_file'}, undef);
258 elsif ($config{'mail_system'} == 5) {
259 # vpopmail always uses ~/Maildir
260 return ( undef, 0, undef, "Maildir" );
262 elsif ($config{'mail_system'} == 6) {
263 # localmail using smail or exim
264 return ( "/var/spool/mail", 0, undef, undef );
267 # No mail server set yet!
268 return (undef, undef, undef, undef);
272 # Use config settings
273 @mail_style_cache = ($config{'mail_dir'}, $config{'mail_style'},
274 $config{'mail_file'}, $config{'mail_sub'});
277 return @mail_style_cache;
280 # can_user(username, [other details])
283 if (!&is_user($_[0])) {
284 # For external files, check if the file is under an allowed
285 # directory, or owned by an allowed user.
286 local @st = stat($_[0]);
287 local @uinfo = &get_mail_uid($st[4]);
288 return 1 if (@uinfo && &can_user(@uinfo));
289 local $dir = &allowed_directory();
290 return defined($dir) && &is_under_directory($dir, $_[0]);
292 local @u = @_ > 1 ? @_ : &get_mail_user($_[0]);
293 return 1 if ($_[0] && $access{'sent'} eq $_[0]);
294 return 1 if ($access{'mmode'} == 1);
296 return 0 if ($_[0] =~ /\.\./);
297 return 0 if ($access{'mmode'} == 0);
299 if ($access{'mmode'} == 2) {
300 # Is user in list of users?
301 foreach $u (split(/\s+/, $access{'musers'})) {
302 return 1 if ($u eq $_[0]);
306 elsif ($access{'mmode'} == 4) {
307 # Is user the current Webmin user?
308 return 1 if ($_[0] eq $remote_user);
310 elsif ($access{'mmode'} == 5) {
311 # Is the user's gid in the list of groups?
313 foreach $gid (split(/\s+/, $access{'musers'})) {
314 return 1 if ($u[3] == $gid);
315 if ($access{'msec'}) {
316 # Check user's secondary groups too
317 local @ginfo = getgrgid($gid);
318 local @m = split(/\s+/, $ginfo[3]);
319 return 1 if (&indexof($_[0], @m) >= 0);
323 elsif ($access{'mmode'} == 3) {
324 # Is the user not in the list of denied users
325 foreach $u (split(/\s+/, $access{'musers'})) {
326 return 0 if ($u eq $_[0]);
330 elsif ($access{'mmode'} == 6) {
331 # Does the user match a regexp?
332 return ($_[0] =~ /^$access{'musers'}$/);
334 elsif ($access{'mmode'} == 7) {
335 # Is the user's UID within the allowed range?
336 return (!$access{'musers'} || $u[2] >= $access{'musers'}) &&
337 (!$access{'musers2'} || $u[2] <= $access{'musers2'});
339 return 0; # can't happen!
342 # movecopy_user_select(number, folders, folder, form-no)
343 # Returns HTML for entering a username to copy mail to
344 sub movecopy_user_select
347 $rv .= "<input type=submit name=move$_[0] value=\"$text{'mail_move'}\" ".
348 "onClick='return check_clicks(form)'>";
349 $rv .= "<input type=submit name=copy$_[0] value=\"$text{'mail_copy'}\" ".
350 "onClick='return check_clicks(form)'>";
351 $rv .= &ui_user_textbox("mfolder$_[0]", undef, $_[3]);
355 # need_delete_warn(&folder)
358 return 1 if ($config{'delete_warn'} eq 'y');
359 return 0 if ($config{'delete_warn'} eq 'n');
361 return $_[0]->{'type'} == 0 &&
362 ($mf = &folder_file($_[0])) &&
363 &disk_usage_kb($mf)*1024 > $config{'delete_warn'};
366 # detect_mail_system()
367 # Works out which mail server is installed
368 sub detect_mail_system
370 foreach my $m (@mail_system_modules) {
371 return $m->[1] if (&check_mail_system($m));
376 # check_mail_system(&mailsystem)
377 sub check_mail_system
381 return &foreign_installed($_[0]->[0]);
385 local $func = $_[0]->[2];
386 return &$func($_[0]);
390 # test_mail_system([&mailsystem])
391 # Returns an error message if the mail system is invalid
396 ($ms) = grep { $_->[1] == $config{'mail_system'} } @mail_system_modules;
399 local $func = $ms->[3];
406 # Make sure Qmail with LDAP extensions is installed
409 return 0 if (&foreign_installed("qmailadmin", 1) != 2);
410 local %qconfig = &foreign_config("qmailadmin");
411 return 0 if (!-r "$qconfig{'qmail_dir'}/control/ldapserver");
416 # Make sure Qmail with VPopMail extensions is installed
419 return 0 if (&foreign_installed("qmailadmin", 1) != 2);
420 return -x "$config{'vpopmail_dir'}/bin/vadddomain";
424 # Make sure Exim is installed
427 return &has_command('exim');
432 # Returns undef the Qmail+LDAP database can be contacted OK, or an error message
435 $config{'ldap_host'} || return $text{'ldap_ehost'};
436 $config{'ldap_port'} =~ /^\d+$/ || return $text{'ldap_eport'};
437 $config{'ldap_login'} || return $text{'ldap_euser'};
438 $config{'ldap_base'} || return $text{'ldap_ebase'};
439 local $err = &connect_qmail_ldap(1);
440 return ref($err) ? undef : $err;
443 # show_users_table(&users-list, [only-with-mail])
444 # Outputs HTML for a table of users, with the appropriate sorting and mode
447 my @users = @{$_[0]};
448 my ($u, %size, %incount, %sentcount, %foldercount);
449 if ($config{'sort_mode'} == 2 || $config{'show_size'} > 0 || $_[1] ||
450 $config{'show_count'} || $config{'show_sent'}) {
451 # Need to check folders
452 foreach $u (@users) {
453 next if ($config{'ignore_users_enabled'} == 1 &&
454 &indexof($u->[0], @ignore_users_list) >= 0);
455 local @folders = &list_user_folders(@$u);
456 $foldercount{$u->[0]} = scalar(@folders);
457 if ($config{'sort_mode'} == 2 ||
458 $config{'show_size'} > 0 || $_[1]) {
459 # Compute size of folders
460 $size{$u->[0]} = $config{'size_mode'} ?
461 &folder_size(@folders) :
462 &folder_size($folders[0]);
464 if ($config{'show_count'}) {
465 # Get number of mails in inbox
466 $incount{$u->[0]} = &mailbox_folder_size($folders[0]);
468 if ($config{'show_sent'}) {
469 # Count number of messages in sent mail
470 local ($sent) = grep { $_->{'sent'} } @folders;
471 $sentcount{$u->[0]} = &mailbox_folder_size($sent)
477 # Sort by chosen mode
478 if ($config{'sort_mode'} == 2) {
479 @users = sort { $size{$b->[0]} <=> $size{$a->[0]} } @users;
481 elsif ($config{'sort_mode'} == 1) {
482 @users = sort { lc($a->[0]) cmp lc($b->[0]) } @users;
485 local @allusers = @users;
487 # Limit to those with mail
488 @users = grep { $size{$_->[0]} } @users;
491 # Show table of users
493 print "<b>$text{'index_nousers'}</b><p>\n";
496 print "<b>$text{'index_nousersmail'}</b><p>\n";
498 elsif ($config{'show_size'} == 2) {
499 # Show full user details
500 local %uconfig = &foreign_config("useradmin");
502 push(@ccols, $text{'find_incount'}) if ($config{'show_count'});
503 push(@ccols, $text{'find_sentcount'}) if ($config{'show_sent'});
504 push(@ccols, $text{'find_fcount'}) if (%foldercount);
505 print &ui_columns_start( [ $text{'find_user'}, $text{'find_real'},
507 $text{'find_size'}, @ccols ], 100);
508 foreach $u (@users) {
509 local $g = getgrgid($u->[3]);
510 next if ($config{'ignore_users_enabled'} == 1 &&
511 &indexof($u->[0], @ignore_users_list) >= 0);
512 $u->[6] =~ s/,.*$// if ($uconfig{'extra_real'} ||
514 local $home = $u->[7];
515 if (length($home) > 30) {
516 $home = "...".substr($home, -30);
519 if ($config{'show_count'}) {
520 push(@ccols, int($incount{$u->[0]}))
522 if ($config{'show_sent'}) {
523 push(@ccols, int($sentcount{$u->[0]}))
526 push(@ccols, int($foldercount{$u->[0]}))
528 print &ui_columns_row(
529 [ "<a href='list_mail.cgi?user=$u->[0]'>$u->[0]</a>",
531 $size{$u->[0]} == 0 ? $text{'index_empty'} :
532 &nice_size($size{$u->[0]}),
534 [ undef, undef, undef, undef, "nowrap" ]);
536 print &ui_columns_end();
539 # Just showing username (and maybe size)
541 foreach $u (@users) {
542 next if ($config{'ignore_users_enabled'} == 1 &&
543 &indexof($u->[0], @ignore_users_list) >= 0);
544 my $g = "<a href='list_mail.cgi?user=".&urlize($u->[0])."'>";
545 $g .= &html_escape($u->[0]);
546 if ($config{'show_size'} == 1) {
547 local @folders = &list_user_folders(@$u);
548 local $total = &folder_size(@folders);
549 if ($size{$u->[0]} > 0) {
550 $g .= $config{'show_size_below'} ? '<br>' : ' ';
553 $g .= &text('find_in',
554 &nice_size($size{$u->[0]}),
555 $foldercount{$u->[0]});
558 $g .= &nice_size($size{$u->[0]});
566 my $w = int(100/$config{'column_count'});
567 my @tds = map { "width=".int($w)."%" } (1..$config{'column_count'});
568 print &ui_grid_table(\@grid, $config{'column_count'}, 100, \@tds, undef,
569 $text{'index_header'});
573 # switch_to_user(user)
574 # Switch to the Unix user that files are accessed as.
577 if (!defined($old_uid)) {
578 local @uinfo = &get_mail_user($_[0]);
581 $) = "$uinfo[3] $uinfo[3]";
588 if (defined($old_uid)) {
591 $old_uid = $old_gid = undef;
597 return "<a href='list_mail.cgi?user=$_[0]&folder=$_[1]->{'index'}'>$text{'mail_return2'}</a>";
601 # Returns the address to use when sending email from a script
604 local $host = &get_from_domain();
605 if ($config{'webmin_from'} =~ /\@/) {
606 return $config{'webmin_from'};
608 elsif (!$config{'webmin_from'}) {
609 return "webmin\@$host";
612 return "$config{'webmin_from'}\@$host";
617 # Returns the default domain for From: addresses
620 return $config{'from_dom'} || &get_display_hostname();
623 # get_user_from_address(&uinfo)
624 # Returns the default From: address for mail sent from some user's mailbox
625 sub get_user_from_address
627 local $uinfo = $_[0];
628 if ($config{'from_addr'}) {
629 return $config{'from_addr'};
631 elsif ($uinfo->[11]) {
634 elsif ($config{'from_virtualmin'} && &foreign_check("virtual-server")) {
635 # Does Virtualmin manage this user?
636 &foreign_require("virtual-server", "virtual-server-lib.pl");
638 foreach $d (&virtual_server::list_domains()) {
639 local @users = &virtual_server::list_domain_users($d, 0, 0, 1);
641 foreach $u (@users) {
642 if ($u->{'user'} eq $uinfo->[0] && $u->{'email'}) {
644 return $u->{'email'};
649 if ($uinfo->[0] =~ /\@/) {
653 return $uinfo->[0].'@'.&get_from_domain();
657 # check_modification(&folder)
658 # Display an error message if a folder has been modified since the time
660 sub check_modification
662 local $newmod = &modification_time($_[0]);
663 if ($in{'mod'} && $in{'mod'} != $newmod && $config{'check_mod'}) {
665 &error(&text('emodified', "list_mail.cgi?user=$in{'user'}&folder=$_[0]->{'index'}"));
669 # spam_report_cmd(user)
670 # Returns a command for reporting spam, or undef if none
674 local %sconfig = &foreign_config("spam");
676 if ($config{'spam_report'} eq 'sa_learn') {
677 $cmd = &has_command($sconfig{'sa_learn'}) ? "$sconfig{'sa_learn'} --spam --mbox" : undef;
679 elsif ($config{'spam_report'} eq 'spamassassin') {
680 $cmd = &has_command($sconfig{'spamassassin'}) ? "$sconfig{'spamassassin'} --r" : undef;
683 $cmd = &has_command($sconfig{'sa_learn'}) ?
684 "$sconfig{'sa_learn'} --spam --mbox" :
685 &has_command($sconfig{'spamassassin'}) ?
686 "$sconfig{'spamassassin'} --r" : undef;
688 return $user eq "root" ? $cmd :
689 $cmd ? &command_as_user($user, 0, $cmd) : undef;
693 # Returns a command for reporting ham, or undef if none
696 local %sconfig = &foreign_config("spam");
697 return &has_command($sconfig{'sa_learn'}) ? "$sconfig{'sa_learn'} --ham --mbox" : undef;
700 # allowed_directory()
701 # Returns base directory for mail files, or undef if not allowed
702 sub allowed_directory
704 if ($access{'dir'}) {
705 return $access{'dir'};
708 return $access{'mmode'} == 1 ? "/" : undef;
715 return $_[0] !~ /^\//;
718 # list_mail_users([max], [filterfunc])
719 # Returns getpw* style structures for all users who can recieve mail. Those with
720 # duplicate info are skipped.
723 local ($max, $filter) = @_;
726 if ($config{'mail_system'} < 3 or $config{'mail_system'} == 6 ) {
727 # Postfix, Sendmail, smail, exim and Qmail all use Unix users
730 local ($dir, $style, $mailbox, $maildir) = &get_mail_style();
732 while(my (@uinfo) = getpwent()) {
733 if ($found{$uinfo[0]}++) {
734 # done this username already
737 if (!$dir && $hfound{$uinfo[7]}++) {
738 # done this home directory (and thus inbox) already
741 if ($dir && $uinfo[0] =~ /\@/) {
742 # If this is a user@domain username, mark user-domain
743 # as done already, to avoid showing dupes
744 local $noat = $uinfo[0];
748 next if ($filter && !&$filter(@uinfo));
750 last if ($max && @rv > $max);
754 elsif ($config{'mail_system'} == 4) {
755 # Qmail+LDAP uses an LDAP db
756 local $ldap = &connect_qmail_ldap();
757 local $rv = $ldap->search(base => $config{'ldap_base'},
758 filter => "(objectClass=qmailUser)");
759 &error($rv->error) if ($rv->code);
761 foreach $u ($rv->all_entries) {
762 local @uinfo = &qmail_dn_to_user($u);
763 next if (!$uinfo[10]); # alias only
764 next if ($filter && !&$filter(@uinfo));
766 last if ($max && @rv > $max);
770 elsif ($config{'mail_system'} == 5) {
771 # Get vpopmail user list for all domains
772 opendir(DOMS, "$config{'vpopmail_dir'}/domains");
773 foreach my $d (readdir(DOMS)) {
774 next if ($d =~ /^\./);
775 local @uinfos = &parse_vpopmail_users("-D $d", $d);
777 @uinfos = grep { &$filter(@$_) } @uinfos;
780 last if ($max && @rv > $max);
787 # parse_vpopmail_users(command, domain)
788 sub parse_vpopmail_users
790 local %attr_map = ( "passwd" => 1,
794 open(UINFO, "$config{'vpopmail_dir'}/bin/vuserinfo $_[0] |");
797 if (/^([^:]+):\s+(.*)$/) {
798 local ($attr, $value) = ($1, $2);
799 if ($attr eq "name") {
800 # Start of a new user
801 $user = [ "$value\@$_[1]" ];
802 $user->[11] = $user->[0];
805 local $amapped = $attr_map{$attr};
806 $user->[$amapped] = $value if ($amapped);
813 # get_mail_user(name)
814 # Looks up a user by name
817 if ($config{'mail_system'} < 3 || $config{'mail_system'} == 6) {
818 # Just find Unix user for Sendmail, Postfix, Qmail and Exim
819 return getpwnam($_[0]);
821 elsif ($config{'mail_system'} == 4) {
823 local $ldap = &connect_qmail_ldap();
824 local $rv = $ldap->search(base => $config{'ldap_base'},
825 filter => "(&(objectClass=qmailUser)(uid=$_[0]))");
826 &error($rv->error) if ($rv->code);
827 local ($u) = $rv->all_entries;
830 local @uinfo = &qmail_dn_to_user($u);
835 # Fall back to Unix user
836 return getpwnam($_[0]);
839 elsif ($config{'mail_system'} == 5) {
841 local ($box, $dom) = split(/\@/, $_[0]);
842 local @users = &parse_vpopmail_users($_[0], $dom);
848 # Looks up a user by UID
851 if ($config{'mail_system'} < 3) {
852 # Just find Unix user
853 return getpwuid($_[0]);
855 elsif ($config{'mail_system'} == 4) {
857 local $ldap = &connect_qmail_ldap();
858 local $rv = $ldap->search(base => $config{'ldap_base'},
859 filter => "(&(objectClass=qmailUser)(uidNumber=$_[0]))");
860 &error($rv->error) if ($rv->code);
861 local ($u) = $rv->all_entries;
862 local @uinfo = &qmail_dn_to_user($u);
866 elsif ($config{'mail_system'} == 5) {
868 return ( ); # not possible, since UIDs aren't used!
872 # connect_qmail_ldap([return-error])
873 # Connect to the LDAP server used for Qmail. Returns an LDAP handle on success,
874 # or an error message on failure.
875 sub connect_qmail_ldap
877 eval "use Net::LDAP";
879 local $err = &text('ldap_emod', "<tt>Net::LDAP</tt>");
880 if ($_[0]) { return $err; }
881 else { &error($err); }
885 local $port = $config{'ldap_port'} || 389;
886 local $ldap = Net::LDAP->new($config{'ldap_host'}, port => $port);
888 local $err = &text('ldap_econn',
889 "<tt>$config{'ldap_host'}</tt>","<tt>$port</tt>");
890 if ($_[0]) { return $err; }
891 else { &error($err); }
894 # Start TLS if configured
895 if ($config{'ldap_tls'}) {
901 if ($config{'ldap_login'}) {
902 $mesg = $ldap->bind(dn => $config{'ldap_login'},
903 password => $config{'ldap_pass'});
906 $mesg = $ldap->bind(anonymous => 1);
908 if (!$mesg || $mesg->code) {
909 local $err = &text('ldap_elogin', "<tt>$config{'ldap_host'}</tt>",
910 $dn, $mesg ? $mesg->error : "Unknown error");
911 if ($_[0]) { return $err; }
912 else { &error($err); }
917 # qmail_dn_to_user(&dn)
920 local $mms = &add_ldapmessagestore(
921 scalar($_[0]->get_value("mailMessageStore")));
922 if (-d "$mms/Maildir") {
923 $mms .= "/" if ($mms !~ /\/$/);
926 return ( scalar($_[0]->get_value("uid")),
927 scalar($_[0]->get_value("userPassword")),
928 scalar($_[0]->get_value("uidNumber")),
929 scalar($_[0]->get_value("gidNumber")),
930 scalar($_[0]->get_value("mailQuotaSize")),
931 scalar($_[0]->get_value("cn")),
932 scalar($_[0]->get_value("cn")),
933 scalar($_[0]->get_value("homeDirectory")),
934 scalar($_[0]->get_value("loginShell")),
937 scalar($_[0]->get_value("mail")),
941 # add_ldapmessagestore(path)
942 sub add_ldapmessagestore
947 elsif ($_[0] =~ /^\//) {
951 &foreign_require("qmailadmin", "qmail-lib.pl");
952 local $pfx = &qmailadmin::get_control_file("ldapmessagestore");
953 return $pfx."/".$_[0];
957 # show_buttons(number, &folders, current-folder, &mail, user, search-mode)
960 local ($num, $folders, $folder, $mail, $user, $search) = @_;
961 local $uuser = &urlize($user);
962 local $spacer = " \n";
965 print "<input type=submit name=delete value=\"$text{'mail_delete'}\" ",
966 "onClick='return check_clicks(form)'>";
967 if ($config{'show_delall'} && !$search) {
968 print "<input type=submit name=deleteall value=\"$text{'mail_deleteall'}\">";
973 print "<input type=submit name=mark$_[0] value=\"$text{'mail_mark'}\">";
974 print "<select name=mode$_[0]>\n";
975 print "<option value=1 checked>$text{'mail_mark1'}\n";
976 print "<option value=0>$text{'mail_mark0'}\n";
977 print "<option value=2>$text{'mail_mark2'}\n";
981 if (&is_user($user)) {
983 if ($config{'open_mode'}) {
984 # Forward messages in a separate window
985 print "<input type=submit name=forward value=\"$text{'mail_forward'}\" onClick='args = \"user=$uuser&folder=$folder->{'index'}\"; for(i=0; i<form.d.length; i++) { if (form.d[i].checked) { args += \"&mailforward=\"+form.d[i].value; } } window.open(\"reply_mail.cgi?\"+args, \"compose\", \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); return false'>";
989 print "<input type=submit name=forward value=\"$text{'mail_forward'}\">";
995 print &movecopy_user_select($_[0], $folders, $folder, 1);
998 # Show spam report buttons
999 if (!$folder->{'spam'} &&
1000 &foreign_installed("spam") &&
1001 $config{'spam_buttons'} =~ /list/ &&
1002 &spam_report_cmd($user)) {
1003 if (&foreign_available("spam")) {
1004 print "<input type=submit value=\"$text{'mail_black'}\" name=black>";
1006 if ($config{'spam_del'}) {
1007 print "<input type=submit value=\"$text{'view_razordel'}\" name=razor>\n";
1010 print "<input type=submit value=\"$text{'view_razor'}\" name=razor>";
1014 elsif ($folder->{'spam'} &&
1015 &foreign_installed("spam") &&
1016 $config{'spam_buttons'} =~ /list/ &&
1017 &ham_report_cmd($user)) {
1018 if (&foreign_available("spam")) {
1019 print "<input type=submit value=\"$text{'mail_white'}\" name=white>";
1021 print "<input type=submit value=\"$text{'view_ham'}\" name=ham>";
1026 if ($config{'open_mode'}) {
1027 # Show mass open button
1028 print "<input type=submit name=new value=\"$text{'mail_open'}\" onClick='for(i=0; i<form.d.length; i++) { if (form.d[i].checked) { window.open(\"view_mail.cgi?user=$uuser&folder=$folder->{'index'}&idx=\"+form.d[i].value, \"view\"+i, \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); } }
1034 if (&is_user($user)) {
1035 if ($config{'open_mode'}) {
1036 # In a separate window
1037 print "<input type=submit name=new value=\"$text{'mail_compose'}\" onClick='window.open(\"reply_mail.cgi?user=$user&new=1\", \"compose\", \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); return false'>";
1040 print "<input type=submit name=new value=\"$text{'mail_compose'}\">";
1046 # get_signature(user)
1047 # Returns the users signature, if any
1050 local $sf = &get_signature_file($_[0]);
1051 $sf || return undef;
1053 open(SIG, $sf) || return undef;
1061 # get_signature_file(user)
1062 # Returns the full path to the file that should contain the user's signature,
1063 # or undef if none is defined
1064 sub get_signature_file
1066 return undef if ($config{'sig_file'} eq '*' || $config{'sig_file'} eq '');
1067 local $sf = $config{'sig_file'};
1069 local @uinfo = getpwnam($_[0]);
1070 $sf = "$uinfo[7]/$sf";
1075 # view_mail_link(user, &folder, index, from-to-text)
1078 local ($user, $folder, $idx, $txt) = @_;
1079 local $uuser = &urlize($user);
1080 local $url = "view_mail.cgi?user=$uuser&idx=$idx&folder=$folder->{'index'}";
1081 if ($config{'open_mode'}) {
1082 return "<a href='' onClick='window.open(\"$url\", \"viewmail\", \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); return false'>".
1083 &simplify_from($txt)."</a>";
1086 return "<a href='$url'>".&simplify_from($txt)."</a>";
1090 # mail_page_header(title, headstuff, bodystuff, rightstuff)
1091 sub mail_page_header
1093 if ($config{'open_mode'}) {
1094 &popup_header($_[0], $_[1], $_[2]);
1097 &ui_print_header(undef, $_[0], "", undef, 0, 0, 0, $_[3], $_[1], $_[2]);
1101 # mail_page_footer(link, text, ...)
1102 sub mail_page_footer
1104 if ($config{'open_mode'}) {
1108 &ui_print_footer(@_);
1112 # delete_user_index_files(name)
1113 # Delete all files associated with some user's mail, such as index and maildir
1115 sub delete_user_index_files
1118 foreach my $folder (&list_user_folders($user)) {
1119 if ($folder->{'type'} == 0) {
1121 local $ifile = &user_index_file($folder->{'file'});
1123 &unlink_logged($ifile);
1126 &unlink_logged(glob("$ifile.{dir,pag,db}"));
1128 &unlink_logged("$ifile.ids");
1130 elsif ($folder->{'type'} == 1) {
1131 # Remove Maildir files file
1132 &unlink_logged(&get_maildir_cachefile($folder->{'file'}));
1135 local $ifile = &folder_new_sort_index_file($folder);
1137 &unlink_logged($ifile);
1140 &unlink_logged(glob("$ifile.{dir,pag,db}"));
1144 local $read = "$module_config_directory/$user.read";
1146 &unlink_logged($read);
1149 &unlink_logged(glob("$read.{dir,pag,db}"));
1153 # get_mail_read(&folder, &mail)
1154 # Returns the read flag for some message, from the DBM
1157 local ($folder, $mail) = @_;
1158 if (!$done_dbmopen_read++) {
1159 &open_dbm_db(\%read, "$module_config_directory/$folder->{'user'}.read", 0600);
1161 return $read{$mail->{'header'}->{'message-id'}};
1164 # set_mail_read(&folder, &mail, read)
1165 # Sets the read flag for some message in the DBM
1168 local ($folder, $mail, $read) = @_;
1169 if (!$done_dbmopen_read++) {
1170 &open_dbm_db(\%read, "$module_config_directory/$folder->{'user'}.read", 0600);
1172 $read{$mail->{'header'}->{'message-id'}} = $read;
1175 # show_mail_table(&mails, &folder, formno)
1176 # Output a full table of messages
1179 local @mail = @{$_[0]};
1180 local (undef, $folder, $formno) = @_;
1182 my $showto = !$folder ? 0 :
1183 $folder->{'sent'} || $folder->{'drafts'} ? 1 : 0;
1184 my @tds = ( "nowrap", "nowrap", "nowrap", "nowrap" );
1186 # Show mailbox headers
1190 splice(@tds, "width=5", 0, 0);
1192 push(@hcols, $showto ? $text{'mail_to'} : $text{'mail_from'});
1193 push(@hcols, $config{'show_to'} ? $showto ? ( $text{'mail_from'} ) :
1194 ( $text{'mail_to'} ) : ());
1195 push(@hcols, $text{'mail_date'});
1196 push(@hcols, $text{'mail_size'});
1197 push(@hcols, $text{'mail_subject'});
1198 my @links = ( &select_all_link("d", $formno),
1199 &select_invert_link("d", $formno) );
1201 print &ui_links_row(\@links);
1203 print &ui_columns_start(\@hcols, 100, 0, \@tds);
1205 # Show rows for actual mail messages
1207 foreach my $mail (@mail) {
1208 local $idx = $mail->{'idx'};
1212 # From and To columns, with links
1213 local $from = $mail->{'header'}->{$showto ? 'to' : 'from'};
1214 $from = $text{'mail_unknown'} if ($from !~ /\S/);
1215 local $mfolder = $mail->{'folder'} || $folder;
1216 push(@cols, &view_mail_link($in{'user'}, $mfolder, $idx, $from));
1217 if ($config{'show_to'}) {
1218 push(@cols, &simplify_from(
1219 $mail->{'header'}->{$showto ? 'from' : 'to'}));
1222 # Date and size columns
1223 push(@cols, &simplify_date($mail->{'header'}->{'date'}, "ymd"));
1224 push(@cols, &nice_size($mail->{'size'}, 1024));
1226 # Subject with icons
1227 local @icons = &message_icons($mail, $mfolder->{'sent'}, $mfolder);
1228 push(@cols, &simplify_subject($mail->{'header'}->{'subject'}).
1229 join(" ", @icons));
1233 print &ui_columns_row(\@cols, \@tds);
1235 elsif (&editable_mail($mail)) {
1236 print &ui_checked_columns_row(\@cols, \@tds, "d", $idx);
1239 print &ui_columns_row([ "", @cols ], \@tds);
1242 if ($config{'show_body'}) {
1243 # Show part of the body too
1245 local $data = &mail_preview($mail);
1247 print "<tr $cb> <td colspan=",(scalar(@cols)+1),"><tt>",
1248 &html_escape($data),"</tt></td> </tr>\n";
1253 print &ui_columns_end();
1255 print &ui_links_row(\@links);