/** * Send an iTip mail message * * @param array Event object to send * @param string iTip method (REQUEST|REPLY|CANCEL) * @param array Hash array with recipient data (name, email) * @param string Mail subject * @param string Mail body text label * @param object Mail_mime object with message data * @return boolean True on success, false on failure */ public function send_itip_message($event, $method, $recipient, $subject, $bodytext, $message = null) { if (!$this->sender['name']) { $this->sender['name'] = $this->sender['email']; } if (!$message) { $message = $this->compose_itip_message($event, $method); } $mailto = rcube_idn_to_ascii($recipient['email']); $headers = $message->headers(); $headers['To'] = format_email_recipient($mailto, $recipient['name']); $headers['Subject'] = $this->cal->gettext(array('name' => $subject, 'vars' => array('title' => $event['title'], 'name' => $this->sender['name']))); // compose a list of all event attendees $attendees_list = array(); foreach ((array) $event['attendees'] as $attendee) { $attendees_list[] = $attendee['name'] && $attendee['email'] ? $attendee['name'] . ' <' . $attendee['email'] . '>' : ($attendee['name'] ? $attendee['name'] : $attendee['email']); } $mailbody = $this->cal->gettext(array('name' => $bodytext, 'vars' => array('title' => $event['title'], 'date' => $this->cal->lib->event_date_text($event, true), 'attendees' => join(', ', $attendees_list), 'sender' => $this->sender['name'], 'organizer' => $this->sender['name']))); // append links for direct invitation replies if ($method == 'REQUEST' && ($token = $this->store_invitation($event, $recipient['email']))) { $mailbody .= "\n\n" . $this->cal->gettext(array('name' => 'invitationattendlinks', 'vars' => array('url' => $this->cal->get_url(array('action' => 'attend', 't' => $token))))); } else { if ($method == 'CANCEL') { $this->cancel_itip_invitation($event); } } $message->headers($headers, true); $message->setTXTBody(rcube_mime::format_flowed($mailbody, 79)); // finally send the message $this->itip_send = true; $sent = $this->rc->deliver_message($message, $headers['X-Sender'], $mailto, $smtp_error); $this->itip_send = false; return $sent; }
/** * Test parse_message() */ function test_parse_message() { $file = file_get_contents(__DIR__ . '/../src/html.msg'); $result = rcube_mime::parse_message($file); $this->assertInstanceOf('rcube_message_part', $result); $this->assertSame('multipart/alternative', $result->mimetype); $this->assertSame('1.0', $result->headers['mime-version']); $this->assertSame('=_68eeaf4ab95b5312965e45c33362338e', $result->ctype_parameters['boundary']); $this->assertSame('1', $result->parts[0]->mime_id); $this->assertSame(12, $result->parts[0]->size); $this->assertSame('text/plain', $result->parts[0]->mimetype); $this->assertSame("this is test", $result->parts[0]->body); $this->assertSame('2', $result->parts[1]->mime_id); $this->assertSame(0, $result->parts[1]->size); $this->assertSame('multipart/related', $result->parts[1]->mimetype); $this->assertCount(2, $result->parts[1]->parts); $this->assertSame('2.1', $result->parts[1]->parts[0]->mime_id); $this->assertSame(257, $result->parts[1]->parts[0]->size); $this->assertSame('text/html', $result->parts[1]->parts[0]->mimetype); $this->assertSame('UTF-8', $result->parts[1]->parts[0]->charset); $this->assertRegExp('/<html>/', $result->parts[1]->parts[0]->body); $this->assertSame('2.2', $result->parts[1]->parts[1]->mime_id); $this->assertSame(793, $result->parts[1]->parts[1]->size); $this->assertSame('image/jpeg', $result->parts[1]->parts[1]->mimetype); $this->assertSame('base64', $result->parts[1]->parts[1]->encoding); $this->assertSame('inline', $result->parts[1]->parts[1]->disposition); $this->assertSame('photo-mini.jpg', $result->parts[1]->parts[1]->filename); }
function mh_find_match($message) { foreach ($this->prefs as $p) { if (stristr(rcube_mime::decode_header($message->{$p}['header']), $p['input'])) { return $p['color']; } } return false; }
/** * Test decoding of header values * Uses rcube_mime::decode_mime_string() */ function test_header_decode_qp() { $test = array('quoted-printable (1)' => array('in' => '=?utf-8?Q?Certifica=C3=A7=C3=A3??=', 'out' => 'Certifica=C3=A7=C3=A3?'), 'quoted-printable (2)' => array('in' => '=?utf-8?Q?Certifica=?= =?utf-8?Q?C3=A7=C3=A3?=', 'out' => 'Certifica=C3=A7=C3=A3'), 'quoted-printable (3)' => array('in' => '=?utf-8?Q??= =?utf-8?Q??=', 'out' => ''), 'quoted-printable (4)' => array('in' => '=?utf-8?Q??= a =?utf-8?Q??=', 'out' => ' a '), 'quoted-printable (5)' => array('in' => '=?utf-8?Q?a?= =?utf-8?Q?b?=', 'out' => 'ab'), 'quoted-printable (6)' => array('in' => '=?utf-8?Q? ?= =?utf-8?Q?a?=', 'out' => ' a'), 'quoted-printable (7)' => array('in' => '=?utf-8?Q?___?= =?utf-8?Q?a?=', 'out' => ' a')); foreach ($test as $idx => $item) { $res = rcube_mime::decode_mime_string($item['in'], 'UTF-8'); $res = quoted_printable_encode($res); $this->assertEqual($item['out'], $res, "Header decoding for: " . $idx); } }
/** * Returns header value */ public function get($name, $decode = true) { $name = strtolower($name); if (isset($this->obj_headers[$name])) { $value = $this->{$this->obj_headers[$name]}; } else { $value = $this->others[$name]; } return $decode ? rcube_mime::decode_header($value, $this->charset) : $value; }
/** * Get recipients' e-mail addresses * * @return array Recipients' addresses */ public function getRecipients() { // get sender address $headers = $this->message->headers(); $to = rcube_mime::decode_address_list($headers['To'], null, false, null, true); $cc = rcube_mime::decode_address_list($headers['Cc'], null, false, null, true); $bcc = rcube_mime::decode_address_list($headers['Bcc'], null, false, null, true); $recipients = array_unique(array_merge($to, $cc, $bcc)); $recipients = array_diff($recipients, array('undisclosed-recipients:')); return $recipients; }
/** * Check the correct configuration of the 'mime_types' mapping option */ function check_mime_extensions() { $types = array('application/zip' => 'zip', 'application/x-tar' => 'tar', 'application/java-archive' => 'jar', 'image/gif' => 'gif', 'image/svg+xml' => 'svg'); $errors = array(); foreach ($types as $mimetype => $expected) { $ext = rcube_mime::get_mime_extensions($mimetype); if ($ext[0] != $expected) { $errors[] = array($mimetype, $ext, $expected); } } return $errors; }
/** * Given a header, this function will decode it according to RFC2047. * Probably not *exactly* conformant, but it does pass all the given * examples (in RFC2047). * * @param string $input Input header value to decode * * @return string Decoded header value */ protected function decodeHeader($input) { return rcube_mime::decode_mime_string($input, $this->params['default_charset']); }
public static function format_flowed($text, $length = 72) { return rcube_mime::format_flowed($text, $length); }
/** * Extract mail headers for new filter form */ private function parse_headers($headers) { $result = array(); if ($headers->subject) { $result[] = array('Subject', rcube_mime::decode_header($headers->subject)); } // @TODO: List-Id, others? foreach (array('From', 'To') as $h) { $hl = strtolower($h); if ($headers->{$hl}) { $list = rcube_mime::decode_address_list($headers->{$hl}); foreach ($list as $item) { if ($item['mailto']) { $result[] = array($h, $item['mailto']); } } } } return $result; }
/** * Handler for POST request to import an event attached to a mail message */ public function mail_import_itip() { $itip_sending = $this->rc->config->get('calendar_itip_send_option', $this->defaults['calendar_itip_send_option']); $uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST); $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST); $mime_id = rcube_utils::get_input_value('_part', rcube_utils::INPUT_POST); $status = rcube_utils::get_input_value('_status', rcube_utils::INPUT_POST); $delete = intval(rcube_utils::get_input_value('_del', rcube_utils::INPUT_POST)); $noreply = intval(rcube_utils::get_input_value('_noreply', rcube_utils::INPUT_POST)); $noreply = $noreply || $status == 'needs-action' || $itip_sending === 0; $instance = rcube_utils::get_input_value('_instance', rcube_utils::INPUT_POST); $savemode = rcube_utils::get_input_value('_savemode', rcube_utils::INPUT_POST); $error_msg = $this->gettext('errorimportingevent'); $success = false; $delegate = null; if ($status == 'delegated') { $delegates = rcube_mime::decode_address_list(rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST, true), 1, false); $delegate = reset($delegates); if (empty($delegate) || empty($delegate['mailto'])) { $this->rc->output->command('display_message', $this->gettext('libcalendaring.delegateinvalidaddress'), 'error'); return; } } // successfully parsed events? if ($event = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event')) { // forward iTip request to delegatee if ($delegate) { $rsvpme = intval(rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST)); $itip = $this->load_itip(); if ($itip->delegate_to($event, $delegate, $rsvpme ? true : false)) { $this->rc->output->show_message('calendar.itipsendsuccess', 'confirmation'); } else { $this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error'); } // the delegator is set to non-participant, thus save as non-blocking $event['free_busy'] = 'free'; } // find writeable calendar to store event $cal_id = !empty($_REQUEST['_folder']) ? rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST) : null; $dontsave = $_REQUEST['_folder'] === '' && $event['_method'] == 'REQUEST'; $calendars = $this->driver->list_calendars(calendar_driver::FILTER_PERSONAL); $calendar = $calendars[$cal_id]; // select default calendar except user explicitly selected 'none' if (!$calendar && !$dontsave) { $calendar = $this->get_default_calendar($event['sensitivity']); } $metadata = array('uid' => $event['uid'], '_instance' => $event['_instance'], 'changed' => is_object($event['changed']) ? $event['changed']->format('U') : 0, 'sequence' => intval($event['sequence']), 'fallback' => strtoupper($status), 'method' => $event['_method'], 'task' => 'calendar'); // update my attendee status according to submitted method if (!empty($status)) { $organizer = null; $emails = $this->get_user_emails(); foreach ($event['attendees'] as $i => $attendee) { if ($attendee['role'] == 'ORGANIZER') { $organizer = $attendee; } else { if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) { $event['attendees'][$i]['status'] = strtoupper($status); if (!in_array($event['attendees'][$i]['status'], array('NEEDS-ACTION', 'DELEGATED'))) { $event['attendees'][$i]['rsvp'] = false; } // unset RSVP attribute $metadata['attendee'] = $attendee['email']; $metadata['rsvp'] = $attendee['role'] != 'NON-PARTICIPANT'; $reply_sender = $attendee['email']; $event_attendee = $attendee; } } } // add attendee with this user's default identity if not listed if (!$reply_sender) { $sender_identity = $this->rc->user->list_emails(true); $event['attendees'][] = array('name' => $sender_identity['name'], 'email' => $sender_identity['email'], 'role' => 'OPT-PARTICIPANT', 'status' => strtoupper($status)); $metadata['attendee'] = $sender_identity['email']; } } // save to calendar if ($calendar && $calendar['editable']) { // check for existing event with the same UID $existing = $this->driver->get_event($event, calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL); if ($existing) { // forward savemode for correct updates of recurring events $existing['_savemode'] = $savemode ?: $event['_savemode']; // only update attendee status if ($event['_method'] == 'REPLY') { // try to identify the attendee using the email sender address $existing_attendee = -1; $existing_attendee_emails = array(); foreach ($existing['attendees'] as $i => $attendee) { $existing_attendee_emails[] = $attendee['email']; if ($event['_sender'] && ($attendee['email'] == $event['_sender'] || $attendee['email'] == $event['_sender_utf'])) { $existing_attendee = $i; } } $event_attendee = null; $update_attendees = array(); foreach ($event['attendees'] as $attendee) { if ($event['_sender'] && ($attendee['email'] == $event['_sender'] || $attendee['email'] == $event['_sender_utf'])) { $event_attendee = $attendee; $update_attendees[] = $attendee; $metadata['fallback'] = $attendee['status']; $metadata['attendee'] = $attendee['email']; $metadata['rsvp'] = $attendee['rsvp'] || $attendee['role'] != 'NON-PARTICIPANT'; if ($attendee['status'] != 'DELEGATED') { break; } } else { if (!empty($attendee['delegated-from']) && (stripos($attendee['delegated-from'], $event['_sender']) !== false || stripos($attendee['delegated-from'], $event['_sender_utf']) !== false)) { $update_attendees[] = $attendee; if (!in_array($attendee['email'], $existing_attendee_emails)) { $existing['attendees'][] = $attendee; } } } } // if delegatee has declined, set delegator's RSVP=True if ($event_attendee && $event_attendee['status'] == 'DECLINED' && $event_attendee['delegated-from']) { foreach ($existing['attendees'] as $i => $attendee) { if ($attendee['email'] == $event_attendee['delegated-from']) { $existing['attendees'][$i]['rsvp'] = true; break; } } } // found matching attendee entry in both existing and new events if ($existing_attendee >= 0 && $event_attendee) { $existing['attendees'][$existing_attendee] = $event_attendee; $success = $this->driver->update_attendees($existing, $update_attendees); } else { if (($event['sequence'] >= $existing['sequence'] || $event['changed'] >= $existing['changed']) && $event_attendee) { $existing['attendees'][] = $event_attendee; $success = $this->driver->update_attendees($existing, $update_attendees); } else { $error_msg = $this->gettext('newerversionexists'); } } } else { if ($status == 'declined' && $delete) { $deleted = $this->driver->remove_event($existing, true); $success = true; } else { if ($event['sequence'] >= $existing['sequence'] || $event['changed'] >= $existing['changed']) { $event['id'] = $existing['id']; $event['calendar'] = $existing['calendar']; // preserve my participant status for regular updates if (empty($status)) { $emails = $this->get_user_emails(); foreach ($event['attendees'] as $i => $attendee) { if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) { foreach ($existing['attendees'] as $j => $_attendee) { if ($attendee['email'] == $_attendee['email']) { $event['attendees'][$i] = $existing['attendees'][$j]; break; } } } } } // set status=CANCELLED on CANCEL messages if ($event['_method'] == 'CANCEL') { $event['status'] = 'CANCELLED'; } // show me as free when declined (#1670) if ($status == 'declined' || $event['status'] == 'CANCELLED' || $event_attendee['role'] == 'NON-PARTICIPANT') { $event['free_busy'] = 'free'; } $success = $this->driver->edit_event($event); } else { if (!empty($status)) { $existing['attendees'] = $event['attendees']; if ($status == 'declined' || $event_attendee['role'] == 'NON-PARTICIPANT') { // show me as free when declined (#1670) $existing['free_busy'] = 'free'; } $success = $this->driver->edit_event($existing); } else { $error_msg = $this->gettext('newerversionexists'); } } } } } else { if (!$existing && ($status != 'declined' || $this->rc->config->get('kolab_invitation_calendars'))) { if ($status == 'declined' || $event['status'] == 'CANCELLED' || $event_attendee['role'] == 'NON-PARTICIPANT') { $event['free_busy'] = 'free'; } // if the RSVP reply only refers to a single instance: // store unmodified master event with current instance as exception if (!empty($instance) && !empty($savemode) && $savemode != 'all') { $master = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event'); if ($master['recurrence'] && !$master['_instance']) { // compute recurring events until this instance's date if ($recurrence_date = rcube_utils::anytodatetime($instance, $master['start']->getTimezone())) { $recurrence_date->setTime(23, 59, 59); foreach ($this->driver->get_recurring_events($master, $master['start'], $recurrence_date) as $recurring) { if ($recurring['_instance'] == $instance) { // copy attendees block with my partstat to exception $recurring['attendees'] = $event['attendees']; $master['recurrence']['EXCEPTIONS'][] = $recurring; $event = $recurring; // set reference for iTip reply break; } } $master['calendar'] = $event['calendar'] = $calendar['id']; $success = $this->driver->new_event($master); } else { $master = null; } } else { $master = null; } } // save to the selected/default calendar if (!$master) { $event['calendar'] = $calendar['id']; $success = $this->driver->new_event($event); } } else { if ($status == 'declined') { $error_msg = null; } } } } else { if ($status == 'declined' || $dontsave) { $error_msg = null; } else { $error_msg = $this->gettext('nowritecalendarfound'); } } } if ($success) { $message = $event['_method'] == 'REPLY' ? 'attendeupdateesuccess' : ($deleted ? 'successremoval' : ($existing ? 'updatedsuccessfully' : 'importedsuccessfully')); $this->rc->output->command('display_message', $this->gettext(array('name' => $message, 'vars' => array('calendar' => $calendar['name']))), 'confirmation'); } if ($success || $dontsave) { $metadata['calendar'] = $event['calendar']; $metadata['nosave'] = $dontsave; $metadata['rsvp'] = intval($metadata['rsvp']); $metadata['after_action'] = $this->rc->config->get('calendar_itip_after_action', $this->defaults['calendar_itip_after_action']); $this->rc->output->command('plugin.itip_message_processed', $metadata); $error_msg = null; } else { if ($error_msg) { $this->rc->output->command('display_message', $error_msg, 'error'); } } // send iTip reply if ($event['_method'] == 'REQUEST' && $organizer && !$noreply && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) { $event['comment'] = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST); $itip = $this->load_itip(); $itip->set_sender_email($reply_sender); if ($itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) { $this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation'); } else { $this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error'); } } $this->rc->output->send(); }
/** * Parse decrypted message body into structure * * @param string Message body * * @return array Message structure */ private function parse_body(&$body) { // Mail_mimeDecode need \r\n end-line, but gpg may return \n $body = preg_replace('/\\r?\\n/', "\r\n", $body); // parse the body into structure $struct = rcube_mime::parse_message($body); return $struct; }
/** * Get message headers for popup window */ function mail_headers($args) { $headers = $args['headers']; $ret = array(); if ($headers->subject) { $ret[] = array('Subject', rcube_mime::decode_header($headers->subject)); } // @TODO: List-Id, others? foreach (array('From', 'To') as $h) { $hl = strtolower($h); if ($headers->{$hl}) { $list = rcube_mime::decode_address_list($headers->{$hl}); foreach ($list as $item) { if ($item['mailto']) { $ret[] = array($h, $item['mailto']); } } } } if ($this->rc->action == 'preview') { $this->rc->output->command('parent.set_env', array('sieve_headers' => $ret)); } else { $this->rc->output->set_env('sieve_headers', $ret); } return $args; }
/** * Check the correct configuration of the 'mime_types' mapping option */ public function check_mime_extensions() { $errors = array(); $types = array('application/zip' => 'zip', 'text/css' => 'css', 'application/pdf' => 'pdf', 'image/gif' => 'gif', 'image/svg+xml' => 'svg'); foreach ($types as $mimetype => $expected) { $ext = rcube_mime::get_mime_extensions($mimetype); if (!in_array($expected, (array) $ext)) { $errors[] = array($mimetype, $ext, $expected); } } return $errors; }
function rc_image_content_type($data) { return rcube_mime::image_content_type($data); }
/** * Test format=flowed unfolding */ function test_unfold_flowed() { $flowed = file_get_contents(TESTS_DIR . 'src/format-flowed.txt'); $unfolded = file_get_contents(TESTS_DIR . 'src/format-flowed-unfolded.txt'); $this->assertEquals($unfolded, rcube_mime::unfold_flowed($flowed), "Test correct unfolding of quoted lines"); }
/** * Helper method to fix potentially invalid mimetypes of PDF attachments */ private function fix_mime_part($part, $message) { // Some versions of Outlook create garbage Content-Type: // application/pdf.A520491B_3BF7_494D_8855_7FAC2C6C0608 if (preg_match('/^application\\/pdf.+/', $part->mimetype)) { $part->mimetype = 'application/pdf'; } // try to fix invalid application/octet-stream mimetypes for PDF attachments if ($part->mimetype == 'application/octet-stream' && preg_match('/\\.pdf$/', strval($part->filename))) { $body = $message->get_part_body($part->mime_id, false, 2048); $real_mimetype = rcube_mime::file_content_type($body, $part->filename, $part->mimetype, true, true); if (in_array($real_mimetype, $this->pdf_mimetypes)) { $part->mimetype = $real_mimetype; } } list($part->ctype_primary, $part->ctype_secondary) = explode('/', $part->mimetype); }
/** * Server-side key pair generation handler */ private function key_generate() { // Crypt_GPG does not support key generation for multiple identities // It is also very slow (which is problematic because it may exceed // request time limit) and requires entropy generator // That's why we use only OpenPGP.js method of key generation return; $user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST, true); $pass = rcube_utils::get_input_value('_password', rcube_utils::INPUT_POST, true); $size = (int) rcube_utils::get_input_value('_size', rcube_utils::INPUT_POST); if ($size > 4096) { $size = 4096; } $ident = rcube_mime::decode_address_list($user, 1, false); if (empty($ident)) { $this->rc->output->show_message('enigma.keygenerateerror', 'error'); $this->rc->output->send(); } $this->enigma->load_engine(); $result = $this->enigma->engine->generate_key(array('user' => $ident[1]['name'], 'email' => $ident[1]['mailto'], 'password' => $pass, 'size' => $size)); if ($result instanceof enigma_key) { $this->rc->output->command('enigma_key_create_success'); $this->rc->output->show_message('enigma.keygeneratesuccess', 'confirmation'); } else { $this->rc->output->show_message('enigma.keygenerateerror', 'error'); } $this->rc->output->send(); }
/** * Server-side key pair generation handler */ private function key_generate() { $user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST, true); $pass = rcube_utils::get_input_value('_password', rcube_utils::INPUT_POST, true); $size = (int) rcube_utils::get_input_value('_size', rcube_utils::INPUT_POST); if ($size > 4096) { $size = 4096; } $ident = rcube_mime::decode_address_list($user, 1, false); if (empty($ident)) { $this->rc->output->show_message('enigma.keygenerateerror', 'error'); $this->rc->output->send(); } $this->enigma->load_engine(); $result = $this->enigma->engine->generate_key(array('user' => $ident[1]['name'], 'email' => $ident[1]['mailto'], 'password' => $pass, 'size' => $size)); if ($result instanceof enigma_key) { $this->rc->output->command('enigma_key_create_success'); $this->rc->output->show_message('enigma.keygeneratesuccess', 'confirmation'); } else { $this->rc->output->show_message('enigma.keygenerateerror', 'error'); } $this->rc->output->send(); }
/** * Collect the email address of a just-sent email recipients into * the automatic addressbook (if it's not already in another * addressbook). */ public function register_recipients($p) { $rcmail = rcmail::get_instance(); if (!$rcmail->config->get('use_auto_abook', true)) { return; } $headers = $p['headers']; if (!class_exists('rcube_mime')) { // RC < 0.8 compatibility code $IMAP = new rcube_imap(null); $all_recipients = array_merge($IMAP->decode_address_list($headers['To'], null, true, $headers['charset']), $IMAP->decode_address_list($headers['Cc'], null, true, $headers['charset']), $IMAP->decode_address_list($headers['Bcc'], null, true, $headers['charset'])); } else { $all_recipients = array_merge(rcube_mime::decode_address_list($headers['To'], null, true, $headers['charset']), rcube_mime::decode_address_list($headers['Cc'], null, true, $headers['charset']), rcube_mime::decode_address_list($headers['Bcc'], null, true, $headers['charset'])); } require_once dirname(__FILE__) . '/automatic_addressbook_backend.php'; $CONTACTS = new automatic_addressbook_backend($rcmail->db, $rcmail->user->ID); foreach ($all_recipients as $recipient) { // Bcc and Cc can be empty if ($recipient['mailto'] != '') { $contact = array('email' => $recipient['mailto'], 'name' => $recipient['name']); // use email address part for name if (empty($contact['name']) || $contact['name'] == $contact['email']) { $contact['name'] = ucfirst(preg_replace('/[\\.\\-]/', ' ', substr($contact['email'], 0, strpos($contact['email'], '@')))); } /* We only want to add the contact to the collected contacts * address book if it is not already in an addressbook, so we * first lookup in every address source. */ $book_types = (array) $rcmail->config->get('autocomplete_addressbooks', 'sql'); foreach ($book_types as $id) { $abook = $rcmail->get_address_book($id); $previous_entries = $abook->search('email', $contact['email'], false, false); if ($previous_entries->count) { break; } } if (!$previous_entries->count) { $plugin = $rcmail->plugins->exec_hook('contact_create', array('record' => $contact, 'source' => $this->abook_id)); if (!$plugin['abort']) { $CONTACTS->insert($contact, false); } } } } }
/** * Workhorse function that does actual conversion. * * @param string Plain text */ protected function _converter($text) { // make links and email-addresses clickable $attribs = array('link_attribs' => array('rel' => 'noreferrer', 'target' => '_blank')); $replacer = new $this->config['replacer']($attribs); if ($this->config['flowed']) { $flowed_char = 0x1; $text = rcube_mime::unfold_flowed($text, chr($flowed_char)); } // search for patterns like links and e-mail addresses and replace with tokens if ($this->config['links']) { $text = $replacer->replace($text); } // split body into single lines $text = preg_split('/\\r?\\n/', $text); $quote_level = 0; $last = null; // wrap quoted lines with <blockquote> for ($n = 0, $cnt = count($text); $n < $cnt; $n++) { $flowed = false; if ($this->config['flowed'] && ord($text[$n][0]) == $flowed_char) { $flowed = true; $text[$n] = substr($text[$n], 1); } if ($text[$n][0] == '>' && preg_match('/^(>+ {0,1})+/', $text[$n], $regs)) { $q = substr_count($regs[0], '>'); $text[$n] = substr($text[$n], strlen($regs[0])); $text[$n] = $this->_convert_line($text[$n], $flowed || $this->config['wrap']); $_length = strlen(str_replace(' ', '', $text[$n])); if ($q > $quote_level) { if ($last !== null) { $text[$last] .= (!$length ? "\n" : '') . $replacer->get_replacement($replacer->add(str_repeat('<blockquote>', $q - $quote_level))) . $text[$n]; unset($text[$n]); } else { $text[$n] = $replacer->get_replacement($replacer->add(str_repeat('<blockquote>', $q - $quote_level))) . $text[$n]; $last = $n; } } else { if ($q < $quote_level) { $text[$last] .= (!$length ? "\n" : '') . $replacer->get_replacement($replacer->add(str_repeat('</blockquote>', $quote_level - $q))) . $text[$n]; unset($text[$n]); } else { $last = $n; } } } else { $text[$n] = $this->_convert_line($text[$n], $flowed || $this->config['wrap']); $q = 0; $_length = strlen(str_replace(' ', '', $text[$n])); if ($quote_level > 0) { $text[$last] .= (!$length ? "\n" : '') . $replacer->get_replacement($replacer->add(str_repeat('</blockquote>', $quote_level))) . $text[$n]; unset($text[$n]); } else { $last = $n; } } $quote_level = $q; $length = $_length; } if ($quote_level > 0) { $text[$last] .= $replacer->get_replacement($replacer->add(str_repeat('</blockquote>', $quote_level))); } $text = join("\n", $text); // colorize signature (up to <sig_max_lines> lines) $len = strlen($text); $sig_sep = "--" . $this->config['space'] . "\n"; $sig_max_lines = rcube::get_instance()->config->get('sig_max_lines', 15); while (($sp = strrpos($text, $sig_sep, $sp ? -$len + $sp - 1 : 0)) !== false) { if ($sp == 0 || $text[$sp - 1] == "\n") { // do not touch blocks with more that X lines if (substr_count($text, "\n", $sp) < $sig_max_lines) { $text = substr($text, 0, max(0, $sp)) . '<span class="sig">' . substr($text, $sp) . '</span>'; } break; } } // insert url/mailto links and citation tags $text = $replacer->resolve($text); // replace line breaks $text = str_replace("\n", $this->config['break'], $text); return $this->config['begin'] . $text . $this->config['end']; }
/** * Helper method to packs all the given messages into a zip archive * * @param array List of message UIDs to download */ private function _download_messages($uids) { $rcmail = rcmail::get_instance(); $imap = $rcmail->storage; $temp_dir = $rcmail->config->get('temp_dir'); $tmpfname = tempnam($temp_dir, 'zipdownload'); $tempfiles = array($tmpfname); // open zip file $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); foreach ($uids as $uid) { $headers = $imap->get_message_headers($uid); $subject = rcube_mime::decode_mime_string((string) $headers->subject); $subject = $this->_convert_filename($subject); $subject = substr($subject, 0, 16); if (isset($subject) && $subject != "") { $disp_name = $subject . ".eml"; } else { $disp_name = "message_rfc822.eml"; } $disp_name = $uid . "_" . $disp_name; $tmpfn = tempnam($temp_dir, 'zipmessage'); $tmpfp = fopen($tmpfn, 'w'); $imap->get_raw_body($uid, $tmpfp); $tempfiles[] = $tmpfn; fclose($tmpfp); $zip->addFile($tmpfn, $disp_name); } $zip->close(); $this->_deliver_zipfile($tmpfname, $imap->get_folder() . '.zip'); // delete temporary files from disk foreach ($tempfiles as $tmpfn) { unlink($tmpfn); } exit; }
/** * Handler for attachment uploads */ public function attachment_upload($session_key, $id_prefix = '') { // Upload progress update if (!empty($_GET['_progress'])) { $this->rc->upload_progress(); } $recid = $id_prefix . rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC); $uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_GPC); if (!is_array($_SESSION[$session_key]) || $_SESSION[$session_key]['id'] != $recid) { $_SESSION[$session_key] = array(); $_SESSION[$session_key]['id'] = $recid; $_SESSION[$session_key]['attachments'] = array(); } // clear all stored output properties (like scripts and env vars) $this->rc->output->reset(); if (is_array($_FILES['_attachments']['tmp_name'])) { foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) { // Process uploaded attachment if there is no error $err = $_FILES['_attachments']['error'][$i]; if (!$err) { $attachment = array('path' => $filepath, 'size' => $_FILES['_attachments']['size'][$i], 'name' => $_FILES['_attachments']['name'][$i], 'mimetype' => rcube_mime::file_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]), 'group' => $recid); $attachment = $this->rc->plugins->exec_hook('attachment_upload', $attachment); } if (!$err && $attachment['status'] && !$attachment['abort']) { $id = $attachment['id']; // store new attachment in session unset($attachment['status'], $attachment['abort']); $_SESSION[$session_key]['attachments'][$id] = $attachment; if (($icon = $_SESSION[$session_key . '_deleteicon']) && is_file($icon)) { $button = html::img(array('src' => $icon, 'alt' => $this->rc->gettext('delete'))); } else { $button = rcube::Q($this->rc->gettext('delete')); } $content = html::a(array('href' => "#delete", 'class' => 'delete', 'onclick' => sprintf("return %s.remove_from_attachment_list('rcmfile%s')", rcmail_output::JS_OBJECT_NAME, $id), 'title' => $this->rc->gettext('delete'), 'aria-label' => $this->rc->gettext('delete') . ' ' . $attachment['name']), $button); $content .= rcube::Q($attachment['name']); $this->rc->output->command('add2attachment_list', "rcmfile{$id}", array('html' => $content, 'name' => $attachment['name'], 'mimetype' => $attachment['mimetype'], 'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']), 'complete' => true), $uploadid); } else { // upload failed if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { $msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize')))))); } else { if ($attachment['error']) { $msg = $attachment['error']; } else { $msg = $this->rc->gettext('fileuploaderror'); } } $this->rc->output->command('display_message', $msg, 'error'); $this->rc->output->command('remove_from_attachment_list', $uploadid); } } } else { if ($_SERVER['REQUEST_METHOD'] == 'POST') { // if filesize exceeds post_max_size then $_FILES array is empty, // show filesizeerror instead of fileuploaderror if ($maxsize = ini_get('post_max_size')) { $msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => $this->rc->show_bytes(parse_bytes($maxsize))))); } else { $msg = $this->rc->gettext('fileuploaderror'); } $this->rc->output->command('display_message', $msg, 'error'); $this->rc->output->command('remove_from_attachment_list', $uploadid); } } $this->rc->output->send('iframe'); }
/** * Get attachment body * @see calendar_driver::get_attachment_body() */ public function get_attachment_body($id, $event) { if (!($cal = $this->get_calendar($event['calendar']))) { return false; } // get old revision of event if ($event['rev']) { if (empty($this->bonnie_api)) { return false; } $cid = substr($id, 4); // call Bonnie API and get the raw mime message list($uid, $mailbox, $msguid) = $this->_resolve_event_identity($event); if ($msg_raw = $this->bonnie_api->rawdata('event', $uid, $event['rev'], $mailbox, $msguid)) { // parse the message and find the part with the matching content-id $message = rcube_mime::parse_message($msg_raw); foreach ((array) $message->parts as $part) { if ($part->headers['content-id'] && trim($part->headers['content-id'], '<>') == $cid) { return $part->body; } } } return false; } return $cal->get_attachment_body($id, $event); }
public static function explode_header_string($separator, $str, $remove_comments = false) { return rcube_mime::explode_header_string($separator, $str, $remove_comments); }
/** * Returns header value */ public function get($name, $decode = true) { $name = strtolower($name); if (isset($this->obj_headers[$name])) { $value = $this->{$this->obj_headers[$name]}; } else { $value = $this->others[$name]; } if ($decode) { if (is_array($value)) { foreach ($value as $key => $val) { $value[$key] = rcube_mime::decode_header($val, $this->charset); $value[$key] = rcube_charset::clean($val); } } else { $value = rcube_mime::decode_header($value, $this->charset); $value = rcube_charset::clean($value); } } return $value; }
/** * Recursive method to convert a Mail_mimeDecode part into a rcube_message_part object * * @param object A message part struct * @param int Part count * @param string Parent MIME ID * * @return object rcube_message_part */ private static function structure_part($part, $count = 0, $parent = '') { $struct = new rcube_message_part(); $struct->mime_id = $part->mime_id ? $part->mime_id : (empty($parent) ? (string) $count : "{$parent}.{$count}"); $struct->headers = $part->headers; $struct->ctype_primary = $part->ctype_primary; $struct->ctype_secondary = $part->ctype_secondary; $struct->mimetype = $part->ctype_primary . '/' . $part->ctype_secondary; $struct->ctype_parameters = $part->ctype_parameters; if ($part->headers['content-transfer-encoding']) { $struct->encoding = $part->headers['content-transfer-encoding']; } if ($part->ctype_parameters['charset']) { $struct->charset = $part->ctype_parameters['charset']; } $part_charset = $struct->charset ? $struct->charset : self::get_charset(); // determine filename if (($filename = $part->d_parameters['filename']) || ($filename = $part->ctype_parameters['name'])) { $struct->filename = rcube_mime::decode_mime_string($filename, $part_charset); } // copy part body and convert it to UTF-8 if necessary $struct->body = $part->ctype_primary == 'text' || !$part->ctype_parameters['charset'] ? rcube_charset::convert($part->body, $part_charset) : $part->body; $struct->size = strlen($part->body); $struct->disposition = $part->disposition; foreach ((array) $part->parts as $child_part) { $struct->parts[] = self::structure_part($child_part, ++$count, $struct->mime_id); } return $struct; }
/** * Send the given message using the configured method. * * @param object $message Reference to Mail_MIME object * @param string $from Sender address string * @param array $mailto Array of recipient address strings * @param array $error SMTP error array (reference) * @param string $body_file Location of file with saved message body (reference), * used when delay_file_io is enabled * @param array $options SMTP options (e.g. DSN request) * * @return boolean Send status. */ public function deliver_message(&$message, $from, $mailto, &$error, &$body_file = null, $options = null) { $plugin = $this->plugins->exec_hook('message_before_send', array('message' => $message, 'from' => $from, 'mailto' => $mailto, 'options' => $options)); if ($plugin['abort']) { if (!empty($plugin['error'])) { $error = $plugin['error']; } if (!empty($plugin['body_file'])) { $body_file = $plugin['body_file']; } return isset($plugin['result']) ? $plugin['result'] : false; } $from = $plugin['from']; $mailto = $plugin['mailto']; $options = $plugin['options']; $message = $plugin['message']; $headers = $message->headers(); // send thru SMTP server using custom SMTP library if ($this->config->get('smtp_server')) { // generate list of recipients $a_recipients = (array) $mailto; if (strlen($headers['Cc'])) { $a_recipients[] = $headers['Cc']; } if (strlen($headers['Bcc'])) { $a_recipients[] = $headers['Bcc']; } // clean Bcc from header for recipients $send_headers = $headers; unset($send_headers['Bcc']); // here too, it because txtHeaders() below use $message->_headers not only $send_headers unset($message->_headers['Bcc']); $smtp_headers = $message->txtHeaders($send_headers, true); if ($message->getParam('delay_file_io')) { // use common temp dir $temp_dir = $this->config->get('temp_dir'); $body_file = tempnam($temp_dir, 'rcmMsg'); if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { self::raise_error(array('code' => 650, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: " . $mime_result->getMessage()), TRUE, FALSE); return false; } $msg_body = fopen($body_file, 'r'); } else { $msg_body = $message->get(); } // send message if (!is_object($this->smtp)) { $this->smtp_init(true); } $sent = $this->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $options); $response = $this->smtp->get_response(); $error = $this->smtp->get_error(); // log error if (!$sent) { self::raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__, 'message' => join("\n", $response)), true, false); } } else { // unset some headers because they will be added by the mail() function $headers_enc = $message->headers($headers); $headers_php = $message->_headers; unset($headers_php['To'], $headers_php['Subject']); // reset stored headers and overwrite $message->_headers = array(); $header_str = $message->txtHeaders($headers_php); // #1485779 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) { $headers_enc['To'] = implode(', ', $m[1]); } } $msg_body = $message->get(); if (PEAR::isError($msg_body)) { self::raise_error(array('code' => 650, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: " . $msg_body->getMessage()), TRUE, FALSE); } else { $delim = $this->config->header_delimiter(); $to = $headers_enc['To']; $subject = $headers_enc['Subject']; $header_str = rtrim($header_str); if ($delim != "\r\n") { $header_str = str_replace("\r\n", $delim, $header_str); $msg_body = str_replace("\r\n", $delim, $msg_body); $to = str_replace("\r\n", $delim, $to); $subject = str_replace("\r\n", $delim, $subject); } if (filter_var(ini_get('safe_mode'), FILTER_VALIDATE_BOOLEAN)) { $sent = mail($to, $subject, $msg_body, $header_str); } else { $sent = mail($to, $subject, $msg_body, $header_str, "-f{$from}"); } } } if ($sent) { $this->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $msg_body)); // remove MDN headers after sending unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']); if ($this->config->get('smtp_log')) { // get all recipient addresses if (is_array($mailto)) { $mailto = implode(',', $mailto); } if ($headers['Cc']) { $mailto .= ',' . $headers['Cc']; } if ($headers['Bcc']) { $mailto .= ',' . $headers['Bcc']; } $mailto = rcube_mime::decode_address_list($mailto, null, false, null, true); self::write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s", $this->user->get_username(), rcube_utils::remote_addr(), implode(', ', $mailto), !empty($response) ? join('; ', $response) : '')); } } else { // allow plugins to catch sending errors with the same parameters as in 'message_before_send' $this->plugins->exec_hook('message_send_error', $plugin + array('error' => $error)); } if (is_resource($msg_body)) { fclose($msg_body); } $message->_headers = array(); $message->headers($headers); return $sent; }
function save_data() { $COMPOSE_ID = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC); $COMPOSE = null; if ($COMPOSE_ID && $_SESSION['compose_data_' . $COMPOSE_ID]) { $SESSION_KEY = 'compose_data_' . $COMPOSE_ID; $COMPOSE =& $_SESSION[$SESSION_KEY]; } if (!$COMPOSE) { die("Invalid session var!"); } $uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_POST); $files = rcube_utils::get_input_value('files', rcube_utils::INPUT_POST); $RCMAIL = rcmail::get_instance(); $RCMAIL->output->reset(); //rcube::write_log('dropbox_attachments', $files); if (is_array($files)) { $multiple = count($files) > 1; foreach ($files as $i => $file) { /*File checks*/ $err = false; if ($file['is_dir'] == 'true') { $err = "UPLOAD_ERR_DIRECTORY"; } // Fetch file $filepath = $this->download_fopen($file['link']); //rcube::write_log('dropbox_attachments', link); //rcube::write_log('dropbox_attachments', $filepath); rcube::write_log('dropbox_attachments', $file['link']); if (!$filepath) { $err = "UPLOAD_ERR_FETCH"; } if (!$err) { $attachment = $this->move_file(array('path' => $filepath, 'size' => $file['bytes'], 'name' => $file['name'], 'mimetype' => rcube_mime::file_content_type($filepath, $file['name']), 'group' => $COMPOSE_ID)); } //rcube::write_log('dropbox_attachments', $attachment); if (!$err && $attachment['status'] && !$attachment['abort']) { $id = $attachment['id']; // store new attachment in session unset($attachment['status'], $attachment['abort']); $RCMAIL->session->append($SESSION_KEY . '.attachments', $id, $attachment); if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) { $button = html::img(array('src' => $icon, 'alt' => $RCMAIL->gettext('delete'))); } else { if ($COMPOSE['textbuttons']) { $button = rcube::Q($RCMAIL->gettext('delete')); } else { $button = ''; } } $content = html::a(array('href' => "#delete", 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id), 'title' => $RCMAIL->gettext('delete'), 'class' => 'delete'), $button); $content .= rcube::Q($attachment['name']); $RCMAIL->output->command('add2attachment_list', "rcmfile{$id}", array('html' => $content, 'name' => $attachment['name'], 'mimetype' => $attachment['mimetype'], 'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']), 'complete' => true), $uploadid); } else { // upload failed if ($err == "UPLOAD_ERR_DIRECTORY") { $msg = "Directory upload not allowed."; } else { if ($err == "UPLOAD_ERR_FETCH") { $msg = "Failed to download file from Dropbox"; } else { if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { $size = $RCMAIL->show_bytes(parse_bytes(ini_get('upload_max_filesize'))); $msg = $RCMAIL->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => $size))); } else { if ($attachment['error']) { $msg = $attachment['error']; } else { $msg = $RCMAIL->gettext('fileuploaderror'); } } } } if ($attachment['error'] || $err != UPLOAD_ERR_NO_FILE) { $RCMAIL->output->command('display_message', $msg, 'error'); $RCMAIL->output->command('remove_from_attachment_list', $uploadid); } } } } $RCMAIL->output->command('auto_save_start', 'false'); $RCMAIL->output->send(); }
/** * Add mobile_text in messages list * * @param array $args * @return array */ public function messages_list($args) { if (isset($args['messages'])) { foreach ($args['messages'] as $message) { $address = rcube_mime::decode_address_list($message->from, null, true); $from = "NNN"; if (isset($address[1])) { if (isset($address[1]['name'])) { $from = $address[1]['name']; } elseif (isset($address[1]['mailto'])) { $from = $address[1]['mailto']; } } // Remove specials chars $from = str_replace(array(' ', '~', '"', "'", '!', '>'), '', $from); $from = strtoupper($from); $message->mobile_text = substr($from, 0, 3); $message->mobile_class = $this->get_mobile_class($message->mobile_text); } } return $args; }