123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- <?php
- if(!isset($_GET["s"])){
-
- header("X-Error: Missing parameter (s)ite");
- die();
- }
- include "data/config.php";
- new favicon($_GET["s"]);
- class favicon{
-
- public function __construct($url){
-
- header("Content-Type: image/png");
-
- if(substr_count($url, "/") !== 2){
-
- header("X-Error: Only provide the protocol and domain");
- $this->defaulticon();
- }
-
- $filename = str_replace(["https://", "http://"], "", $url);
- header("Content-Disposition: inline; filename=\"{$filename}.png\"");
-
- include "lib/curlproxy.php";
- $this->proxy = new proxy(false);
-
- $this->filename = parse_url($url, PHP_URL_HOST);
-
- /*
- Check if we have the favicon stored locally
- */
- if(file_exists("icons/" . $filename . ".png")){
-
- $handle = fopen("icons/" . $filename . ".png", "r");
- echo fread($handle, filesize("icons/" . $filename . ".png"));
- fclose($handle);
- return;
- }
-
- /*
- Scrape html
- */
- try{
-
- $payload = $this->proxy->get($url, $this->proxy::req_web, true);
-
- }catch(Exception $error){
-
- header("X-Error: Could not fetch HTML (" . $error->getMessage() . ")");
- $this->favicon404();
- }
- //$payload["body"] = '<link rel="manifest" id="MANIFEST_LINK" href="/data/manifest/" crossorigin="use-credentials" />';
-
- // get link tags
- preg_match_all(
- '/< *link +(.*)[\/]?>/Uixs',
- $payload["body"],
- $linktags
- );
-
- /*
- Get relevant tags
- */
-
- $linktags = $linktags[1];
- $attributes = [];
-
- /*
- header("Content-Type: text/plain");
- print_r($linktags);
- print_r($payload);
- die();*/
-
- for($i=0; $i<count($linktags); $i++){
-
- // get attributes
- preg_match_all(
- '/([A-Za-z0-9]+) *= *("[^"]*"|[^" ]+)/s',
- $linktags[$i],
- $tags
- );
-
- for($k=0; $k<count($tags[1]); $k++){
-
- $attributes[$i][] = [
- "name" => $tags[1][$k],
- "value" => trim($tags[2][$k], "\" \n\r\t\v\x00")
- ];
- }
- }
- unset($payload);
- unset($linktags);
- $href = [];
-
- // filter out the tags we want
- foreach($attributes as &$group){
-
- $tmp_href = null;
- $tmp_rel = null;
- $badtype = false;
-
- foreach($group as &$attribute){
-
- switch($attribute["name"]){
-
- case "rel":
-
- $attribute["value"] = strtolower($attribute["value"]);
-
- if(
- (
- $attribute["value"] == "icon" ||
- $attribute["value"] == "manifest" ||
- $attribute["value"] == "shortcut icon" ||
- $attribute["value"] == "apple-touch-icon" ||
- $attribute["value"] == "mask-icon"
- ) === false
- ){
-
- break;
- }
-
- $tmp_rel = $attribute["value"];
- break;
-
- case "type":
- $attribute["value"] = explode("/", $attribute["value"], 2);
-
- if(strtolower($attribute["value"][0]) != "image"){
-
- $badtype = true;
- break;
- }
- break;
-
- case "href":
-
- // must not contain invalid characters
- // must be bigger than 1
- if(
- filter_var($attribute["value"], FILTER_SANITIZE_URL) == $attribute["value"] &&
- strlen($attribute["value"]) > 0
- ){
-
- $tmp_href = $attribute["value"];
- break;
- }
- break;
- }
- }
-
- if(
- $badtype === false &&
- $tmp_rel !== null &&
- $tmp_href !== null
- ){
-
- $href[$tmp_rel] = $tmp_href;
- }
- }
-
- /*
- Priority list
- */
- /*
- header("Content-Type: text/plain");
- print_r($href);
- die();*/
-
- if(isset($href["icon"])){ $href = $href["icon"]; }
- elseif(isset($href["apple-touch-icon"])){ $href = $href["apple-touch-icon"]; }
- elseif(isset($href["manifest"])){
-
- // attempt to parse manifest, but fallback to []
- $href = $this->parsemanifest($href["manifest"], $url);
- }
-
- if(is_array($href)){
-
- if(isset($href["mask-icon"])){ $href = $href["mask-icon"]; }
- elseif(isset($href["shortcut icon"])){ $href = $href["shortcut icon"]; }
- else{
-
- $href = "/favicon.ico";
- }
- }
-
- $href = $this->proxy->getabsoluteurl($href, $url);
- /*
- header("Content-type: text/plain");
- echo $href;
- die();*/
-
-
- /*
- Download the favicon
- */
- //$href = "https://git.lolcat.ca/assets/img/logo.svg";
-
- try{
- $payload =
- $this->proxy->get(
- $href,
- $this->proxy::req_image,
- true,
- $url
- );
-
- }catch(Exception $error){
-
- header("X-Error: Could not fetch the favicon (" . $error->getMessage() . ")");
- $this->favicon404();
- }
-
- /*
- Parse the file format
- */
- $image = null;
- $format = $this->proxy->getimageformat($payload, $image);
-
- /*
- Convert the image
- */
- try{
-
- /*
- @todo: fix issues with avif+transparency
- maybe using GD as fallback?
- */
- if($format !== false){
- $image->setFormat($format);
- }
-
- $image->setBackgroundColor(new ImagickPixel("transparent"));
- $image->readImageBlob($payload["body"]);
- $image->resizeImage(16, 16, imagick::FILTER_LANCZOS, 1);
- $image->setFormat("png");
-
- $image = $image->getImageBlob();
-
- // save favicon
- $handle = fopen("icons/" . $this->filename . ".png", "w");
- fwrite($handle, $image, strlen($image));
- fclose($handle);
-
- echo $image;
-
- }catch(ImagickException $error){
-
- header("X-Error: Could not convert the favicon: (" . $error->getMessage() . ")");
- $this->favicon404();
- }
-
- return;
- }
-
- private function parsemanifest($href, $url){
-
- if(
- // check if base64-encoded JSON manifest
- preg_match(
- '/^data:application\/json;base64,([A-Za-z0-9=]*)$/',
- $href,
- $json
- )
- ){
-
- $json = base64_decode($json[1]);
-
- if($json === false){
-
- // could not decode the manifest regex
- return [];
- }
-
- }else{
-
- try{
- $json =
- $this->proxy->get(
- $this->proxy->getabsoluteurl($href, $url),
- $this->proxy::req_web,
- false,
- $url
- );
-
- $json = $json["body"];
-
- }catch(Exception $error){
-
- // could not fetch the manifest
- return [];
- }
- }
-
- $json = json_decode($json, true);
-
- if($json === null){
-
- // manifest did not return valid json
- return [];
- }
-
- if(
- isset($json["start_url"]) &&
- $this->proxy->validateurl($json["start_url"])
- ){
-
- $url = $json["start_url"];
- }
-
- if(!isset($json["icons"][0]["src"])){
-
- // manifest does not contain a path to the favicon
- return [];
- }
-
- // horay, return the favicon path
- return $json["icons"][0]["src"];
- }
-
- private function favicon404(){
-
- // fallback to google favicons
- // ... probably blocked by cuckflare
- try{
-
- $image =
- $this->proxy->get(
- "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://{$this->filename}&size=16",
- $this->proxy::req_image
- );
- }catch(Exception $error){
-
- $this->defaulticon();
- }
-
- // write favicon from google
- $handle = fopen("icons/" . $this->filename . ".png", "w");
- fwrite($handle, $image["body"], strlen($image["body"]));
- fclose($handle);
-
- echo $image["body"];
- die();
- }
-
- private function defaulticon(){
-
- // give 404 and fuck off
- http_response_code(404);
-
- $handle = fopen("lib/favicon404.png", "r");
- echo fread($handle, filesize("lib/favicon404.png"));
- fclose($handle);
-
- die();
- }
- }
|