From 0cb68dfd036749c36521c0fa7f5474b595f3e76a Mon Sep 17 00:00:00 2001 From: Jamie Cameron Date: Sun, 12 Jun 2011 22:46:55 -0700 Subject: [PATCH] Track last IMAP / POP3 logins for users, and display in UI --- CHANGELOG | 1 + IDEAS | 3 -- collectinfo.pl | 2 + edit_user.cgi | 14 ++++++ feature-mail.pl | 89 +++++++++++++++++++++++++++++++++++++ help/lastlogin.html | 6 +++ lang/en | 5 +++ list-users.pl | 7 +++ virtual-server-lib-funcs.pl | 23 ++++++++++ virtual-server-lib.pl | 2 + 10 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 help/lastlogin.html diff --git a/CHANGELOG b/CHANGELOG index b4478e6de..2708c7e3c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1297,3 +1297,4 @@ The default DNS TTL for one or more domains can now be changed via the --ttl fla ---- Changes since 3.86 ---- Updated the Roundcube script installer to version 0.5.3, PHPprojekt to 6.0.6, phpMyAdmin to 3.4.2, phpMyFAQ to 2.6.17, Movable Type to 5.11, Instiki to 0.19.2, and SugarCRM to 6.2.0. Added the new API command set-global-feature to turn features and plugins on and off from the command line. +The last IMAP, POP3 and SMTP logins for mailbox users are now tracked by Virtualmin, and can be viewed on the Edit Mailbox page and in the output from the list-users API command. diff --git a/IDEAS b/IDEAS index 75844140a..1d0127d11 100644 --- a/IDEAS +++ b/IDEAS @@ -62,6 +62,3 @@ - https://www.virtualmin.com/node/18405 - CHANGELOG - backup/restore - - - Track last-login time for mailbox users (IMAP or FTP) - - http://www.virtualmin.com/node/18417 diff --git a/collectinfo.pl b/collectinfo.pl index c76933f32..0002a1bba 100755 --- a/collectinfo.pl +++ b/collectinfo.pl @@ -35,3 +35,5 @@ package virtual_server; # Update IP list cache &build_local_ip_list(); +# Update DB of per-user last login times +&update_last_login_times(); diff --git a/edit_user.cgi b/edit_user.cgi index 89023cdc0..ec73d7555 100755 --- a/edit_user.cgi +++ b/edit_user.cgi @@ -295,6 +295,20 @@ if ($hasspam) { 2, \@tds); } +# Show most recent logins +if ($hasemail && !$in{'new'}) { + # XXX help page + $ll = &get_last_login_time($user->{'user'}); + @grid = ( ); + foreach $k (keys %$ll) { + push(@grid, $text{'user_lastlogin_'.$k}, + &make_date($ll->{$k})); + } + print &ui_table_row(&hlink($text{'user_lastlogin'}, "lastlogin"), + @grid ? &ui_grid_table(\@grid, 2, 50) + : $text{'user_lastlogin_never'}); + } + if ($hasemail) { print &ui_hidden_table_end("table2a"); } diff --git a/feature-mail.pl b/feature-mail.pl index 23a9b8807..268d3f3da 100755 --- a/feature-mail.pl +++ b/feature-mail.pl @@ -4907,6 +4907,95 @@ sub same_dn return lc($dn0) eq lc($dn1); } +# update_last_login_times() +# Scans the mail log and updates the last time a user logs in via IMAP, POP3 +# or SMTP. +sub update_last_login_times +{ +# Find the mail log +my $maillog = $config{'bw_maillog'}; +$maillog = &get_mail_log() if ($maillog eq "auto"); +return 0 if (!$maillog); + +# Seek to the last position +&lock_file($mail_login_file); +my @st = stat($maillog); +my $lastpost; +my %logins; +&read_file($mail_login_file, \%logins); +$lastpos = $logins{'lastpos'} || $st[7]; +if ($lastpos > $st[7]) { + # Off end .. file has probably been rotated + $lastpos = 0; + } +open(MAILLOG, $maillog); +seek(MAILLOG, $lastpos, 0); +my $now = time(); +my @tm = localtime($now); +while() { + s/\r|\n//g; + + # Remove Solaris extra part like [ID 197553 mail.info] + s/\[ID\s+\d+\s+\S+\]\s+//; + + if (/^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\S+)\s+dovecot:\s+(pop3|imap)-login:\s+Login:\s+user=<([^>]+)>/) { + # POP3 or IMAP login with dovecot + my $ltime; + eval { $ltime = timelocal($5, $4, $3, $2, + $apache_mmap{lc($1)}, $tm[5]); }; + if (!$ltime || $ltime > $now+(24*60*60)) { + # Must have been last year! + eval { $ltime = timelocal($5, $4, $3, $2, + $apache_mmap{lc($1)}, $tm[5]-1); }; + } + &add_last_login_time(\%logins, $ltime, $7, $8); + } + elsif (/^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\S+)\s+.*sasl_username=([^ ,]+)/) { + # Postfix SMTP + my $ltime; + eval { $ltime = timelocal($5, $4, $3, $2, + $apache_mmap{lc($1)}, $tm[5]); }; + if (!$ltime || $ltime > $now+(24*60*60)) { + # Must have been last year! + eval { $ltime = timelocal($5, $4, $3, $2, + $apache_mmap{lc($1)}, $tm[5]-1); }; + } + &add_last_login_time(\%logins, $ltime, 'smtp', $7); + } + } +close(MAILLOG); +@st = stat($maillog); +$logins{'lastpos'} = $st[7]; +&write_file($mail_login_file, \%logins); +&unlock_file($mail_login_file); +return 1; +} + +# add_last_login_time(&logins, time, type, username) +# Add to the hash of login types for some user +sub add_last_login_time +{ +my ($logins, $ltime, $ltype, $user) = @_; +my %curr = map { split(/=/, $_) } split(/\s+/, $logins->{$user}); +$curr{$ltype} = $ltime; +$logins->{$user} = join(" ", map { $_."=".$curr{$_} } keys %curr); +} + +# get_last_login_time(username) +# Returns a hash ref of last login types to times for a user +sub get_last_login_time +{ +my ($user) = @_; +my %logins; +&read_file_cached($mail_login_file, \%logins); +if ($logins{$user}) { + return { map { split(/=/, $_) } split(/\s+/, $logins{$user}) }; + } +else { + return undef; + } +} + $done_feature_script{'mail'} = 1; 1; diff --git a/help/lastlogin.html b/help/lastlogin.html new file mode 100644 index 000000000..bd06cc6b6 --- /dev/null +++ b/help/lastlogin.html @@ -0,0 +1,6 @@ +
Last email login
+ +This field shows the last login times via IMAP, POP3 or SMTP detected by +Virtualmin for this user.

+ +