1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 extern crate crossbeam_utils;
6 #[macro_use]
7 extern crate cstr;
8 #[macro_use]
9 extern crate failure;
10 extern crate libc;
11 extern crate lmdb;
12 #[macro_use]
13 extern crate log;
14 extern crate moz_task;
15 extern crate nserror;
16 extern crate nsstring;
17 extern crate once_cell;
18 extern crate rkv;
19 extern crate serde_json;
20 extern crate tempfile;
21 #[macro_use]
22 extern crate xpcom;
23 
24 mod error;
25 mod ffi;
26 mod iter;
27 mod persist;
28 mod statics;
29 
30 use crate::{
31     error::{XULStoreError, XULStoreResult},
32     iter::XULStoreIterator,
33     persist::{flush_writes, persist},
34     statics::DATA_CACHE,
35 };
36 use nsstring::nsAString;
37 use std::collections::btree_map::Entry;
38 use std::fmt::Display;
39 
40 const SEPARATOR: char = '\u{0009}';
41 
make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String42 pub(crate) fn make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String {
43     format!("{}{}{}{}{}", doc, SEPARATOR, id, SEPARATOR, attr)
44 }
45 
set_value( doc: &nsAString, id: &nsAString, attr: &nsAString, value: &nsAString, ) -> XULStoreResult<()>46 pub(crate) fn set_value(
47     doc: &nsAString,
48     id: &nsAString,
49     attr: &nsAString,
50     value: &nsAString,
51 ) -> XULStoreResult<()> {
52     debug!("XULStore set value: {} {} {} {}", doc, id, attr, value);
53 
54     // bug 319846 -- don't save really long attributes or values.
55     if id.len() > 512 || attr.len() > 512 {
56         return Err(XULStoreError::IdAttrNameTooLong);
57     }
58 
59     let value = if value.len() > 4096 {
60         warn!("XULStore: truncating long attribute value");
61         String::from_utf16(&value[0..4096])?
62     } else {
63         String::from_utf16(value)?
64     };
65 
66     let mut cache_guard = DATA_CACHE.lock()?;
67     let data = match cache_guard.as_mut() {
68         Some(data) => data,
69         None => return Ok(()),
70     };
71     data.entry(doc.to_string())
72         .or_default()
73         .entry(id.to_string())
74         .or_default()
75         .insert(attr.to_string(), value.clone());
76 
77     persist(make_key(doc, id, attr), Some(value))?;
78 
79     Ok(())
80 }
81 
has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool>82 pub(crate) fn has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool> {
83     debug!("XULStore has value: {} {} {}", doc, id, attr);
84 
85     let cache_guard = DATA_CACHE.lock()?;
86     let data = match cache_guard.as_ref() {
87         Some(data) => data,
88         None => return Ok(false),
89     };
90 
91     match data.get(&doc.to_string()) {
92         Some(ids) => match ids.get(&id.to_string()) {
93             Some(attrs) => Ok(attrs.contains_key(&attr.to_string())),
94             None => Ok(false),
95         },
96         None => Ok(false),
97     }
98 }
99 
get_value( doc: &nsAString, id: &nsAString, attr: &nsAString, ) -> XULStoreResult<String>100 pub(crate) fn get_value(
101     doc: &nsAString,
102     id: &nsAString,
103     attr: &nsAString,
104 ) -> XULStoreResult<String> {
105     debug!("XULStore get value {} {} {}", doc, id, attr);
106 
107     let cache_guard = DATA_CACHE.lock()?;
108     let data = match cache_guard.as_ref() {
109         Some(data) => data,
110         None => return Ok(String::new()),
111     };
112 
113     match data.get(&doc.to_string()) {
114         Some(ids) => match ids.get(&id.to_string()) {
115             Some(attrs) => match attrs.get(&attr.to_string()) {
116                 Some(value) => Ok(value.clone()),
117                 None => Ok(String::new()),
118             },
119             None => Ok(String::new()),
120         },
121         None => Ok(String::new()),
122     }
123 }
124 
remove_value( doc: &nsAString, id: &nsAString, attr: &nsAString, ) -> XULStoreResult<()>125 pub(crate) fn remove_value(
126     doc: &nsAString,
127     id: &nsAString,
128     attr: &nsAString,
129 ) -> XULStoreResult<()> {
130     debug!("XULStore remove value {} {} {}", doc, id, attr);
131 
132     let mut cache_guard = DATA_CACHE.lock()?;
133     let data = match cache_guard.as_mut() {
134         Some(data) => data,
135         None => return Ok(()),
136     };
137 
138     let mut ids_empty = false;
139     if let Some(ids) = data.get_mut(&doc.to_string()) {
140         let mut attrs_empty = false;
141         if let Some(attrs) = ids.get_mut(&id.to_string()) {
142             attrs.remove(&attr.to_string());
143             if attrs.is_empty() {
144                 attrs_empty = true;
145             }
146         }
147         if attrs_empty {
148             ids.remove(&id.to_string());
149             if ids.is_empty() {
150                 ids_empty = true;
151             }
152         }
153     };
154     if ids_empty {
155         data.remove(&doc.to_string());
156     }
157 
158     persist(make_key(doc, id, attr), None)?;
159 
160     Ok(())
161 }
162 
remove_document(doc: &nsAString) -> XULStoreResult<()>163 pub(crate) fn remove_document(doc: &nsAString) -> XULStoreResult<()> {
164     debug!("XULStore remove document {}", doc);
165 
166     let mut cache_guard = DATA_CACHE.lock()?;
167     let data = match cache_guard.as_mut() {
168         Some(data) => data,
169         None => return Ok(()),
170     };
171 
172     if let Entry::Occupied(entry) = data.entry(doc.to_string()) {
173         for (id, attrs) in entry.get() {
174             for attr in attrs.keys() {
175                 persist(make_key(entry.key(), id, attr), None)?;
176             }
177         }
178         entry.remove_entry();
179     }
180 
181     Ok(())
182 }
183 
get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator>184 pub(crate) fn get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator> {
185     debug!("XULStore get IDs for {}", doc);
186 
187     let cache_guard = DATA_CACHE.lock()?;
188     let data = match cache_guard.as_ref() {
189         Some(data) => data,
190         None => return Ok(XULStoreIterator::new(vec![].into_iter())),
191     };
192 
193     match data.get(&doc.to_string()) {
194         Some(ids) => {
195             let ids: Vec<String> = ids.keys().cloned().collect();
196             Ok(XULStoreIterator::new(ids.into_iter()))
197         }
198         None => Ok(XULStoreIterator::new(vec![].into_iter())),
199     }
200 }
201 
get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator>202 pub(crate) fn get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator> {
203     debug!("XULStore get attrs for doc, ID: {} {}", doc, id);
204 
205     let cache_guard = DATA_CACHE.lock()?;
206     let data = match cache_guard.as_ref() {
207         Some(data) => data,
208         None => return Ok(XULStoreIterator::new(vec![].into_iter())),
209     };
210 
211     match data.get(&doc.to_string()) {
212         Some(ids) => match ids.get(&id.to_string()) {
213             Some(attrs) => {
214                 let attrs: Vec<String> = attrs.keys().cloned().collect();
215                 Ok(XULStoreIterator::new(attrs.into_iter()))
216             }
217             None => Ok(XULStoreIterator::new(vec![].into_iter())),
218         },
219         None => Ok(XULStoreIterator::new(vec![].into_iter())),
220     }
221 }
222 
shutdown() -> XULStoreResult<()>223 pub(crate) fn shutdown() -> XULStoreResult<()> {
224     flush_writes()
225 }
226