initial import
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
pub mod settings;
|
||||
|
||||
pub use settings::Settings;
|
||||
@@ -0,0 +1,51 @@
|
||||
use dotenvy::dotenv;
|
||||
use dotenvy::from_filename;
|
||||
use std::env;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pop3Config {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImapConfig {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Settings {
|
||||
pub pop3: Pop3Config,
|
||||
pub imap: ImapConfig,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn from_env_file(filename: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
if filename == ".env" {
|
||||
dotenv().ok();
|
||||
} else {
|
||||
from_filename(filename).ok();
|
||||
}
|
||||
|
||||
let pop3 = Pop3Config {
|
||||
host: env::var("POP3_HOST")?,
|
||||
port: env::var("POP3_PORT")?.parse()?,
|
||||
username: env::var("POP3_USERNAME")?,
|
||||
password: env::var("POP3_PASSWORD")?,
|
||||
};
|
||||
|
||||
let imap = ImapConfig {
|
||||
host: env::var("IMAP_HOST")?,
|
||||
port: env::var("IMAP_PORT")?.parse()?,
|
||||
username: env::var("IMAP_USERNAME")?,
|
||||
password: env::var("IMAP_PASSWORD")?,
|
||||
};
|
||||
|
||||
Ok(Settings { pop3, imap })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use crate::config::settings::ImapConfig;
|
||||
use native_tls::TlsConnector;
|
||||
|
||||
pub struct ImapClient {
|
||||
session: Option<imap::Session<native_tls::TlsStream<std::net::TcpStream>>>,
|
||||
}
|
||||
|
||||
impl ImapClient {
|
||||
pub fn new(_config: &ImapConfig) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
Ok(ImapClient {
|
||||
session: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn login(&mut self, config: &ImapConfig) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let tls = TlsConnector::builder().build()?;
|
||||
let client = imap::connect((config.host.as_str(), config.port), &config.host, &tls)?;
|
||||
let session = client.login(&config.username, &config.password).map_err(|e| e.0)?;
|
||||
self.session = Some(session);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_inbox(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(ref mut session) = self.session {
|
||||
session.select("INBOX")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn append_message(&mut self, message: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(ref mut session) = self.session {
|
||||
session.append("INBOX", message.as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn logout(self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(mut session) = self.session {
|
||||
session.logout()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
mod config;
|
||||
mod pop3_client;
|
||||
mod imap_client;
|
||||
|
||||
use clap::Parser;
|
||||
use config::Settings;
|
||||
use pop3_client::Pop3Client;
|
||||
use imap_client::ImapClient;
|
||||
|
||||
/// POP3 to IMAP Email Importer
|
||||
///
|
||||
/// This utility migrates emails from a POP3 server to an IMAP server.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Path to the .env file containing server configurations
|
||||
#[clap(short, long, default_value = ".env")]
|
||||
env_file: String,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = Args::parse();
|
||||
|
||||
println!("Starting POP3 to IMAP email importer...");
|
||||
|
||||
// Load configuration from specified .env file
|
||||
let settings = Settings::from_env_file(&args.env_file)?;
|
||||
|
||||
// Connect to POP3 server
|
||||
println!("Connecting to POP3 server at {}:{}...", settings.pop3.host, settings.pop3.port);
|
||||
let mut pop3_client = Pop3Client::new(&settings.pop3)?;
|
||||
pop3_client.login(&settings.pop3)?;
|
||||
println!("Successfully connected to POP3 server");
|
||||
|
||||
// Connect to IMAP server
|
||||
println!("Connecting to IMAP server at {}:{}...", settings.imap.host, settings.imap.port);
|
||||
let mut imap_client = ImapClient::new(&settings.imap)?;
|
||||
imap_client.login(&settings.imap)?;
|
||||
imap_client.select_inbox()?;
|
||||
println!("Successfully connected to IMAP server");
|
||||
|
||||
// List messages in POP3 inbox
|
||||
let messages = pop3_client.list_messages()?;
|
||||
println!("Found {} messages in POP3 inbox", messages.len());
|
||||
|
||||
// Process each message
|
||||
for (msg_id, _) in messages {
|
||||
println!("Processing message ID: {}", msg_id);
|
||||
|
||||
// Retrieve message content
|
||||
let message_content = pop3_client.retrieve_message(msg_id)?;
|
||||
|
||||
// Append message to IMAP inbox
|
||||
imap_client.append_message(&message_content)?;
|
||||
println!("Message {} imported successfully", msg_id);
|
||||
|
||||
// Optionally delete message from POP3 server after successful import
|
||||
// pop3_client.delete_message(msg_id)?;
|
||||
}
|
||||
|
||||
// Clean up connections
|
||||
pop3_client.quit()?;
|
||||
imap_client.logout()?;
|
||||
|
||||
println!("Email import completed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use crate::config::settings::Pop3Config;
|
||||
use rust_pop3_client::Pop3Connection;
|
||||
|
||||
pub struct Pop3Client {
|
||||
connection: Pop3Connection,
|
||||
}
|
||||
|
||||
impl Pop3Client {
|
||||
pub fn new(config: &Pop3Config) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let connection = Pop3Connection::new(&config.host, config.port)?;
|
||||
Ok(Pop3Client { connection })
|
||||
}
|
||||
|
||||
pub fn login(&mut self, config: &Pop3Config) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.connection.login(&config.username, &config.password)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list_messages(&mut self) -> Result<Vec<(u32, u32)>, Box<dyn std::error::Error>> {
|
||||
let infos = self.connection.list()?;
|
||||
let list = infos.into_iter().map(|info| (info.message_id, info.message_size)).collect();
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
pub fn retrieve_message(&mut self, msg_id: u32) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let mut buffer = Vec::new();
|
||||
self.connection.retrieve(msg_id, &mut buffer)?;
|
||||
let message = String::from_utf8(buffer)?;
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn delete_message(&mut self, msg_id: u32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.connection.delete(msg_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn quit(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// The rust-pop3-client doesn't seem to have an explicit quit method
|
||||
// The connection should be closed when the object is dropped
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user