adguard_flm/manager/
mod.rs

1//! Filter list manager library main facade interface.
2pub mod filter_list_manager_impl;
3pub(crate) mod filter_lists_builder;
4pub mod managers;
5pub mod models;
6mod update_filters_action;
7
8use crate::manager::models::active_rules_info::ActiveRulesInfo;
9use crate::manager::models::configuration::request_proxy_mode::RequestProxyMode;
10use crate::manager::models::configuration::Locale;
11use crate::manager::models::disabled_rules_raw::DisabledRulesRaw;
12use crate::manager::models::filter_group::FilterGroup;
13use crate::manager::models::filter_list_rules::FilterListRules;
14use crate::manager::models::filter_list_rules_raw::FilterListRulesRaw;
15use crate::manager::models::filter_tag::FilterTag;
16use crate::manager::models::rules_count_by_filter::RulesCountByFilter;
17use crate::manager::models::{PullMetadataResult, UpdateResult};
18use crate::{ActiveRulesInfoRaw, FLMResult, StoredFilterMetadata};
19use models::configuration::Configuration;
20use models::filter_list_metadata::FilterListMetadata;
21use models::filter_list_metadata_with_body::FilterListMetadataWithBody;
22use models::full_filter_list::FullFilterList;
23use models::FilterId;
24use std::path::Path;
25
26/// FilterListManager is the interface of a filter list manager.
27pub trait FilterListManager {
28    /// In the constructor, the object is configured and initialized depending on the passed configuration.
29    /// *NOTE:* You must create its own manager for different filter lists types.
30    ///
31    /// * `configuration` - Configuration object for this manager
32    fn new(configuration: Configuration) -> FLMResult<Box<Self>>;
33
34    /// Installs a custom filter list
35    ///
36    /// * `download_url` - Remote server or a `file://` URL. Can be an
37    ///   *empty string*. In that case filter will be local. (e.g. won't be
38    ///   updated).
39    /// * `is_trusted` - Does this filter considered trusted.
40    /// * `title` - Override title. If passed, title from metadata will be
41    ///   ignored.
42    /// * `description` - Override description. If passed, description from
43    ///   metadata will be ignored.
44    ///
45    /// Returns the inserted filter list.
46    fn install_custom_filter_list(
47        &self,
48        download_url: String,
49        is_trusted: bool,
50        title: Option<String>,
51        description: Option<String>,
52    ) -> FLMResult<FullFilterList>;
53
54    /// Fetches filter list by url and returns its raw metadata.
55    ///
56    /// * `url` - Remote server or a `file://` URL.
57    ///
58    /// Returns filter list metadata.
59    fn fetch_filter_list_metadata(&self, url: String) -> FLMResult<FilterListMetadata>;
60
61    /// Fetches filter list by url and returns its raw metadata and body.
62    ///
63    /// * `url` - Remote server or a `file://` URL.
64    ///
65    /// Returns filter list metadata and body.
66    fn fetch_filter_list_metadata_with_body(
67        &self,
68        url: String,
69    ) -> FLMResult<FilterListMetadataWithBody>;
70
71    /// Toggles filter lists, using their `filter_id`.
72    ///
73    /// * `ids` - List of [`FilterId`].
74    /// * `is_enabled` - Does this filter list enabled.
75    ///
76    /// Returns SQL's affected rows count.
77    fn enable_filter_lists(&self, ids: Vec<FilterId>, is_enabled: bool) -> FLMResult<usize>;
78
79    /// Toggles `is_installed` property of filter list.
80    ///
81    /// * `ids` - List of [`FilterId`].
82    /// * `is_installed` - new flag value.
83    ///
84    /// Returns SQL's affected rows count.
85    fn install_filter_lists(&self, ids: Vec<FilterId>, is_installed: bool) -> FLMResult<usize>;
86
87    /// Deletes custom filter lists, using their filter_id.
88    ///
89    /// * `ids` - List of [`FilterId`].
90    ///
91    /// Returns SQL's affected rows count.
92    fn delete_custom_filter_lists(&self, ids: Vec<FilterId>) -> FLMResult<usize>;
93
94    /// Gets all tags from DB.
95    fn get_all_tags(&self) -> FLMResult<Vec<FilterTag>>;
96
97    /// Gets all groups from DB.
98    fn get_all_groups(&self) -> FLMResult<Vec<FilterGroup>>;
99
100    /// Returns all filter data including its rules by [`FilterId`]. Fields [`title`, `description`] will be
101    /// localised with selected [`Locale`].
102    fn get_full_filter_list_by_id(&self, filter_id: FilterId) -> FLMResult<Option<FullFilterList>>;
103
104    /// Returns all stored filters metadata. This is the lightweight counterpart of `.get_full_filter_lists()`
105    /// Fields [`title`, `description`] will be localised with selected [`Locale`].
106    fn get_stored_filters_metadata(&self) -> FLMResult<Vec<StoredFilterMetadata>>;
107
108    /// Returns stored filter metadata by  [`FilterId`]. This is the lightweight counterpart of `.get_full_filter_list_by_id(filter_id)`
109    /// Fields [`title`, `description`] will be localised with selected [`Locale`].
110    fn get_stored_filter_metadata_by_id(
111        &self,
112        filter_id: FilterId,
113    ) -> FLMResult<Option<StoredFilterMetadata>>;
114
115    /// Save custom filter list rules. Note that `filter.time_updated` will be updated too.
116    ///
117    /// # Failure
118    ///
119    /// Returns [`Err`] if the specified [`FilterId`] is not found in the
120    /// database, or it is not from custom filter.
121    fn save_custom_filter_rules(&self, rules: FilterListRules) -> FLMResult<()>;
122
123    /// Saves a set of disabled filters for a specific [`FilterId`]
124    ///
125    /// # Failure
126    ///
127    /// Fails if rules_list entity does not exist for passed `filter_id`.
128    /// This because if you want to keep disabled filters, you should already
129    /// have a `rules_list` entity.
130    fn save_disabled_rules(
131        &self,
132        filter_id: FilterId,
133        disabled_rules: Vec<String>,
134    ) -> FLMResult<()>;
135
136    /// Filters updates is conducted in the multiple steps:
137    /// - Search for filters ready for update.
138    /// - Fetch them.
139    /// - Save `last_download_time`, and update metadata.
140    /// - Collect updated filters.
141    ///
142    /// # Internal checks
143    ///
144    /// This method conducts the following checks while updating filters:
145    /// - Filter expiration (or `ignore_filters_expiration` flag)
146    /// - Content equality via checksum comparison (not Checksum metadata field) (`ignore_filters_expiration` flag disables this check)
147    /// - Filter status (is_enabled) or `ignore_filters_status` flag
148    /// - Local urls without `download_url` won't be updated
149    /// - For index filters, versions are checked through `Version` metadata field checking up against the latest version from the index file
150    /// - Also `loose_timeout` can limit count of updated filters if non-zero
151    ///
152    /// # Parameters
153    ///
154    /// * `ignore_filters_expiration` - Does not rely on filter's expire
155    ///   information, also ignores content equality via checksum comparison.
156    /// * `loose_timeout` - Not a strict timeout, checked after processing each
157    ///   filter. If the total time exceeds this value, filters processing will
158    ///   stop, and the number of unprocessed filters will be set in result
159    ///   value. Pass 0 to disable timeout.
160    /// * `ignore_filters_status` - Include disabled filters
161    ///
162    /// Returns [`UpdateResult`] with update information.
163    ///
164    /// # Failure
165    ///
166    /// Returns [`None`] if DB is empty.
167    ///
168    /// Returns [`Err`] if you can not get records from db, or common error
169    /// encountered.
170    ///
171    /// Note: should be used no more than once an hour.
172    fn update_filters(
173        &self,
174        ignore_filters_expiration: bool,
175        loose_timeout: i32,
176        ignore_filters_status: bool,
177    ) -> FLMResult<Option<UpdateResult>>;
178
179    /// This method works almost the same as [`Self::update_filters`].
180    /// But also, you MUST pass the list of [`FilterId`].
181    /// Empty list will cause an empty result [`Ok(UpdateResult.updated_list == 0)`] if database exists.
182    /// This returns [`Ok(None)`] if db is empty
183    fn update_filters_by_ids(
184        &self,
185        ids: Vec<FilterId>,
186        ignore_filters_expiration: bool,
187        loose_timeout: i32,
188        ignore_filters_status: bool,
189    ) -> FLMResult<Option<UpdateResult>>;
190
191    /// Tries to update passed list of [`FilterId`].
192    /// The logic is the same as in the filter update method [`FilterListManager::update_filters`]
193    /// with exceptions:
194    /// * This returns [`None`] if DB result set is empty.
195    /// * This always ignores filters `expires` and `is_enabled` parameters.
196    ///
197    /// * `ids` - List of [`FilterId`].
198    /// * `loose_timeout` - See [`FilterListManager::update_filters`]
199    ///   `loose_timeout` parameter for explanation.
200    ///
201    /// Note: should be used once an hour or less.
202    fn force_update_filters_by_ids(
203        &self,
204        ids: Vec<FilterId>,
205        loose_timeout: i32,
206    ) -> FLMResult<Option<UpdateResult>>;
207
208    /// Tries to change [`Locale`] in configuration.
209    /// Will search `suggested_locale` in database. If it cannot find exact
210    /// locale, like `en_GB`, it will try to find language code - `en`. Locales
211    /// with "-", like `en-GB`, will be normalised to internal format - `en_GB`.
212    ///
213    /// Returns a [`bool`] indicating the success of changing the locale.
214    /// If the locale is not found, `false` will be returned.
215    fn change_locale(&mut self, suggested_locale: Locale) -> FLMResult<bool>;
216
217    /// The method is used for creating a database and downloading filters.
218    /// If the database exists, it attempts to bring it to a state compatible
219    /// with the current indexes. Also, migrations update will be processed in this method, too.
220    /// Additionally, the method checks the downloaded indexes for consistency.
221    ///
222    /// This method follows the algorithm below:
223    ///
224    /// 1. Downloads the filters index (registry).
225    /// 2. Checks the index consistency.
226    ///     a. Take filters from the index.
227    ///     b. For each filter check that `filter.group_id` > 0 and the group
228    ///        is present in the index.
229    ///     c. For each filter check that tag is present in the index.
230    ///     d. `filter.name` (title) is not empty.
231    ///     e. `filter.download_url` must be unique.
232    ///     f. Everything else is not a critical issue.
233    /// 3. Opens the database with `O_CREAT`.
234    ///    a. Check that the database is empty (by the presence of the `filter`
235    ///       table).
236    ///    b. If empty, pour the schema and save the data from the indexes and
237    ///       finish the exercise.
238    ///    c. Otherwise, go to the next step.
239    /// 4. Select all filters from the database, then iterate on every filter.
240    ///    When comparing filters from the index and the database, we rely on
241    ///    the filter.id.
242    ///    a. If it is a custom filter - (`group_id` < 1) -> continue.
243    ///    b. Do not work with `filter_id` < 1 (reserved filters) -> continue.
244    ///    c. If a filter is enabled and is not in the new index -> move it to
245    ///       the custom group and change its ID.
246    ///    d. If the filter is disabled or not installed -> delete it.
247    ///    e. Take the filter and replace the following fields with values from
248    ///       the index:
249    ///       * `display_number`
250    ///       * `title`
251    ///       * `description`
252    ///       * `homepage`
253    ///       * `expires`
254    ///       * `download_url`
255    ///       * `subscription_url`
256    ///       * `last_update_time`
257    ///    f. Mark the filter in the index as processed.
258    /// 5. Remove old groups/tags/locales.
259    /// 6. Fill in new groups/tags/locales.
260    /// 7. Fill in our updated filters along with the raw filters from the
261    ///    index.
262    ///
263    /// Note: should be used no more than once a week.
264    ///
265    /// # Returns
266    ///
267    /// [`PullMetadataResult`] - the result of index metadata update
268    fn pull_metadata(&self) -> FLMResult<PullMetadataResult>;
269
270    /// Updates custom filter data.
271    ///
272    /// * `filter_id` - Custom filter id.
273    /// * `title` - New `title` for filter.
274    /// * `is_trusted` - New `is_trusted` status for filter.
275    ///
276    /// # Failure
277    ///
278    /// Fails if manager couldn't find a filter by `filter_id` or if `filter_id`
279    /// is not from a custom filter. Fails if title is empty.
280    fn update_custom_filter_metadata(
281        &self,
282        filter_id: FilterId,
283        title: String,
284        is_trusted: bool,
285    ) -> FLMResult<bool>;
286
287    /// Gets absolute path for current database.
288    fn get_database_path(&self) -> FLMResult<String>;
289
290    /// The method “raises” the state of the database to the working state.
291    ///
292    /// **If the database doesn't exist:**
293    /// - Creates database
294    /// - Rolls up the schema
295    /// - Rolls migrations
296    /// - Performs bootstrap.
297    ///
298    /// **If the database is an empty file:**
299    /// - Rolls the schema
300    /// - Rolls migrations
301    /// - Performs bootstrap.
302    ///
303    ///... and so on.
304    fn lift_up_database(&self) -> FLMResult<()>;
305
306    /// Gets version of current database scheme.
307    ///
308    /// # Special case
309    ///
310    /// Can return [`None`] if database file exists, but metadata table does not
311    /// exist.
312    fn get_database_version(&self) -> FLMResult<Option<i32>>;
313
314    /// Installs custom filter from string
315    ///
316    /// * `download_url` - Download url for filter. String will be placed
317    ///   *as is*.  See [FilterListManager::install_custom_filter_list] for the
318    ///   format.
319    /// * `last_download_time` - Set `filter.last_download_time` value, which
320    ///   will be added to `filter.expires` and compared to `now()` at
321    ///   [`Self::update_filters`] method.
322    /// * `is_enabled` - True if the filter is enabled.
323    /// * `is_trusted` - True if the filter is trusted.
324    /// * `filter_body` - Filter contents.
325    /// * `custom_title` - Filter may have customized title.
326    ///   See [FilterListManager::install_custom_filter_list].
327    /// * `custom_description` - Filter may have customized description.
328    ///   See [FilterListManager::install_custom_filter_list].
329    ///
330    /// # Failure
331    ///
332    /// Returns [`Err`] if `last_download_time` has unsupported format.
333    fn install_custom_filter_from_string(
334        &self,
335        download_url: String,
336        last_download_time: i64,
337        is_enabled: bool,
338        is_trusted: bool,
339        filter_body: String,
340        custom_title: Option<String>,
341        custom_description: Option<String>,
342    ) -> FLMResult<FullFilterList>;
343
344    /// Gets a list of [`ActiveRulesInfo`] from filters with `filter.is_enabled=true` flag.
345    fn get_active_rules(&self) -> FLMResult<Vec<ActiveRulesInfo>>;
346
347    /// Gets a list of [`ActiveRulesInfoRaw`] from filters with `filter.is_enabled=true` flag.
348    /// * `filter_by` - If empty, returns all active rules, otherwise returns intersection between `filter_by` and all active rules
349    fn get_active_rules_raw(&self, filter_by: Vec<FilterId>) -> FLMResult<Vec<ActiveRulesInfoRaw>>;
350
351    /// Gets a list of [`FilterListRulesRaw`] structures containing.
352    /// `rules` and `disabled_rules` as strings, directly from database fields.
353    ///
354    /// This method acts in the same way as the `IN` database operator. Only found entities will be returned
355    fn get_filter_rules_as_strings(&self, ids: Vec<FilterId>)
356        -> FLMResult<Vec<FilterListRulesRaw>>;
357
358    /// Reads the rule list for a specific filter in chunks, applying exceptions from the disabled_rules list on the fly.
359    /// The default size of the read buffer is 1 megabyte. But this size can be exceeded if a longer string appears in the list of filter rules.
360    /// The main purpose of this method is to reduce RAM consumption when reading large size filters.
361    ///
362    /// # Failure
363    ///
364    /// May return [`crate::FLMError::EntityNotFound()`] with [`FilterId`] if rule list is not found for such id
365    fn save_rules_to_file_blob<P: AsRef<Path>>(
366        &self,
367        filter_id: FilterId,
368        file_path: P,
369    ) -> FLMResult<()>;
370
371    /// Returns lists of disabled rules by list of filter IDs
372    fn get_disabled_rules(&self, ids: Vec<FilterId>) -> FLMResult<Vec<DisabledRulesRaw>>;
373
374    /// Sets a new proxy mode. Value will be applied on next method call
375    fn set_proxy_mode(&mut self, mode: RequestProxyMode);
376
377    /// Returns lists of rules count by list of filter IDs
378    fn get_rules_count(&self, ids: Vec<FilterId>) -> FLMResult<Vec<RulesCountByFilter>>;
379}