/** * Return the full rendered version of the Horde_Mime_Part object. * * URL parameters used by this function: * - c: (integer) The VCARD component that contains an image. * - p: (integer) The index of image inside the component to display. * * @return array See parent::render(). * @throws Horde_Exception */ protected function _renderInline() { $vars = $GLOBALS['injector']->getInstance('Horde_Variables'); if (!isset($vars->p)) { $imp_contents = $this->getConfigParam('imp_contents'); $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_VcardImport', array('mime_id' => $this->_mimepart->getMimeId(), 'muid' => strval($imp_contents->getIndicesOb()))); $this->_imageUrl = $this->getConfigParam('imp_contents')->urlView($this->_mimepart, 'download_render', array('params' => array('mode' => IMP_Contents::RENDER_INLINE))); return parent::_renderInline(); } /* Send the requested photo. */ $data = $this->_mimepart->getContents(); $ical = new Horde_Icalendar(); if (!$ical->parsevCalendar($data, 'VCALENDAR', $this->_mimepart->getCharset())) { // TODO: Error reporting return array(); } $components = $ical->getComponents(); if (!isset($components[$vars->c])) { // TODO: Error reporting return array(); } $name = $components[$vars->c]->getAttributeDefault('FN', false); if ($name === false) { $name = $components[$vars->c]->printableName(); } if (empty($name)) { $name = preg_replace('/\\..*?$/', '', $this->_mimepart->getName()); } $photos = $components[$vars->c]->getAllAttributes('PHOTO'); if (!isset($photos[$vars->p])) { // TODO: Error reporting return array(); } $type = 'image/' . Horde_String::lower($photos[$vars->p]['params']['TYPE']); return array($this->_mimepart->getMimeId() => array('data' => base64_decode($photos[$vars->p]['value']), 'name' => $name . '.' . Horde_Mime_Magic::mimeToExt($type), 'type' => $type)); }
/** * Returns an unsorted file list of the specified directory. * * @param string $path The path of the directory. * @param string|array $filter Regular expression(s) to filter * file/directory name on. * @param boolean $dotfiles Show dotfiles? * @param boolean $dironly Show only directories? * * @return array File list. * @throws Horde_Vfs_Exception */ protected function _listFolder($path, $filter = null, $dotfiles = true, $dironly = false) { $list = array(); if ($path == '/') { try { $apps = $this->_registry->listApps(null, false, Horde_Perms::READ); } catch (Horde_Exception $e) { throw new Horde_Vfs_Exception($e->getMessage()); } foreach ($apps as $app) { if ($this->_registry->hasMethod('browse', $app)) { $file = array('name' => $app, 'date' => time(), 'type' => '**dir', 'size' => -1); $list[] = $file; } } return $list; } if (substr($path, 0, 1) == '/') { $path = substr($path, 1); } $pieces = explode('/', $path); try { $items = $this->_registry->callByPackage($pieces[0], 'browse', array('path' => $path, 'properties' => array('name', 'browseable', 'contenttype', 'contentlength', 'modified'))); } catch (Horde_Exception $e) { throw new Horde_Vfs_Exception($e->getMessage()); } if (!is_array(reset($items))) { /* We return an object's content. */ throw new Horde_Vfs_Exception('Unknown error'); } foreach ($items as $sub_path => $i) { if ($dironly && !$i['browseable']) { continue; } $name = basename($sub_path); if ($this->_filterMatch($filter, $name)) { continue; } $type = class_exists('Horde_Mime_Magic') ? Horde_Mime_Magic::mimeToExt(empty($i['contenttype']) ? 'application/octet-stream' : $i['contenttype']) : '**none'; $file = array('name' => $name, 'date' => empty($i['modified']) ? 0 : $i['modified'], 'type' => $i['browseable'] ? '**dir' : $type, 'size' => empty($i['contentlength']) ? 0 : $i['contentlength']); $list[] = $file; } return $list; }
/** * Sends the correct HTTP headers to the browser to download this image. * * @param string $view The view to download. */ public function downloadHeaders($view = 'full') { global $browser, $conf; $filename = $this->filename; if ($view != 'full') { if ($ext = Horde_Mime_Magic::mimeToExt('image/' . $conf['image']['type'])) { $filename .= '.' . $ext; } } $browser->downloadHeaders($filename); }
/** * Stores an object in the Kolab message store. * * TODO * * @return string The object id, possibly updated. * @throws Turba_Exception */ protected function _store($attributes, $object_id = null) { if (isset($attributes['__type']) && $attributes['__type'] == 'Group') { $this->_convertMembers($attributes); $data = $this->_getListData(); } else { if (isset($attributes['photo']) && isset($attributes['phototype'])) { $filename = 'photo.' . Horde_Mime_Magic::mimeToExt($attributes['phototype']); $attributes['_attachments'][$filename] = array('type' => $attributes['phototype'], 'content' => Horde_Stream_Wrapper_String::getStream($attributes['photo'])); $attributes['picture'] = $filename; unset($attributes['photo'], $attributes['phototype']); } // EAS sets the date fields to '' instead of null -> fix it up $fix_date_fields = array('birthday', 'anniversary'); foreach ($fix_date_fields as $fix_date) { if (empty($attributes[$fix_date])) { unset($attributes[$fix_date]); } } $data = $this->_getData(); } if ($object_id === null) { $object_id = $data->create($attributes); } else { $data->modify($attributes); } /* Invalidate cache. */ $this->_connected = false; return Horde_Url::uriB64Encode($object_id); }
/** * Parse a MIME message and create a new ticket. * * @param string $text This is the full text of the MIME message. * @param array $info An array of information for the new ticket. * This should include: * - 'queue' => queue id * - 'type' => type id * - 'state' => state id * - 'priority' => priority id * - 'ticket' => ticket id (prevents creation * of new tickets) * @param string $auth_user This will be the Horde user that creates the * ticket. If null, we will try to deduce from * the message's From: header. We do NOT default * to $GLOBALS['registry']->getAuth(). * * @return Whups_Ticket Ticket. */ public static function processMail($text, array $info, $auth_user = null) { global $conf; $message = Horde_Mime_Part::parseMessage($text); if (preg_match("/^(.*?)\r?\n\r?\n/s", $text, $matches)) { $hdrText = $matches[1]; } else { $hdrText = $text; } $headers = Horde_Mime_Headers::parseHeaders($hdrText); // If this message was generated by Whups, don't process it. if ($headers->getValue('X-Whups-Generated')) { return true; } // Try to avoid bounces, auto-replies, and mailing list responses. $from = $headers->getValue('from'); if (strpos($headers->getValue('Content-Type'), 'multipart/report') !== false || stripos($from, 'mailer-daemon@') !== false || stripos($from, 'postmaster@') !== false || !is_null($headers->getValue('X-Failed-Recipients')) || !is_null($headers->getValue('X-Autoreply-Domain')) || $headers->getValue('Auto-Submitted') == 'auto-replied' || $headers->getValue('Precedence') == 'auto_reply' || $headers->getValue('X-Precedence') == 'auto_reply' || $headers->getValue('X-Auto-Response-Suppress') == 'All' || $headers->getValue('X-List-Administrivia') == 'Yes') { return true; } // Use the message subject as the ticket summary. $info['summary'] = trim($headers->getValue('subject')); if (empty($info['summary'])) { $info['summary'] = _("[No Subject]"); } // Format the message into a comment. $comment = _("Received message:") . "\n\n"; if (!empty($GLOBALS['conf']['mail']['include_headers'])) { foreach ($headers->toArray(array('nowrap' => true)) as $name => $vals) { if (!in_array(strtolower($name), array('subject', 'from', 'to', 'cc', 'date'))) { if (is_array($vals)) { foreach ($vals as $val) { $comment .= $name . ': ' . $val . "\n"; } } else { $comment .= $name . ': ' . $vals . "\n"; } } } $comment .= "\n"; } // Look for the body part. $body_id = $message->findBody(); if ($body_id) { $part = $message->getPart($body_id); $content = Horde_String::convertCharset($part->getContents(), $part->getCharset(), 'UTF-8'); switch ($part->getType()) { case 'text/plain': $comment .= $content; break; case 'text/html': $comment .= Horde_Text_Filter::filter($content, array('Html2text'), array(array('width' => 0))); break; default: $comment .= _("[ Could not render body of message. ]"); break; } } else { $comment .= _("[ Could not render body of message. ]"); } $info['comment'] = $comment . "\n"; // Try to determine the Horde user for creating the ticket. if (empty($auth_user)) { $tmp = new Horde_Mail_Rfc822_Address($from); $auth_user = self::_findAuthUser($tmp->bare_address); } $author = $auth_user; if (empty($auth_user) && !empty($info['default_auth'])) { $auth_user = $info['default_auth']; if (!empty($from)) { $info['user_email'] = $from; } } if (empty($auth_user) && !empty($conf['mail']['username'])) { $auth_user = $conf['mail']['username']; if (!empty($from)) { $info['user_email'] = $from; } } // Authenticate as the correct Horde user. if (!empty($auth_user) && $auth_user != $GLOBALS['registry']->getAuth()) { $GLOBALS['registry']->setAuth($auth_user, array()); } // Attach message. $attachments = array(); if (!empty($GLOBALS['conf']['mail']['attach_message'])) { $tmp_name = Horde::getTempFile('whups'); $fp = @fopen($tmp_name, 'wb'); if (!$fp) { throw new Whups_Exception(sprintf('Cannot open file %s for writing.', $tmp_name)); } fwrite($fp, $text); fclose($fp); $attachments[] = array('name' => _("Original Message") . '.eml', 'tmp_name' => $tmp_name); } // Extract attachments. $dl_list = array_slice(array_keys($message->contentTypeMap()), 1); foreach ($dl_list as $key) { $part = $message->getPart($key); if ($key == $body_id && $part->getType() == 'text/plain' || $part->getType() == 'multipart/alternative' || $part->getType() == 'multipart/mixed') { continue; } $tmp_name = Horde::getTempFile('whups'); $fp = @fopen($tmp_name, 'wb'); if (!$fp) { throw new Whups_Exception(sprintf('Cannot open file %s for writing.', $tmp_name)); } fwrite($fp, $part->getContents()); fclose($fp); $part_name = $part->getName(true); if (!$part_name) { $ptype = $part->getPrimaryType(); switch ($ptype) { case 'multipart': case 'application': $part_name = sprintf(_("%s part"), ucfirst($part->getSubType())); break; default: $part_name = sprintf(_("%s part"), ucfirst($ptype)); break; } if ($ext = Horde_Mime_Magic::mimeToExt($part->getType())) { $part_name .= '.' . $ext; } } $attachments[] = array('name' => $part_name, 'tmp_name' => $tmp_name); } // See if we can match this message to an existing ticket. if ($ticket = self::_findTicket($info)) { $ticket->change('comment', $info['comment']); $ticket->change('comment-email', $from); if ($attachments) { $ticket->change('attachments', $attachments); } $ticket->commit($author); } elseif (!empty($info['ticket'])) { // Didn't match an existing ticket though a ticket number had been // specified. throw new Whups_Exception(sprintf(_("Could not find ticket \"%s\"."), $info['ticket'])); } else { if (!empty($info['guess-queue'])) { // Try to guess the queue name for the new ticket from the // message subject. $queues = $GLOBALS['whups_driver']->getQueues(); foreach ($queues as $queueId => $queueName) { if (preg_match('/\\b' . preg_quote($queueName, '/') . '\\b/i', $info['summary'])) { $info['queue'] = $queueId; break; } } } $info['attachments'] = $attachments; // Create a new ticket. $ticket = Whups_Ticket::newTicket($info, $author); } }
/** * Updates the properties of this event from a Horde_Icalendar_Vevent * object. * * @param Horde_Icalendar_Vevent $vEvent The iCalendar data to update * from. * @param boolean $parseAttendees Parse attendees too? * @since Kronolith 4.2 */ public function fromiCalendar($vEvent, $parseAttendees = false) { // Unique ID. try { $uid = $vEvent->getAttribute('UID'); if (!empty($uid)) { $this->uid = $uid; } } catch (Horde_Icalendar_Exception $e) { } // Organizer try { $organizer = $vEvent->getAttribute('ORGANIZER'); if (!empty($organizer)) { $this->organizer = str_replace(array('MAILTO:', 'mailto:'), '', $organizer); } } catch (Horde_Icalendar_Exception $e) { } // Sequence. try { $seq = $vEvent->getAttribute('SEQUENCE'); if (is_int($seq)) { $this->sequence = $seq; } } catch (Horde_Icalendar_Exception $e) { } // Title, tags and description. try { $title = $vEvent->getAttribute('SUMMARY'); if (!is_array($title)) { $this->title = $title; } } catch (Horde_Icalendar_Exception $e) { } // Tags try { $this->_tags = $vEvent->getAttributeValues('CATEGORIES'); } catch (Horde_Icalendar_Exception $e) { } // Description try { $desc = $vEvent->getAttribute('DESCRIPTION'); if (!is_array($desc)) { $this->description = $desc; } } catch (Horde_Icalendar_Exception $e) { } // Remote Url try { $url = $vEvent->getAttribute('URL'); if (!is_array($url)) { $this->url = $url; } } catch (Horde_Icalendar_Exception $e) { } // Location try { $location = $vEvent->getAttribute('LOCATION'); if (!is_array($location)) { $this->location = $location; } } catch (Horde_Icalendar_Exception $e) { } try { $geolocation = $vEvent->getAttribute('GEO'); $this->geoLocation = array('lat' => $geolocation['latitude'], 'lon' => $geolocation['longitude']); } catch (Horde_Icalendar_Exception $e) { } // Class try { $class = $vEvent->getAttribute('CLASS'); if (!is_array($class)) { $class = Horde_String::upper($class); $this->private = $class == 'PRIVATE' || $class == 'CONFIDENTIAL'; } } catch (Horde_Icalendar_Exception $e) { } // Status. try { $status = $vEvent->getAttribute('STATUS'); if (!is_array($status)) { $status = Horde_String::upper($status); if ($status == 'DECLINED') { $status = 'CANCELLED'; } if (defined('Kronolith::STATUS_' . $status)) { $this->status = constant('Kronolith::STATUS_' . $status); } } } catch (Horde_Icalendar_Exception $e) { } // Reset allday flag in case this has changed. Will be recalculated // next time isAllDay() is called. $this->allday = false; // Start and end date. $tzid = null; try { $start = $vEvent->getAttribute('DTSTART'); $startParams = $vEvent->getAttribute('DTSTART', true); // We don't support different timezones for different attributes, // so use the DTSTART timezone for the complete event. if (isset($startParams[0]['TZID'])) { // Horde_Date supports timezone aliases, so try that first. $tz = $startParams[0]['TZID']; try { // Check if the timezone name is supported by PHP natively. new DateTimeZone($tz); $this->timezone = $tzid = $tz; } catch (Exception $e) { } } if (!is_array($start)) { // Date-Time field $this->start = new Horde_Date($start, $tzid); } else { // Date field $this->start = new Horde_Date(array('year' => (int) $start['year'], 'month' => (int) $start['month'], 'mday' => (int) $start['mday']), $tzid); } } catch (Horde_Icalendar_Exception $e) { throw new Kronolith_Exception($e); } catch (Horde_Date_Exception $e) { throw new Kronolith_Exception($e); } try { $end = $vEvent->getAttribute('DTEND'); if (!is_array($end)) { // Date-Time field $this->end = new Horde_Date($end, $tzid); // All day events are transferred by many device as // DSTART: YYYYMMDDT000000 DTEND: YYYYMMDDT2359(59|00) // Convert accordingly if (is_object($this->start) && $this->start->hour == 0 && $this->start->min == 0 && $this->start->sec == 0 && $this->end->hour == 23 && $this->end->min == 59) { $this->end = new Horde_Date(array('year' => (int) $this->end->year, 'month' => (int) $this->end->month, 'mday' => (int) $this->end->mday + 1), $tzid); } } else { // Date field $this->end = new Horde_Date(array('year' => (int) $end['year'], 'month' => (int) $end['month'], 'mday' => (int) $end['mday']), $tzid); } } catch (Horde_Icalendar_Exception $e) { $end = null; } if (is_null($end)) { try { $duration = $vEvent->getAttribute('DURATION'); if (!is_array($duration)) { $this->end = new Horde_Date($this->start); $this->end->sec += $duration; $end = 1; } } catch (Horde_Icalendar_Exception $e) { } if (is_null($end)) { // End date equal to start date as per RFC 2445. $this->end = new Horde_Date($this->start); if (is_array($start)) { // Date field $this->end->mday++; } } } // vCalendar 1.0 alarms try { $alarm = $vEvent->getAttribute('AALARM'); if (!is_array($alarm) && intval($alarm)) { $this->alarm = intval(($this->start->timestamp() - $alarm) / 60); } } catch (Horde_Icalendar_Exception $e) { } // vCalendar 2.0 alarms foreach ($vEvent->getComponents() as $alarm) { if (!$alarm instanceof Horde_Icalendar_Valarm) { continue; } try { if ($alarm->getAttribute('ACTION') == 'NONE') { continue; } } catch (Horde_Icalendar_Exception $e) { } try { // @todo consider implementing different ACTION types. // $action = $alarm->getAttribute('ACTION'); $trigger = $alarm->getAttribute('TRIGGER'); $triggerParams = $alarm->getAttribute('TRIGGER', true); } catch (Horde_Icalendar_Exception $e) { continue; } if (!is_array($triggerParams)) { $triggerParams = array($triggerParams); } $haveTrigger = false; foreach ($triggerParams as $tp) { if (isset($tp['VALUE']) && $tp['VALUE'] == 'DATE-TIME') { if (isset($tp['RELATED']) && $tp['RELATED'] == 'END') { $this->alarm = intval(($this->end->timestamp() - $trigger) / 60); } else { $this->alarm = intval(($this->start->timestamp() - $trigger) / 60); } $haveTrigger = true; break; } elseif (isset($tp['RELATED']) && $tp['RELATED'] == 'END') { $this->alarm = -intval($trigger / 60); $this->alarm -= $this->durMin; $haveTrigger = true; break; } } if (!$haveTrigger) { $this->alarm = -intval($trigger / 60); } break; } // Alarm snoozing/dismissal if ($this->alarm) { try { // If X-MOZ-LASTACK is set, this event is either dismissed or // snoozed. $vEvent->getAttribute('X-MOZ-LASTACK'); try { // If X-MOZ-SNOOZE-TIME is set, this event is snoozed. $snooze = $vEvent->getAttribute('X-MOZ-SNOOZE-TIME'); $this->_snooze = intval(($snooze - time()) / 60); } catch (Horde_Icalendar_Exception $e) { // If X-MOZ-SNOOZE-TIME is not set, this event is dismissed. $this->_snooze = -1; } } catch (Horde_Icalendar_Exception $e) { } } // Attached files, we require a UID so we can attach the file in VFS. if ($this->uid) { $attach = false; $attach_params = array(); try { $attach = $vEvent->getAttribute('ATTACH'); $attach_params = $vEvent->getAttribute('ATTACH', true); if (!is_array($attach)) { $attach = array($attach); } foreach ($attach as $key => $attribute) { if (isset($attach_params[$key]['VALUE']) && Horde_String::lower($attach_params[$key]['VALUE']) == 'uri') { // @todo } elseif (Horde_String::upper($attach_params[$key]['ENCODING']) == 'BASE64') { $mime_type = !empty($attach_params[$key]['FMTTYPE']) ? $attach_params[$key]['FMTTYPE'] : ''; // @todo We really should add stream support to VFS $file_data = base64_decode($attribute); $vfs = $this->vfsInit(); $dir = Kronolith::VFS_PATH . '/' . $this->getVfsUid(); // @todo - Is there a way to get a filename from a // non-uri ATTACH attribute?? $filename = sprintf(_("File %d.%s"), $key, Horde_Mime_Magic::mimeToExt($mime_type)); try { $vfs->writeData($dir, $filename, $file_data, true); } catch (Horde_Vfs_Exception $e) { Horde::log($e->getMessage(), 'ERR'); } } } } catch (Horde_Icalendar_Exception $e) { } } // Attendance. // Importing attendance may result in confusion: editing an imported // copy of an event can cause invitation updates to be sent from // people other than the original organizer. So we don't import by // default. However to allow updates by synchronization, this behavior // can be overriden. // X-ATTENDEE is there for historical reasons. @todo remove in // Kronolith 5. $attendee = $users = null; if ($parseAttendees) { try { $attendee = $vEvent->getAttribute('ATTENDEE'); $params = $vEvent->getAttribute('ATTENDEE', true); } catch (Horde_Icalendar_Exception $e) { try { $attendee = $vEvent->getAttribute('X-ATTENDEE'); $params = $vEvent->getAttribute('X-ATTENDEE', true); } catch (Horde_Icalendar_Exception $e) { } } if ($attendee && !is_array($attendee)) { $attendee = array($attendee); $params = array($params); } try { $users = $vEvent->getAttribute('X-HORDE-ATTENDEE'); $userParams = $vEvent->getAttribute('X-HORDE-ATTENDEE', true); if (!is_array($users)) { $users = array($users); $userParams = array($userParams); } foreach ($userParams as &$param) { $param['hordeUser'] = true; } if ($attendee) { $attendee = array_merge($attendee, $users); $params = array_merge($params, $userParams); } else { $attendee = $users; $params = $userParams; } } catch (Horde_Icalendar_Exception $e) { } } if ($attendee) { // Clear the attendees since we might be editing/replacing the event $this->attendees = new Kronolith_Attendee_List(); for ($i = 0; $i < count($attendee); ++$i) { // Default according to rfc2445: $attendance = Kronolith::PART_REQUIRED; // vCalendar 2.0 style: if (!empty($params[$i]['ROLE'])) { switch ($params[$i]['ROLE']) { case 'OPT-PARTICIPANT': $attendance = Kronolith::PART_OPTIONAL; break; case 'NON-PARTICIPANT': $attendance = Kronolith::PART_NONE; break; } } // vCalendar 1.0 style; if (!empty($params[$i]['EXPECT'])) { switch ($params[$i]['EXPECT']) { case 'REQUEST': $attendance = Kronolith::PART_OPTIONAL; break; case 'FYI': $attendance = Kronolith::PART_NONE; break; } } $response = Kronolith::RESPONSE_NONE; if (empty($params[$i]['PARTSTAT']) && !empty($params[$i]['STATUS'])) { $params[$i]['PARTSTAT'] = $params[$i]['STATUS']; } if (!empty($params[$i]['PARTSTAT'])) { switch ($params[$i]['PARTSTAT']) { case 'ACCEPTED': $response = Kronolith::RESPONSE_ACCEPTED; break; case 'DECLINED': $response = Kronolith::RESPONSE_DECLINED; break; case 'TENTATIVE': $response = Kronolith::RESPONSE_TENTATIVE; break; } } if (!empty($params[$i]['hordeUser'])) { $this->attendees->add(new Kronolith_Attendee(array('user' => $attendee[$i], 'role' => $attendance, 'response' => $response))); } else { $tmp = new Horde_Mail_Rfc822_Address(str_replace(array('MAILTO:', 'mailto:'), '', $attendee[$i])); $email = $tmp->bare_address; $name = isset($params[$i]['CN']) ? $params[$i]['CN'] : null; $this->addAttendee($email, $attendance, $response, $name); } } } $this->_handlevEventRecurrence($vEvent); $this->initialized = true; }