<?php
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is SOGo z-push backend.
 *
 * The Initial Developer of the Original Code is
 *   Philipp Kewisch <mozilla@kewis.ch>
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

include_once("backend/backend.php");
include_once("proto.php");
include_once("config.php");

include_once("sogobackend/aidan_ical.php");
include_once("sogobackend/sogoUtils.php");

$store = initstore();

class DatabaseSOGO extends PDO {
    public function __construct() {
        $dsn = SOGO_SQL_DRIVER .
               ":host=" . SOGO_SQL_HOST ;
               (defined(SOGO_SQL_PORT) ? ";port=" . SOGO_SQL_PORT : "") .
               (defined(SOGO_SQL_DBNAME) ? ";dbname=" . SOGO_SQL_DBNAME : "");

        parent::__construct($dsn, SOGO_SQL_USERNAME, SOGO_SQL_PASSWORD);
    }
};


class BackendSOGO extends Backend {
    public $db;
    public $username;


    function __construct() {
        $this->db = new DatabaseSOGO();
    }

    public function GetExporter($folderid = false) {
        return new ExporterSOGO($this, $folderid);
    }

    public function GetHierarchyImporter() {
        return new HierarchyImporterSOGO($this);
    }

    public function GetContentsImporter($folderid) {
        return new ContentsImporterSOGO($this, $folderid);
    }

    public function GetWasteBasket() {
        return null;
    }

    public function Logon($username, $domain, $password) {
        $this->_server = "{" . IMAP_SERVER . ":" . IMAP_PORT . "/imap" . IMAP_OPTIONS . "}";
        $this->_mbox = @imap_open($this->_server , $username, $password, OP_HALFOPEN);

        $this->username = $username;

        return ($this->_mbox != null);
    }

    public function Setup($user, $devid, $protocol) {
        $this->_user = $user;
        $this->_devid = $devid;
        $this->_protocol = $protocol;
        return true;
    }

    public function Logoff() {
        if ($this->_mbox) {
            @imap_close($this->_mbox);
        }
    }

    public function GetHierarchy() {
        debugLog("GetHierarchy");
        global $store;
        return array($store);
    }

    public function SendMail($rfc822, $forward = false, $reply = false, $parent = false) {
        return true;

    }
};

class ExporterSOGO {
    private $_backend;
    private $_folderid;
    private $_count;

    private $db;
    private $stmtCalendarChanges;
    private $stmtCalendarDeletes;
    private $stmtContactsChanges;
    private $stmtContactsDeletes;

    function __construct($backend, $folderid) {
        $this->_backend = $backend;
        $this->_folderid = $folderid;
        $this->db = &$backend->db;
    }

    function Config(&$importer, $folderid, $restrict, $syncstate, $flags, $truncation) {
        $this->_importer = &$importer;
        $this->_restrict = $restrict;
        $this->_syncstate = ($syncstate ? unserialize($syncstate) : array());
        $this->_flags = $flags;
        $this->_truncation = $truncation;

        if(!isset($this->_syncstate) || !$this->_syncstate) {
            $this->_syncstate = array();
        }

        if ($this->_folderid) {
            // Content synchronization

            // Get the table names used for synchronization
            $this->_getFolderInfo();

            // Prepare the statements
            $this->_prepareStatements();

            $sqlopts = array();
            if (isset($this->_syncstate["lastmod"])) {
                $sqlopts[":lastmod"] = $this->_syncstate["lastmod"];
            } else {
                $sqlopts[":lastmod"] = 0;
            }
            
            // Now update the sync state
            $this->_syncstate["lastmod"] = time();

            $this->stmtCalendarChanges->execute($sqlopts);
            $this->stmtCalendarDeletes->execute($sqlopts);


            debugLog("Configured sync for " . $sqlopts[":lastmod"]);
            debugLog("Sending " . $this->stmtCalendarChanges->rowCount() .
                     " changes and " . $this->stmtCalendarDeletes->rowCount() .
                     " deletes");
        } else {
            // Folder synchronization
        }
    }

    private function _prepareStatements() {
        $calendarChangesSql = "SELECT t.c_content,t.c_name FROM " .
                                $this->_calendar_table . " AS t," .
                                $this->_calendar_quick_table . " AS q" .
                              " WHERE t.c_lastmodified >= :lastmod" .
                              "   AND t.c_name = q.c_name" .
                              "   AND q.c_component = 'vevent'" .
                              "   AND c_deleted IS NULL";
        $this->stmtCalendarChanges = $this->db->prepare($calendarChangesSql);

        $calendarDeletesSql = "SELECT c_content,c_name FROM " .
                                $this->_calendar_table .
                              " WHERE c_lastmodified >= :lastmod" .
                              "   AND c_deleted = 1";
        $this->stmtCalendarDeletes = $this->db->prepare($calendarDeletesSql);

        $contactsChangesSql = "SELECT t.c_content,t.c_name FROM " .
                                $this->_contacts_table . " AS t," .
                                $this->_contacts_quick_table . " AS q" .
                              " WHERE t.c_lastmodified >= :lastmod" .
                              "   AND t.c_name = q.c_name" .
                              "   AND q.c_component = 'vevent'" .
                              "   AND c_deleted IS NULL";
        $this->stmtContactsChanges = $this->db->prepare($contactsChangesSql);

        $contactsDeletesSql = "SELECT t.c_content,t.c_name FROM " .
                                $this->_contacts_table . " AS t, " .
                                $this->_contacts_quick_table . " AS q" .
                              " WHERE t.c_lastmodified >= :lastmod" .
                              "   AND t.c_name = q.c_name" .
                              "   AND q.c_component = 'vevent'" .
                              "   AND c_deleted = 1";
        $this->stmtContactDeletes = $this->db->prepare($contactsDeletesSql);
    }


