2 // Parse the vcal file and return the data hash.
3 function parse_vcal($cal_file) {
8 //echo "Parsing vcal file... <br />\n";
10 if (!$fd=@fopen($cal_file,"r")) {
11 $errormsg .= "Can't read temporary file: $cal_file\n";
14 // reflect the section where we are in the file:
15 // VCALENDAR, TZ/DAYLIGHT, VEVENT, ALARM
17 $substate = "none"; // reflect the sub section
18 $subsubstate = ""; // reflect the sub-sub section
23 while (!feof($fd) && !$error) {
25 $buff = fgets($fd, 4096);
28 // parser debugging code...
29 //echo "line = $line <br />";
30 //echo "state = $state <br />";
31 //echo "substate = $substate <br />";
32 //echo "subsubstate = $subsubstate <br />";
33 //echo "buff = " . htmlspecialchars ( $buff ) . "<br /><br />\n";
35 if ($state == "VEVENT") {
36 if ( ! empty ( $subsubstate ) ) {
37 if (preg_match("/^END:(.+)$/i", $buff, $match)) {
38 if ( $match[1] == $subsubstate ) {
41 } else if ( $subsubstate == "VALARM" &&
42 preg_match ( "/TRIGGER:(.+)$/i", $buff, $match ) ) {
43 //echo "Set reminder to $match[1]<br />";
44 //reminder time is $match[1]
47 else if (preg_match("/^BEGIN:(.+)$/i", $buff, $match)) {
48 $subsubstate = $match[1];
50 // we suppose ":" is on the same line as property name, this can perhaps cause problems
51 else if (preg_match("/^SUMMARY.*:(.+)$/i", $buff, $match)) {
52 $substate = "summary";
53 $event[$substate] = $match[1];
54 } elseif (preg_match("/^DESCRIPTION:(.+)$/i", $buff, $match)) {
55 $substate = "description";
56 $event[$substate] = $match[1];
57 } elseif (preg_match("/^DESCRIPTION;ENCODING=QUOTED-PRINTABLE:(.+)$/i", $buff, $match)) {
58 // $substate = "description";
59 // $event[$substate] = quoted_printable_decode ( $match[1] );
60 $substate = "descriptionqp";
61 $event[$substate] = $match[1];
62 } elseif (preg_match("/^CLASS.*:(.+)$/i", $buff, $match)) {
64 $event[$substate] = $match[1];
65 } elseif (preg_match("/^PRIORITY.*:(.+)$/i", $buff, $match)) {
66 $substate = "priority";
67 $event[$substate] = $match[1];
68 } elseif (preg_match("/^DTSTART.*:(.+)$/i", $buff, $match)) {
69 $substate = "dtstart";
70 $event[$substate] = $match[1];
71 } elseif (preg_match("/^DTEND.*:(.+)$/i", $buff, $match)) {
73 $event[$substate] = $match[1];
74 } elseif (preg_match("/^RRULE.*:(.+)$/i", $buff, $match)) {
76 $event[$substate] = $match[1];
77 } elseif (preg_match("/^EXDATE.*:(.+)$/i", $buff, $match)) {
79 $event[$substate] = $match[1];
80 } elseif (preg_match("/^DALARM.*:(.+)$/i", $buff, $match)) {
82 $event[$substate] = $match[1];
83 } elseif (preg_match("/^CATEGORIES.*:(.+)$/i", $buff, $match)) {
84 $substate = "categories";
85 $event[$substate] = $match[1];
86 } elseif (preg_match("/^UID.*:(.+)$/i", $buff, $match)) {
88 $event[$substate] = $match[1];
89 } elseif (preg_match("/^END:VEVENT$/i", $buff, $match)) {
93 if ($tmp_data = format_vcal($event)) $vcal_data[] = $tmp_data;
94 // clear out data for new event
97 // TODO: QUOTED-PRINTABLE descriptions
100 } elseif (preg_match("/^[ ]{1}(.+)$/", $buff, $match)) {
101 if ($substate != "none") {
102 $event[$substate] .= $match[1];
104 $errormsg .= "Error in file $cal_file line $line:<br />$buff\n";
107 // For unsupported properties
111 } elseif ($state == "VCALENDAR") {
112 if (preg_match("/^TZ.*:(.+)$/i", $buff, $match)) {
113 $event['tz'] = $match[1];
114 } elseif (preg_match("/^DAYLIGHT.*:(.+)$/i", $buff, $match)) {
115 $event['daylight'] = $match[1];
116 } elseif (preg_match("/^BEGIN:VEVENT$/i", $buff)) {
118 } elseif (preg_match("/^END:VCALENDAR$/i", $buff)) {
121 } elseif ($state == "NONE") {
122 if (preg_match("/^BEGIN:VCALENDAR$/i", $buff))
123 $state = "VCALENDAR";
124 else if (preg_match("/^BEGIN:ALARM$/i", $buff))
134 // Convert vcal format (yyyymmddThhmmssZ) to epoch time
135 function vcaldate_to_timestamp($vdate,$plus_d = '0',$plus_m = '0', $plus_y = '0') {
138 $y = substr($vdate, 0, 4) + $plus_y;
139 $m = substr($vdate, 4, 2) + $plus_m;
140 $d = substr($vdate, 6, 2) + $plus_d;
141 $H = substr($vdate, 9, 2);
142 $M = substr($vdate, 11, 2);
143 $S = substr($vdate, 13, 2);
144 $Z = substr($vdate, 15, 1);
146 $TS = gmmktime($H,$M,$S,$m,$d,$y);
148 // Problem here if server in different timezone
149 $TS = mktime($H,$M,$S,$m,$d,$y);
155 // Put all vcal data into import hash structure
156 function format_vcal($event) {
157 // Start and end time
158 $fevent['StartTime'] = vcaldate_to_timestamp($event['dtstart']);
159 if ($fevent['StartTime'] == '-1') return false;
160 $fevent['EndTime'] = vcaldate_to_timestamp($event['dtend']);
162 // Calculate duration in minutes
163 $fevent['Duration'] = ($fevent['EndTime'] - $fevent['StartTime']) / 60;
164 if ($fevent['Duration'] == '1440') { $fevent['Duration'] = '0'; $fevent['Untimed'] = 1; } //All day (untimed)
166 if (! empty($event['summary'])) $fevent['Summary'] = $event['summary'];
167 if (! empty($event['description'])) $fevent['Description'] = $event['description'];
168 if (! empty($event['descriptionqp'])) {
169 $fevent['Description'] = quoted_printable_decode ( $event['descriptionqp'] );
171 // hack for mozilla sunbird's extra = signs
172 $fevent['Description'] = preg_replace('/^=/', '', $fevent['Description']);
173 $fevent['Description'] = str_replace("\n=", "\n", $fevent['Description']);
175 if (! empty($event['class'])) {
176 $fevent['Private'] = preg_match("/private|confidential/i", $event['class']) ? '1' : '0';
178 $fevent['Private'] = '0';
180 if (! empty($fevent['UID'])) $fevent['UID'] = $event['uid'];
184 // vcal 1.0 repeats can be very complicated and the webcalendar doesn't
185 // actually support all of the ways repeats can be specified. We will
186 // focus on vcals dumped from Palm Desktop and Lotus Notes, which are simple
187 // and the ones webcalendar should fully support.
188 if (! empty($event['rrule'])) {
190 $RR = explode(" ", $event['rrule']);
192 if (preg_match("/^D(.+)$/i", $RR[0], $match)) {
193 $fevent['Repeat']['Interval'] = '1';
194 $fevent['Repeat']['Frequency'] = $match[1];
195 } elseif (preg_match("/^W(.+)$/i", $RR[0], $match)) {
196 $fevent['Repeat']['Interval'] = '2';
197 $fevent['Repeat']['Frequency'] = $match[1];
198 $fevent['Repeat']['RepeatDays'] = rrule_repeat_days($RR);
199 } elseif (preg_match("/^MP(.+)$/i", $RR[0], $match)) {
200 $fevent['Repeat']['Interval'] = '3';
201 $fevent['Repeat']['Frequency'] = $match[1];
202 if ($RR[1] == '5+') {
203 $fevent['Repeat']['Interval'] = '6'; // Last week (monthlyByDayR)
205 } elseif (preg_match("/^MD(.+)$/i", $RR[0], $match)) {
206 $fevent['Repeat']['Interval'] = '4';
207 $fevent['Repeat']['Frequency'] = $match[1];
208 } elseif (preg_match("/^YM(.+)$/i", $RR[0], $match)) {
209 $fevent['Repeat']['Interval'] = '5';
210 $fevent['Repeat']['Frequency'] = $match[1];
211 } elseif (preg_match("/^YD(.+)$/i", $RR[0], $match)) {
212 $fevent['Repeat']['Interval'] = '5';
213 $fevent['Repeat']['Frequency'] = $match[1];
218 // No end in Palm is 12-31-2031
219 if (($end != '20311231') && ($end != '#0')) {
220 $fevent['Repeat']['EndTime'] = rrule_endtime($fevent['Repeat']['Interval'],$fevent['Repeat']['Frequency'],$event['dtstart'],$end);
223 // Repeating exceptions?
224 if (!empty($event['exdate'])) {
225 $fevent['Repeat']['Exceptions'] = array();
226 $EX = explode(",", $event['exdate']);
227 foreach ( $EX as $exdate ){
228 $fevent['Repeat']['Exceptions'][] = vcaldate_to_timestamp($exdate);
234 // $fevent[Category];
235 // $fevent[AlarmSet];
236 // $fevent[AlarmAdvanceAmount];
237 // $fevent[AlarmAdvanceType];
242 // Figure out days of week for weekly repeats
243 function rrule_repeat_days($RA) {
246 $sun = $mon = $tue = $wed = $thu = $fri = $sat = 'n';
247 for ($i = 1; $i < $j; $i++) {
248 if ($RA[$i] == 'SU') {
250 } elseif ($RA[$i] == 'MO') {
252 } elseif ($RA[$i] == 'TU') {
254 } elseif ($RA[$i] == 'WE') {
256 } elseif ($RA[$i] == 'TH') {
258 } elseif ($RA[$i] == 'FR') {
260 } elseif ($RA[$i] == 'SA') {
264 return $sun.$mon.$tue.$wed.$thu.$fri.$sat;
268 // Calculate repeating ending time
269 function rrule_endtime($int,$freq,$start,$end) {
270 // if # then we have to add the difference to the start time
271 if (preg_match("/^#(.+)$/i", $end, $M)) {
273 $plus_d = $plus_m = $plus_y = '0';
276 } elseif ($int == '2') {
278 } elseif ($int == '3') {
280 } elseif ($int == '4') {
282 } elseif ($int == '5') {
284 } elseif ($int == '6') {
287 $endtime = vcaldate_to_timestamp($start,$plus_d,$plus_m,$plus_y);
289 // if we have the enddate
291 $endtime = vcaldate_to_timestamp($end);