<?php

/**
  * SquirrelMail Calendar Plugin File Backend
  * Copyright (C) 2004-2005 Paul Lesneiwski <pdontthink@angrynerds.com>
  * This program is licensed under GPL. See COPYING for details
  *
  */


include_once(SM_PATH . 'plugins/calendar/functions.php');
include_once(SM_PATH . 'plugins/calendar_file_backend/constants.php');
include_once(SM_PATH . 'plugins/calendar_file_backend/functions.php');



/**
  * Retrieves all events, holidays, and other for the given calendar
  * for all time periods.
  *
  * @param string $calID The ID of the calendar for which to retrieve events
  * @param string $user The user for which events are being retrieved
  *
  * @return array An array of calendar events.  This array is keyed by
  *               event id, where associated values are Event objects
  *
  */
function cal_file_get_all_events_do($calID, $user)
{

   global $EVENT_DIR;


   $event_dir = $EVENT_DIR . '/' . $calID;
   $events = array();


   // get all events
   //
   if (is_dir($event_dir))
      $events = array_merge($events,
                cal_file_get_all_events_in_dir($user, $event_dir));


   return $events;

}



/**
  * Retrieves all one-time events for the given calendar
  * for the given month, including any that overlap
  * into previous/next months.  
  *
  * @param string $calID The ID of the calendar for which to retrieve events
  * @param int $year The year of the month for which to retrieve events
  * @param int $month The month for which to retrieve events
  * @param string $user The user for which events are being retrieved
  *
  * @return array An array of calendar events.  This array is keyed by
  *               event id, where associated values are Event objects
  *
  */
function cal_file_get_events_for_month_do($calID, $year, $month, $user)
{

   global $EVENT_DIR;


   $event_dir = $EVENT_DIR . '/' . $calID;
   $events = array();


   // get events for the given month
   // we assume that missing directory simply means no events exist yet for that month
   //
   $this_month_event_dir = $event_dir . '/' . $year . '/' . $month;
   if (is_dir($this_month_event_dir))
      $events = array_merge($events,
                cal_file_get_all_events_in_dir($user, $this_month_event_dir));


   return $events;

}



/**
  * Retrieves all recurring events for the given calendar.
  *
  * @param string $calID The ID of the calendar for which to retrieve events
  * @param string $user The user for which events are being retrieved
  *
  * @return array An array of calendar events.  This array is keyed by
  *               event id, where associated values are Event objects
  *
  */
function cal_file_get_recurring_events_do($calID, $user)
{

   global $EVENT_DIR;


   $event_dir = $EVENT_DIR . '/' . $calID;
   $events = array();


   // get recurring events
   // we assume that missing directory simply means no recurring events exist yet
   //
   $recurring_event_dir = $event_dir . '/recurring';
   if (is_dir($recurring_event_dir))
      $events = array_merge($events,
                cal_file_get_all_events_in_dir($user, $recurring_event_dir));


   return $events;

}



/**
  * Get all holidays for the given calendar
  *
  * Retrieves all the holidays for the given calendar from the backend
  *
  * @param string $calID The ID of the calendar whose holidays
  *                      are being retrieved
  * @param string $user The user for which to retrieve holidays
  *
  * @return array An array of holidays.  This array is keyed by
  *               holiday id, where associated values are Event objects
  *
  */
function cal_file_get_calendar_holidays_do($calID, $user)
{

   global $EVENT_DIR;


   $event_dir = $EVENT_DIR . '/' . $calID;
   $holidays = array();


   // get holiday events
   // we assume that missing directory simply means no holidays exist yet
   //
   $holiday_dir = $event_dir . '/holidays';
   if (is_dir($holiday_dir))
      $holidays = array_merge($holidays,
                  cal_file_get_all_events_in_dir($user, $holiday_dir));


   return $holidays;

}



/**
  * Retrieves all events in the given directory 
  *
  * Calls itself recursively if need to parse multiple dirs.
  *
  * @param string $user The user for which events are being retrieved
  * @param string $event_dir The directory where the search for events
  *                          should begin
  * @param string $eventID If given, *ONLY* the event with the given
  *                        ID will be returned. (optional)
  *
  * @return array An array of Event objects
  *
  */
