Browse Source

is_logged_in and log_in endpoints finished

Yurii Sokolovskyi 1 month ago
parent
commit
21b5ea9bae
5 changed files with 83 additions and 66 deletions
  1. 15 13
      src/config.rs
  2. 13 2
      src/imap.rs
  3. 45 44
      src/main.rs
  4. 8 5
      src/server.rs
  5. 2 2
      src/smtp_client.rs

+ 15 - 13
src/config.rs

@@ -10,7 +10,7 @@
 // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 // OF THIS SOFTWARE.
 
-use std::sync::{OnceLock, RwLock};
+use std::sync::{Mutex, OnceLock};
 use std::fs::File;
 use std::io::{self, BufRead, BufReader, BufWriter, Write};
 use std::path::{Path, PathBuf};
@@ -34,8 +34,8 @@ pub struct Config {
     pub subsections: Vec<Subsection>,
     pub imap_domain: String,
     pub imap_port: u32,
-    pub username: String,
-    pub password: String,
+    pub username: Mutex<String>,
+    pub password: Mutex<String>,
     pub api_addr: String,
     pub api_port: u32,
     pub smtp_domain: String,
@@ -55,8 +55,8 @@ impl Config {
             "description" => self.description = value.to_string(),
             "imap_domain" => self.imap_domain = value.to_string(),
             "imap_port" => self.imap_port = value.parse().unwrap(),
-            "username" => self.username = value.to_string(),
-            "password" => self.password = value.to_string(),
+            "username" => self.username = Mutex::new(value.to_string()),
+            "password" => self.password = Mutex::new(value.to_string()),
             "maildir" => self.maildir = PathBuf::from(value),
             "api_addr" => self.api_addr = value.to_string(),
             "api_port" => self.api_port = value.parse().unwrap(),
@@ -90,10 +90,10 @@ impl Subsection {
     }
 }
 
-pub static INSTANCE: OnceLock<RwLock<Config>> = OnceLock::new();
+pub static INSTANCE: OnceLock<Config> = OnceLock::new();
 
 impl Config {
-    pub fn global() -> &'static RwLock<Config> {
+    pub fn global() -> &'static Config {
         INSTANCE.get().expect("Config is not initialized")
     }
 
@@ -150,16 +150,18 @@ impl Config {
         Ok(conf)
     }
     
-    pub fn set_username(&mut self, username: String){
-        self.username = username;
+    pub fn set_username(&self, username: String){
+        let mut username_lock = Config::global().username.lock().unwrap();
+        *username_lock = username;
     }
 
-    pub fn set_password(&mut self, password: String){
-        self.password = password;
+    pub fn set_password(&self, password: String){
+        let mut password_lock = Config::global().password.lock().unwrap();
+        *password_lock = password;
     }
 
     pub fn update_credentials(
-        &mut self,
+        &self,
         new_username: &str,
         new_password: &str,
     ) -> io::Result<()> {
@@ -170,7 +172,7 @@ impl Config {
         let reader = BufReader::new(file);
 
         // Create a temporary file for writing the updated configuration
-        let temp_path = args.config.clone().as_ref().with_extension("tmp");
+        let temp_path = <PathBuf as AsRef<Path>>::as_ref(&args.config.clone()).with_extension("tmp");
         let temp_file = File::create(&temp_path)?;
         let mut writer = BufWriter::new(temp_file);
 

+ 13 - 2
src/imap.rs

@@ -57,9 +57,20 @@ pub async fn connect_to_imap() -> anyhow::Result<Client<TlsStream<TcpStream>>>{
     Ok(client)
 }
 
-async fn get_session() -> anyhow::Result<Session<TlsStream<TcpStream>>>{
+pub async fn get_session() -> anyhow::Result<Session<TlsStream<TcpStream>>>{
     let mut client = connect_to_imap().await?;
-    let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
+
+    let config = Config::global();
+    let username_clone = {
+        let username_lock = config.username.lock().unwrap();
+        username_lock.clone()
+    };
+    let password_clone = {
+        let password_lock = config.password.lock().unwrap();
+        password_lock.clone()
+    };
+    
+    let mut session_result = client.login(username_clone, password_clone).await;
     let session = match session_result {
         Ok(session) => {session}
         Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}

+ 45 - 44
src/main.rs

@@ -10,7 +10,7 @@ use std::collections::HashMap;
 use std::fs::{create_dir_all, File};
 use std::future::Future;
 use std::io::{Write};
-use std::sync::{Mutex, RwLock};
+use std::sync::Mutex;
 use std::time::{Instant};
 use anyhow::anyhow;
 use serde::{Deserialize, Serialize};
@@ -40,29 +40,46 @@ mod server;
 mod js;
 mod sonic;
 
-struct IdleTaskStorage{
-    handlers: HashMap<String, JoinHandle<Result<(), anyhow::Error>>>
+struct ImapTaskStorage {
+    downloading: Option<JoinHandle<()>>,
+    idle_handlers: HashMap<String, JoinHandle<Result<(), anyhow::Error>>>
 }
 
-impl IdleTaskStorage {
+impl ImapTaskStorage {
     fn new() -> Self {
-        IdleTaskStorage {
-            handlers: HashMap::new(),
+        ImapTaskStorage {
+            downloading: None,
+            idle_handlers: HashMap::new(),
         }
     }
     
-    pub fn add(&mut self, mailbox: String, handler: JoinHandle<Result<(), anyhow::Error>>) {
-        self.handlers.insert(mailbox, handler);
+    pub fn add_idle_task(&mut self, mailbox: String, handler: JoinHandle<Result<(), anyhow::Error>>) {
+        // TODO check if the task already exists
+        // TODO add a task when a new folder spotted on the IMAP
+        self.idle_handlers.insert(mailbox, handler);
     }
     
-    pub fn delete(&mut self, mailbox: String){
-        if self.handlers.contains_key(&mailbox.clone()) {
-            self.handlers.remove(&mailbox.clone());
+    pub fn remove_idle_task(&mut self, mailbox: String){
+        if self.idle_handlers.contains_key(&mailbox.clone()) {
+            self.idle_handlers.remove(&mailbox.clone());
         }
     }
+    
+    pub fn start_downloading(&mut self){
+        if let Some(ref mut handle) = self.downloading {
+            handle.abort();
+        }
+        
+        self.downloading = Some(tokio::spawn(async move {
+            match imap::download_email_from_imap().await {
+                Ok(_) => {}
+                Err(_) => println!("Cannot download new emails")
+            }
+        }));
+    }
 }
 
-pub static IDLE_TASK_STORAGE: Lazy<Mutex<IdleTaskStorage>> = Lazy::new(|| Mutex::new(IdleTaskStorage::new()));
+pub static IMAP_TASK_STORAGE: Lazy<Mutex<ImapTaskStorage>> = Lazy::new(|| Mutex::new(ImapTaskStorage::new()));
 
 
 /// appends an extension to a path
@@ -311,8 +328,8 @@ pub async fn create_folder_lar(name: String) -> anyhow::Result<()>{
 
             // start monitoring
             let handler = check_for_updates(name.clone());
-            let mut idle_task_storage = IDLE_TASK_STORAGE.lock().unwrap();
-            idle_task_storage.add(name.clone(), handler);
+            let mut idle_task_storage = IMAP_TASK_STORAGE.lock().unwrap();
+            idle_task_storage.add_idle_task(name.clone(), handler);
             
             Ok(())
         }
@@ -333,8 +350,8 @@ pub async fn delete_folder_lar(name: String) -> anyhow::Result<()>{
             }
 
             // stop monitoring the folder
-            let mut idle_task_storage = IDLE_TASK_STORAGE.lock().unwrap();
-            idle_task_storage.delete(name.clone());
+            let mut idle_task_storage = IMAP_TASK_STORAGE.lock().unwrap();
+            idle_task_storage.remove_idle_task(name.clone());
             
             Ok(())
         }        
@@ -356,12 +373,12 @@ pub async fn rename_folder_lar(old_name: String, new_name: String) -> anyhow::Re
             }
 
             // stop monitoring old folder
-            let mut idle_task_storage = IDLE_TASK_STORAGE.lock().unwrap();
-            idle_task_storage.delete(old_name.clone());
+            let mut idle_task_storage = IMAP_TASK_STORAGE.lock().unwrap();
+            idle_task_storage.remove_idle_task(old_name.clone());
             
             // start monitoring new folder
             let handler = check_for_updates(new_name.clone());
-            idle_task_storage.add(new_name.clone(), handler);
+            idle_task_storage.add_idle_task(new_name.clone(), handler);
             
             Ok(())
         }
@@ -379,35 +396,16 @@ fn parse_args(){
     
     config.maildir = args.maildir.clone();
     config.out_dir = args.out_dir.clone();
-    INSTANCE.set(RwLock::new(config)).unwrap();
+    INSTANCE.set(config).unwrap();
 }
 
-pub async fn run_download(){
-    // downloading new emails
-    match imap::download_email_from_imap().await {
-        Ok(_) => {}
-        Err(_) => println!("Cannot download new emails")
-    }
-
-    // Uncomment to convert to HTML without downloading
-    // let mut uid = 1; // fake uid for tests
-    // for entry in std::fs::read_dir(Config::global().maildir.clone().join("INBOX").join("new")).expect("could not read maildir").filter_map(|m| m.ok()){
-    //     let path = entry.path();
-    // 
-    //     match add_email(path.clone(), uid.clone()){
-    //         Ok(_) => {}
-    //         Err(_) => {println!("Error adding email from {:?}", path.clone())}
-    //     };
-    //     
-    //     uid += 1;
-    // }
-
-
+pub fn start_checkin_for_all_updates(){
     // start updates monitoring
     for mailbox in Indexes::get_local_mailboxes() {
         let handler = check_for_updates(mailbox.clone());
-        let mut idle_task_storage = IDLE_TASK_STORAGE.lock().unwrap();
-        idle_task_storage.add(mailbox.clone(), handler);
+        let mut idle_task_storage = IMAP_TASK_STORAGE.lock().unwrap();
+        idle_task_storage.add_idle_task(mailbox.clone(), handler);
+        drop(idle_task_storage);
     }
 } 
 
@@ -420,7 +418,10 @@ async fn run(){
         run_api().await
     });
 
-    run_download().await;
+    let mut idle_task_storage = IMAP_TASK_STORAGE.lock().unwrap();
+    idle_task_storage.start_downloading();
+    drop(idle_task_storage);
+    start_checkin_for_all_updates();
     
     // dont stop the program while API is running
     api_task.await.expect("Error while running API");

+ 8 - 5
src/server.rs

@@ -12,8 +12,8 @@ use chrono::{DateTime, Utc};
 use warp::Filter;
 use warp::fs::file;
 use warp::http::{response, StatusCode};
-use crate::{create_folder_lar, delete_folder_lar, rename_folder_lar};
-use crate::imap::connect_to_imap;
+use crate::{create_folder_lar, delete_folder_lar, IMAP_TASK_STORAGE, rename_folder_lar, start_checkin_for_all_updates};
+use crate::imap::{connect_to_imap, get_session};
 use crate::models::MailAddress;
 use crate::sonic::SearchSonic;
 use crate::util::read_and_decompress_file;
@@ -281,7 +281,7 @@ pub async fn get_sorted_threads_by_date(folder: String, limit: usize, offset: us
 }
 
 pub async fn is_logged_in() -> String {
-    let response = if (Config::global().username == "" || Config::global().password == "") {
+    let response = if (*Config::global().username.lock().unwrap() == "" || *Config::global().password.lock().unwrap() == "") {
         serde_json::to_string(&IsLoggedInResponse { is_logged_in: false })
     }else{
         serde_json::to_string(&IsLoggedInResponse { is_logged_in: true })
@@ -294,11 +294,14 @@ pub async fn log_in(email: String, password: String) -> bool {
     Config::global().set_username(email.clone());
     Config::global().set_password(password.clone());
     
-    match connect_to_imap().await { 
+    match get_session().await{ 
         Ok(_) => {
             match Config::global().update_credentials(&email, &password){
                 Ok(_) => { 
-                    // TODO start downloading emails
+                    let mut idle_task_storage = IMAP_TASK_STORAGE.lock().unwrap();
+                    idle_task_storage.start_downloading();
+                    drop(idle_task_storage);
+                    start_checkin_for_all_updates();
                     true
                 },
                 Err(_) => false

+ 2 - 2
src/smtp_client.rs

@@ -157,8 +157,8 @@ async fn login() -> Result<TlsStream<StdTcpStream>, Box<dyn std::error::Error>>
 
     // Authenticate using LOGIN method
     send_command(&mut stream, "AUTH LOGIN\r\n").await?;
-    send_command(&mut stream, &(encode(Config::global().username.clone()) + "\r\n")).await?;
-    send_command(&mut stream, &(encode(Config::global().password.clone()) + "\r\n")).await?; // TODO use Engine::encode()
+    send_command(&mut stream, &(encode(Config::global().username.lock().unwrap().clone()) + "\r\n")).await?;
+    send_command(&mut stream, &(encode(Config::global().password.lock().unwrap().clone()) + "\r\n")).await?; // TODO use Engine::encode()
 
     Ok(stream)
 }