IMAPServer_shared/mailbox/
mod.rs

1use std::path::Path;
2
3use argonautica::{Hasher, Verifier};
4use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
5use futures::compat::Future01CompatExt;
6use futures::StreamExt;
7use log::debug;
8use log::warn;
9use rand::prelude::*;
10use tokio::fs::{create_dir_all, metadata, read_dir};
11
12use crate::config::Config;
13use crate::database::establish_connection;
14use crate::models::{NewUser, User};
15use crate::schema::users;
16use crate::schema::users::dsl::*;
17
18#[derive(Clone)]
19pub struct Mailbox {
20    pub user: String,
21    pub mailbox_root: String,
22    password_hash: String,
23}
24
25fn string_to_static_str(s: String) -> &'static str {
26    Box::leak(s.into_boxed_str())
27}
28
29impl Mailbox {
30    pub async fn new(user: String, password: String) -> Option<Self> {
31        let config = Config::load().await.expect("unable to load config");
32
33        let mut hasher = Hasher::default();
34        let password_hash_new = hasher
35            .with_password(password)
36            .with_secret_key(config.shared_secret)
37            .hash_non_blocking()
38            .compat()
39            .await
40            .expect("unable to hash password");
41
42        let mut rng = StdRng::from_entropy();
43
44        // This does need to stay mutable even when the compiler says otherwise. If it is not mut it fails to generate random numbers
45        #[allow(unused_mut)]
46            let mut random_number: String;
47
48        if rng.gen() {
49            let random_number_int: i32 = rng.gen_range(0, 2 ^ 32 - 1);
50
51            random_number = format!("{:?}", random_number_int);
52        } else {
53            warn!("Unable to generate random number");
54            return None;
55        }
56
57        let connection = establish_connection();
58        let user_local = user.clone();
59        let results: Result<User, diesel::result::Error> = users
60            .filter(email.eq(&user))
61            .limit(1)
62            .get_result::<User>(&connection);
63
64        match results {
65            Ok(m) => {
66                let mailbox_root = format!("{}/{}", config.mailbox_root, m.email);
67                return Some(Mailbox {
68                    mailbox_root,
69                    user,
70                    password_hash: m.password_hash,
71                });
72            }
73            Err(_) => {
74                let new_user = NewUser {
75                    email: &user,
76                    password_hash: &password_hash_new,
77                    uid_validity_identifier: &random_number,
78                };
79
80                diesel::insert_into(users::table)
81                    .values(&new_user)
82                    .execute(&connection)
83                    .expect("Failed to add new User");
84
85                let mailbox_root = format!("{}/{}", config.mailbox_root, user);
86
87                return Some(Mailbox {
88                    mailbox_root,
89                    user: user_local,
90                    password_hash: password_hash_new,
91                });
92            }
93        }
94    }
95
96    pub async fn load(user: String) -> Option<Self> {
97        let connection = establish_connection();
98        let config = Config::load().await.expect("unable to load config");
99        let user_local = user.clone();
100
101        let results: Result<User, diesel::result::Error> = users
102            .filter(email.eq(user))
103            .limit(1)
104            .get_result::<User>(&connection);
105
106        match results {
107            Ok(results) => {
108                let mailbox_root = format!("{}/{}", config.mailbox_root, results.email);
109                Some(Mailbox {
110                    mailbox_root,
111                    user: user_local,
112                    password_hash: results.password_hash,
113                })
114            }
115            _ => None,
116        }
117    }
118
119    pub async fn load_all() -> Option<Vec<Self>> {
120        let connection = establish_connection();
121        let config = Config::load().await.expect("unable to load config");
122
123        let results: Vec<User> = users
124            .load::<User>(&connection)
125            .expect("Error getting Mailboxes");
126
127        let mut returns: Vec<Self> = Vec::new();
128
129        for entry in &results {
130            let mailbox_root = format!("{}/{}", config.mailbox_root, entry.email);
131            returns.push(Mailbox {
132                mailbox_root,
133                user: entry.email.clone(),
134                password_hash: entry.password_hash.clone(),
135            })
136        }
137
138        Some(returns)
139    }
140
141    pub async fn check_password_plain(&self, password: String) -> Result<(), ()> {
142        let config = Config::load().await.expect("unable to load config");
143        let local_hash = self.password_hash.clone();
144
145        let mut verifier = Verifier::default();
146        let verified = verifier
147            .with_hash(local_hash)
148            .with_password(password)
149            .with_secret_key(config.shared_secret)
150            .verify_non_blocking()
151            .compat()
152            .await;
153        match verified {
154            Ok(v) => {
155                if v {
156                    return Ok(());
157                } else {
158                    return Err(());
159                }
160            }
161            Err(_) => {
162                return Err(());
163            }
164        }
165    }
166
167    pub async fn get_lsub(&self, args: Vec<&str>) -> Option<Vec<String>> {
168        if args.len() == 4 {
169            debug!("get_lsub 4");
170        } else if args.len() == 5 {
171            debug!("get_lsub 5");
172        }
173
174        let searched = args.get(args.len() - 1).expect("unable to get searched path").replace("\"", "");
175
176        let mut dirs = read_dir(self.mailbox_root.to_owned())
177            .await
178            .expect("unable to read dir");
179
180        let mut dirs_lsub: Vec<&str> = Vec::new();
181
182        while let Some(dir) = dirs.next().await {
183            let dir = dir.expect("unable to get dir");
184            // FIXME this will break with subdirs
185            if dir.file_name().into_string().unwrap() != searched && searched != "*" { continue; }
186            let subscribed_str: &str = "(subscribed)";
187            if args.contains(&subscribed_str) {
188                let path_string = format!(
189                    "* LSUB (\\Subscribed) \".\" {}\r\n",
190                    dir.file_name()
191                        .into_string()
192                        .expect("unable to get filename")
193                );
194                let path_string = string_to_static_str(path_string);
195                dirs_lsub.push(path_string);
196                continue;
197            } else {
198                let path_string = format!(
199                    "* LSUB (\\HasNoChildren) \".\" {}\r\n",
200                    dir.file_name()
201                        .into_string()
202                        .expect("unable to get filename")
203                );
204                let path_string = string_to_static_str(path_string);
205                dirs_lsub.push(path_string);
206                continue;
207            }
208        }
209
210        if dirs_lsub.len() > 0 {
211            let dirs_lsub: Vec<String> = dirs_lsub.iter().map(|s| (*s).to_string()).collect();
212            Some(dirs_lsub)
213        } else {
214            None
215        }
216    }
217
218    pub async fn get_list(&self, args: Vec<&str>) -> Option<Vec<String>> {
219        if args.len() == 4 {
220            debug!("get_list 4");
221        } else if args.len() == 5 {
222            debug!("get_list 5");
223        }
224
225        let searched = args.get(args.len() - 1).expect("unable to get searched path").replace("\"", "");
226
227        let mut dirs = read_dir(self.mailbox_root.to_owned())
228            .await
229            .expect("unable to read dir");
230
231        let mut dirs_list: Vec<&str> = Vec::new();
232
233        while let Some(dir) = dirs.next().await {
234            let dir = dir.expect("unable to get dir");
235            // FIXME this will break with subdirs
236            if dir.file_name().into_string().unwrap() != searched && searched != "*" { continue; }
237            let subscribed_str: &str = "(subscribed)";
238            if args.contains(&subscribed_str) {
239                // TODO actually check if subscribed or not.
240                let path_string = format!(
241                    "* LIST (\\Subscribed) \".\" {}\r\n",
242                    dir.file_name()
243                        .into_string()
244                        .expect("unable to get filename")
245                );
246                let path_string = string_to_static_str(path_string);
247                dirs_list.push(path_string);
248                continue;
249            } else {
250                // TODO actually check if subscribed or not.
251                let path_string = format!(
252                    "* LIST (\\HasNoChildren) \".\" {}\r\n",
253                    dir.file_name()
254                        .into_string()
255                        .expect("unable to get filename")
256                );
257                let path_string = string_to_static_str(path_string);
258                dirs_list.push(path_string);
259                continue;
260            }
261        }
262
263        if dirs_list.len() > 0 {
264            let dirs_list: Vec<String> = dirs_list.iter().map(|s| (*s).to_string()).collect();
265            Some(dirs_list)
266        } else {
267            None
268        }
269    }
270
271    pub async fn check_mailbox_root(&self) -> Result<(), std::io::Error> {
272        self.check_mailbox_folder("").await?;
273        Ok(())
274    }
275
276    pub async fn check_mailbox_folder<P>(&self, path_part: P) -> Result<(), std::io::Error>
277        where
278            P: AsRef<Path>,
279    {
280        let path = Path::new(&self.mailbox_root);
281        let path = path.join(&path_part.as_ref().to_owned());
282        let metadata = metadata(&path).await;
283        match metadata {
284            Err(_) => {
285                warn!("Mailbox folder {:?} was missing. Recreating", &path);
286
287                create_dir_all(path).await?
288            }
289            Ok(_) => {}
290        }
291        Ok(())
292    }
293
294    pub async fn create_folder<P>(&self, path: P) -> Result<(), std::io::Error>
295        where
296            P: AsRef<Path>,
297    {
298        let path = &path.as_ref().to_owned();
299        self.check_mailbox_folder(path).await?;
300
301        Ok(())
302    }
303}