<?php

require_once(INSTALL_PATH . 'plugins/calendar/program/backend/backend.php');
require_once(INSTALL_PATH . 'plugins/calendar/program/backend/caldav/caldav-client.php');

final class calendar_caldav extends Backend 
{
  private $rcmail;
  private $type;
  public  $utils;
  private $usertimezone;
  private $username;
  private $caldav;
  public $caldavs = array();
  private $account;
  
  public function __construct($rcmail, $type) {
    $this->rcmail = $rcmail;
    $this->type = $type;
    $url = $this->rcmail->config->get('caldav_url', false);
    if(!$url)
      return;
    if ($rcmail->config->get('timezone') === "auto") {
      $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600;
    }
    else {
      $tz = $rcmail->config->get('timezone');
      if($rcmail->config->get('dst_active')) {
        $tz++;
      }
    }
    $this->usertimezone = $tz;
    
    $user = $this->rcmail->config->get('caldav_user','demo');
    $pass = $this->rcmail->config->get('caldav_password',$this->rcmail->encrypt('pass'));
    $auth = $this->rcmail->config->get('caldav_auth','basic');
    $extr = $this->rcmail->config->get('caldav_extr','1');
    
    $account = array(
      'user' => $user,
      'pass' => $pass,
      'url'  => $url,
      'auth' => $auth,
      'extr' => $extr,
    );
    $this->account = $account;
    $this->connect($account['url'], $account['user'], $account['pass'], $account['auth']);
    $public_caldavs = $rcmail->config->get('public_caldavs', array());
    foreach($public_caldavs as $category => $caldav){
      $public_caldavs[$category]['pass'] = $rcmail->encrypt($caldav['pass']);
    }
    $caldavs = array_merge($rcmail->config->get('caldavs', array()), $public_caldavs);
    $this->caldavs = $caldavs;
  }
  
  private function connect($url, $user, $pass, $auth = 'basic'){
    if($this->type == 'caldav'){
      $rcmail = $this->rcmail;
      $googleuser = $rcmail->config->get('googleuser', false);
      $googlepass= $rcmail->config->get('googlepass', false);
      if($user == '%gu'){
        if($googleuser){
          $user = str_replace('%gu', $googleuser, $user);
        }
      }
      else if($user == '%u'){
        $user = str_replace('%u', $_SESSION['username'], $user);
      }
      $pass_clear = false;
      if($pass == '%gp'){
        if($googlepass){
          $pass = str_replace('%gp', $rcmail->decrypt($googlepass), $pass);
          $pass_clear = true;
        }
      }
      else if($pass == '%p'){
        $pass = str_replace('%p', $rcmail->decrypt($_SESSION['password']), $pass);
        $pass_clear = true;
      }
      if(!$pass_clear){
        $pass = $this->rcmail->decrypt($pass);
        if($pass == '%gp'){
          if($googlepass){
            $pass = str_replace('%gp', $rcmail->decrypt($googlepass), $pass);
          }
        }
        else if($pass == '%p'){
          $pass = str_replace('%p', $rcmail->decrypt($_SESSION['password']), $pass);
      }
      }
      if(strpos($url,'%gu'))
        $url = str_replace('%gu', $googleuser, $url);
      if(strpos($url,'%u'))
        $url = str_replace('%u', $_SESSION['username'], $url);
      $url = slashify($url);
      if($url == "/"){
        $url = 'https://www.google.com/calendar/dav/john.doe@gmail.com/events/';
      }
      if(!$auth)
        $auth = 'basic';
      if($user != '%u' && $user != '%gu' && $pass !='%p' && $pass != '%gp' && strpos($url, '%u') === false && strpos($url, '%gu') === false)
        $ret = $this->caldav = new CalDAVClient(trim($url), trim($user), trim($pass), trim($auth));
      if(!$ret){
        write_log('calendar',"CalDAV Invalid URL: '".$account['url']."'");
        $ret = $this->caldav = new CalDAVClient('', '', '', '');
      }
    }
  }
  
  private function q($str) {
    return $this->rcmail->db->quoteIdentifier($str);
  }
  
  private function table($str) {
    return $this->rcmail->db->quoteIdentifier(get_table_name($str));
  }
  
