1
use chrono::{TimeZone, Utc};
2
use std::path::{Path, PathBuf};
3

            
4
use crate::proto::finger_protocol::MailStatus;
5

            
6
1
pub fn detect_new_mail_for_user(
7
1
    username: &str,
8
1
    homedir: &Path,
9
1
) -> anyhow::Result<Option<MailStatus>> {
10
1
    let mail_storage_paths: Vec<PathBuf> = vec![
11
1
        homedir.join("Mail"),
12
1
        homedir.join("Mailbox"),
13
1
        homedir.join("Maildir"),
14
1
        PathBuf::from("/var/mail").join(username),
15
1
        PathBuf::from("/var/spool/mail").join(username),
16
1
        PathBuf::from("/var/maildir").join(username),
17
1
        PathBuf::from("/var/spool/maildir").join(username),
18
    ];
19

            
20
7
    for path in mail_storage_paths {
21
7
        tracing::trace!("Checking for mail at path: {}", path.display());
22
7
        if path.is_dir() && path.join("new").is_dir() && path.join("cur").is_dir() {
23
            tracing::trace!("Detected maildir structure at path: {}", path.display());
24
            if let Ok(status) = detect_new_mail_by_maildir(&path) {
25
                return Ok(Some(status));
26
            }
27
7
        } else if path.is_file() {
28
            tracing::trace!("Detected mailbox file at path: {}", path.display());
29
            if let Ok(status) = detect_new_mail_by_mailbox(&path) {
30
                return Ok(Some(status));
31
            }
32
7
        }
33
    }
34

            
35
1
    Ok(None)
36
1
}
37

            
38
fn detect_new_mail_by_mailbox(mailbox_path: &Path) -> anyhow::Result<MailStatus> {
39
    let stat = nix::sys::stat::stat(mailbox_path)?;
40

            
41
    let mail_recv = stat.st_mtime;
42
    let mail_read = stat.st_atime;
43

            
44
    if mail_recv > mail_read {
45
        Ok(MailStatus::NewMailReceived {
46
            received_time: Utc
47
                .timestamp_opt(mail_recv, 0)
48
                .single()
49
                .unwrap_or_else(Default::default),
50
            unread_since: Utc
51
                .timestamp_opt(mail_read, 0)
52
                .single()
53
                .unwrap_or_else(Default::default),
54
        })
55
    } else {
56
        Ok(MailStatus::MailLastRead(
57
            Utc.timestamp_opt(mail_read, 0)
58
                .single()
59
                .unwrap_or_else(Default::default),
60
        ))
61
    }
62
}
63

            
64
fn detect_new_mail_by_maildir(maildir_path: &Path) -> anyhow::Result<MailStatus> {
65
    let new_mail_path = maildir_path.join("new");
66
    let cur_mail_path = maildir_path.join("cur");
67

            
68
    let mail_recv = nix::sys::stat::stat(&new_mail_path)?.st_mtime;
69
    let mail_read = nix::sys::stat::stat(&cur_mail_path)?.st_mtime;
70

            
71
    if new_mail_path.read_dir()?.next().is_none() && cur_mail_path.read_dir()?.next().is_none() {
72
        Ok(MailStatus::NoMail)
73
    } else if mail_recv > mail_read {
74
        Ok(MailStatus::NewMailReceived {
75
            received_time: Utc
76
                .timestamp_opt(mail_recv, 0)
77
                .single()
78
                .unwrap_or_else(Default::default),
79
            unread_since: Utc
80
                .timestamp_opt(mail_read, 0)
81
                .single()
82
                .unwrap_or_else(Default::default),
83
        })
84
    } else {
85
        Ok(MailStatus::MailLastRead(
86
            Utc.timestamp_opt(mail_read, 0)
87
                .single()
88
                .unwrap_or_else(Default::default),
89
        ))
90
    }
91
}