function cal_file_get_all_events_in_dir($user, $event_dir, $eventID='')
{

   global $CAL_FILE_EXTENSION;


   if (empty($event_dir))
   {
      global $color;
      plain_error_message('CAL FILE BACKEND ERROR (cal_file_get_all_events_in_dir): no event directory given', $color);
      exit;
   }


   // iterate through each event and pull the right ones
   //
   $eventList = array();
   $DIR = opendir($event_dir);
   while (($dirfile = readdir($DIR)) !== FALSE)
   {

      $file = $event_dir . '/' . $dirfile;


      // call recursively into child directories
      //
      if (is_dir($file) && $dirfile != '.' && $dirfile != '..')
      {
         $childList = cal_file_get_all_events_in_dir($user, $file, $eventID);
         $eventList = array_merge($eventList, $childList);
         continue;
      }


      // all event files are prefaced with "sm_cal_evt"
// NOTE: not true for imported calendar events
      // and end with ".ics"
      //
      if (/*strpos($dirfile, 'sm_cal_evt') !== 0 ||*/ strpos($dirfile, $CAL_FILE_EXTENSION) != strlen($dirfile) - 4)
         continue;


      // check each event for user access
      //
      else if (is_file($file))
      {

         // if we are only looking for one event, skip all others
         //
         if (!empty($eventID) && $dirfile != $eventID . $CAL_FILE_EXTENSION)
            continue;


         $event = getEventFromFile($file);


         // check access
         //
         if (!$event->canRead($user) && !$event->canWrite($user) && !$event->isOwner($user))
            continue;


         // instantiate and add event to return array
         //
         $eventList[$event->getID()] = $event;


         // if we are only looking for one event, we're done!
         //
         if (!empty($eventID))
            break;

      }

   }

   closedir($DIR);
   return $eventList;

}



/**
  * Retrieves event attributes from the given file into
  * an Event object, which it then returns.
  *
  * Note: assumes file exists and is readable!
  *
  * @param string $file The full file path to the desired event
  *                     attributes file.
  *
  * @return object An Event object corresponding to the given file.
  *
  */
function getEventFromFile($file)
{

   // read the file all at once
   //
   $fileContents = file($file);


   return Event::getEventFromICal($fileContents);

}



/**
  * Stores event attributes from the given Event object
  * into the given file.
  *
  * Note: overwrites whatever data might already be in the file!
  *
  * @param object $event The Event object to be written to file
  * @param string $file The full file path to the desired event
  *                     attributes file.
  *
  */
function writeEventToFile($event, $file)
{

   global $color;
   $EVENT_FILE = @fopen($file, 'w');
   if (!$EVENT_FILE)
   {
      @fclose($EVENT_FILE);
      plain_error_message('ERROR IN CALENDAR FILE BACKEND (writeEventToFile): cannot open event file', $color);
      exit;
   }


   //get iCal text
   //
   $iCalText = $event->getICal(TRUE);


   // write event
   //
   if (!fwrite($EVENT_FILE, $iCalText))
   {
      fclose($EVENT_FILE);
      plain_error_message('ERROR IN CALENDAR FILE BACKEND (writeEventToFile): cannot write to event file', $color);
      exit;
   }


   fclose($EVENT_FILE);

}



/**
  * Creates a new event
  *
  * Takes the given event object and inserts it into the
  * backend as a new event with the ID as given in the 
  * event object.
  *
  * @param string $calendarID The ID of the calendar having an event added
  * @param object $event The new event object
  *
  * @return string The ID of the newly created event
  *
  */
function cal_file_create_event_do($calendarID, $event)
{


   global $EVENT_DIR, $CAL_FILE_EXTENSION, $color;
   $calendar_events_dir = $EVENT_DIR . '/' . $calendarID;
   $recurring_event_dir = $calendar_events_dir . '/recurring';
   $task_event_dir = $calendar_events_dir . '/tasks';



   // make sure main event directory is there; create it if not
   //
   if (!is_dir($EVENT_DIR))
      if (!mkdir($EVENT_DIR, 0770))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event directory', $color);
         exit;
      }


   // make sure main calendar events directory, recurring events 
   // directory, task/todo events directory are there; create them if not
   //
   if (!is_dir($calendar_events_dir))
   {
      if (!mkdir($calendar_events_dir, 0770))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create calendar events directory', $color);
         exit;
      }


      if (!mkdir($recurring_event_dir, 0770))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create calendar recurring events directory', $color);
         exit;
      }


      if (!mkdir($task_event_dir, 0770))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create calendar todo/task events directory', $color);
         exit;
      }
   }



   $filename = $event->getID() . $CAL_FILE_EXTENSION;



   // todo/task events are easy - they go in just one place
   //
   if ($event->isTask())
   {

      $eventFile = $task_event_dir . '/' . $filename;


      if (file_exists($eventFile))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
         exit;
      }


      // ready to write the event to disk
      //
      writeEventToFile($event, $eventFile);
      return $event->getID();

   }



   // recurring events (but not recurring todo's) are easy - they go in just one place
   //
   else if ($event->isRecurring())
   {

      $eventFile = $recurring_event_dir . '/' . $filename;


      if (file_exists($eventFile))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
         exit;
      }


      // ready to write the event to disk
      //
      writeEventToFile($event, $eventFile);
      return $event->getID();

   }



   // one-time events are the most difficult...
   // 
   else if ($event->isOneTime())
   {

      list($year, $month, $day) = $event->startDate();
      

      // store it on start day
      //
      if (!is_dir($calendar_events_dir . '/' . $year))
         if (!mkdir($calendar_events_dir . '/' . $year, 0770))
         {
            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event year directory', $color);
            exit;
         }


      if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month))
         if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month, 0770))
         {
            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event month directory', $color);
            exit;
         }


      if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day))
         if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day, 0770))
         {
            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event day directory', $color);
            exit;
         }


      if (file_exists($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day . '/' . $filename))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
         exit;
      }


      // ready to write the event to disk
      //
      writeEventToFile($event, $calendar_events_dir . '/' . $year . '/' . $month . '/' . $day . '/' . $filename);



      // iterate through all the months that this event occurs in
      // and save it on the first of evey month past the month
      // it actually starts in
      //
      $month++;
      if ($month == 13)
      {
         $month = 1;
         $year++;
      }
      while ($event->occursOnDay($year, $month, 1))
      {

         // store event on first of this month
         //
         if (!is_dir($calendar_events_dir . '/' . $year))
            if (!mkdir($calendar_events_dir . '/' . $year, 0770))
            {
               plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event year directory', $color);
               exit;
            }


         if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month))
            if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month, 0770))
            {
               plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event month directory', $color);
               exit;
            }


         if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month . '/1'))
            if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month . '/1', 0770))
            {
               plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event day directory', $color);
               exit;
            }


         if (file_exists($calendar_events_dir . '/' . $year . '/' . $month . '/1/' . $filename))
         {
            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
            exit;
         }


         // ready to write the event to disk
         //
         writeEventToFile($event, $calendar_events_dir . '/' . $year . '/' . $month . '/1/' . $filename);



         $month++;
         if ($month == 13)
         {
            $month = 1;
            $year++;
         }

      }



      return $event->getID();

   }



   else
   {
      plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): bad event type', $color);
      exit;
   }

}