  private function generateId(){
    $letters="abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    for($i = 0; $i<=6; $i++){
      $rndStr .= $letters[(rand() % strlen($letters))];
    }
    $icalStamp = date("Ymd\THis\Z");
    return $icalStamp."-".$rndStr;
  }

  private function syncCalDAV($events,$request='PUT',$categories=false){
    $return = false;
    if($this->type == 'caldav'){
      $event = $this->utils->exportEvents(0,0,$events);
      $caldav_props = unserialize($events[0]['caldav']);
      $this->connect($this->account['url'], $this->account['user'], $this->account['pass'], $this->account['auth']);
      if($categories){
        $caldavs = $this->caldavs;
        if(!empty($caldavs[$categories])){
          $this->connect($caldavs[$categories]['url'],$caldavs[$categories]['user'],$caldavs[$categories]['pass'],$caldavs[$categories]['auth']);
        }
      }
      if($request == 'DELETE'){
        $this->caldav->DoDELETERequest($caldav_props[0] . '.ics');
        $return = true;
      }
      else if($request == 'PUT'){
        $ret = $this->caldav->DoPUTRequest($caldav_props[0].'.ics', $event, $caldav_props[1]);
        if($ret){
          if(stripos($ret,'HTTP/1.1') !== false){
            if(stripos($ret,'HTTP/1.1 204') !== false || $caldav_props[2] == '*'){
              $ret = $this->caldav->GetEntryByHref($caldav_props[0]);
              $count = preg_match('/Etag:(.*?)\n/i',$ret,$temp);
              if($count > 0){
                $etag = trim(str_replace('"','',$temp[1]));
                $caldav_props = array($caldav_props[0], $etag, $caldav_props[2]);
              }
            }
            else{
              $caldav_props = null;
            }
          }
          else{
            $caldav_props = array($caldav_props[0], $ret, $caldav_props[2]);
          }
          if(is_array($caldav_props)){
            $query = $this->rcmail->db->query(
              "UPDATE " . $this->table('events') . " 
              SET ".$this->q('caldav')."=?
              WHERE ".$this->q('uid')."=?
              AND ".$this->q('user_id')."=?",
              serialize($caldav_props),
              $events[0]['uid'],
              $this->rcmail->user->ID
            );
            $return = true;
          }
          else{
            $this->replicateEvents($events[0]['start'],$events[0]['start'] + 86400,$events[0]['categories']);
          }
        }
      }
    }
    else{
      $return = true;
    }
    return $return;
  }
  
  public function searchEvents($str) {
    if (!empty($this->rcmail->user->ID)) {
      $cal_searchset = $this->rcmail->config->get('cal_searchset', array('summary'));
      $all_day = '';
      $str = str_replace(array('\\'),array(''),$str);
      $str = str_replace('%','\%',$str);
      $str = str_replace('*','%',$str);
      $method = 'LIKE';
      $wildcard = '%';
      if(strlen($str) > 2){
        $sql_filter = " AND (" . $this->rcmail->db->ilike($cal_searchset[0], $wildcard.$str.$wildcard);
        if(count($cal_searchset) > 1){
          for($i=1;$i<count($cal_searchset);$i++){
            if($cal_searchset[$i] == 'all_day'){
              $all_day .= " AND " . $this->q('all_day') . "=1";
            }
            else{
              $sql_filter .= " OR " . $this->rcmail->db->ilike($cal_searchset[$i], $wildcard.$str.$wildcard);
            }
          }
          $sql_filter .= ")";
        }
        $sql_result = $this->rcmail->db->query(
          "SELECT * FROM ".$this->table('events').
          " WHERE " . $this->q('del') . "<>1".
          " AND " . $this->q('user_id') . "=?".
          $all_day.
          $sql_filter.
          " ORDER BY " . $this->q('summary'),
          $this->rcmail->user->ID);
        while ($sql_result && ($sql_arr = $this->rcmail->db->fetch_assoc($sql_result))) {
          $results[$sql_arr['event_id']] = $sql_arr;
        }
        if(!$_SESSION['removelayers']){
          $events_table = $this->rcmail->config->get('db_table_events', 'events');
          $this->rcmail->config->set('db_table_events',$this->rcmail->config->get('db_table_events_cache', 'events_cache'));
          $sql_result = $this->rcmail->db->query(
            "SELECT * FROM ".$this->table('events').
            " WHERE " . $this->q('del') . "<>1".
            " AND " . $this->q('user_id') . "=?".
            $all_day.
            $sql_filter.
            " ORDER BY " . $this->q('summary'),
            $this->rcmail->user->ID);
          while ($sql_result && ($sql_arr = $this->rcmail->db->fetch_assoc($sql_result))) {
            $results['c'.$sql_arr['event_id']] = $sql_arr;
          }
          $feeds = array_merge((array)$this->rcmail->config->get('public_calendarfeeds',array()),(array)$this->rcmail->config->get('calendarfeeds',array()));
          foreach($feeds as $url => $category){
            $arr = parse_url($url);
            if($arr['path'] == './'){
              if($_SERVER['HTTPS'])
                $https = 's';
              else
                $https = '';
              $url = 'http' . $https . "://" . $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT'] . substr($url,1);
              $arr = parse_url($url);
            }
            $con = '?';
            if(strstr($url,'?'))
              $con = '&';
            if(stripos($arr['query'],'plugin.calendar_showlayer') && strtolower($arr['host']) == strtolower($_SERVER['HTTP_HOST'])){
              $user_id = $this->rcmail->user->ID;
              $temparr = explode('&',$arr['query']);
              foreach($temparr as $key => $val){
                if(strpos($val,'_userid=') === 0){
                  $temp = explode("=",$val);
                  $remote_user = $temp[1];
                  $this->rcmail->user->ID = $remote_user;
                  $sql_result = $this->rcmail->db->query(
                    "SELECT * FROM ".$this->table('events').
                    " WHERE " . $this->q('del') . "<>1".
                    " AND " . $this->q('user_id') . "=?".
                    $all_day.
                    $sql_filter.
                    " ORDER BY " . $this->q('summary'),
                    $this->rcmail->user->ID);
                  while ($sql_result && ($sql_arr = $this->rcmail->db->fetch_assoc($sql_result))) {
                    $results['c'.$sql_arr['event_id']] = $sql_arr;
                  }
                  $this->rcmail->user->ID = $user_id;
                  break;
                }
              }
            }
          }
          $this->rcmail->config->set('db_table_events', $events_table);
          $arr = $this->utils->getUser($remote_user);
          $prefs = unserialize($arr['preferences']);
          $events_table = $this->rcmail->config->get('db_table_events', 'events');
          $db_table = str_replace('_caldav','',$events_table);
          $map = $this->rcmail->config->get('backend_db_table_map',array());
          if($prefs['backend'] == 'caldav'){
            $db_table .= $map['caldav'];
          }
          else{
            $db_table .= $map['database'];
          }
          foreach($feeds as $url => $category){
            $arr = parse_url($url);
            $con = '?';
            if(strstr($url,'?'))
              $con = '&';
            if(stripos($arr['query'],'plugin.calendar_showlayer') && strtolower($arr['host']) == strtolower($_SERVER['HTTP_HOST'])){
              $user_id = $this->rcmail->user->ID;
              $temparr = explode('&',$arr['query']);
              foreach($temparr as $key => $val){
                if(strpos($val,'_userid=') === 0){
                  $temp = explode("=",$val);
                  $remote_user = $temp[1];
                  $this->rcmail->user->ID = $remote_user;
                  $sql_result = $this->rcmail->db->query(
                    "SELECT * FROM ".$db_table.
                    " WHERE " . $this->q('del') . "<>1".
                    " AND " . $this->q('user_id') . "=?".
                    $all_day.
                    $sql_filter.
                    " ORDER BY " . $this->q('summary'),
                    $this->rcmail->user->ID);
                  while ($sql_result && ($sql_arr = $this->rcmail->db->fetch_assoc($sql_result))) {
                    $results[$sql_arr['event_id']] = $sql_arr;
                  }
                  $this->rcmail->user->ID = $user_id;
                  break;
                }
              }
            }
          }
          $this->rcmail->config->set('db_table_events', $events_table);
        }
      }
    }
    if(is_array($results)){
      $ret = $results;
    }
    else
      $ret = false;
    return $ret;
  }
  
  public function scheduleReminders($event){
    if (!empty($this->rcmail->user->ID) && $event) {
      $map = $this->rcmail->config->get('backend_db_table_map');
      if(stripos($this->table('events'),$map['caldav'])){
        $col = 'caldav';
      }
      else{
        if($this->rcmail->config->get('db_table_events_cache') == get_table_name('events')){
          $col = 'cache';
        }
        else{
          $col = 'events';
        }
      }
      $query = $this->rcmail->db->query(
        "DELETE FROM " . $this->table('reminders') . "
        WHERE ".$this->q($col)."=? AND ".$this->q('user_id')."=?",
        $event['event_id'],
        $this->rcmail->user->ID
      );
      if($event['del'] == '1'){
        return;
      }
      if($event['recurring'] != 0){
        $events = $this->getEvents(
          time() - 86400,
          time() + 86400,
          array(),
          false,
          array('uid',$event['uid'])
        );
      }
      else{
        $events[0] = $event;
      }
      if(is_array($events)){
        $start = time();
        $props = array();
        $props['user_id'] = $this->rcmail->user->ID;
        if($_SESSION['username'])
          $props['username'] = $_SESSION['username'];
        else{
          $user = $this->utils->getUser($props['user_id']);
          $props['username'] = $user['username'];
        }
        $props['user_data'] = $this->rcmail->user->data;
        $props['lang'] = $this->rcmail->user->language;
        foreach($events as $key => $event){
          if($event['reminderservice'] != '0'){
            $next = $event['start'] - $event['reminder'];
            $schedule = false;
            if($event['reminderservice'] == 'popup'){
              if(!$event['remindersent'])
                $schedule = true; // schedule missed reminders
              if($next > $start)
                $schedule = true; // always schedule future reminders
            }
            else if($event['reminderservice'] == 'email'){
              if($next > $start && $event['clone'])
                $schedule = true; // schedule future reminders for recurring events
              if(!$event['remindersent'] && !$event['clone'] && $next > $start)
                $schedule = true; // schedule future reminders for single events
              if(!empty($this->caldavs[$event['categories']])){
                if($this->caldavs[$event['categories']]['extr'] == 'external')
                  $schedule = false;
              }
              else{
                if($this->account['extr'] == 'external')
                  $schedule = false;
              }
            }
            if($schedule){
              $mapped = $this->utils->eventArrayMap($event);
              $props['event'] = $mapped;
              $props['ics'] = $this->utils->exportEvents(0,0,array(0=>$event), false, true);
              $query = $this->rcmail->db->query(
                "INSERT INTO " . $this->table('reminders') . "
                (".
                  $this->q('user_id').", ".
                  $this->q($col).", ".
                  $this->q('type').", ".
                  $this->q('props').", ".
                  $this->q('runtime').")
                VALUES (?, ?, ?, ?, ?)",
                $this->rcmail->user->ID,
                $event['event_id'],
                $event['reminderservice'],
                serialize($props),
                $next
              );
              if($event['reminderservice'] == 'email'){
                break;
              }
            }
          }
        }
      }
    }
  }

  public function newEvent(
    $start,
    $end,
    $summary,
    $description,
    $location,
    $categories,
    $allDay,
    $recur,
    $expires,
    $occurrences,
    $byday=false,
    $bymonth=false,
    $bymonthday=false,
    $recurrence_id=false,
    $exdates=false,
    $reminderbefore=false,
    $remindertype=false,
    $remindermailto=false,
    $uid=false,
    $client=false
  ) {
    if (!empty($this->rcmail->user->ID)) {
      $srecur = (string) $recur;
      $rr = substr($recur,0,1);
      $recur = substr($recur,1);
      // PostgreSQL sets 'f' instead of '0' for false, which messes up our conditionals!
      if ($byday==false) $byday='0';
      if ($bymonth==false) $bymonth='0';
      if ($bymonthday==false) $bymonthday='0';
      if ($recurrence_id==false) $recurrence_id='0';
      if ($exdates==false) $exdates='0';
      if ($recur==false) $recur='0';
      if ($reminderbefore==false) $reminderbefore='0';
      if ($remindertype==false) $remindertype='0';
      if ($remindermailto==false) $remindermailto='0';
      if ($uid==false) $uid='0';
      if ($client==false) $client='0';
      //
      if(!$uid)
        $uid = $this->generateId();
      $exists = $this->getEventByUID($uid, $recurrence_id);
      if(is_array($exists)){
        if($exists['del'] != '0'){
          $query = $this->rcmail->db->query(
            "UPDATE " . $this->table('events') . " 
              SET ".$this->q('del')."=?
              WHERE ".$this->q('event_id')."=?
              AND ".$this->q('user_id')."=?",
            0,
            $exists['event_id'],
            $this->rcmail->user->ID
          );
        }
        if(
          $start != $exists['start'] ||
          $end != $exists['end'] ||
          $summary != $exists['summary'] ||
          $description != $exists['description'] ||
          $location != $exists['location'] ||
          $categories != $exists['categories'] ||
          $recurrence_id != $exists['recurrence_id'] ||
          $exdates != $exists['exdates'] ||
          $reminderbefore != $exists['reminder'] ||
          $remindertype != $exists['reminderservice'] ||
          $remindermailto != $exists['remindermailto'] ||
          $srecur != $exists['rr'] . $exists['recurring'] ||
          $expires != $exists['expires'] ||
          $occurences != $exists['occurences'] ||
          $byday != $exists['byday'] ||
          $bymonth != $exists['bymonth'] ||
          $bymonthday != $exists['bymonthday'] ||
          $allDay != $exists['all_day']
        ){
          $ret = $this->editEvent(
            $exists['event_id'],
            $start,
            $end,
            $summary,
            $description,
            $location,
            $categories,
            $srecur,
            $expires,
            $occurrences,
            $byday,
            $bymonth,
            $bymonthday,
            $recurrence_id,
            $exdates,
            $reminderbefore,
            $remindertype,
            $remindermailto,
            $allDay
          );
        }
        else{
          $ret = $exists;
        }
        $ret['edit'] = 1;
        return $ret;
      }
      $query = $this->rcmail->db->query(
        "INSERT INTO " . $this->table('events') . "
         (".
           $this->q('user_id').", ".
           $this->q('start').", ".
           $this->q('end').", ".
           $this->q('summary').", ".
           $this->q('description').", ".
           $this->q('location').", ".
           $this->q('categories').", ".
           $this->q('all_day').", ".
           $this->q('recurring').", ".
           $this->q('rr').", ".
           $this->q('expires').", ".
           $this->q('occurrences').", ".
           $this->q('byday').", ".
           $this->q('bymonth').", ".
           $this->q('bymonthday').", ".
           $this->q('reminder').", ".
           $this->q('reminderservice').", ".
           $this->q('remindermailto').", ".
           $this->q('remindersent').", ".
           $this->q('recurrence_id').", ".
           $this->q('exdates').", ".
           $this->q('uid').", ".
           $this->q('client').", ".
           $this->q('caldav').", ".
           $this->q('timestamp').")
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
        $this->rcmail->user->ID,
        $start,
        $end,
        $summary,
        $description,
        $location,
        $categories,
        $allDay,
        $recur,
        $rr,
        $expires,
        $occurrences,
        $byday,
        $bymonth,
        $bymonthday,
        $reminderbefore,
        $remindertype,
        $remindermailto,
        0,
        $recurrence_id,
        $exdates,
        $uid,
        $client,
        serialize(array(0 => $uid, 1 => false, 2 => '*')),
        date('Y-m-d H:i:s', time())
      );
      $id = $this->rcmail->db->insert_id($this->rcmail->config->get('db_sequencs_events'));
      $events = $this->getEventsByUID($uid);
      //find me: investigate why this is here ...
      if($this->rcmail->action == 'plugin.newEvent' || $this->rcmail->action == 'plugin.editEvent' || $this->rcmail->action == 'plugin.saveical' || $this->rcmail->action == 'plugin.calendar_upload' || $this->rcmail->action == 'plugin.moveEvent'|| $this->rcmail->action == 'plugin.resizeEvent' || $this->rcmail->action == 'plugin.calendar_showlayer'){
        $events[0]['sync'] = $this->syncCalDAV($events,'PUT',$categories);
      }
      $this->calModified($this->rcmail->user->ID);
      $this->scheduleReminders($events[0]);
      return $events[0];
    }
  }

  public function editEvent(
    $id,
    $start,
    $end,
    $summary,
    $description,
    $location,
    $categories,
    $recur,
    $expires,
    $occurrences,
    $byday=false,
    $bymonth=false,
    $bymonthday=false,
    $recurrence_id=false,
    $exdates=false,
    $reminderbefore=false,
    $remindertype=false,
    $remindermailto=false,
    $allDay=false,
    $old_categories=false
  ) {
    if (!empty($this->rcmail->user->ID)) {
      $srecur = $recur;
      $rr = substr($recur,0,1);
      $recur = substr($recur,1);
      // PostgreSQL sets 'f' instead of '0' for false, which messes up our conditionals! 
      if ($byday==false) $byday='0';
      if ($bymonth==false) $bymonth='0';
      if ($bymonthday==false) $bymonthday='0';
      if ($recurrence_id==false) $recurrence_id='0';
      if ($exdates==false) $exdates='0';
      if ($reminderbefore==false) $reminderbefore='0';
      if ($remindertype==false) $remindertype='0';
      if ($remindermailto==false) $remindermailto='0';
      if ($allday==false) $allday='0';
      if ($recurrence_id==false) $recurrence_id='0';
      if ($recur==false) $recur='0';
      if ($uid==false) $uid='0';
      if ($client==false) $client='0';
      //
      $query = $this->rcmail->db->query(
        "UPDATE " . $this->table('events') . " 
         SET ".
           $this->q('summary')."=?, ".
           $this->q('start')."=?, ".
           $this->q('end')."=?, ".
           $this->q('description')."=?, ".
           $this->q('location')."=?, ".
           $this->q('categories')."=?, ".
           $this->q('rr')."=?, ".
           $this->q('recurring')."=?, ".
           $this->q('expires')."=?, ".
           $this->q('occurrences')."=?, ".
           $this->q('byday')."=?, ".
           $this->q('bymonth')."=?, ".
           $this->q('bymonthday')."=?, ".
           $this->q('recurrence_id')."=?, ".
           $this->q('exdates')."=?, ".
           $this->q('all_day')."=?, ".
           $this->q('reminder')."=?, ".
           $this->q('reminderservice')."=?, ".
           $this->q('remindermailto')."=?, ".
           $this->q('remindersent')."=?, ".
           $this->q('timestamp')."=?, ".
           $this->q('del')."=?, ".
           $this->q('notified')."=?
         WHERE ".$this->q('event_id')."=?
         AND ".$this->q('user_id')."=?",
        $summary,
        $start,
        $end,
        $description,
        $location,
        $categories,
        $rr,
        $recur,
        $expires,
        $occurrences,
        $byday,
        $bymonth,
        $bymonthday,
        $recurrence_id,
        $exdates,
        $allDay,
        $reminderbefore,
        $remindertype,
        $remindermailto,
        0,
        date('Y-m-d H:i:s', time()),
        0,
        0,
        $id,
        $this->rcmail->user->ID
      );
      $this->calModified($this->rcmail->user->ID);
      $event = $this->getEvent($id);
      // find me: get uid from GUI and calendar.php
      $events = $this->getEventsByUID($event['uid']);
      $this->scheduleReminders($events[0]);
      if($this->type == 'caldav'){
        //find me: investigate why this is here ...
        if($this->rcmail->action == 'plugin.editEvent' || $this->rcmail->action == 'plugin.newEvent' || $this->rcmail->action == 'plugin.calendar_upload' || $this->rcmail->action == 'plugin.calendar_showlayer'){
          $caldavs = $this->caldavs;
          if($categories != $old_categories){
            if(!empty($caldavs[$old_categories]) || !empty($caldavs[$categories])){
              $sync = $this->newEvent($start, $end, $summary, $description, $location, $categories, $allDay, $srecur, $expires, $occurrences, $byday, $bymonth, $bymonthday);
              if($sync)
                $events[0] = $this->removeEvent($id, $old_categories);
            }
            else{
              $events[0]['sync'] = $this->syncCalDAV($events);
            }
          }
          else{
            $events[0]['sync'] = $this->syncCalDAV($events);
          }
        }
      }
      else{
        $events[0]['sync'] = true;
      }
      return $events[0];
    }
  }

  public function moveEvent(
    $id,
    $start,
    $end,
    $allDay,
    $reminder
  ) {
    if (!empty($this->rcmail->user->ID)) {
      $query = $this->rcmail->db->query(
        "UPDATE " . $this->table('events') . " 
         SET ".$this->q('start')."=?, ".
               $this->q('end')."=?, ".
               $this->q('all_day')."=?, ".
               $this->q('remindersent')."=?, ".
               $this->q('timestamp')."=?, ".
               $this->q('notified')."=?
         WHERE ".$this->q('event_id')."=?
         AND ".$this->q('user_id')."=?",
        $start,
        $end,
        $allDay,
        0,
        date('Y-m-d H:i:s', time()),
        0,
        $id,
        $this->rcmail->user->ID
      );
      $this->calModified($this->rcmail->user->ID);
      $event = $this->getEvent($id);
      // find me: get uid from GUI and calendar.php
      $events = $this->getEventsByUID($event['uid']);
      if($this->rcmail->action == 'plugin.moveEvent'){
        $events[0]['sync'] = $this->syncCalDAV($events,'PUT',$events[0]['categories']);
      }
      $this->scheduleReminders($events[0]);
      return $events[0];
    }
  }
  
  public function resizeEvent(
    $id,
    $start,
    $end,
    $reminder
  ) {
    if (!empty($this->rcmail->user->ID)) {
      $query = $this->rcmail->db->query(
        "UPDATE " . $this->table('events') . " 
         SET ".$this->q('start')."=?, ".
               $this->q('end')."=?, ".
               $this->q('remindersent')."=?, ".
               $this->q('timestamp')."=?, ".
               $this->q('notified')."=?
         WHERE ".$this->q('event_id')."=?
         AND ".$this->q('user_id')."=?",
        $start,
        $end,
        0,
        date('Y-m-d H:i:s', time()),
        0,
        $id,
        $this->rcmail->user->ID
      );
      $this->calModified($this->rcmail->user->ID);
      $event = $this->getEvent($id);
      // find me: get uid from GUI and calendar.php
      $events = $this->getEventsByUID($event['uid']);
      if($this->rcmail->action == 'plugin.resizeEvent'){
        $events[0]['sync'] = $this->syncCalDAV($events,'PUT',$events[0]['categories']);
      }
      $this->scheduleReminders($events[0]);
      return $events[0];
    }
  }

  public function removeEvent($id, $categories=false) {
    if (!empty($this->rcmail->user->ID)) {
      $query = $this->rcmail->db->query(
        "UPDATE " . $this->table('events') . " 
         SET ".$this->q('del')."=?, ".$this->q('timestamp')."=?
         WHERE ".$this->q('event_id')."=?
         AND ".$this->q('user_id')."=?",
        1,
        date('Y-m-d H:i:s', time()),
        $id,
        $this->rcmail->user->ID
      );
      $event = $this->getEvent($id);
      // find me: get uid from GUI and calendar.php
      $events = $this->getEventsByUID($event['uid']);
      //find me: investigate why this is here ...
      if($this->rcmail->action == 'plugin.editEvent' || $this->rcmail->action == 'plugin.removeEvent' || $this->rcmail->action == 'plugin.calendar_showlayer'){
        $caldav_props = unserialize($event['caldav']);
        $caldavs = $this->caldavs;
        if($event['recurrence_id'] != 0){
          $initialevent = $this->getEventByUID($event['uid']);
          if($initialevent['exdates']){
            $exdates = (array) @unserialize($initialevent['exdates']);
            $exdates[] = (int) $event['start'];
          }
          else{
            $exdates = array((int) $event['start']);
          }
          $initialevent['exdates'] = serialize($exdates);
          $initialevent['recurrence_id'] = null;
          $events[0]['sync'] = $this->syncCalDAV(array(0 => $initialevent),'PUT',$initialevent['categories']);
        }
        else{
          if(!$categories)
            $categories = $events[0]['categories'];
          $events[0]['sync'] = $this->syncCalDAV($events,'DELETE',$categories);
        }
      }
      $this->calModified($this->rcmail->user->ID);
      $this->scheduleReminders($events[0]);
      return $events[0];
    }
  }
  
  public function purgeEvents() {
   if (!empty($this->rcmail->user->ID)) {
      $query = $this->rcmail->db->query(
        "DELETE FROM " . $this->table('events') . "
         WHERE ".$this->q('user_id')."=?
         AND ".$this->q('del')."<>?",
         $this->rcmail->user->ID,
         0
      );
    } 
  }
  
  public function removeDuplicate($id) {
    if (!empty($this->rcmail->user->ID)) {
      $query = $this->rcmail->db->query(
        "DELETE FROM " . $this->table('events') . "
         WHERE ".$this->q('user_id')."=? AND ".$this->q('event_id')."=?",
         $this->rcmail->user->ID,
         $id
      );
      $this->calModified($this->rcmail->user->ID, true);
    }  
  }
  
  public function truncateEvents($mode=0) {
    if (!empty($this->rcmail->user->ID)) {
      if($mode == 0){
        $query = $this->rcmail->db->query(
          "DELETE FROM " . $this->table('events') . "
          WHERE ".$this->q('user_id')."=? AND ".$this->q('del')."=?",
          $this->rcmail->user->ID,
          1
        );
        $this->calModified($this->rcmail->user->ID, true);
      }
      else if($mode == 1){
        $query = $this->rcmail->db->query(
          "UPDATE " . $this->table('events') . " 
          SET ".$this->q('del')."=?, ".$this->q('timestamp')."=?
          WHERE ".$this->q('user_id')."=?",
          2,
          date('Y-m-d H:i:s', time()),
          $this->rcmail->user->ID
        );
      }
      else if($mode == 2){
        $query = $this->rcmail->db->query(
          "UPDATE " . $this->table('events') . " 
          SET ".$this->q('del')."=?, ".$this->q('timestamp')."=?
          WHERE ".$this->q('user_id')."=?",
          0,
          date('Y-m-d H:i:s', time()),
          $this->rcmail->user->ID
        );
      }
      else if($mode == 3){
        $query = $this->rcmail->db->query(
          "DELETE FROM " . $this->table('events') . "
          WHERE ".$this->q('user_id')."=?",
          $this->rcmail->user->ID
        );
      }
    }
  }
  
  public function exportEvents($categories=false){
    if($this->type == 'caldav'){
      if($categories){
        $caldavs = $this->caldavs;
        if(is_array($caldavs[$categories])){
          $this->connect($caldavs[$categories]['url'],$caldavs[$categories]['user'],$caldavs[$categories]['pass'],$caldavs[$categories]['auth']);
        }
      }
      $response = trim($this->caldav->DoRequest());
      $temparr = explode("\r\n\r\n", $response,2);
      return $temparr[1];
    }
  }
  
  private function _getEvents(
    $estart,
    $eend,
    $category=false,
    $type='events'
  ) {
    if($this->type == 'caldav'){
      $events = array();
      $map = $this->rcmail->config->get('backend_db_table_map');
      if(stripos($this->table('events'),$map['caldav'])){
        $startYear  = date('Y',$estart);
        $startMonth = date('m',$estart);
        $startDay   = date('d',$estart);
        $endYear    = date('Y',$eend  );
        $endMonth   = date('m',$eend  );
        $endDay     = date('d',$eend  );
        $caldavs = $this->caldavs;
        if(!$category || ($category && !$caldavs[$category])){
          if($type == 'events'){
            $events = (array) $this->caldav->GetEvents($startYear.$startMonth.$startDay."T000000Z",$endYear.$endMonth.$endDay."T000000Z");
          }
          else if($type == 'alarms'){
            // cron login successful ?
            if(method_exists($this->caldav, 'GetEventAlarms')){
              $events = (array) $this->caldav->GetEventAlarms($startYear.$startMonth.$startDay."T000000Z",$endYear.$endMonth.$endDay."T000000Z");
            }
          }
        }
        else{
          if($category && $caldavs[$category]){
            $caldav = $caldavs[$category];
            $this->connect($caldav['url'],$caldav['user'],$caldav['pass'],$caldav['auth']);
            $startYear  = date('Y',$estart);
            $startMonth = date('m',$estart);
            $startDay   = date('d',$estart);
            if($type == 'events'){
              $layers = (array) $this->caldav->GetEvents($startYear.$startMonth.$startDay."T000000Z",$endYear.$endMonth.$endDay."T000000Z");
            }
            else if($type == 'alarms'){
              $layers = (array) $this->caldav->GetEventAlarms($startYear.$startMonth.$startDay."T000000Z",$endYear.$endMonth.$endDay."T000000Z");
            }
            foreach($layers as $key => $layer){
              $layers[$key]['data'] = str_replace("\nCATEGORIES:","\nX-CATEGORIES:",$layers[$key]['data']);
              $insert = "\nCATEGORIES:" . $category;
              $layers[$key]['data'] = str_replace("\nBEGIN:VEVENT", "\nBEGIN:VEVENT" . $insert, $layers[$key]['data']);
            }
            $events = $layers;
          }
        }
        $concat = '';
        foreach($events as $key => $val){
          $insert = "\nX-HREF:".$val['href']."\nX-ETAG:".$val['etag'];
          $val['data'] = str_replace("\nBEGIN:VEVENT", "\nBEGIN:VEVENT" . $insert, $val['data']);
          $concat .= $val['data'];
        }
        if($concat != ''){
          $this->utils->importEvents($concat);
          $query = $this->rcmail->db->query(
            "UPDATE " . $this->table('events') . " 
            SET ".$this->q('timestamp')."=?, ".$this->q('notified')."=?
            WHERE ".$this->q('user_id')."=?",
            '0',
            '1',
            $this->rcmail->user->ID
          );
        }
      }
    }
  }
  
  public function removeReminder(
    $id,
    $event_id,
    $ts
  ) {
    if (!empty($this->rcmail->user->ID)) {
      if(!$id && !$event_id){
        $reminders = (array) $this->getReminders($ts);
        $query = $this->rcmail->db->query(
          "DELETE FROM " . $this->table('reminders') . "
          WHERE " . $this->q('type') . "=? AND ".$this->q('user_id')."=? AND " . $this->q('runtime') . "<?",
          'popup',
          $this->rcmail->user->ID,
          $ts
        );
        foreach($reminders as $reminder){
          $query = $this->rcmail->db->query(
            "UPDATE " .$this->table('events') . " 
            SET ". $this->q('remindersent')."=?
            WHERE ".$this->q('event_id')."=?
            AND ".$this->q('user_id')."=?",
            $ts,
            $reminder['id'],
            $this->rcmail->user->ID
          );
        }
      }
      else{
        $query = $this->rcmail->db->query(
          "DELETE FROM " . $this->table('reminders') . "
          WHERE " . $this->q('reminder_id') . "=? AND ".$this->q('user_id')."=?",
          $id,
          $this->rcmail->user->ID
        );
        $query = $this->rcmail->db->query(
          "UPDATE " .$this->table('events') . " 
          SET ". $this->q('remindersent')."=?
          WHERE ".$this->q('event_id')."=?
          AND ".$this->q('user_id')."=?",
          $ts,
          $event_id,
          $this->rcmail->user->ID
        );
      }
    }
  }
  
  public function getReminders(
    $start,
    $type='popup'
  ) {
    $ret = array();
    if (!empty($this->rcmail->user->ID)) {
      $result = $this->rcmail->db->query(
        "SELECT * FROM " . $this->table('reminders') . " 
         WHERE ".$this->q('user_id')."=? AND ".$this->q('runtime')."<? AND ".$this->q('type')."=?",
         $this->rcmail->user->ID,
         $start,
         $type
      );
      $reminders = array();
      while ($result && ($reminder = $this->rcmail->db->fetch_assoc($result))) {
        $reminders[] = $reminder;
      }
      foreach($reminders as $key => $val){
        $col = 'events';
        if($val['caldav'] != '0')
          $col = 'caldav';
        else if($val['cache'] != '0')
          $col = 'cache';
        $result = $this->rcmail->db->query(
          "SELECT * FROM " . $this->table('events') . " 
          WHERE ".$this->q('user_id')."=? AND ".$this->q('event_id')."=?",
          $this->rcmail->user->ID,
          $val[$col]
        );
        if($result){
          $event = $this->rcmail->db->fetch_assoc($result);
          if($event['recurring'] != '0'){
            $duration = 0;
            if($event['end'] != '0'){
              $duration = (int) $event['end'] - (int) $event['start'];
            }
            $start = (int) $val['runtime'] + (int) $event['reminder'];
            $end = $start + $duration;
            $event['start'] = $start;
            if($event['end'] != '0'){
              $event['end'] = $start + $duration;
            }
          }
          $event['reminder_id'] = $val['reminder_id'];
          $ret[$event['start'] . $event['uid'] . $event['event_id']] = $this->utils->eventArrayMap($event);
        }
      }
    }
    return $ret;
  }
  
  public function replicateEvents(
    $estart,
    $eend,
    $category=false,
    $type='events'
  ){
    if (!empty($this->rcmail->user->ID)) {
      $this->_getEvents($estart, $eend, $category, $type);
    }
  }
  
  public function getEvents(
    $estart,
    $eend,
    $labels=array(),
    $category=false,
    $filter=false,
    $client=false,
    $type='events'
  ) {
    if (!empty($this->rcmail->user->ID)) {
      $mem_limit = (int) ini_get('memory_limit');
      $start = -1;
      $end = $eend + 1;
      if($filter){
        $filterfield = $filter[0];
        $filtercomp = '=?';
        $filterval = $filter[1];
      }
      else{
        $filterfield = 'del';
        $filtercomp = '<>?';
        $filterval = 1;
      }
      $result = $this->rcmail->db->query(
        "SELECT * FROM " . $this->table('events') . " 
         WHERE ".$this->q('user_id')."=? AND ".
                 $this->q('start').">? AND ".
                 $this->q('start')."<? AND ".
                 $this->q($filterfield) . $filtercomp,
         $this->rcmail->user->ID,
         $start,
         $end,
         $filterval
      );
      $events = array();
      while ($result && ($event = $this->rcmail->db->fetch_assoc($result))) {
        if($this->rcmail->action == 'plugin.getEvents'){
          $db_category = $event['categories'];
          if($category){
            if($db_category != $category){
              continue;
            }
          }
          else{
            $caldavs = $this->caldavs;
            if(!empty($caldavs[$db_category])){
              continue;
            }
          }
        }
        // backwards compatibility
        if(empty($event['rr'])){
          switch($event['recurring']){
            case    86400:
            case    86401:
              $event['rr'] = 'd';
              break;
            case   604800:
            case  1209600:
            case  1814400:
            case  2419200:
              $event['rr'] = 'w';
              break;
            case  2592000:
              $event['rr'] = 'm';
              break;
            case 31536000:
              $event['rr'] = 'y';
              break;
          }
        }
        $append = $event['start'];
        $add = true;
        if($event['exdates']){
          $exdates = @unserialize($event['exdates']);
          if(is_array($exdates) && count($exdates) > 0){
            $exdates = $this->utils->array_flatten($exdates);
            $exdates = array_flip($exdates);
            if(isset($exdates[$event['start']])){
              $add = false;
            }
          }
        }
        if($event['recurrence_id']){
          $append = $event['recurrence_id'];
        }
        if($event['start'] >= $estart || $event['end'] >= $estart || $event['recurring'] != 0){
          if($event['start'] >= $estart || $event['end'] >= $estart){
            if($add){
              $events[md5($event['uid'].$append)]=array( 
               'event_id'        => (int)    $event['event_id'],
                'uid'             => (string) $event['uid'], 
                'start'           => (int)    $event['start'], 
                'end'             => (int)    $event['end'],
                'expires'         => (string) $event['expires'],
                'rr'              => (string) $event['rr'],
                'recur'           => (string) $event['recurring'],
                'occurrences'     => (int)    $event['occurrences'],
                'recurrence_id'   => (string) $event['recurrence_id'],
                'summary'         => (string) $event['summary'], 
                'description'     => (string) $event['description'],
                'location'        => (string) $event['location'],
                'categories'      => (string) $event['categories'],
                'all_day'         => (int)    $event['all_day'],
                'timestamp'       => (string) $event['timestamp'],
                'del'             => (int)    $event['del'],
                'notified'        => (int)    $event['notified'],
                'byday'           => (string) $event['byday'],
                'bymonth'         => (string) $event['bymonth'],
                'bymonthday'      => (string) $event['bymonthday'],
                'reminder'        => (int)    $event['reminder'],
                'reminderservice' => (string) $event['reminderservice'],
                'remindermailto'  => (string) $event['remindermailto'],
                'editable'        => true,
                'clone'           => false 
              );
            }
          }
          if($event['recurring'] != 0 && !$client){
            $rrule = $this->utils->rrule($event);
            if($rrule){
              $basedate = date('Ymd\THis',$event['start']);
              $rule = new RRule(new iCalDate($basedate), $rrule);
              do{
                $date = $rule->GetNext();
                if(isset($date)){
                  $clone_date = $date->Render();
                  $clone_start = strtotime($clone_date);
                  $clone_end = strtotime($clone_date) + $event['end'] - $event['start'];
                  $dst_adjust = 0;
                  if(date('I', $event['start']) != date('I', $clone_start)){
                    if(date('I',$event['start']) == 0 && date('I',$clone_start) == 1)
                      $dst_adjust = 3600;
                    else
                      $dst_adjust = -3600;
                  }
                  $clone_start = $clone_start + $dst_adjust;
                  $clone_end = $clone_end + $dst_adjust;
                  if($clone_start > $event['start'] && $clone_start > $estart){
                    if(!isset($exdates[$clone_start])){
                      if($add){
                        $set_clone = (string) $event['start'];
                        $set_clone_end = (string) $event['end'];
                      }
                      else{
                        $set_clone = false;
                        $set_clone_end = false;
                        $add = true;
                      }
                      $event['editable'] = true;
                      $event['recur'] = $event['recurring'];
                      $events[md5($event['uid'].$clone_start)] = array( 
                        'event_id'        => (int) $event['event_id'],
                        'uid'             => (string) $event['uid'],
                        'start'           => (int) $clone_start,
                        'end'             => (int) $clone_end,
                        'expires'         => (string) $event['expires'],
                        'rr'              => (string) $event['rr'],
                        'recur'           => (string) $event['recurring'],
                        'occurrences'     => (int)    $event['occurrences'],
                        'recurrence_id'   => (string) $event['recurrence_id'],
                        'summary'         => (string) $event['summary'], 
                        'description'     => (string) $event['description'],
                        'location'        => (string) $event['location'],
                        'categories'      => (string) $event['categories'],
                        'all_day'         => (int)    $event['all_day'],
                        'timestamp'       => (string) $event['timestamp'],
                        'del'             => (int)    $event['del'],
                        'byday'           => (string) $event['byday'],
                        'bymonth'         => (string) $event['bymonth'],
                        'bymonthday'      => (string) $event['bymonthday'],
                        'reminder'        => (int)    $event['reminder'],
                        'reminderservice' => (string) $event['reminderservice'],
                        'remindermailto'  => (string) $event['remindermailto'],
                        'editable'        => (bool)   $event['editable'],
                        'clone'           => $set_clone,
                        'clone_end'       => $set_clone_end,
                        'caldav'          => (string) $event['caldav'],
                        'notified'        => (int) $event['notified'],
                        'initial'         => (array) $this->utils->eventArrayMap($event)
                      );
                    }
                  }
                }
              }
              while(isset($date) && strtotime($date->Render()) <= $eend);
            }
          }
        }
      }
      $public_caldavs = $this->rcmail->config->get('public_caldavs', array());
      foreach($events as $key => $event){
        $category = $event['categories'];
        if(!empty($public_caldavs[$category]) && $public_caldavs[$category]['readonly']){
          $events[$key]['editable'] = false;
        }
      }
      return $events;
    }
  }
  
  private function calModified($userid=false,$del=false) {
    if($userid && !$del){
      file_put_contents('./plugins/calendar/temp/' . $userid . '.pid', time());
    }
    if($userid && $del){
      @unlink('./plugins/calendar/temp/' . $userid . '.pid');
    }
  }

  public function removeTimestamps() {
    if (!empty($this->rcmail->user->ID)) {
      $query = $this->rcmail->db->query(
        "UPDATE " . $this->table('events') . " 
         SET ".$this->q('timestamp')."=?
         WHERE ".$this->q('user_id')."=?",
        '0',
        $this->rcmail->user->ID
      );
    }
  }
  
  public function getEvent($eventid){
    if (!empty($this->rcmail->user->ID)) {
      $result = $this->rcmail->db->query(
        "SELECT * FROM " . $this->table('events') . " 
         WHERE ".$this->q('user_id')."=? AND ".$this->q('event_id')."=?",
         $this->rcmail->user->ID,
         $eventid
      );
      $event = $this->rcmail->db->fetch_assoc($result);
      if($event){
        $event['recur'] = $event['recurring'];
        return $event;
      }
    }
    return array();
  }
  
  public function getEventByUID($uid, $recurrence_id=0){
    $event = array();
    if (!empty($this->rcmail->user->ID)) {
      $result = $this->rcmail->db->query(
        "SELECT * FROM " . $this->table('events') . " 
         WHERE ".$this->q('user_id')."=? AND ".$this->q('uid')."=? AND ".$this->q('recurrence_id')."=?",
         $this->rcmail->user->ID,
         $uid,
         $recurrence_id
      );
      while ($result && ($event = $this->rcmail->db->fetch_assoc($result))){
        $events[] = $event;
      }
      if(is_array($events) && count($events) > 1)
        write_log('calendar', "ERROR: Event with UID " . $event['uid'] . " is not unique");
      $event = $events[0];
    }
    return $event;
  }
  
  public function getEventsByUID($uid){
    if (!empty($this->rcmail->user->ID)) {
      $result = $this->rcmail->db->query(
        "SELECT * FROM " . $this->table('events') . " 
         WHERE ".$this->q('user_id')."=? AND ".$this->q('uid')."=?",
         $this->rcmail->user->ID,
         $uid
      );
      while ($result && ($event = $this->rcmail->db->fetch_assoc($result))){
        $temparr[] = $event;
      }
    }
    $i = 0;
    $events = array();
    if(is_array($temparr)){
      foreach($temparr as $key => $event){
        if(!$event['recurrence_id']){
          if(!empty($events[0]))
            write_log('calendar', "ERROR: Event with UID " . $event['uid'] . " is not unique");
          $events[0] = $event;
        }
        else{
          $events[$i] = $event;
        }
        $i++;
      }
    }
    return $events;
  }
  
  public function test($str = 'db_table_events') {
    $this->rcmail->db->db_handle->loadModule('Manager');
    if(!PEAR::isError($result = $this->rcmail->db->db_handle->listTableFields($this->rcmail->config->get($str))))
      $ret = $result;
    else
      $ret = array();
    return $ret;
  }
}
?>
