|
@ -37,6 +37,7 @@ static CLIENT: Lazy<Client> = Lazy::new(|| { |
|
|
|
|
|
|
|
|
// Build Regex only once since this takes a lot of time.
|
|
|
// Build Regex only once since this takes a lot of time.
|
|
|
static ICON_REL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)icon$|apple.*icon").unwrap()); |
|
|
static ICON_REL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)icon$|apple.*icon").unwrap()); |
|
|
|
|
|
static ICON_REL_BLACKLIST: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)mask-icon").unwrap()); |
|
|
static ICON_SIZE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?x)(\d+)\D*(\d+)").unwrap()); |
|
|
static ICON_SIZE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?x)(\d+)\D*(\d+)").unwrap()); |
|
|
|
|
|
|
|
|
// Special HashMap which holds the user defined Regex to speedup matching the regex.
|
|
|
// Special HashMap which holds the user defined Regex to speedup matching the regex.
|
|
@ -52,7 +53,9 @@ fn icon(domain: String) -> Cached<Content<Vec<u8>>> { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
match get_icon(&domain) { |
|
|
match get_icon(&domain) { |
|
|
Some(i) => Cached::ttl(Content(ContentType::new("image", "x-icon"), i), CONFIG.icon_cache_ttl()), |
|
|
Some((icon, icon_type)) => { |
|
|
|
|
|
Cached::ttl(Content(ContentType::new("image", icon_type), icon), CONFIG.icon_cache_ttl()) |
|
|
|
|
|
}, |
|
|
_ => Cached::ttl(Content(ContentType::new("image", "png"), FALLBACK_ICON.to_vec()), CONFIG.icon_cache_negttl()), |
|
|
_ => Cached::ttl(Content(ContentType::new("image", "png"), FALLBACK_ICON.to_vec()), CONFIG.icon_cache_negttl()), |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -243,7 +246,7 @@ fn is_domain_blacklisted(domain: &str) -> bool { |
|
|
is_blacklisted |
|
|
is_blacklisted |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fn get_icon(domain: &str) -> Option<Vec<u8>> { |
|
|
fn get_icon(domain: &str) -> Option<(Vec<u8>, String)> { |
|
|
let path = format!("{}/{}.png", CONFIG.icon_cache_folder(), domain); |
|
|
let path = format!("{}/{}.png", CONFIG.icon_cache_folder(), domain); |
|
|
|
|
|
|
|
|
// Check for expiration of negatively cached copy
|
|
|
// Check for expiration of negatively cached copy
|
|
@ -252,7 +255,11 @@ fn get_icon(domain: &str) -> Option<Vec<u8>> { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if let Some(icon) = get_cached_icon(&path) { |
|
|
if let Some(icon) = get_cached_icon(&path) { |
|
|
return Some(icon); |
|
|
let icon_type = match get_icon_type(&icon) { |
|
|
|
|
|
Some(x) => x, |
|
|
|
|
|
_ => "x-icon", |
|
|
|
|
|
}; |
|
|
|
|
|
return Some((icon, icon_type.to_string())); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if CONFIG.disable_icon_download() { |
|
|
if CONFIG.disable_icon_download() { |
|
@ -261,9 +268,9 @@ fn get_icon(domain: &str) -> Option<Vec<u8>> { |
|
|
|
|
|
|
|
|
// Get the icon, or None in case of error
|
|
|
// Get the icon, or None in case of error
|
|
|
match download_icon(&domain) { |
|
|
match download_icon(&domain) { |
|
|
Ok(icon) => { |
|
|
Ok((icon, icon_type)) => { |
|
|
save_icon(&path, &icon); |
|
|
save_icon(&path, &icon); |
|
|
Some(icon) |
|
|
Some((icon, icon_type.unwrap_or("x-icon").to_string())) |
|
|
} |
|
|
} |
|
|
Err(e) => { |
|
|
Err(e) => { |
|
|
error!("Error downloading icon: {:?}", e); |
|
|
error!("Error downloading icon: {:?}", e); |
|
@ -324,7 +331,6 @@ fn icon_is_expired(path: &str) -> bool { |
|
|
expired.unwrap_or(true) |
|
|
expired.unwrap_or(true) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
|
struct Icon { |
|
|
struct Icon { |
|
|
priority: u8, |
|
|
priority: u8, |
|
|
href: String, |
|
|
href: String, |
|
@ -348,7 +354,7 @@ fn get_favicons_node(node: &std::rc::Rc<markup5ever_rcdom::Node>, icons: &mut Ve |
|
|
let attr_name = attr.name.local.as_ref(); |
|
|
let attr_name = attr.name.local.as_ref(); |
|
|
let attr_value = attr.value.as_ref(); |
|
|
let attr_value = attr.value.as_ref(); |
|
|
|
|
|
|
|
|
if attr_name == "rel" && ICON_REL_REGEX.is_match(attr_value) { |
|
|
if attr_name == "rel" && ICON_REL_REGEX.is_match(attr_value) && !ICON_REL_BLACKLIST.is_match(attr_value) { |
|
|
has_rel = true; |
|
|
has_rel = true; |
|
|
} else if attr_name == "href" { |
|
|
} else if attr_name == "href" { |
|
|
href = Some(attr_value); |
|
|
href = Some(attr_value); |
|
@ -597,7 +603,7 @@ fn parse_sizes(sizes: Option<&str>) -> (u16, u16) { |
|
|
(width, height) |
|
|
(width, height) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fn download_icon(domain: &str) -> Result<Vec<u8>, Error> { |
|
|
fn download_icon(domain: &str) -> Result<(Vec<u8>, Option<&str>), Error> { |
|
|
if is_domain_blacklisted(domain) { |
|
|
if is_domain_blacklisted(domain) { |
|
|
err!("Domain is blacklisted", domain) |
|
|
err!("Domain is blacklisted", domain) |
|
|
} |
|
|
} |
|
@ -605,6 +611,7 @@ fn download_icon(domain: &str) -> Result<Vec<u8>, Error> { |
|
|
let icon_result = get_icon_url(&domain)?; |
|
|
let icon_result = get_icon_url(&domain)?; |
|
|
|
|
|
|
|
|
let mut buffer = Vec::new(); |
|
|
let mut buffer = Vec::new(); |
|
|
|
|
|
let mut icon_type: Option<&str> = None; |
|
|
|
|
|
|
|
|
use data_url::DataUrl; |
|
|
use data_url::DataUrl; |
|
|
|
|
|
|
|
@ -616,17 +623,31 @@ fn download_icon(domain: &str) -> Result<Vec<u8>, Error> { |
|
|
Ok((body, _fragment)) => { |
|
|
Ok((body, _fragment)) => { |
|
|
// Also check if the size is atleast 67 bytes, which seems to be the smallest png i could create
|
|
|
// Also check if the size is atleast 67 bytes, which seems to be the smallest png i could create
|
|
|
if body.len() >= 67 { |
|
|
if body.len() >= 67 { |
|
|
|
|
|
// Check if the icon type is allowed, else try an icon from the list.
|
|
|
|
|
|
icon_type = get_icon_type(&body); |
|
|
|
|
|
if icon_type.is_none() { |
|
|
|
|
|
debug!("Icon from {} data:image uri, is not a valid image type", domain); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
info!("Extracted icon from data:image uri for {}", domain); |
|
|
buffer = body; |
|
|
buffer = body; |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
_ => warn!("data uri is invalid"), |
|
|
_ => warn!("Extracted icon from data:image uri is invalid"), |
|
|
}; |
|
|
}; |
|
|
} else { |
|
|
} else { |
|
|
match get_page_with_cookies(&icon.href, &icon_result.cookies, &icon_result.referer) { |
|
|
match get_page_with_cookies(&icon.href, &icon_result.cookies, &icon_result.referer) { |
|
|
Ok(mut res) => { |
|
|
Ok(mut res) => { |
|
|
info!("Downloaded icon from {}", icon.href); |
|
|
|
|
|
res.copy_to(&mut buffer)?; |
|
|
res.copy_to(&mut buffer)?; |
|
|
|
|
|
// Check if the icon type is allowed, else try an icon from the list.
|
|
|
|
|
|
icon_type = get_icon_type(&buffer); |
|
|
|
|
|
if icon_type.is_none() { |
|
|
|
|
|
buffer.clear(); |
|
|
|
|
|
debug!("Icon from {}, is not a valid image type", icon.href); |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
info!("Downloaded icon from {}", icon.href); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
_ => warn!("Download failed for {}", icon.href), |
|
|
_ => warn!("Download failed for {}", icon.href), |
|
@ -635,10 +656,10 @@ fn download_icon(domain: &str) -> Result<Vec<u8>, Error> { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if buffer.is_empty() { |
|
|
if buffer.is_empty() { |
|
|
err!("Empty response") |
|
|
err!("Empty response downloading icon") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Ok(buffer) |
|
|
Ok((buffer, icon_type)) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fn save_icon(path: &str, icon: &[u8]) { |
|
|
fn save_icon(path: &str, icon: &[u8]) { |
|
@ -650,7 +671,18 @@ fn save_icon(path: &str, icon: &[u8]) { |
|
|
create_dir_all(&CONFIG.icon_cache_folder()).expect("Error creating icon cache"); |
|
|
create_dir_all(&CONFIG.icon_cache_folder()).expect("Error creating icon cache"); |
|
|
} |
|
|
} |
|
|
Err(e) => { |
|
|
Err(e) => { |
|
|
info!("Icon save error: {:?}", e); |
|
|
warn!("Icon save error: {:?}", e); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_icon_type(bytes: &[u8]) -> Option<&'static str> { |
|
|
|
|
|
match bytes { |
|
|
|
|
|
[137, 80, 78, 71, ..] => Some("png"), |
|
|
|
|
|
[0, 0, 1, 0, ..] => Some("x-icon"), |
|
|
|
|
|
[82, 73, 70, 70, ..] => Some("webp"), |
|
|
|
|
|
[255, 216, 255, ..] => Some("jpeg"), |
|
|
|
|
|
[66, 77, ..] => Some("bmp"), |
|
|
|
|
|
_ => None |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|