/**
  * Delete event
  *
  * Removes the given event from the given calendar.
  *
  * @param string $calendarID The ID of the calendar whose event is being removed
  * @param string $eventID The ID of the event to be removed
  *
  */
function cal_file_delete_event_do($calendarID, $eventID)
{

   global $EVENT_DIR, $CAL_FILE_EXTENSION, $color;
   $calendar_events_dir = $EVENT_DIR . '/' . $calendarID;
   $recurring_event_dir = $calendar_events_dir . '/recurring';
   $task_event_dir = $calendar_events_dir . '/tasks';
   $event = cal_file_get_event_do($calendarID, $eventID);


   // todo/task events are easy - they are in just one place
   //
   if ($event->isTask())
   {

      $eventFile = $task_event_dir . '/' . $eventID . $CAL_FILE_EXTENSION;


      if (!unlink($eventFile))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
         exit;
      }

   }



   // recurring events (but not recurring todo's) are easy - they are in just one place
   //
   if ($event->isRecurring())
   {

      $eventFile = $recurring_event_dir . '/' . $eventID . $CAL_FILE_EXTENSION;


      if (!unlink($eventFile))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
         exit;
      }

   }



   // one-time events are the most difficult...
   //
   else if ($event->isOneTime())
   {

      list($year, $month, $day) = $event->startDate();


      if (!unlink($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day . '/' . $eventID . $CAL_FILE_EXTENSION))
      {
         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
         exit;
      }


      // iterate through all the months that this event occurs in
      // and remove it from the first of evey month past the month
      // it actually starts in
      //
      $month++;
      if ($month == 13)
      {
         $month = 1;
         $year++;
      }
      while ($event->occursOnDay($year, $month, 1))
      {

         if (!unlink($calendar_events_dir . '/' . $year . '/' . $month . '/1/' . $eventID . $CAL_FILE_EXTENSION))
         {
            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
            exit;
         }


         $month++;
         if ($month == 13)
         {
            $month = 1;
            $year++;
         }

      }

   }



   else
   {
      plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): bad event type', $color);
      exit;
   }

}



/**
  * Updates an event
  *
  * Updates the given event by replacing it in the backend
  * with the given event object.
  *
  * @param string $calendarID The ID of the calendar whose event is being updated
  * @param object $event The updated event object
  *
  */
function cal_file_update_event_do($calendarID, $event)
{

   // just delete and create anew, since we have no easy way to know
   // how many copies of this event we'd need to delete from various
   // directories
   //
   cal_file_delete_event_do($calendarID, $event->getID());
   cal_file_create_event_do($calendarID, $event);

}



/**
  * Get event
  *
  * Retrieves the given event from the backend
  *
  * @param string $calendarID The ID of the calendar whose event is to be retrieved
  * @param string $eventID The ID of the event to be retrieved
  *
  * @return object A Event object corresponding to the desired event or FALSE if not found
  *
  */
function cal_file_get_event_do($calendarID, $eventID)
{

   global $EVENT_DIR, $username;
   $user = $username;


   $event_dir = $EVENT_DIR . '/' . $calendarID;


   if (is_dir($event_dir))
   {
      $events = cal_file_get_all_events_in_dir($user, $event_dir, $eventID);

      if (!empty($events))
         return array_shift($events);
   }


   // not found!
   //
   return FALSE;

   global $color;
   plain_error_message('ERROR IN EVENT FILE BACKEND (cal_file_get_event_do): cannot find event file for calendar ID/event ID ' . $calendarID . ' / ' . $eventID, $color);
   exit;

}



?>
