1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14:
15:
16: require_once 'Horde/Icalendar.php';
17:
18:
19: require_once 'Horde/MIME.php';
20: require_once 'Horde/MIME/Message.php';
21: require_once 'Horde/MIME/Headers.php';
22: require_once 'Horde/MIME/Part.php';
23: require_once 'Horde/MIME/Structure.php';
24:
25:
26: require_once 'Horde/Kolab/Resource/Epoch.php';
27: require_once 'Horde/Kolab/Resource/Itip.php';
28: require_once 'Horde/Kolab/Resource/Reply.php';
29: require_once 'Horde/Kolab/Resource/Freebusy.php';
30:
31:
32: define('RM_ACT_ALWAYS_ACCEPT', 'ACT_ALWAYS_ACCEPT');
33: define('RM_ACT_REJECT_IF_CONFLICTS', 'ACT_REJECT_IF_CONFLICTS');
34: define('RM_ACT_MANUAL_IF_CONFLICTS', 'ACT_MANUAL_IF_CONFLICTS');
35: define('RM_ACT_MANUAL', 'ACT_MANUAL');
36: define('RM_ACT_ALWAYS_REJECT', 'ACT_ALWAYS_REJECT');
37:
38:
39: define('RM_ITIP_DECLINE', 1);
40: define('RM_ITIP_ACCEPT', 2);
41: define('RM_ITIP_TENTATIVE', 3);
42:
43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54:
55: class Kolab_Resource
56: {
57: 58: 59: 60: 61: 62: 63: 64:
65: function _getResourceData($sender, $resource)
66: {
67: require_once 'Horde/Kolab/Server.php';
68: $db = Horde_Kolab_Server::singleton();
69: if ($db instanceOf PEAR_Error) {
70: $db->code = OUT_LOG | EX_SOFTWARE;
71: return $db;
72: }
73:
74: $dn = $db->uidForMail($resource, Horde_Kolab_Server_Object::RESULT_MANY);
75: if ($dn instanceOf PEAR_Error) {
76: $dn->code = OUT_LOG | EX_NOUSER;
77: return $dn;
78: }
79: if (is_array($dn)) {
80: if (count($dn) > 1) {
81: Horde::logMessage(sprintf("%s objects returned for %s",
82: $count($dn), $resource), 'WARN');
83: return false;
84: } else {
85: $dn = $dn[0];
86: }
87: }
88: $user = $db->fetch($dn, 'Horde_Kolab_Server_Object_Kolab_User');
89:
90: $cn = $user->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_CN);
91: $id = $user->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_MAIL);
92: $hs = $user->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_HOMESERVER);
93: if (is_a($hs, 'PEAR_Error')) {
94: return $hs;
95: }
96: $hs = strtolower($hs);
97: $actions = $user->get(Horde_Kolab_Server_Object_Kolab_User::ATTRIBUTE_IPOLICY, false);
98: if (is_a($actions, 'PEAR_Error')) {
99: $actions->code = OUT_LOG | EX_UNAVAILABLE;
100: return $actions;
101: }
102: if ($actions === false) {
103: $actions = array(RM_ACT_MANUAL);
104: }
105:
106: $policies = array();
107: $defaultpolicy = false;
108: foreach ($actions as $action) {
109: if (preg_match('/(.*):(.*)/', $action, $regs)) {
110: $policies[strtolower($regs[1])] = $regs[2];
111: } else {
112: $defaultpolicy = $action;
113: }
114: }
115:
116: if (array_key_exists($sender, $policies)) {
117:
118: $action = $policies[$sender];
119: } else {
120: $action = false;
121: $dn = $db->uidForMailOrAlias($sender);
122: if (is_a($dn, 'PEAR_Error')) {
123: $dn->code = OUT_LOG | EX_NOUSER;
124: return $dn;
125: }
126: if ($dn) {
127:
128: foreach ($policies as $gid => $policy) {
129: if ($db->memberOfGroupAddress($dn, $gid)) {
130:
131: if (!$action) {
132: $action = $policy;
133: } else {
134: $action = min($action, $policy);
135: }
136: }
137: }
138: }
139: if (!$action && $defaultpolicy) {
140: $action = $defaultpolicy;
141: }
142: }
143: return array('cn' => $cn, 'id' => $id,
144: 'homeserver' => $hs, 'action' => $action);
145: }
146:
147: function &_getICal($filename)
148: {
149: $requestText = '';
150: $handle = fopen($filename, 'r');
151: while (!feof($handle)) {
152: $requestText .= fread($handle, 8192);
153: }
154:
155: $mime = &MIME_Structure::parseTextMIMEMessage($requestText);
156:
157: $parts = $mime->contentTypeMap();
158: foreach ($parts as $mimeid => $conttype) {
159: if ($conttype == 'text/calendar') {
160: $part = $mime->getPart($mimeid);
161:
162: $iCalendar = new Horde_Icalendar();
163: $iCalendar->parsevCalendar($part->transferDecode());
164:
165: return $iCalendar;
166: }
167: }
168:
169: return false;
170: }
171:
172: function _imapConnect($id)
173: {
174: global $conf;
175:
176:
177: list($user, $domain) = explode('@', $id);
178: if (empty($domain)) {
179: $domain = $conf['kolab']['filter']['email_domain'];
180: }
181: $calendar_user = $conf['kolab']['filter']['calendar_id'] . '@' . $domain;
182:
183:
184: $auth = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Auth')->create(isset($conf['auth']['driver']) ? null : 'kolab');
185: $authenticated = $auth->authenticate($calendar_user,
186: array('password' => $conf['kolab']['filter']['calendar_pass']),
187: false);
188:
189: if (is_a($authenticated, 'PEAR_Error')) {
190: $authenticated->code = OUT_LOG | EX_UNAVAILABLE;
191: return $authenticated;
192: }
193: if (!$authenticated) {
194: return PEAR::raiseError(sprintf('Failed to authenticate as calendar user: %s',
195: $auth->getLogoutReasonString()),
196: OUT_LOG | EX_UNAVAILABLE);
197: }
198: @session_start();
199:
200: $secret = $GLOBALS['injector']->getInstance('Horde_Secret');
201:
202: $_SESSION['__auth'] = array(
203: 'authenticated' => true,
204: 'userId' => $calendar_user,
205: 'timestamp' => time(),
206: 'credentials' => $secret->write($secret->getKey('auth'),
207: serialize(array('password' => $conf['kolab']['filter']['calendar_pass']))),
208: 'remote_addr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
209: );
210:
211:
212: require_once 'Horde/Kolab/Storage/List.php';
213: $list = &Kolab_List::singleton();
214: $default = $list->getForeignDefault($id, 'event');
215: if (!$default || is_a($default, 'PEAR_Error')) {
216: $default = new Kolab_Folder();
217: $default->setList($list);
218: $default->setName($conf['kolab']['filter']['calendar_store']);
219:
220: $attributes = array('default' => true,
221: 'type' => 'event',
222: 'owner' => $id);
223: $result = $default->save($attributes);
224: if (is_a($result, 'PEAR_Error')) {
225: $result->code = OUT_LOG | EX_UNAVAILABLE;
226: return $result;
227: }
228: }
229: return $default;
230: }
231:
232: function handleMessage($fqhostname, $sender, $resource, $tmpfname)
233: {
234: global $conf;
235:
236: $rdata = $this->_getResourceData($sender, $resource);
237: if (is_a($rdata, 'PEAR_Error')) {
238: return $rdata;
239: } else if ($rdata === false) {
240:
241: return true;
242: } else if ($rdata['homeserver'] && $rdata['homeserver'] != $fqhostname) {
243:
244: return true;
245: }
246:
247: $cn = $rdata['cn'];
248: $id = $rdata['id'];
249: if (isset($rdata['action'])) {
250: $action = $rdata['action'];
251: } else {
252:
253: $action = RM_ACT_MANUAL;
254: }
255: Horde::logMessage(sprintf('Action for %s is %s',
256: $sender, $action), 'DEBUG');
257:
258:
259: if ($action == RM_ACT_MANUAL) {
260: Horde::logMessage(sprintf('Passing through message to %s', $id), 'INFO');
261: return true;
262: }
263:
264:
265: $iCalendar = &$this->_getICal($tmpfname);
266: if ($iCalendar === false) {
267:
268: Horde::logMessage(sprintf('Could not parse iCalendar data, passing through to %s', $id), 'INFO');
269: return true;
270: }
271:
272: $itip = &$iCalendar->findComponent('VEVENT');
273: if ($itip === false) {
274: Horde::logMessage(sprintf('No VEVENT found in iCalendar data, passing through to %s', $id), 'INFO');
275: return true;
276: }
277: $itip = new Horde_Kolab_Resource_Itip($itip);
278:
279:
280:
281: $method = strtoupper(
282: $iCalendar->getAttributeDefault(
283: 'METHOD',
284: $itip->getMethod()
285: )
286: );
287:
288:
289: Horde::logMessage(sprintf('Processing %s method for %s', $method, $id), 'DEBUG');
290:
291:
292: $uid = $itip->getUid();
293: Horde::logMessage(sprintf('Event has UID %s', $uid), 'DEBUG');
294:
295:
296: $organiser = $itip->getOrganizer();
297: Horde::logMessage(sprintf('Request made by %s', $organiser), 'DEBUG');
298:
299:
300: $summary = $itip->getSummary();
301:
302: $estart = new Horde_Kolab_Resource_Epoch($itip->getStart());
303: $dtstart = $estart->getEpoch();
304: $eend = new Horde_Kolab_Resource_Epoch($itip->getEnd());
305: $dtend = $eend->getEpoch();
306:
307: Horde::logMessage(sprintf('Event starts on <%s> %s and ends on <%s> %s.',
308: $dtstart, $this->iCalDate2Kolab($dtstart), $dtend, $this->iCalDate2Kolab($dtend)), 'DEBUG');
309:
310: if ($action == RM_ACT_ALWAYS_REJECT) {
311: if ($method == 'REQUEST') {
312: Horde::logMessage(sprintf('Rejecting %s method', $method), 'INFO');
313: return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_DECLINE,
314: $organiser, $uid, $is_update);
315: } else {
316: Horde::logMessage(sprintf('Passing through %s method for ACT_ALWAYS_REJECT policy', $method), 'INFO');
317: return true;
318: }
319: }
320:
321: $is_update = false;
322: $imap_error = false;
323: $ignore = array();
324:
325: $folder = $this->_imapConnect($id);
326: if (is_a($folder, 'PEAR_Error')) {
327: $imap_error = &$folder;
328: }
329: if (!is_a($imap_error, 'PEAR_Error') && !$folder->exists()) {
330: $imap_error = &PEAR::raiseError('Error, could not open calendar folder!',
331: OUT_LOG | EX_TEMPFAIL);
332: }
333:
334: if (!is_a($imap_error, 'PEAR_Error')) {
335: $data = $folder->getData();
336: if (is_a($data, 'PEAR_Error')) {
337: $imap_error = &$data;
338: }
339: }
340:
341: if (is_a($imap_error, 'PEAR_Error')) {
342: Horde::logMessage(sprintf('Failed accessing IMAP calendar: %s',
343: $folder->getMessage()), 'ERR');
344: if ($action == RM_ACT_MANUAL_IF_CONFLICTS) {
345: return true;
346: }
347: }
348:
349: switch ($method) {
350: case 'REQUEST':
351: if ($action == RM_ACT_MANUAL) {
352: Horde::logMessage(sprintf('Passing through %s method', $method), 'INFO');
353: break;
354: }
355:
356: if (is_a($imap_error, 'PEAR_Error') || !$data->objectUidExists($uid)) {
357: $old_uid = null;
358: } else {
359: $old_uid = $uid;
360: $ignore[] = $uid;
361: $is_update = true;
362: }
363:
364:
365: $object = $itip->getKolabObject();
366:
367: $outofperiod=0;
368:
369:
370:
371: if ($action != RM_ACT_ALWAYS_ACCEPT) {
372:
373: try {
374: require_once 'Horde/Kolab/Resource/Freebusy.php';
375: $fb = Horde_Kolab_Resource_Freebusy::singleton();
376: $vfb = $fb->get($resource);
377: } catch (Exception $e) {
378: return PEAR::raiseError($e->getMessage(),
379: OUT_LOG | EX_UNAVAILABLE);
380: }
381:
382: $vfbstart = $vfb->getAttributeDefault('DTSTART', 0);
383: $vfbend = $vfb->getAttributeDefault('DTEND', 0);
384: Horde::logMessage(sprintf('Free/busy info starts on <%s> %s and ends on <%s> %s',
385: $vfbstart, $this->iCalDate2Kolab($vfbstart), $vfbend, $this->iCalDate2Kolab($vfbend)), 'DEBUG');
386:
387: $evfbend = new Horde_Kolab_Resource_Epoch($vfbend);
388: if ($vfbstart && $dtstart > $evfbend->getEpoch()) {
389: $outofperiod=1;
390: } else {
391:
392: $busyperiods = $vfb->getBusyPeriods();
393: Horde::logMessage(sprintf('Busyperiods: %s',
394: print_r($busyperiods, true)), 'DEBUG');
395: $extraparams = $vfb->getExtraParams();
396: Horde::logMessage(sprintf('Extraparams: %s',
397: print_r($extraparams, true)), 'DEBUG');
398: $conflict = false;
399: if (!empty($object['recurrence'])) {
400: $recurrence = new Horde_Date_Recurrence($dtstart);
401: $recurrence->fromHash($object['recurrence']);
402: $duration = $dtend - $dtstart;
403: $events = array();
404: $next_start = $vfbstart;
405: $next = $recurrence->nextActiveRecurrence($vfbstart);
406: while ($next !== false && $next->compareDate($vfbend) <= 0) {
407: $next_ts = $next->timestamp();
408: $events[$next_ts] = $next_ts + $duration;
409: $next = $recurrence->nextActiveRecurrence(array('year' => $next->year,
410: 'month' => $next->month,
411: 'mday' => $next->mday + 1,
412: 'hour' => $next->hour,
413: 'min' => $next->min,
414: 'sec' => $next->sec));
415: }
416: } else {
417: $events = array($dtstart => $dtend);
418: }
419:
420: foreach ($events as $dtstart => $dtend) {
421: Horde::logMessage(sprintf('Requested event from %s to %s',
422: strftime('%a, %d %b %Y %H:%M:%S %z', $dtstart),
423: strftime('%a, %d %b %Y %H:%M:%S %z', $dtend)
424: ), 'DEBUG');
425: foreach ($busyperiods as $busyfrom => $busyto) {
426: if (empty($busyfrom) && empty($busyto)) {
427: continue;
428: }
429: Horde::logMessage(sprintf('Busy period from %s to %s',
430: strftime('%a, %d %b %Y %H:%M:%S %z', $busyfrom),
431: strftime('%a, %d %b %Y %H:%M:%S %z', $busyto)
432: ), 'DEBUG');
433: if ((isset($extraparams[$busyfrom]['X-UID'])
434: && in_array(base64_decode($extraparams[$busyfrom]['X-UID']), $ignore))
435: || (isset($extraparams[$busyfrom]['X-SID'])
436: && in_array(base64_decode($extraparams[$busyfrom]['X-SID']), $ignore))) {
437:
438: continue;
439: }
440: if (($busyfrom >= $dtstart && $busyfrom < $dtend) || ($dtstart >= $busyfrom && $dtstart < $busyto)) {
441: Horde::logMessage('Request overlaps', 'DEBUG');
442: $conflict = true;
443: break;
444: }
445: }
446: if ($conflict) {
447: break;
448: }
449: }
450:
451: if ($conflict) {
452: if ($action == RM_ACT_MANUAL_IF_CONFLICTS) {
453:
454: Horde::logMessage('Conflict detected; Passing mail through', 'INFO');
455: return true;
456: } else if ($action == RM_ACT_REJECT_IF_CONFLICTS) {
457: Horde::logMessage('Conflict detected; rejecting', 'INFO');
458: return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE,
459: $organiser, $uid, $is_update);
460: }
461: }
462: }
463: }
464:
465: if (is_a($imap_error, 'PEAR_Error')) {
466: Horde::logMessage('Could not access users calendar; rejecting', 'INFO');
467: return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE,
468: $organiser, $uid, $is_update);
469: }
470:
471:
472:
473:
474:
475: Horde::logMessage(sprintf('Adding event %s', $uid), 'INFO');
476:
477: if (!empty($conf['kolab']['filter']['simple_locks'])) {
478: if (!empty($conf['kolab']['filter']['simple_locks_timeout'])) {
479: $timeout = $conf['kolab']['filter']['simple_locks_timeout'];
480: } else {
481: $timeout = 60;
482: }
483: if (!empty($conf['kolab']['filter']['simple_locks_dir'])) {
484: $lockdir = $conf['kolab']['filter']['simple_locks_dir'];
485: } else {
486: $lockdir = Horde::getTempDir() . '/Kolab_Filter_locks';
487: if (!is_dir($lockdir)) {
488: mkdir($lockdir, 0700);
489: }
490: }
491: if (is_dir($lockdir)) {
492: $lockfile = $lockdir . '/' . $resource . '.lock';
493: $counter = 0;
494: while ($counter < $timeout && file_exists($lockfile)) {
495: sleep(1);
496: $counter++;
497: }
498: if ($counter == $timeout) {
499: Horde::logMessage(sprintf('Lock timeout of %s seconds exceeded. Rejecting invitation.', $timeout), 'ERR');
500: return $this->sendITipReply($cn, $id, $itip, RM_ITIP_DECLINE,
501: $organiser, $uid, $is_update);
502: }
503: $result = file_put_contents($lockfile, 'LOCKED');
504: if ($result === false) {
505: Horde::logMessage(sprintf('Failed creating lock file %s.', $lockfile), 'ERR');
506: } else {
507: $this->lockfile = $lockfile;
508: }
509: } else {
510: Horde::logMessage(sprintf('The lock directory %s is missing. Disabled locking.', $lockdir), 'ERR');
511: }
512: }
513:
514: $itip->setAccepted($resource);
515:
516: $result = $data->save($itip->getKolabObject(), $old_uid);
517: if (is_a($result, 'PEAR_Error')) {
518: $result->code = OUT_LOG | EX_UNAVAILABLE;
519: return $result;
520: }
521:
522: if ($outofperiod) {
523: Horde::logMessage('No freebusy information available', 'NOTICE');
524: return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_TENTATIVE,
525: $organiser, $uid, $is_update);
526: } else {
527: return $this->sendITipReply($cn, $resource, $itip, RM_ITIP_ACCEPT,
528: $organiser, $uid, $is_update);
529: }
530:
531: case 'CANCEL':
532: Horde::logMessage(sprintf('Removing event %s', $uid), 'INFO');
533:
534: if (is_a($imap_error, 'PEAR_Error')) {
535: $body = sprintf(Horde_Kolab_Resource_Translation::t("Unable to access %s's calendar:"), $resource) . "\n\n" . $summary;
536: $subject = sprintf(Horde_Kolab_Resource_Translation::t("Error processing \"%s\""), $summary);
537: } else if (!$data->objectUidExists($uid)) {
538: Horde::logMessage(sprintf('Canceled event %s is not present in %s\'s calendar',
539: $uid, $resource), 'WARNING');
540: $body = sprintf(Horde_Kolab_Resource_Translation::t("The following event that was canceled is not present in %s's calendar:"), $resource) . "\n\n" . $summary;
541: $subject = sprintf(Horde_Kolab_Resource_Translation::t("Error processing \"%s\""), $summary);
542: } else {
543: 544: 545: 546:
547: Horde::logMessage(sprintf('Deleting %s because of cancel',
548: $uid), 'DEBUG');
549:
550: $result = $data->delete($uid);
551: if (is_a($result, 'PEAR_Error')) {
552: Horde::logMessage(sprintf('Deleting %s failed with %s',
553: $uid, $result->getMessage()), 'DEBUG');
554: }
555:
556: $body = Horde_Kolab_Resource_Translation::t("The following event has been successfully removed:") . "\n\n" . $summary;
557: $subject = sprintf(Horde_Kolab_Resource_Translation::t("%s has been cancelled"), $summary);
558: }
559:
560: Horde::logMessage(sprintf('Sending confirmation of cancelation to %s', $organiser), 'WARNING');
561:
562: $body = new MIME_Part('text/plain', Horde_String::wrap($body, 76));
563: $mime = &MIME_Message::convertMimePart($body);
564: $mime->setTransferEncoding('quoted-printable');
565: $mime->transferEncodeContents();
566:
567:
568: $msg_headers = new MIME_Headers();
569: $msg_headers->addHeader('Date', date('r'));
570: $msg_headers->addHeader('From', $resource);
571: $msg_headers->addHeader('To', $organiser);
572: $msg_headers->addHeader('Subject', $subject);
573: $msg_headers->addMIMEHeaders($mime);
574:
575: $reply = new Horde_Kolab_Resource_Reply(
576: $resource, $organiser, $msg_headers, $mime
577: );
578: Horde::logMessage('Successfully prepared cancellation reply', 'INFO');
579: return $reply;
580:
581: default:
582:
583:
584: Horde::logMessage(sprintf('Ignoring %s method and passing message through to %s',
585: $method, $resource), 'INFO');
586: return true;
587: }
588: }
589:
590: 591: 592: 593: 594:
595: function cleanup()
596: {
597: if (!empty($this->lockfile)) {
598: @unlink($this->lockfile);
599: if (file_exists($this->lockfile)) {
600: Horde::logMessage(sprintf('Failed removing the lockfile %s.', $lockfile), 'ERR');
601: }
602: $this->lockfile = null;
603: }
604: }
605:
606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617:
618: function sendITipReply(
619: $cn, $resource, $itip, $type, $organiser, $uid, $is_update, $comment = null
620: ) {
621: Horde::logMessage(sprintf('sendITipReply(%s, %s, %s, %s)',
622: $cn, $resource, get_class($itip), $type),
623: 'DEBUG');
624:
625: $itip_reply = new Horde_Kolab_Resource_Itip_Response(
626: $itip,
627: new Horde_Kolab_Resource_Itip_Resource_Base(
628: $resource, $cn
629: )
630: );
631: switch($type) {
632: case RM_ITIP_DECLINE:
633: $type = new Horde_Kolab_Resource_Itip_Response_Type_Decline(
634: $resource, $itip
635: );
636: break;
637: case RM_ITIP_ACCEPT:
638: $type = new Horde_Kolab_Resource_Itip_Response_Type_Accept(
639: $resource, $itip
640: );
641: break;
642: case RM_ITIP_TENTATIVE:
643: $type = new Horde_Kolab_Resource_Itip_Response_Type_Tentative(
644: $resource, $itip
645: );
646: break;
647: }
648: list($headers, $message) = $itip_reply->getMessage(
649: $type,
650: '-//kolab.org//NONSGML Kolab Server 2//EN',
651: $comment
652: );
653:
654: Horde::logMessage(sprintf('Sending %s iTip reply to %s',
655: $type->getStatus(),
656: $organiser), 'DEBUG');
657:
658: $reply = new Horde_Kolab_Resource_Reply(
659: $resource, $organiser, $headers, $message
660: );
661: Horde::logMessage('Successfully prepared iTip reply', 'DEBUG');
662: return $reply;
663: }
664:
665: 666: 667: 668: 669: 670: 671:
672: function cleanArray($ical_date)
673: {
674: if (!array_key_exists('hour', $ical_date)) {
675: $temp['DATE'] = '1';
676: }
677: $temp['hour'] = array_key_exists('hour', $ical_date) ? $ical_date['hour'] : '00';
678: $temp['minute'] = array_key_exists('minute', $ical_date) ? $ical_date['minute'] : '00';
679: $temp['second'] = array_key_exists('second', $ical_date) ? $ical_date['second'] : '00';
680: $temp['year'] = array_key_exists('year', $ical_date) ? $ical_date['year'] : '0000';
681: $temp['month'] = array_key_exists('month', $ical_date) ? $ical_date['month'] : '00';
682: $temp['mday'] = array_key_exists('mday', $ical_date) ? $ical_date['mday'] : '00';
683: $temp['zone'] = array_key_exists('zone', $ical_date) ? $ical_date['zone'] : 'UTC';
684:
685: return $temp;
686: }
687:
688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699:
700: function iCalDate2Kolab($ical_date, $type= ' ')
701: {
702: Horde::logMessage(sprintf('Converting to kolab format %s',
703: print_r($ical_date, true)), 'DEBUG');
704:
705:
706: if (is_array($ical_date)) {
707:
708: $temp = $this->cleanArray($ical_date);
709: if (array_key_exists('DATE', $temp)) {
710: if ($type == 'ENDDATE') {
711: $etemp = new Horde_Kolab_Resource_Epoch($temp);
712:
713: $epoch= $etemp->getEpoch() - 86400;
714: $date = gmstrftime('%Y-%m-%d', $epoch);
715: } else {
716: $date= sprintf('%04d-%02d-%02d', $temp['year'], $temp['month'], $temp['mday']);
717: }
718: } else {
719: $time = sprintf('%02d:%02d:%02d', $temp['hour'], $temp['minute'], $temp['second']);
720: if ($temp['zone'] == 'UTC') {
721: $time .= 'Z';
722: }
723: $date = sprintf('%04d-%02d-%02d', $temp['year'], $temp['month'], $temp['mday']) . 'T' . $time;
724: }
725: } else {
726: $date = gmstrftime('%Y-%m-%dT%H:%M:%SZ', $ical_date);
727: }
728: Horde::logMessage(sprintf('To <%s>', $date), 'DEBUG');
729: return $date;
730: }
731: }
732: