<?php
/******************************************************************************
 *  SiteBar 3 - The Bookmark Server for Personal and Team Use.                *
 *  Copyright (C) 2003,2004  Ondrej Brablc <http://brablc.com/mailto?o>       *
 *                                                                            *
 *  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/tree.inc.php');
require_once('./inc/converter.inc.php');

define ('BM_T_NETSCAPE',   'NETSCAPE-Bookmark-file-1');
// Faked first line, used just for export
define ('BM_T_IE',         'IE:NETSCAPE-Bookmark-file-1');
define ('BM_T_OPERA',      'Opera Hotlist version 2.0');

class BookmarkManager extends Converter
{
    var $tree;
    // Content of the whole file - memory should not be problem
    var $lines = array();
    // Were bookmarks loaded?
    var $success = false;
    // Export SiteBar attributes
    var $useSiteBarAttributes = true;

    var $importedLinks = 0;
    var $importedFolders = 0;

    function BookmarkManager($useEngine=true, $charSet=null)
    {
        if (!$charSet)
        {
            $charSet = 'en-utf-8';
        }
        // Charset always contains country
        $this->Converter($useEngine, $charSet, true);
    }

/******************************************************************************/

    function import($filename, $type=null)
    {
        if (!($fp = fopen($filename, 'r')))
        {
            $this->error('Can\'t open file!');
            return;
        }

        while (!feof($fp))
        {
            $this->lines[] = rtrim(fgets($fp,65536));
        }
        fclose( $fp);

        if (!count($this->lines))
        {
            $this->error('File is empty!');
            return;
        }

        $this->tree = new Tree_Node();

        if (!$type)
        {
            $type = $this->lines[0];
        }

        if (stristr($type, BM_T_NETSCAPE))
        {
            $this->success = $this->importNetscape($this->tree);
        }
        elseif (stristr($type, BM_T_OPERA))
        {
            $this->success = $this->importOpera($this->tree);
        }
        else
        {
            $this->error('Unknown bookmark file format!');
        }
    }

    function importNetscape(&$parent)
    {
        while (($line = array_shift($this->lines))!==null)
        {
            if (preg_match('/<meta\s+http-equiv=["\']Content-Type["\']\s+'.
                'content=["\'].*?\bcharset=(.*?)["\']\s*>/i', $line, $reg))
            {
                if (strcasecmp($reg[1],$this->charSet))
                {
                    if ($this->useEngine && $this->getEngine()!=CHARSET_IGNORE)
                    {
                        $this->setCharSet($reg[1]);
                        $this->warn('Character set overriden from document to %s!', array($reg[1]));
                    }
                    else
                    {
                        $this->warn('There is no conversion engine available to convert from %s character set!', array($reg[1]));
                    }
                }
            }

            $line = $this->toUTF8($line);

            // Open node
            if (preg_match('/<DT.*><H3([^>]*)>([^<]+)<\/H3>/i', $line, $reg ))
            {
                $rec = array();
                $params = $reg[1];
                $rec['name'] = $reg[2];
                if (preg_match('/SB_SORT_MODE="([^"]+)"/i', $params, $reg))
                {
                    $rec['sort_mode'] = $reg[1];
                }
                if (preg_match('/SB_ORDER="([^"]+)"/i', $params, $reg))
                {
                    $rec['order'] = $reg[1];
                }
                $this->_importNetscapeComment($rec);

                $node = new Tree_Node($rec);

                // Yes recursive!
                $this->importNetscape($node);
                $parent->addNode($node);
                $this->importedFolders++;
                continue;
            }

            // Add link to current node
            if (preg_match('/<DT.*><A HREF="([^"]+)"([^>]*)>([^<]+)<\/A>/i',$line, $reg ))
            {
                $rec = array();
                $rec['url'] = $reg[1];
                $params = $reg[2];
                $rec['name'] = $reg[3];
                if (preg_match('/ICON="([^"]+)"/i', $params, $reg))
                {
                    $rec['favicon'] = $reg[1];
                }
                if (preg_match('/SB_PRIVATE="([^"]+)"/i', $params, $reg))
                {
                    $rec['private'] = $reg[1];
                }
                if (preg_match('/SB_VALIDATE="([^"]+)"/i', $params, $reg))
                {
                    $rec['validate'] = $reg[1];
                }
                if (preg_match('/SB_ORDER="([^"]+)"/i', $params, $reg))
                {
                    $rec['order'] = $reg[1];
                }
                $this->_importNetscapeComment($rec);
                $parent->addLink(new Tree_Link($rec));
                $this->importedLinks++;
                continue;
            }

            // Close node - break recursion
            if (stristr($line, "</DL>"))
            {
                return true;
            }
        }
        return true;
    }

    function _importNetscapeComment(&$rec)
    {
        $line = array_shift($this->lines);
        if (preg_match('/<DD>(.*)/i', $line, $reg ))
        {
            $rec['comment'] = $this->toUTF8($reg[1]);
        }
        else
        {
            // Put it back if it is not comment
            array_unshift($this->lines,$line);
        }
    }

    function importOpera(&$parent)
    {
        while (($line = array_shift($this->lines))!==null)
        {
            $line = $this->toUTF8($line);
            // Open node
            if ($line == "#FOLDER")
            {
                $rec = array();
                while ($line != "")
                {
                    $line = trim(array_shift($this->lines));
                    $parts = explode('=',$line,2);

                    if (count($parts)>1)
                    {
                        list($name,$value) = $parts;
                        if ($name=='NAME')
                        {
                            $rec['name'] = $value;
                        }
                        if ($name=='DESCRIPTION')
                        {
                            $rec['comment'] = $value;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                $node = new Tree_Node($rec);

                // Yes recursive!
                $this->importOpera($node);
                $parent->addNode($node);
                $this->importedFolders++;
                continue;
            }

            // Add link to current node
            if ($line == "#URL")
            {
                $rec = array();
                while ($line != "")
                {
                    $line = trim(array_shift($this->lines));
                    $parts = explode('=',$line,2);

                    if (count($parts)>1)
                    {
                        list($name,$value) = $parts;

                        if ($name=='NAME')
                        {
                            $rec['name'] = $value;
                        }
                        if ($name=='URL')
                        {
                            $rec['url'] = $value;
                        }
                        if ($name=='DESCRIPTION')
                        {
                            $rec['comment'] = $value;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                $parent->addLink(new Tree_Link($rec));
                $this->importedLinks++;
                continue;
            }

            // Close node - break recursion
            if ($line == "-")
            {
                return true;
            }
        }
        return true;
    }

/******************************************************************************/

    function export($base, $type)
    {
        if (stristr($type, BM_T_NETSCAPE))
        {
            $this->useSiteBarAttributes = true;
            $this->success = $this->exportNetscape($base);
        }
        elseif (stristr($type, BM_T_IE))
        {
            $this->useSiteBarAttributes = false;
            $this->success = $this->exportNetscape($base);
        }
        elseif (stristr($type, BM_T_OPERA))
        {
            $this->success = $this->exportOpera($base);
        }
        else
        {
            $this->error('Unknown bookmark file format!');
        }
    }

    function _exportChildren($callbackNode, $callbackLink, $base, $level)
    {
        foreach ($base->getChildren() as $child)
        {
            if ($child->type_flag=='n')
            {
                $this->$callbackNode($child, $level+1);
            }
            else
            {
                $this->$callbackLink($child, $level+1);
            }
        }
    }

    function exportNetscape($base)
    {
?>
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
     It will be read and overwritten.
     DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=<?php echo strtoupper($this->charSet)?>">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>

<DL><p>
<?php
        $this->_exportChildren(
            '_exportNetscapeNode',
            '_exportNetscapeLink',
            $base, 1);
?>
</DL><p>
<?php
    }

    function _exportNetscapeNode($node, $level)
    {
        $filler = str_repeat("\t", $level);
        echo $filler . '<DT><H3' .
            ' ADD_DATE="' . mktime() . '"'.
            ($this->useSiteBarAttributes
             ?' SB_SORT_MODE="'. $node->sort_mode . '"'
             :'').
            ($this->useSiteBarAttributes
             ?' SB_ORDER="'. $node->order . '"'
             :'').
            '>' . $this->fromUTF8(str_replace('&#39;',"'",$node->name)) . "</H3>\n";
        if ($node->comment)
        {
            echo $filler. '<DD>' .
                $this->fromUTF8($node->comment) . "\n";
        }
        echo $filler . "<DL><p>\n";

        $this->_exportChildren(
            '_exportNetscapeNode',
            '_exportNetscapeLink',
            $node, $level+1);

        echo $filler . "</DL><p>\n";
    }

    function _exportNetscapeLink($link, $level)
    {
         $filler = str_repeat("\t", $level);

         echo $filler . '<DT><A'.
              ' HREF="' . $link->url . '"'.
              ($link->favicon?' ICON="'. $link->favicon . '"':'') .
              ' ADD_DATE="' . mktime() . '"' .
              ($link->private && $this->useSiteBarAttributes
               ?' SB_PRIVATE="' . $link->private . '"'
               :'') .
              ($link->validate && $this->useSiteBarAttributes
               ?' SB_VALIDATE="' . $link->validate . '"'
               :'') .
              ($this->useSiteBarAttributes
               ?' SB_ORDER="'. $link->order . '"'
               :'') .
              '>' .  $this->fromUTF8(str_replace('&#39;',"'",$link->name)) . "</A>\n";
         if ($link->comment)
         {
            echo $filler.'<DD>'.$this->fromUTF8($link->comment)."\n";
         }
    }

    function exportOpera($base)
    {
?>
Opera Hotlist version 2.0
Options: encoding = utf8, version=3

<?php
        $this->_exportChildren(
            '_exportOperaNode',
            '_exportOperaLink',
            $base, 1);
    }

    function _exportOperaNode($node, $level)
    {
        echo "#FOLDER\n";
        echo "\tNAME=".$this->fromUTF8($node->name)."\n";
        if ($node->comment)
        {
            echo "\tDESCRIPTION=".$this->fromUTF8($node->comment)."\n";
        }
        echo "\n";

        $this->_exportChildren(
            '_exportOperaNode',
            '_exportOperaLink',
            $node, 1);

        echo "-\n\n";
    }

    function _exportOperaLink($link, $level)
    {
        echo "#URL\n";
        echo "\tNAME=".$this->fromUTF8($link->name)."\n";
        echo "\tURL=".$link->url."\n";
        if ($link->comment)
        {
            echo "\tDESCRIPTION=".$this->fromUTF8($link->comment)."\n";
        }
        echo "\n";
    }
}
?>
