1
use itertools::Itertools;
2
use serde::{Deserialize, Serialize};
3
use zlink::ReplyError;
4

            
5
use crate::{
6
    proto::finger_protocol::FingerResponseUserEntry,
7
    server::{
8
        fingerd::{self, FingerRequestInfo, FingerRequestNetworking, finger_utmp_users},
9
        ignore_list::IgnoreList,
10
    },
11
};
12

            
13
#[zlink::proxy("no.ntnu.pvv.roowho2.finger")]
14
pub trait VarlinkFingerClientProxy {
15
    async fn finger(
16
        &mut self,
17
        user_queries: Option<Vec<String>>,
18
        match_fullnames: bool,
19
        request_info: FingerRequestInfo,
20
        request_networking: FingerRequestNetworking,
21
        disable_user_account_db: bool,
22
        raw_remote_output: bool,
23
    ) -> zlink::Result<Result<VarlinkFingerResponse, VarlinkFingerClientError>>;
24
}
25

            
26
#[derive(Debug, Deserialize)]
27
#[serde(tag = "method", content = "parameters")]
28
pub enum VarlinkFingerClientRequest {
29
    #[serde(rename = "no.ntnu.pvv.roowho2.finger.Finger")]
30
    Finger {
31
        user_queries: Option<Vec<String>>,
32
        match_fullnames: bool,
33
        request_info: FingerRequestInfo,
34
        request_networking: FingerRequestNetworking,
35
        disable_user_account_db: bool,
36
        raw_remote_output: bool,
37
    },
38
}
39

            
40
#[derive(Debug, Serialize)]
41
#[serde(untagged)]
42
pub enum VarlinkFingerClientResponse {
43
    Finger(VarlinkFingerResponse),
44
}
45

            
46
pub type VarlinkFingerResponse = Vec<FingerResponseUserEntry>;
47

            
48
#[derive(Debug, Clone, PartialEq, ReplyError)]
49
#[zlink(interface = "no.ntnu.pvv.roowho2.finger")]
50
pub enum VarlinkFingerClientError {
51
    InvalidRequest,
52
    TimedOut,
53
    Disabled,
54
}
55

            
56
pub async fn handle_finger_request(
57
    finger_ignore_list: &Option<IgnoreList>,
58
    user_queries: Option<Vec<String>>,
59
    match_fullnames: bool,
60
    request_info: FingerRequestInfo,
61
    _request_networking: FingerRequestNetworking,
62
    _disable_user_account_db: bool,
63
    _raw_remote_output: bool,
64
) -> VarlinkFingerResponse {
65
    tracing::debug!(
66
      user_queries = ?user_queries,
67
      match_fullnames = match_fullnames,
68
      request_info = ?request_info,
69
      "Handling Finger request",
70
    );
71
    match user_queries {
72
        Some(usernames) => usernames
73
            .into_iter()
74
            .flat_map::<Vec<_>, _>(|username| {
75
                fingerd::search_for_user(
76
                    &username,
77
                    match_fullnames,
78
                    &request_info,
79
                    finger_ignore_list.as_ref(),
80
                )
81
                .into_iter()
82
                .map(|res| (username.clone(), res))
83
                .collect()
84
            })
85
            .dedup_by(|a, b| match (&a.1, &b.1) {
86
                (Ok(user_a), Ok(user_b)) => user_a.username == user_b.username,
87
                _ => false,
88
            })
89
            .filter_map(|(username, user)| match user {
90
                Ok(user_info) => Some(user_info),
91
                Err(err) => {
92
                    tracing::error!(
93
                        "Error retrieving local user information for '{}': {}",
94
                        username,
95
                        err
96
                    );
97
                    None
98
                }
99
            })
100
            .map(Box::new)
101
            .map(FingerResponseUserEntry::Structured)
102
            .collect(),
103

            
104
        None => finger_utmp_users(&request_info, finger_ignore_list.as_ref())
105
            .into_iter()
106
            .filter_map(|res| match res {
107
                Ok(user_info) => Some(user_info),
108
                Err(err) => {
109
                    tracing::error!("Error retrieving local user information: {}", err);
110
                    None
111
                }
112
            })
113
            .map(Box::new)
114
            .map(FingerResponseUserEntry::Structured)
115
            .collect(),
116
    }
117
}