feat: netscape cookie import

This commit is contained in:
zhom
2026-02-21 15:50:23 +04:00
parent 97da1ca288
commit c61b3d3188
19 changed files with 657 additions and 85 deletions
+110
View File
@@ -62,6 +62,14 @@ pub struct CookieCopyResult {
pub errors: Vec<String>,
}
/// Result of a cookie import operation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CookieImportResult {
pub cookies_imported: usize,
pub cookies_replaced: usize,
pub errors: Vec<String>,
}
pub struct CookieManager;
impl CookieManager {
@@ -493,4 +501,106 @@ impl CookieManager {
Ok(results)
}
/// Parse Netscape format cookies from text content
fn parse_netscape_cookies(content: &str) -> (Vec<UnifiedCookie>, Vec<String>) {
let mut cookies = Vec::new();
let mut errors = Vec::new();
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
for (i, line) in content.lines().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let fields: Vec<&str> = line.split('\t').collect();
if fields.len() < 7 {
errors.push(format!(
"Line {}: expected 7 tab-separated fields, got {}",
i + 1,
fields.len()
));
continue;
}
let domain = fields[0].to_string();
let path = fields[2].to_string();
let is_secure = fields[3].eq_ignore_ascii_case("TRUE");
let expires = fields[4].parse::<i64>().unwrap_or(0);
let name = fields[5].to_string();
let value = fields[6].to_string();
cookies.push(UnifiedCookie {
name,
value,
domain,
path,
expires,
is_secure,
is_http_only: false,
same_site: 0,
creation_time: now,
last_accessed: now,
});
}
(cookies, errors)
}
/// Public API: Import cookies from Netscape format content
pub async fn import_netscape_cookies(
app_handle: &AppHandle,
profile_id: &str,
content: &str,
) -> Result<CookieImportResult, String> {
let profile_manager = ProfileManager::instance();
let profiles_dir = profile_manager.get_profiles_dir();
let profiles = profile_manager
.list_profiles()
.map_err(|e| format!("Failed to list profiles: {e}"))?;
let profile = profiles
.iter()
.find(|p| p.id.to_string() == profile_id)
.ok_or_else(|| format!("Profile not found: {profile_id}"))?;
let is_running = profile_manager
.check_browser_status(app_handle.clone(), profile)
.await
.unwrap_or(false);
if is_running {
return Err(format!(
"Cannot import cookies while browser is running for profile: {}",
profile.name
));
}
let (cookies, parse_errors) = Self::parse_netscape_cookies(content);
if cookies.is_empty() {
return Err("No valid cookies found in the file".to_string());
}
let db_path = Self::get_cookie_db_path(profile, &profiles_dir)?;
let write_result = match profile.browser.as_str() {
"camoufox" => Self::write_firefox_cookies(&db_path, &cookies),
"wayfern" => Self::write_chrome_cookies(&db_path, &cookies),
_ => return Err(format!("Unsupported browser type: {}", profile.browser)),
};
match write_result {
Ok((imported, replaced)) => Ok(CookieImportResult {
cookies_imported: imported,
cookies_replaced: replaced,
errors: parse_errors,
}),
Err(e) => Err(format!("Failed to write cookies: {e}")),
}
}
}
+22
View File
@@ -283,9 +283,30 @@ async fn copy_profile_cookies(
app_handle: tauri::AppHandle,
request: cookie_manager::CookieCopyRequest,
) -> Result<Vec<cookie_manager::CookieCopyResult>, String> {
if !crate::cloud_auth::CLOUD_AUTH
.has_active_paid_subscription()
.await
{
return Err("Cookie copying requires an active Pro subscription".to_string());
}
cookie_manager::CookieManager::copy_cookies(&app_handle, request).await
}
#[tauri::command]
async fn import_cookies_from_file(
app_handle: tauri::AppHandle,
profile_id: String,
content: String,
) -> Result<cookie_manager::CookieImportResult, String> {
if !crate::cloud_auth::CLOUD_AUTH
.has_active_paid_subscription()
.await
{
return Err("Cookie import requires an active Pro subscription".to_string());
}
cookie_manager::CookieManager::import_netscape_cookies(&app_handle, &profile_id, &content).await
}
#[tauri::command]
fn check_wayfern_terms_accepted() -> bool {
wayfern_terms::WayfernTermsManager::instance().is_terms_accepted()
@@ -1313,6 +1334,7 @@ pub fn run() {
enable_sync_for_all_entities,
read_profile_cookies,
copy_profile_cookies,
import_cookies_from_file,
check_wayfern_terms_accepted,
check_wayfern_downloaded,
accept_wayfern_terms,
+16
View File
@@ -2008,6 +2008,14 @@ pub async fn update_camoufox_config(
profile_id: String,
config: CamoufoxConfig,
) -> Result<(), String> {
if config.fingerprint.is_some()
&& !crate::cloud_auth::CLOUD_AUTH
.has_active_paid_subscription()
.await
{
return Err("Fingerprint editing requires an active Pro subscription".to_string());
}
if !crate::cloud_auth::CLOUD_AUTH
.is_fingerprint_os_allowed(config.os.as_deref())
.await
@@ -2028,6 +2036,14 @@ pub async fn update_wayfern_config(
profile_id: String,
config: WayfernConfig,
) -> Result<(), String> {
if config.fingerprint.is_some()
&& !crate::cloud_auth::CLOUD_AUTH
.has_active_paid_subscription()
.await
{
return Err("Fingerprint editing requires an active Pro subscription".to_string());
}
if !crate::cloud_auth::CLOUD_AUTH
.is_fingerprint_os_allowed(config.os.as_deref())
.await