1<?php
2
3/**
4  * SquirrelMail Calendar Plugin File Backend
5  * Copyright (C) 2004-2005 Paul Lesneiwski <pdontthink@angrynerds.com>
6  * This program is licensed under GPL. See COPYING for details
7  *
8  */
9
10
11include_once(SM_PATH . 'plugins/calendar/functions.php');
12include_once(SM_PATH . 'plugins/calendar_file_backend/constants.php');
13include_once(SM_PATH . 'plugins/calendar_file_backend/functions.php');
14
15
16
17/**
18  * Retrieves all events, holidays, and other for the given calendar
19  * for all time periods.
20  *
21  * @param string $calID The ID of the calendar for which to retrieve events
22  * @param string $user The user for which events are being retrieved
23  *
24  * @return array An array of calendar events.  This array is keyed by
25  *               event id, where associated values are Event objects
26  *
27  */
28function cal_file_get_all_events_do($calID, $user)
29{
30
31   global $EVENT_DIR;
32
33
34   $event_dir = $EVENT_DIR . '/' . $calID;
35   $events = array();
36
37
38   // get all events
39   //
40   if (is_dir($event_dir))
41      $events = array_merge($events,
42                cal_file_get_all_events_in_dir($user, $event_dir));
43
44
45   return $events;
46
47}
48
49
50
51/**
52  * Retrieves all one-time events for the given calendar
53  * for the given month, including any that overlap
54  * into previous/next months.
55  *
56  * @param string $calID The ID of the calendar for which to retrieve events
57  * @param int $year The year of the month for which to retrieve events
58  * @param int $month The month for which to retrieve events
59  * @param string $user The user for which events are being retrieved
60  *
61  * @return array An array of calendar events.  This array is keyed by
62  *               event id, where associated values are Event objects
63  *
64  */
65function cal_file_get_events_for_month_do($calID, $year, $month, $user)
66{
67
68   global $EVENT_DIR;
69
70
71   $event_dir = $EVENT_DIR . '/' . $calID;
72   $events = array();
73
74
75   // get events for the given month
76   // we assume that missing directory simply means no events exist yet for that month
77   //
78   $this_month_event_dir = $event_dir . '/' . $year . '/' . $month;
79   if (is_dir($this_month_event_dir))
80      $events = array_merge($events,
81                cal_file_get_all_events_in_dir($user, $this_month_event_dir));
82
83
84   return $events;
85
86}
87
88
89
90/**
91  * Retrieves all recurring events for the given calendar.
92  *
93  * @param string $calID The ID of the calendar for which to retrieve events
94  * @param string $user The user for which events are being retrieved
95  *
96  * @return array An array of calendar events.  This array is keyed by
97  *               event id, where associated values are Event objects
98  *
99  */
100function cal_file_get_recurring_events_do($calID, $user)
101{
102
103   global $EVENT_DIR;
104
105
106   $event_dir = $EVENT_DIR . '/' . $calID;
107   $events = array();
108
109
110   // get recurring events
111   // we assume that missing directory simply means no recurring events exist yet
112   //
113   $recurring_event_dir = $event_dir . '/recurring';
114   if (is_dir($recurring_event_dir))
115      $events = array_merge($events,
116                cal_file_get_all_events_in_dir($user, $recurring_event_dir));
117
118
119   return $events;
120
121}
122
123
124
125/**
126  * Get all holidays for the given calendar
127  *
128  * Retrieves all the holidays for the given calendar from the backend
129  *
130  * @param string $calID The ID of the calendar whose holidays
131  *                      are being retrieved
132  * @param string $user The user for which to retrieve holidays
133  *
134  * @return array An array of holidays.  This array is keyed by
135  *               holiday id, where associated values are Event objects
136  *
137  */
138function cal_file_get_calendar_holidays_do($calID, $user)
139{
140
141   global $EVENT_DIR;
142
143
144   $event_dir = $EVENT_DIR . '/' . $calID;
145   $holidays = array();
146
147
148   // get holiday events
149   // we assume that missing directory simply means no holidays exist yet
150   //
151   $holiday_dir = $event_dir . '/holidays';
152   if (is_dir($holiday_dir))
153      $holidays = array_merge($holidays,
154                  cal_file_get_all_events_in_dir($user, $holiday_dir));
155
156
157   return $holidays;
158
159}
160
161
162
163/**
164  * Retrieves all events in the given directory
165  *
166  * Calls itself recursively if need to parse multiple dirs.
167  *
168  * @param string $user The user for which events are being retrieved
169  * @param string $event_dir The directory where the search for events
170  *                          should begin
171  * @param string $eventID If given, *ONLY* the event with the given
172  *                        ID will be returned. (optional)
173  *
174  * @return array An array of Event objects
175  *
176  */
177function cal_file_get_all_events_in_dir($user, $event_dir, $eventID='')
178{
179
180   global $CAL_FILE_EXTENSION;
181
182
183   if (empty($event_dir))
184   {
185      global $color;
186      plain_error_message('CAL FILE BACKEND ERROR (cal_file_get_all_events_in_dir): no event directory given', $color);
187      exit;
188   }
189
190
191   // iterate through each event and pull the right ones
192   //
193   $eventList = array();
194   $DIR = opendir($event_dir);
195   while (($dirfile = readdir($DIR)) !== FALSE)
196   {
197
198      $file = $event_dir . '/' . $dirfile;
199
200
201      // call recursively into child directories
202      //
203      if (is_dir($file) && $dirfile != '.' && $dirfile != '..')
204      {
205         $childList = cal_file_get_all_events_in_dir($user, $file, $eventID);
206         $eventList = array_merge($eventList, $childList);
207         continue;
208      }
209
210
211      // all event files are prefaced with "sm_cal_evt"
212// NOTE: not true for imported calendar events
213      // and end with ".ics"
214      //
215      if (/*strpos($dirfile, 'sm_cal_evt') !== 0 ||*/ strpos($dirfile, $CAL_FILE_EXTENSION) != strlen($dirfile) - 4)
216         continue;
217
218
219      // check each event for user access
220      //
221      else if (is_file($file))
222      {
223
224         // if we are only looking for one event, skip all others
225         //
226         if (!empty($eventID) && $dirfile != $eventID . $CAL_FILE_EXTENSION)
227            continue;
228
229
230         $event = getEventFromFile($file);
231
232
233         // check access
234         //
235         if (!$event->canRead($user) && !$event->canWrite($user) && !$event->isOwner($user))
236            continue;
237
238
239         // instantiate and add event to return array
240         //
241         $eventList[$event->getID()] = $event;
242
243
244         // if we are only looking for one event, we're done!
245         //
246         if (!empty($eventID))
247            break;
248
249      }
250
251   }
252
253   closedir($DIR);
254   return $eventList;
255
256}
257
258
259
260/**
261  * Retrieves event attributes from the given file into
262  * an Event object, which it then returns.
263  *
264  * Note: assumes file exists and is readable!
265  *
266  * @param string $file The full file path to the desired event
267  *                     attributes file.
268  *
269  * @return object An Event object corresponding to the given file.
270  *
271  */
272function getEventFromFile($file)
273{
274
275   // read the file all at once
276   //
277   $fileContents = file($file);
278
279
280   return Event::getEventFromICal($fileContents);
281
282}
283
284
285
286/**
287  * Stores event attributes from the given Event object
288  * into the given file.
289  *
290  * Note: overwrites whatever data might already be in the file!
291  *
292  * @param object $event The Event object to be written to file
293  * @param string $file The full file path to the desired event
294  *                     attributes file.
295  *
296  */
297function writeEventToFile($event, $file)
298{
299
300   global $color;
301   $EVENT_FILE = @fopen($file, 'w');
302   if (!$EVENT_FILE)
303   {
304      @fclose($EVENT_FILE);
305      plain_error_message('ERROR IN CALENDAR FILE BACKEND (writeEventToFile): cannot open event file', $color);
306      exit;
307   }
308
309
310   //get iCal text
311   //
312   $iCalText = $event->getICal(TRUE);
313
314
315   // write event
316   //
317   if (!fwrite($EVENT_FILE, $iCalText))
318   {
319      fclose($EVENT_FILE);
320      plain_error_message('ERROR IN CALENDAR FILE BACKEND (writeEventToFile): cannot write to event file', $color);
321      exit;
322   }
323
324
325   fclose($EVENT_FILE);
326
327}
328
329
330
331/**
332  * Creates a new event
333  *
334  * Takes the given event object and inserts it into the
335  * backend as a new event with the ID as given in the
336  * event object.
337  *
338  * @param string $calendarID The ID of the calendar having an event added
339  * @param object $event The new event object
340  *
341  * @return string The ID of the newly created event
342  *
343  */
344function cal_file_create_event_do($calendarID, $event)
345{
346
347
348   global $EVENT_DIR, $CAL_FILE_EXTENSION, $color;
349   $calendar_events_dir = $EVENT_DIR . '/' . $calendarID;
350   $recurring_event_dir = $calendar_events_dir . '/recurring';
351   $task_event_dir = $calendar_events_dir . '/tasks';
352
353
354
355   // make sure main event directory is there; create it if not
356   //
357   if (!is_dir($EVENT_DIR))
358      if (!mkdir($EVENT_DIR, 0770))
359      {
360         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event directory', $color);
361         exit;
362      }
363
364
365   // make sure main calendar events directory, recurring events
366   // directory, task/todo events directory are there; create them if not
367   //
368   if (!is_dir($calendar_events_dir))
369   {
370      if (!mkdir($calendar_events_dir, 0770))
371      {
372         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create calendar events directory', $color);
373         exit;
374      }
375
376
377      if (!mkdir($recurring_event_dir, 0770))
378      {
379         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create calendar recurring events directory', $color);
380         exit;
381      }
382
383
384      if (!mkdir($task_event_dir, 0770))
385      {
386         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create calendar todo/task events directory', $color);
387         exit;
388      }
389   }
390
391
392
393   $filename = $event->getID() . $CAL_FILE_EXTENSION;
394
395
396
397   // todo/task events are easy - they go in just one place
398   //
399   if ($event->isTask())
400   {
401
402      $eventFile = $task_event_dir . '/' . $filename;
403
404
405      if (file_exists($eventFile))
406      {
407         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
408         exit;
409      }
410
411
412      // ready to write the event to disk
413      //
414      writeEventToFile($event, $eventFile);
415      return $event->getID();
416
417   }
418
419
420
421   // recurring events (but not recurring todo's) are easy - they go in just one place
422   //
423   else if ($event->isRecurring())
424   {
425
426      $eventFile = $recurring_event_dir . '/' . $filename;
427
428
429      if (file_exists($eventFile))
430      {
431         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
432         exit;
433      }
434
435
436      // ready to write the event to disk
437      //
438      writeEventToFile($event, $eventFile);
439      return $event->getID();
440
441   }
442
443
444
445   // one-time events are the most difficult...
446   //
447   else if ($event->isOneTime())
448   {
449
450      list($year, $month, $day) = $event->startDate();
451
452
453      // store it on start day
454      //
455      if (!is_dir($calendar_events_dir . '/' . $year))
456         if (!mkdir($calendar_events_dir . '/' . $year, 0770))
457         {
458            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event year directory', $color);
459            exit;
460         }
461
462
463      if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month))
464         if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month, 0770))
465         {
466            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event month directory', $color);
467            exit;
468         }
469
470
471      if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day))
472         if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day, 0770))
473         {
474            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event day directory', $color);
475            exit;
476         }
477
478
479      if (file_exists($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day . '/' . $filename))
480      {
481         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
482         exit;
483      }
484
485
486      // ready to write the event to disk
487      //
488      writeEventToFile($event, $calendar_events_dir . '/' . $year . '/' . $month . '/' . $day . '/' . $filename);
489
490
491
492      // iterate through all the months that this event occurs in
493      // and save it on the first of evey month past the month
494      // it actually starts in
495      //
496      $month++;
497      if ($month == 13)
498      {
499         $month = 1;
500         $year++;
501      }
502      while ($event->occursOnDay($year, $month, 1))
503      {
504
505         // store event on first of this month
506         //
507         if (!is_dir($calendar_events_dir . '/' . $year))
508            if (!mkdir($calendar_events_dir . '/' . $year, 0770))
509            {
510               plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event year directory', $color);
511               exit;
512            }
513
514
515         if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month))
516            if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month, 0770))
517            {
518               plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event month directory', $color);
519               exit;
520            }
521
522
523         if (!is_dir($calendar_events_dir . '/' . $year . '/' . $month . '/1'))
524            if (!mkdir($calendar_events_dir . '/' . $year . '/' . $month . '/1', 0770))
525            {
526               plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): cannot create event day directory', $color);
527               exit;
528            }
529
530
531         if (file_exists($calendar_events_dir . '/' . $year . '/' . $month . '/1/' . $filename))
532         {
533            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): event ' . $filename . ' already exists!', $color);
534            exit;
535         }
536
537
538         // ready to write the event to disk
539         //
540         writeEventToFile($event, $calendar_events_dir . '/' . $year . '/' . $month . '/1/' . $filename);
541
542
543
544         $month++;
545         if ($month == 13)
546         {
547            $month = 1;
548            $year++;
549         }
550
551      }
552
553
554
555      return $event->getID();
556
557   }
558
559
560
561   else
562   {
563      plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_create_event_do): bad event type', $color);
564      exit;
565   }
566
567}
568
569
570
571/**
572  * Delete event
573  *
574  * Removes the given event from the given calendar.
575  *
576  * @param string $calendarID The ID of the calendar whose event is being removed
577  * @param string $eventID The ID of the event to be removed
578  *
579  */
580function cal_file_delete_event_do($calendarID, $eventID)
581{
582
583   global $EVENT_DIR, $CAL_FILE_EXTENSION, $color;
584   $calendar_events_dir = $EVENT_DIR . '/' . $calendarID;
585   $recurring_event_dir = $calendar_events_dir . '/recurring';
586   $task_event_dir = $calendar_events_dir . '/tasks';
587   $event = cal_file_get_event_do($calendarID, $eventID);
588
589
590   // todo/task events are easy - they are in just one place
591   //
592   if ($event->isTask())
593   {
594
595      $eventFile = $task_event_dir . '/' . $eventID . $CAL_FILE_EXTENSION;
596
597
598      if (!unlink($eventFile))
599      {
600         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
601         exit;
602      }
603
604   }
605
606
607
608   // recurring events (but not recurring todo's) are easy - they are in just one place
609   //
610   if ($event->isRecurring())
611   {
612
613      $eventFile = $recurring_event_dir . '/' . $eventID . $CAL_FILE_EXTENSION;
614
615
616      if (!unlink($eventFile))
617      {
618         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
619         exit;
620      }
621
622   }
623
624
625
626   // one-time events are the most difficult...
627   //
628   else if ($event->isOneTime())
629   {
630
631      list($year, $month, $day) = $event->startDate();
632
633
634      if (!unlink($calendar_events_dir . '/' . $year . '/' . $month . '/' . $day . '/' . $eventID . $CAL_FILE_EXTENSION))
635      {
636         plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
637         exit;
638      }
639
640
641      // iterate through all the months that this event occurs in
642      // and remove it from the first of evey month past the month
643      // it actually starts in
644      //
645      $month++;
646      if ($month == 13)
647      {
648         $month = 1;
649         $year++;
650      }
651      while ($event->occursOnDay($year, $month, 1))
652      {
653
654         if (!unlink($calendar_events_dir . '/' . $year . '/' . $month . '/1/' . $eventID . $CAL_FILE_EXTENSION))
655         {
656            plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): cannot delete event', $color);
657            exit;
658         }
659
660
661         $month++;
662         if ($month == 13)
663         {
664            $month = 1;
665            $year++;
666         }
667
668      }
669
670   }
671
672
673
674   else
675   {
676      plain_error_message('ERROR IN CALENDAR FILE BACKEND (cal_file_delete_event_do): bad event type', $color);
677      exit;
678   }
679
680}
681
682
683
684/**
685  * Updates an event
686  *
687  * Updates the given event by replacing it in the backend
688  * with the given event object.
689  *
690  * @param string $calendarID The ID of the calendar whose event is being updated
691  * @param object $event The updated event object
692  *
693  */
694function cal_file_update_event_do($calendarID, $event)
695{
696
697   // just delete and create anew, since we have no easy way to know
698   // how many copies of this event we'd need to delete from various
699   // directories
700   //
701   cal_file_delete_event_do($calendarID, $event->getID());
702   cal_file_create_event_do($calendarID, $event);
703
704}
705
706
707
708/**
709  * Get event
710  *
711  * Retrieves the given event from the backend
712  *
713  * @param string $calendarID The ID of the calendar whose event is to be retrieved
714  * @param string $eventID The ID of the event to be retrieved
715  *
716  * @return object A Event object corresponding to the desired event or FALSE if not found
717  *
718  */
719function cal_file_get_event_do($calendarID, $eventID)
720{
721
722   global $EVENT_DIR, $username;
723   $user = $username;
724
725
726   $event_dir = $EVENT_DIR . '/' . $calendarID;
727
728
729   if (is_dir($event_dir))
730   {
731      $events = cal_file_get_all_events_in_dir($user, $event_dir, $eventID);
732
733      if (!empty($events))
734         return array_shift($events);
735   }
736
737
738   // not found!
739   //
740   return FALSE;
741
742   global $color;
743   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);
744   exit;
745
746}
747
748
749
750?>
751