    private function _getFolderInfo() {
        $userid = $this->_backend->username;

        // Get the calendar tables
        $calendar = defined(SOGO_SYNC_CALENDAR) ? SOGO_SYNC_CALENDAR : "personal";
        $stmt = $this->db->query("SELECT c_location, c_quick_location FROM sogo_folder_info" .
                                   " WHERE c_path2=" . $this->db->quote($userid) .
                                   "   AND c_path3='Calendar'" .
                                   "   AND c_path4=" . $this->db->quote($calendar) .
                                   " LIMIT 1");
        $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $location = split("/", $results[0]["c_location"]);
        $this->_calendar_table = array_pop($location);

        $location = split("/", $results[0]["c_quick_location"]);
        $this->_calendar_quick_table = array_pop($location);

        // Get the contact tables
        $calendar = defined(SOGO_SYNC_CONTACTS) ? $SOGO_SYNC_CONTACTS : "personal";
        $stmt = $this->db->query("SELECT c_location, c_quick_location FROM sogo_folder_info" .
                                   " WHERE c_path2=" . $this->db->quote($userid) .
                                   "   AND c_path3='Contacts'" .
                                   "   AND c_path4=" . $this->db->quote($calendar) .
                                   " LIMIT 1");
        $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $location = split("/", $results[0]["c_location"]);
        $this->_contacts_table = array_pop($location);

        $location = split("/", $results[0]["c_quick_location"]);
        $this->_contacts_quick_table = array_pop($location);
    }

    function Synchronize() {
        if (!$this->_importer) {
            return false;
        }
        debugLog("Synchronize ");

        if ($this->_folderid == false) {
            if (isset($this->_syncstate["importedfolders"]) &&
                $this->_syncstate["importedfolders"]) {
                // Nothing more to do, the folders exist already
                return true;
            } else {
                // Import our calendar folder
                global $store;
                debugLog("Importing folder " . $store);
                $this->_importer->ImportFolderChange($store);
                $this->_syncstate["importedfolders"] = true;
                return true;
            }
        } else {
            if ($row = $this->stmtCalendarChanges->fetch(PDO::FETCH_ASSOC)) {
                // Import a change/addition
                $syncAppt = convertICSToSyncAppt($row["c_content"], $row["c_name"], "root");
                $this->_importer->ImportMessageChange($row["c_name"], $syncAppt);
            } else if ($row = $this->stmtCalendarDeletes->fetch(PDO::FETCH_ASSOC)) {
                debugLog("DELETION: " + var_export($row,1));
                // Import a deletion
                $this->_importer->ImportMessageDeletion($row["c_name"]);
            } else {
                // Done syncing
                $this->stmtCalendarChanges->closeCursor();
                $this->stmtCalendarDeletes->closeCursor();
                return true;
            }
        }
                
        return array();
    }
    function GetState() {
        return serialize($this->_syncstate);
    }

    function GetChangeCount() {
        // TODO PDO doesn't guarantee that this works for all database types.
        // mysql and pgsql should work though.
        return $this->stmtCalendarChanges->rowCount() +
               $this->stmtCalendarDeletes->rowCount();
    }
};

class HierarchyImporterSOGO {
    var $_backend;
    var $_syncstate;
    var $_flags;

    function __construct($backend) {
        $this->_backend = $backend;
    }    

    function Config($state) {
        $this->_syncstate = unserialize($state);
    }

    function ImportFolderChange($folder) {
        debugLog("FolderChange");   
    }

    function ImportFolderDeletion($folder) {
        debugLog("FolderDeletion"); 
    }

    function GetState() {
        return serialize($this->_syncstate);
    }

};

class ContentsImporterSOGO {
    var $_backend;
    var $_folderid;
    var $_syncstate;
    var $_flags;

    function __construct($backend, $folderid) {
        $this->_backend = $backend;
        $this->_folderid = $folderid;
    }

    function Config($state, $flags = 0) {
        $this->_syncstate = unserialize($state);
        $this->_flags = $flags;
    }

    function ImportMessageChange($id, $message) {
        debugLog("messagechange");
    }

    function ImportMessageDeletion($id) {
        debugLog("messagedelete");
    }
    
    function ImportMessageReadFlag($id, $flags) {
        debugLog("messagereadFlag");
    }

    function ImportMessageMove($message) {
        debugLog("messageMove");
    }

    function GetState() {
        return serialize($this->_syncstate);
    }

};

?>
