<?php
/******************************************************************************
 *  SiteBar 3 - The Bookmark Server for Personal and Team Use.                *
 *  Copyright (C) 2004  Gunnar Wrobel <sitebar@gunnarwrobel.de>               *
 *                                                                            *
 *  This program is free software; you can redistribute it and/or modify      *
 *  it under the terms of the GNU General Public License as published by      *
 *  the Free Software Foundation; either version 2 of the License, or         *
 *  (at your option) any later version.                                       *
 *                                                                            *
 *  This program is distributed in the hope that it will be useful,           *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *  GNU General Public License for more details.                              *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with this program; if not, write to the Free Software               *
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
 ******************************************************************************/

require_once('./inc/database.inc.php');
require_once('./inc/pageparser.inc.php');
require_once('./inc/usermanager.inc.php');
require_once('./inc/errorhandler.inc.php');

class FaviconCache extends ErrorHandler
{
    // The class needs the db as storage place
    var $db;
    // And the usermanager for user related functions
    var $um;

    function FaviconCache()
    {
        $this->db =& Database::staticInstance();
        $this->um =& UserManager::staticInstance();
    }

    function & staticInstance()
    {
        static $cache;

        if (!$cache)
        {
            $cache = new FaviconCache();
        }

        return $cache;
    }

    function hasSpace()
    {
        // Check how many icons we have
        $res = $this->db->select('count(*) count', 'sitebar_favicon');
        $rec = $this->db->fetchRecord($res);

        // We have free place then return
        if ($rec['count']<$this->um->getParam('config','max_icon_cache'))
        {
            return true;
        }

        $maxAge = $this->um->getParam('config','max_icon_age');

        // We have too many icons in total, purge the old icons
        for ($i=$maxAge; $i>0; $i--)
        {
            $result = $this->db->delete('sitebar_favicon',
                'to_days(now()) - to_days(changed) >' . $i);

            // If we delete something, then hooray and return
            if ($this->db->getAffectedRows())
            {
                return true;
            }
        }

        return false;
    }

    function purge($lid = null)
    {
        if ($lid === null)
        {
            $this->db->delete('sitebar_favicon');
        }
        else
        {
            // Get the url corresponding to the link id
            $select = $this->db->select( 'favicon', 'sitebar_link', array('lid' => $lid));
            $found = $this->db->fetchRecord($select);

            if (is_array($found) && isset($found['favicon']) && $found['favicon'])
            {
                // Delete it from cache anyway
                $this->db->delete('sitebar_favicon', array('favicon_md5'=> md5($found['favicon'])));
            }
        }
    }

    function faviconGetAll()
    {
        $select = $this->db->select(array('favicon_md5'), 'sitebar_favicon', null, 'length(ico) asc, ico asc');
        $found  = $this->db->fetchRecords($select);
        return $found;
    }

    function isFaviconCached($favicon)
    {
        $select = $this->db->select('count(*) count', 'sitebar_favicon',
            array('favicon_md5' => md5($favicon)));
        $found  = $this->db->fetchRecord($select);
        return $found['count'];
    }

    function saveFavicon($favicon, $ico)
    {
        $this->db->insert('sitebar_favicon', array
        (
            'favicon_md5' => md5($favicon),
            'ico' => $ico,
            'changed' => array('now' => ''),
        ));
    }

    function faviconGet($favicon_md5, $lid, $refresh=false)
    {
        $maxAge = $this->um->getParam('config','max_icon_age');

        $select = $this->db->select(
            'ico, to_days(now()) - to_days(changed) as age',
            'sitebar_favicon',
            array('favicon_md5' => $favicon_md5));

        $found  = $this->db->fetchRecord($select, true);
        $oldIcon = null;

        if (is_array($found))
        {
            $oldIcon = $found['ico'];
            if (!$refresh && $found['age']<=$maxAge)
            {
                return $oldIcon;
            }
        }

        // if lid is not specified, the favicon wont be fetched into the cache
        if ($lid)
        {
            // Get the url corresponding to the link id
            $rset = $this->db->select( null, 'sitebar_link', array('lid' => $lid));

            // Fetch the link properties
            $rlink = $this->db->fetchRecord($rset);

            // No such link? Return empty?
            if (!$rlink || !$rlink['favicon'])
            {
                return null;
            }

            $newIcon = '';

            // Retrieve and test icon as binary string
            $page = new PageParser($rlink['favicon']);
            $errorCode = $page->retrieveFAVICON($newIcon);

            // We have unrecoverable error
            if ($errorCode >= 500)
            {
                return null;
            }

            // We have probably connection problem
            if ($errorCode >= 400)
            {
                if ($oldIcon)
                {
                    $this->db->update('sitebar_favicon',
                        array('changed' => array('now' => '')),
                        array('favicon_md5' => $favicon_md5));
                }
                return $oldIcon;
            }

            if ($oldIcon) // We had a records
            {
                $this->db->update('sitebar_favicon',
                    array('ico' => $newIcon),
                    array('favicon_md5' => $favicon_md5));

            }
            else if ($this->hasSpace()) // New record
            {
                $this->saveFavicon($rlink['favicon'], $newIcon);
            }

            return $newIcon;
        }
    }

    // If lid is not specified, the cache wont be updated
    function faviconReturn($favicon_md5, $lid = null, $refresh=false)
    {
        $ico = $this->faviconGet($favicon_md5, $lid, $refresh);

        // 30 days keep cached in browser
        $age = 60*60*24*30;

        if (!$ico)
        {
            // Sent wrong icon image
            Skin::set($this->um->getParam('user','skin'));
            $ico = file_get_contents(Skin::imgsrc('link_wrong_favicon'));
            $age = 60*60*24; // Try tomorrow
        }

        if ($refresh)
        {
            $age = -1;
        }

        header("Accept-Ranges: bytes");
        header("Cache-Control: max-age=$age");
        header("Content-Length: ". strlen($ico));

        $type = "image/x-icon";

        if (substr($ico,0,3)=="GIF")
        {
            $type = "image/gif";
        }
        else if (substr($ico,6,4)=="JFIF")
        {
            $type = "image/jpeg";
        }
        else if (substr($ico,1,3)=='PNG')
        {
            $type = "image/png";
        }

        header("Content-Type: $type");

        print $ico;
    }
}
?>