Locates the first child component of the specified class, and returns a
reference to it.
public findComponent ( $childclass ) : boolean | Horde_Icalendar_ | ||
return | boolean | Horde_Icalendar_ |
/** * Yet another problem: Outlook seems to remove the organizer from * the iCal when forwarding -- we put the original sender back in * as organizer. * * @param string $icaltext The ical message. * @param MIME_Headers $from The message sender. */ function _addOrganizer(&$icaltxt, $from) { global $conf; if (isset($conf['kolab']['filter']['email_domain'])) { $email_domain = $conf['kolab']['filter']['email_domain']; } else { $email_domain = 'localhost'; } $iCal = new Horde_Icalendar(); $iCal->parsevCalendar($icaltxt); $vevent =& $iCal->findComponent('VEVENT'); if ($vevent) { $organizer = $vevent->getAttribute('ORGANIZER', true); if (is_a($organizer, 'PEAR_Error')) { $adrs = imap_rfc822_parse_adrlist($from, $email_domain); if (count($adrs) > 0) { $org_email = 'mailto:' . $adrs[0]->mailbox . '@' . $adrs[0]->host; $org_name = $adrs[0]->personal; if ($org_name) { $vevent->setAttribute('ORGANIZER', $org_email, array('CN' => $org_name), false); } else { $vevent->setAttribute('ORGANIZER', $org_email, array(), false); } Horde::log(sprintf("Adding missing organizer '%s <%s>' to iCal.", $org_name, $org_email), 'DEBUG'); $icaltxt = $iCal->exportvCalendar(); } } } }
/** * Retrieve Free/Busy data for the specified resource. * * @param string $resource Fetch the Free/Busy data for this resource. * * @return Horde_Icalendar_Vfreebusy The Free/Busy data. */ public function get($resource) { global $conf; $url = self::getUrl($resource); Horde::log(sprintf('Freebusy URL for resource %s is %s', $resource, $url), 'DEBUG'); list($user, $domain) = explode('@', $resource); if (empty($domain)) { $domain = $conf['kolab']['filter']['email_domain']; } /** * This section matches Kronolith_Freebusy and should be merged with it * again in a single Horde_Freebusy module. */ $options = array('method' => 'GET', 'timeout' => 5, 'allowRedirects' => true); if (!empty($conf['http']['proxy']['proxy_host'])) { $options = array_merge($options, $conf['http']['proxy']); } $http = new HTTP_Request($url, $options); $http->setBasicAuth($conf['kolab']['filter']['calendar_id'] . '@' . $domain, $conf['kolab']['filter']['calendar_pass']); @$http->sendRequest(); if ($http->getResponseCode() != 200) { throw new Horde_Kolab_Resource_Exception(sprintf('Unable to retrieve free/busy information for %s', $resource), Horde_Kolab_Resource_Exception::NO_FREEBUSY); } $vfb_text = $http->getResponseBody(); // Detect the charset of the iCalendar data. $contentType = $http->getResponseHeader('Content-Type'); if ($contentType && strpos($contentType, ';') !== false) { list(, $charset, ) = explode(';', $contentType); $vfb_text = Horde_String::convertCharset($vfb_text, trim(str_replace('charset=', '', $charset)), 'UTF-8'); } $iCal = new Horde_Icalendar(); $iCal->parsevCalendar($vfb_text, 'VCALENDAR'); $vfb =& $iCal->findComponent('VFREEBUSY'); if ($vfb === false) { throw new Horde_Kolab_Resource_Exception(sprintf('Invalid or no free/busy information available for %s', $resource), Horde_Kolab_Resource_Exception::NO_FREEBUSY); } $vfb->simplify(); return $vfb; }
/** * @throws Kronolith_Exception */ public function search($email, $private_only = false) { $server = $GLOBALS['injector']->getInstance('Horde_Kolab_Session')->getFreebusyServer(); if (empty($server)) { throw new Horde_Exception_NotFound(); } $http = $GLOBALS['injector']->getInstance('Horde_Core_Factory_HttpClient')->create(array('request.username' => $GLOBALS['registry']->getAuth(), 'request.password' => $GLOBALS['registry']->getAuthCredential('password'))); try { $response = $http->get(sprintf('%s/%s.xfb', $server, $email)); } catch (Horde_Http_Exception $e) { throw new Horde_Exception_NotFound(); } if ($response->code != 200) { throw new Horde_Exception_NotFound(); } $vfb_text = $response->getBody(); $iCal = new Horde_Icalendar(); $iCal->parsevCalendar($vfb_text); $vfb = $iCal->findComponent('VFREEBUSY'); if ($vfb === false) { throw new Horde_Exception_NotFound(); } return $vfb; }
/** * Handle meeting responses. * * @param array $response The response data. Contains: * - requestid: The identifier of the meeting request. Used by the server * to fetch the original meeting request details. * - response: The user's response to the request. One of the response * code constants. * - folderid: The collection id that contains the meeting request. * - * * @return string The UID of any created calendar entries, otherwise false. * @throws Horde_ActiveSync_Exception, Horde_Exception_NotFound */ public function meetingResponse(array $response) { if (empty($response['folderid']) || empty($response['requestid']) || empty($response['response'])) { throw new Horde_ActiveSync_Exception('Invalid meeting response.'); } // First thing we need is to obtain the meeting request. $imap_message = $this->_imap->getImapMessage($response['folderid'], $response['requestid']); if (empty($imap_message)) { throw new Horde_Exception_NotFound(); } $imap_message = $imap_message[$response['requestid']]; // Find the request if (!($part = $imap_message->hasiCalendar())) { $this->_logger->err('Unable to find the meeting request.'); throw new Horde_Exception_NotFound(); } // Parse the vCal $vCal = new Horde_Icalendar(); $data = $part->getContents(); if (!$vCal->parsevCalendar($data, 'VCALENDAR', $part->getCharset())) { throw new Horde_ActiveSync_Exception('Unknown error parsing vCal data.'); } if (!($vEvent = $vCal->findComponent('vEvent'))) { throw new Horde_ActiveSync_Exception('Unknown error locating vEvent.'); } // Create an event from the vEvent. // Note we don't use self::changeMessage since we don't want to treat // this as an incoming message addition from the PIM. Otherwise, the // message may not get synched back to the PIM. try { $uid = $this->_connector->calendar_import_vevent($vEvent); } catch (Horde_Exception $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } // Start building the iTip response email. try { $organizer = parse_url($vEvent->getAttribute('ORGANIZER')); $organizer = $organizer['path']; } catch (Horde_Icalendar_Exception $e) { $this->_logger->err('Unable to find organizer.'); throw new Horde_ActiveSync_Exception($e); } $ident = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($this->_user); $cn = $ident->getValue('fullname'); $email = $ident->getValue('from_addr'); // Can't use Horde_Itip_Resource_Identity since it takes an IMP identity $resource = new Horde_Itip_Resource_Base($email, $cn); switch ($response['response']) { case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_ACCEPTED: $type = new Horde_Itip_Response_Type_Accept($resource); break; case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_DECLINED: $type = new Horde_Itip_Response_Type_Decline($resource); break; case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_TENTATIVE: $type = new Horde_Itip_Response_Type_Tentative($resource); break; } // Delete the original request. EAS Specs require this. Most clients // will remove the email from the UI as soon as the response is sent. // Failure to remove it from the server will result in an inconsistent // sync state. $this->_imap->deleteMessages(array($response['requestid']), $response['folderid']); return $uid; }
/** * Handle meeting responses. * * @param array $response The response data. Contains: * - requestid: The identifier of the meeting request. Used by the server * to fetch the original meeting request details. * - response: The user's response to the request. One of the response * code constants. * - folderid: The collection id that contains the meeting request. * - * * @return string The UID of any created calendar entries, otherwise false. * @throws Horde_ActiveSync_Exception, Horde_Exception_NotFound */ public function meetingResponse(array $response) { global $injector; if (empty($response['folderid']) || empty($response['requestid']) || empty($response['response'])) { throw new Horde_ActiveSync_Exception('Invalid meeting response.'); } // First thing we need is to obtain the meeting request. $imap_message = $this->_imap->getImapMessage($response['folderid'], $response['requestid']); if (empty($imap_message)) { throw new Horde_Exception_NotFound(); } $imap_message = $imap_message[$response['requestid']]; // Find the request if (!($part = $imap_message->hasiCalendar())) { $this->_logger->err('Unable to find the meeting request.'); throw new Horde_Exception_NotFound(); } // Parse the vCal $vCal = new Horde_Icalendar(); $data = $part->getContents(); if (!$vCal->parsevCalendar($data, 'VCALENDAR', $part->getCharset())) { throw new Horde_ActiveSync_Exception('Unknown error parsing vCal data.'); } if (!($vEvent = $vCal->findComponent('vEvent'))) { throw new Horde_ActiveSync_Exception('Unknown error locating vEvent.'); } // Update the vCal so the response will be reflected when imported. $ident = $injector->getInstance('Horde_Core_Factory_Identity')->create($this->_user); $cn = $ident->getValue('fullname'); $email = $ident->getValue('from_addr'); switch ($response['response']) { case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_ACCEPTED: $itip_response = 'ACCEPTED'; break; case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_TENTATIVE: $itip_response = 'TENTATIVE'; break; case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_DECLINED: $itip_response = 'DECLINED'; } $vEvent->updateAttendee($email, $itip_response); // Create an event from the vEvent. // Note we don't use self::changeMessage since we don't want to treat // this as an incoming message addition from the PIM. Otherwise, the // message may not get synched back to the PIM. try { $uid = $this->_connector->calendar_import_vevent($vEvent); } catch (Horde_Exception $e) { $this->_logger->err($e->getMessage()); throw new Horde_ActiveSync_Exception($e); } if (!empty($response['sendresponse'])) { if ($response['sendresponse'] !== true) { $comment = $response['sendresponse']->data; if ($response['sendresponse']->type == Horde_ActiveSync::BODYPREF_TYPE_HTML) { $comment = Horde_Text_Filter::filter($comment, 'Html2text', array('charset' => 'UTF-8', 'nestingLimit' => 1000)); } } else { $comment = ''; } // Start building the iTip response email. try { $organizer = parse_url($vEvent->getAttribute('ORGANIZER')); $organizer = $organizer['path']; } catch (Horde_Icalendar_Exception $e) { $this->_logger->err('Unable to find organizer.'); throw new Horde_ActiveSync_Exception($e); } $ident = $injector->getInstance('Horde_Core_Factory_Identity')->create($event->creator); if (!$ident->getValue('from_addr')) { throw new Horde_ActiveSync_Exception(_("You do not have an email address configured in your Personal Information Preferences.")); } $resource = new Horde_Itip_Resource_Identity($ident, $vEvent->getAttribute('ATTENDEE'), (string) $ident->getFromAddress()); switch ($response['response']) { case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_ACCEPTED: $type = new Horde_Itip_Response_Type_Accept($resource, $comment); break; case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_DECLINED: $type = new Horde_Itip_Response_Type_Decline($resource, $comment); break; case Horde_ActiveSync_Request_MeetingResponse::RESPONSE_TENTATIVE: $type = new Horde_Itip_Response_Type_Tentative($resource, $comment); break; } try { // Send the reply. Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse($type, new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()), $injector->getInstance('Horde_Mail')); $this->_logger->info('Reply sent.'); } catch (Horde_Itip_Exception $e) { $this->_logger->err(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error'); } } // Delete the original request. EAS Specs require this. Most clients // will remove the email from the UI as soon as the response is sent. // Failure to remove it from the server will result in an inconsistent // sync state. try { $this->_imap->deleteMessages(array($response['requestid']), $response['folderid']); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } return $uid; }
/** * Retrieve external free/busy data. * * @param array $servers The remote servers to query * @param Horde_Kolab_FreeBusy_Access $access The object holding the * relevant access * parameters. * * @return Horde_Icalender The remote free/busy information. */ function &_fetchRemote($servers, $access) { $vFb = null; foreach ($servers as $server) { $url = 'https://' . urlencode($access->user) . ':' . urlencode($access->pass) . '@' . $server . $_SERVER['REQUEST_URI']; $remote = @file_get_contents($url); if (!$remote) { $message = sprintf("Unable to read free/busy information from %s", 'https://' . urlencode($access->user) . ':XXX' . '@' . $server . $_SERVER['REQUEST_URI']); Horde::log($message, 'INFO'); } $rvCal = new Horde_Icalendar(); $result = $rvCal->parsevCalendar($remote); if (is_a($result, 'PEAR_Error')) { $message = sprintf("Unable to parse free/busy information from %s: %s", 'https://' . urlencode($access->user) . ':XXX' . '@' . $server . $_SERVER['REQUEST_URI'], $result->getMessage()); Horde::log($message, 'INFO'); } $rvFb =& $rvCal->findComponent('vfreebusy'); if (!$pvFb) { $message = sprintf("Unable to find free/busy information in data from %s.", 'https://' . urlencode($access->user) . ':XXX' . '@' . $server . $_SERVER['REQUEST_URI']); Horde::log($message, 'INFO'); } if ($ets = $rvFb->getAttributeDefault('DTEND', false) !== false) { // PENDING(steffen): Make value configurable if ($ets < time()) { $message = sprintf("free/busy information from %s is too old.", 'https://' . urlencode($access->user) . ':XXX' . '@' . $server . $_SERVER['REQUEST_URI']); Horde::log($message, 'INFO'); } } if (!empty($vFb)) { $vFb->merge($rvFb); } else { $vFb = $rvFb; } } return $vFb; }