about summary refs log tree commit diff stats
path: root/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
blob: 2dc759573a61e67b66af4527c0c8210919d79547 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use std::{collections::HashMap, fs::File, io};

use anyhow::{Context, Result};
use lz4_flex::decompress_size_prepended;
use serde::Deserialize;
use serde_json::Value;
use url::Url;

use crate::task::Project;

pub mod handle;
pub use handle::handle;

impl Project {
    pub(super) fn get_sessionstore(&self) -> Result<SessionStore> {
        let path = dirs::home_dir()
            .expect("Will exist")
            .join(".mozilla/firefox")
            .join(self.to_project_display())
            .join("sessionstore-backups/recovery.jsonlz4");
        let file = decompress_mozlz4(
            File::open(&path)
                .with_context(|| format!("Failed to open path '{}'", path.display()))?,
        )
        .with_context(|| format!("Failed to decompress file as mozlzh '{}'", path.display()))?;

        let contents: SessionStore = serde_json::from_str(&file).with_context(|| {
            format!(
                "Failed to deserialize file ('{}') as session store.",
                path.display()
            )
        })?;
        Ok(contents)
    }
}

fn decompress_mozlz4<P: io::Read>(mut file: P) -> Result<String> {
    const MOZLZ4_MAGIC_NUMBER: &[u8] = b"mozLz40\0";

    let mut buf = [0u8; 8];
    file.read_exact(&mut buf)
        .context("Failed to read the mozlz40 header.")?;

    assert_eq!(buf, MOZLZ4_MAGIC_NUMBER);

    let mut buf = vec![];
    file.read_to_end(&mut buf).context("Failed to read file")?;

    let uncompressed = decompress_size_prepended(&buf).context("Failed to decompress file")?;

    Ok(String::from_utf8(uncompressed).expect("This should be valid json and thus utf8"))
}

#[derive(Deserialize, Debug)]
pub struct SessionStore {
    pub windows: Vec<Window>,
}

#[derive(Deserialize, Debug)]
pub struct Window {
    pub tabs: Vec<Tab>,
    pub selected: usize,
}

#[derive(Deserialize, Debug)]
pub struct Tab {
    pub entries: Vec<TabEntry>,
    #[serde(rename = "lastAccessed")]
    pub last_accessed: u64,
    pub hidden: bool,
    #[serde(rename = "searchMode")]
    pub search_mode: Option<Value>,
    #[serde(rename = "userContextId")]
    pub user_context_id: u32,
    pub attributes: TabAttributes,
    #[serde(rename = "extData")]
    pub ext_data: Option<HashMap<String, Value>>,
    pub index: usize,
    #[serde(rename = "requestedIndex")]
    pub requested_index: Option<u32>,
    pub image: Option<Url>,
}

#[derive(Deserialize, Debug)]
pub struct TabEntry {
    pub url: Url,
    pub title: String,
    #[serde(rename = "cacheKey")]
    pub cache_key: u32,
    #[serde(rename = "ID")]
    pub id: u32,
    #[serde(rename = "docshellUUID")]
    pub docshell_uuid: Value,
    #[serde(rename = "resultPrincipalURI")]
    pub result_principal_uri: Option<Url>,
    #[serde(rename = "hasUserInteraction")]
    pub has_user_interaction: bool,
    #[serde(rename = "triggeringPrincipal_base64")]
    pub triggering_principal_base64: Value,
    #[serde(rename = "docIdentifier")]
    pub doc_identifier: u32,
    pub persist: bool,
}

#[derive(Deserialize, Debug, Clone, Copy)]
pub struct TabAttributes {}