source: github/program/steps/mail/sendmail.inc

Last change on this file was 07795ba, checked in by Aleksander Machniak <alec@…>, 41 hours ago

Fix invalid option selected in default_font selector when font is unset (#1489112)

  • Property mode set to 100644
File size: 28.1 KB
Line 
1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/steps/mail/sendmail.inc                                       |
6 |                                                                       |
7 | This file is part of the Roundcube Webmail client                     |
8 | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
9 |                                                                       |
10 | Licensed under the GNU General Public License version 3 or            |
11 | any later version with exceptions for skins & plugins.                |
12 | See the README file for a full license statement.                     |
13 |                                                                       |
14 | PURPOSE:                                                              |
15 |   Compose a new mail message with all headers and attachments         |
16 |   and send it using the PEAR::Net_SMTP class or with PHP mail()       |
17 |                                                                       |
18 +-----------------------------------------------------------------------+
19 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
20 +-----------------------------------------------------------------------+
21*/
22
23// remove all scripts and act as called in frame
24$OUTPUT->reset();
25$OUTPUT->framed = TRUE;
26
27$savedraft = !empty($_POST['_draft']) ? true : false;
28
29$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
30$COMPOSE    =& $_SESSION['compose_data_'.$COMPOSE_ID];
31
32/****** checks ********/
33
34if (!isset($COMPOSE['id'])) {
35  raise_error(array('code' => 500, 'type' => 'php',
36    'file' => __FILE__, 'line' => __LINE__,
37    'message' => "Invalid compose ID"), true, false);
38
39  $OUTPUT->show_message('internalerror', 'error');
40  $OUTPUT->send('iframe');
41}
42
43if (!$savedraft) {
44  if (empty($_POST['_to']) && empty($_POST['_cc']) && empty($_POST['_bcc'])
45    && empty($_POST['_subject']) && $_POST['_message']) {
46    $OUTPUT->show_message('sendingfailed', 'error');
47    $OUTPUT->send('iframe');
48  }
49
50  if(!empty($CONFIG['sendmail_delay'])) {
51    $wait_sec = time() - intval($CONFIG['sendmail_delay']) - intval($CONFIG['last_message_time']);
52    if ($wait_sec < 0) {
53      $OUTPUT->show_message('senttooquickly', 'error', array('sec' => $wait_sec * -1));
54      $OUTPUT->send('iframe');
55    }
56  }
57}
58
59
60/****** message sending functions ********/
61
62// encrypt parts of the header
63function rcmail_encrypt_header($what)
64{
65  global $CONFIG, $RCMAIL;
66  if (!$CONFIG['http_received_header_encrypt']) {
67    return $what;
68  }
69  return $RCMAIL->encrypt($what);
70}
71
72// get identity record
73function rcmail_get_identity($id)
74{
75  global $RCMAIL, $message_charset;
76  global $RCMAIL;
77
78  if ($sql_arr = $RCMAIL->user->get_identity($id)) {
79    $out = $sql_arr;
80
81    if ($message_charset != RCMAIL_CHARSET) {
82      foreach ($out as $k => $v)
83        $out[$k] = rcube_charset_convert($v, RCMAIL_CHARSET, $message_charset);
84    }
85
86    $out['mailto'] = $sql_arr['email'];
87    $out['string'] = format_email_recipient($sql_arr['email'], $sql_arr['name']);
88
89    return $out;
90  }
91
92  return FALSE;
93}
94
95/**
96 * go from this:
97 * <img src="http[s]://.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
98 *
99 * to this:
100 *
101 * <img src="/path/on/server/.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
102 */
103function rcmail_fix_emoticon_paths($mime_message)
104{
105  global $RCMAIL;
106
107  $body = $mime_message->getHTMLBody();
108
109  // remove any null-byte characters before parsing
110  $body = preg_replace('/\x00/', '', $body);
111
112  $searchstr = 'program/js/tiny_mce/plugins/emotions/img/';
113  $offset = 0;
114
115  // keep track of added images, so they're only added once
116  $included_images = array();
117
118  if (preg_match_all('# src=[\'"]([^\'"]+)#', $body, $matches, PREG_OFFSET_CAPTURE)) {
119    foreach ($matches[1] as $m) {
120      // find emoticon image tags
121      if (preg_match('#'.$searchstr.'(.*)$#', $m[0], $imatches)) {
122        $image_name = $imatches[1];
123
124        // sanitize image name so resulting attachment doesn't leave images dir
125        $image_name = preg_replace('/[^a-zA-Z0-9_\.\-]/i', '', $image_name);
126        $img_file = INSTALL_PATH . '/' . $searchstr . $image_name;
127
128        if (! in_array($image_name, $included_images)) {
129          // add the image to the MIME message
130          if (!$mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name)) {
131            $RCMAIL->output->show_message("emoticonerror", 'error');
132          }
133          array_push($included_images, $image_name);
134        }
135
136        $body = substr_replace($body, $img_file, $m[1] + $offset, strlen($m[0]));
137        $offset += strlen($img_file) - strlen($m[0]);
138      }
139    }
140  }
141
142  $mime_message->setHTMLBody($body);
143}
144
145/**
146 * Extract image attachments from HTML content (data URIs)
147 */
148function rcmail_extract_inline_images($mime_message, $from)
149{
150    $body   = $mime_message->getHTMLBody();
151    $offset = 0;
152    $list   = array();
153    $regexp = '# src=[\'"](data:(image/[a-z]+);base64,([a-z0-9+/=\r\n]+))([\'"])#i';
154
155    // get domain for the Content-ID, must be the same as in Mail_Mime::get()
156    if (preg_match('#@([0-9a-zA-Z\-\.]+)#', $from, $matches)) {
157        $domain = $matches[1];
158    } else {
159        $domain = 'localhost';
160    }
161
162    if (preg_match_all($regexp, $body, $matches, PREG_OFFSET_CAPTURE)) {
163        foreach ($matches[1] as $idx => $m) {
164            $data = preg_replace('/\r\n/', '', $matches[3][$idx][0]);
165            $data = base64_decode($data);
166
167            if (empty($data)) {
168                continue;
169            }
170
171            $hash      = md5($data) . '@' . $domain;
172            $mime_type = $matches[2][$idx][0];
173            $name      = $list[$hash];
174
175            // add the image to the MIME message
176            if (!$name) {
177                $ext         = preg_replace('#^[^/]+/#', '', $mime_type);
178                $name        = substr($hash, 0, 8) . '.' . $ext;
179                $list[$hash] = $name;
180
181                $mime_message->addHTMLImage($data, $mime_type, $name, false, $hash);
182            }
183
184            $body = substr_replace($body, $name, $m[1] + $offset, strlen($m[0]));
185            $offset += strlen($name) - strlen($m[0]);
186        }
187    }
188
189    $mime_message->setHTMLBody($body);
190}
191
192/**
193 * Parse and cleanup email address input (and count addresses)
194 *
195 * @param string  Address input
196 * @param boolean Do count recipients (saved in global $RECIPIENT_COUNT)
197 * @param boolean Validate addresses (errors saved in global $EMAIL_FORMAT_ERROR)
198 * @return string Canonical recipients string separated by comma
199 */
200function rcmail_email_input_format($mailto, $count=false, $check=true)
201{
202  global $RCMAIL, $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
203
204  // simplified email regexp, supporting quoted local part
205  $email_regexp = '(\S+|("[^"]+"))@\S+';
206
207  $delim = trim($RCMAIL->config->get('recipients_separator', ','));
208  $regexp  = array("/[,;$delim]\s*[\r\n]+/", '/[\r\n]+/', "/[,;$delim]\s*\$/m", '/;/', '/(\S{1})(<'.$email_regexp.'>)/U');
209  $replace = array($delim.' ', ', ', '', $delim, '\\1 \\2');
210
211  // replace new lines and strip ending ', ', make address input more valid
212  $mailto = trim(preg_replace($regexp, $replace, $mailto));
213
214  $result = array();
215  $items = rcube_explode_quoted_string($delim, $mailto);
216
217  foreach($items as $item) {
218    $item = trim($item);
219    // address in brackets without name (do nothing)
220    if (preg_match('/^<'.$email_regexp.'>$/', $item)) {
221      $item = rcube_idn_to_ascii(trim($item, '<>'));
222      $result[] = $item;
223    // address without brackets and without name (add brackets)
224    } else if (preg_match('/^'.$email_regexp.'$/', $item)) {
225      $item = rcube_idn_to_ascii($item);
226      $result[] = $item;
227    // address with name (handle name)
228    } else if (preg_match('/<*'.$email_regexp.'>*$/', $item, $matches)) {
229      $address = $matches[0];
230      $name = trim(str_replace($address, '', $item));
231      if ($name[0] == '"' && $name[count($name)-1] == '"') {
232        $name = substr($name, 1, -1);
233      }
234      $name = stripcslashes($name);
235      $address = rcube_idn_to_ascii(trim($address, '<>'));
236      $result[] = format_email_recipient($address, $name);
237      $item = $address;
238    } else if (trim($item)) {
239      continue;
240    }
241
242    // check address format
243    $item = trim($item, '<>');
244    if ($item && $check && !check_email($item)) {
245      $EMAIL_FORMAT_ERROR = $item;
246      return;
247    }
248  }
249
250  if ($count) {
251    $RECIPIENT_COUNT += count($result);
252  }
253
254  return implode(', ', $result);
255}
256
257
258function rcmail_generic_message_footer($isHtml)
259{
260  global $CONFIG;
261
262  if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) {
263    $file = $CONFIG['generic_message_footer_html'];
264    $html_footer = true;
265  }
266  else {
267    $file = $CONFIG['generic_message_footer'];
268    $html_footer = false;
269  }
270
271  if ($file && realpath($file)) {
272    // sanity check
273    if (!preg_match('/\.(php|ini|conf)$/', $file) && strpos($file, '/etc/') === false) {
274      $footer = file_get_contents($file);
275      if ($isHtml && !$html_footer)
276        $footer = '<pre>' . $footer . '</pre>';
277      return $footer;
278    }
279  }
280
281  return false;
282}
283
284
285/****** compose message ********/
286
287if (strlen($_POST['_draft_saveid']) > 3)
288  $olddraftmessageid = get_input_value('_draft_saveid', RCUBE_INPUT_POST);
289
290$message_id = rcmail_gen_message_id();
291
292// set default charset
293$message_charset = isset($_POST['_charset']) ? $_POST['_charset'] : $OUTPUT->get_charset();
294
295$EMAIL_FORMAT_ERROR = NULL;
296$RECIPIENT_COUNT = 0;
297
298$mailto = rcmail_email_input_format(get_input_value('_to', RCUBE_INPUT_POST, TRUE, $message_charset), true);
299$mailcc = rcmail_email_input_format(get_input_value('_cc', RCUBE_INPUT_POST, TRUE, $message_charset), true);
300$mailbcc = rcmail_email_input_format(get_input_value('_bcc', RCUBE_INPUT_POST, TRUE, $message_charset), true);
301
302if ($EMAIL_FORMAT_ERROR) {
303  $OUTPUT->show_message('emailformaterror', 'error', array('email' => $EMAIL_FORMAT_ERROR));
304  $OUTPUT->send('iframe');
305}
306
307if (empty($mailto) && !empty($mailcc)) {
308  $mailto = $mailcc;
309  $mailcc = null;
310}
311else if (empty($mailto))
312  $mailto = 'undisclosed-recipients:;';
313
314// Get sender name and address...
315$from = get_input_value('_from', RCUBE_INPUT_POST, true, $message_charset);
316// ... from identity...
317if (is_numeric($from)) {
318  if (is_array($identity_arr = rcmail_get_identity($from))) {
319    if ($identity_arr['mailto'])
320      $from = $identity_arr['mailto'];
321    if ($identity_arr['string'])
322      $from_string = $identity_arr['string'];
323  }
324  else {
325    $from = null;
326  }
327}
328// ... if there is no identity record, this might be a custom from
329else if ($from_string = rcmail_email_input_format($from)) {
330  if (preg_match('/(\S+@\S+)/', $from_string, $m))
331    $from = trim($m[1], '<>');
332  else
333    $from = null;
334}
335
336if (!$from_string && $from)
337  $from_string = $from;
338
339// compose headers array
340$headers = array();
341
342// if configured, the Received headers goes to top, for good measure
343if ($CONFIG['http_received_header'])
344{
345  $nldlm = "\r\n\t";
346  // FROM/VIA
347  $http_header = 'from ';
348  if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
349    $host = $_SERVER['HTTP_X_FORWARDED_FOR'];
350    $hostname = gethostbyaddr($host);
351    if ($CONFIG['http_received_header_encrypt']) {
352      $http_header .= rcmail_encrypt_header($hostname);
353      if ($host != $hostname)
354        $http_header .= ' ('. rcmail_encrypt_header($host) . ')';
355    } else {
356      $http_header .= (($host != $hostname) ? $hostname : '[' . $host . ']');
357      if ($host != $hostname)
358        $http_header .= ' (['. $host .'])';
359    }
360    $http_header .= $nldlm . ' via ';
361  }
362  $host = $_SERVER['REMOTE_ADDR'];
363  $hostname = gethostbyaddr($host);
364  if ($CONFIG['http_received_header_encrypt']) {
365    $http_header .= rcmail_encrypt_header($hostname);
366    if ($host != $hostname)
367      $http_header .= ' ('. rcmail_encrypt_header($host) . ')';
368  } else {
369    $http_header .= (($host != $hostname) ? $hostname : '[' . $host . ']');
370    if ($host != $hostname)
371      $http_header .= ' (['. $host .'])';
372  }
373  // BY
374  $http_header .= $nldlm . 'by ' . $_SERVER['HTTP_HOST'];
375  // WITH
376  $http_header .= $nldlm . 'with HTTP (' . $_SERVER['SERVER_PROTOCOL'] .
377      ' '.$_SERVER['REQUEST_METHOD'] . '); ' . date('r');
378  $http_header = wordwrap($http_header, 69, $nldlm);
379
380  $headers['Received'] = $http_header;
381}
382
383$headers['Date'] = rcmail_user_date();
384$headers['From'] = rcube_charset_convert($from_string, RCMAIL_CHARSET, $message_charset);
385$headers['To'] = $mailto;
386
387// additional recipients
388if (!empty($mailcc)) {
389  $headers['Cc'] = $mailcc;
390}
391if (!empty($mailbcc)) {
392  $headers['Bcc'] = $mailbcc;
393}
394if (!empty($identity_arr['bcc']) && stripos($headers['Bcc'], $identity_arr['bcc']) === false) {
395  $headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc'];
396  $RECIPIENT_COUNT ++;
397}
398
399if (($max_recipients = (int) $RCMAIL->config->get('max_recipients')) > 0) {
400  if ($RECIPIENT_COUNT > $max_recipients) {
401    $OUTPUT->show_message('toomanyrecipients', 'error', array('max' => $max_recipients));
402    $OUTPUT->send('iframe');
403  }
404}
405
406// add subject
407$headers['Subject'] = trim(get_input_value('_subject', RCUBE_INPUT_POST, TRUE, $message_charset));
408
409if (!empty($identity_arr['organization'])) {
410  $headers['Organization'] = $identity_arr['organization'];
411}
412if (!empty($_POST['_replyto'])) {
413  $headers['Reply-To'] = rcmail_email_input_format(get_input_value('_replyto', RCUBE_INPUT_POST, TRUE, $message_charset));
414}
415else if (!empty($identity_arr['reply-to'])) {
416  $headers['Reply-To'] = rcmail_email_input_format($identity_arr['reply-to'], false, true);
417}
418if (!empty($headers['Reply-To'])) {
419  $headers['Mail-Reply-To'] = $headers['Reply-To'];
420}
421if (!empty($_POST['_followupto'])) {
422  $headers['Mail-Followup-To'] = rcmail_email_input_format(get_input_value('_followupto', RCUBE_INPUT_POST, TRUE, $message_charset));
423}
424if (!empty($COMPOSE['reply_msgid'])) {
425  $headers['In-Reply-To'] = $COMPOSE['reply_msgid'];
426}
427
428// remember reply/forward UIDs in special headers
429if (!empty($COMPOSE['reply_uid']) && $savedraft) {
430  $headers['X-Draft-Info'] = array('type' => 'reply', 'uid' => $COMPOSE['reply_uid']);
431}
432else if (!empty($COMPOSE['forward_uid']) && $savedraft) {
433  $headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $COMPOSE['forward_uid']);
434}
435
436if (!empty($COMPOSE['references'])) {
437  $headers['References'] = $COMPOSE['references'];
438}
439
440if (!empty($_POST['_priority'])) {
441  $priority = intval($_POST['_priority']);
442  $a_priorities = array(1=>'highest', 2=>'high', 4=>'low', 5=>'lowest');
443  if ($str_priority = $a_priorities[$priority]) {
444    $headers['X-Priority'] = sprintf("%d (%s)", $priority, ucfirst($str_priority));
445  }
446}
447
448if (!empty($_POST['_receipt'])) {
449  $headers['Return-Receipt-To'] = $from_string;
450  $headers['Disposition-Notification-To'] = $from_string;
451}
452
453// additional headers
454$headers['Message-ID'] = $message_id;
455$headers['X-Sender'] = $from;
456
457if (is_array($headers['X-Draft-Info'])) {
458  $headers['X-Draft-Info'] = rcmail_draftinfo_encode($headers['X-Draft-Info'] + array('folder' => $COMPOSE['mailbox']));
459}
460if (!empty($CONFIG['useragent'])) {
461  $headers['User-Agent'] = $CONFIG['useragent'];
462}
463
464// exec hook for header checking and manipulation
465$data = $RCMAIL->plugins->exec_hook('message_outgoing_headers', array('headers' => $headers));
466
467// sending aborted by plugin
468if ($data['abort'] && !$savedraft) {
469  $OUTPUT->show_message($data['message'] ? $data['message'] : 'sendingfailed');
470  $OUTPUT->send('iframe');
471}
472else
473  $headers = $data['headers'];
474
475
476$isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST);
477
478// fetch message body
479$message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset);
480
481if ($isHtml) {
482  $font   = rcube_fontdefs($RCMAIL->config->get('default_font'));
483  $bstyle = $font && is_string($font) ? " style='font-family: $font'" : '';
484
485  // append doctype and html/body wrappers
486  $message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' .
487    "\r\n<html><body$bstyle>\r\n" . $message_body;
488}
489
490if (!$savedraft) {
491  if ($isHtml) {
492    // remove signature's div ID
493    $message_body = preg_replace('/\s*id="_rc_sig"/', '', $message_body);
494
495    // add inline css for blockquotes
496    $bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px';
497    $message_body = preg_replace('/<blockquote>/',
498      '<blockquote type="cite" style="'.$bstyle.'">', $message_body);
499  }
500
501  // Check spelling before send
502  if ($CONFIG['spellcheck_before_send'] && $CONFIG['enable_spellcheck']
503    && empty($COMPOSE['spell_checked']) && !empty($message_body)
504  ) {
505    $message_body = str_replace("\r\n", "\n", $message_body);
506    $spellchecker = new rcube_spellchecker(get_input_value('_lang', RCUBE_INPUT_GPC));
507    $spell_result = $spellchecker->check($message_body, $isHtml);
508
509    $COMPOSE['spell_checked'] = true;
510
511    if (!$spell_result) {
512      $result = $isHtml ? $spellchecker->get_words() : $spellchecker->get_xml();
513      $OUTPUT->show_message('mispellingsfound', 'error');
514      $OUTPUT->command('spellcheck_resume', $isHtml, $result);
515      $OUTPUT->send('iframe');
516    }
517  }
518
519  // generic footer for all messages
520  if ($footer = rcmail_generic_message_footer($isHtml)) {
521    $footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset);
522    $message_body .= "\r\n" . $footer;
523  }
524}
525
526if ($isHtml) {
527  $message_body .= "\r\n</body></html>\r\n";
528}
529
530// sort attachments to make sure the order is the same as in the UI (#1488423)
531$files = get_input_value('_attachments', RCUBE_INPUT_POST);
532if ($files) {
533  $files = explode(',', $files);
534  $files = array_flip($files);
535  foreach ($files as $idx => $val) {
536    $files[$idx] = $COMPOSE['attachments'][$idx];
537    unset($COMPOSE['attachments'][$idx]);
538  }
539
540  $COMPOSE['attachments'] = array_merge(array_filter($files), $COMPOSE['attachments']);
541}
542
543// set line length for body wrapping
544$LINE_LENGTH = $RCMAIL->config->get('line_length', 72);
545
546// Since we can handle big messages with disk usage, we need more time to work
547@set_time_limit(0);
548
549// create PEAR::Mail_mime instance
550$MAIL_MIME = new Mail_mime("\r\n");
551
552// Check if we have enough memory to handle the message in it
553// It's faster than using files, so we'll do this if we only can
554if (is_array($COMPOSE['attachments']) && $CONFIG['smtp_server']
555  && ($mem_limit = parse_bytes(ini_get('memory_limit'))))
556{
557  $memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
558
559  foreach ($COMPOSE['attachments'] as $id => $attachment)
560    $memory += $attachment['size'];
561
562  // Yeah, Net_SMTP needs up to 12x more memory, 1.33 is for base64
563  if ($memory * 1.33 * 12 > $mem_limit)
564    $MAIL_MIME->setParam('delay_file_io', true);
565}
566
567// For HTML-formatted messages, construct the MIME message with both
568// the HTML part and the plain-text part
569
570if ($isHtml) {
571  $plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
572    array('body' => $message_body, 'type' => 'html', 'message' => $MAIL_MIME));
573
574  $MAIL_MIME->setHTMLBody($plugin['body']);
575
576  // replace emoticons
577  $plugin['body'] = rcmail_replace_emoticons($plugin['body']);
578
579  // add a plain text version of the e-mail as an alternative part.
580  $h2t = new rcube_html2text($plugin['body'], false, true, 0, $message_charset);
581  $plainTextPart = rc_wordwrap($h2t->get_text(), $LINE_LENGTH, "\r\n", false, $message_charset);
582  $plainTextPart = wordwrap($plainTextPart, 998, "\r\n", true);
583
584  // make sure all line endings are CRLF (#1486712)
585  $plainTextPart = preg_replace('/\r?\n/', "\r\n", $plainTextPart);
586
587  $plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
588    array('body' => $plainTextPart, 'type' => 'alternative', 'message' => $MAIL_MIME));
589
590  $MAIL_MIME->setTXTBody($plugin['body']);
591
592  // look for "emoticon" images from TinyMCE and change their src paths to
593  // be file paths on the server instead of URL paths.
594  rcmail_fix_emoticon_paths($MAIL_MIME);
595
596  // Extract image Data URIs into message attachments (#1488502)
597  rcmail_extract_inline_images($MAIL_MIME, $from);
598}
599else {
600  $plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
601    array('body' => $message_body, 'type' => 'plain', 'message' => $MAIL_MIME));
602
603  $message_body = $plugin['body'];
604
605  // compose format=flowed content if enabled
606  if ($flowed = ($savedraft || $RCMAIL->config->get('send_format_flowed', true)))
607    $message_body = rcube_mime::format_flowed($message_body, min($LINE_LENGTH+2, 79), $message_charset);
608  else
609    $message_body = rc_wordwrap($message_body, $LINE_LENGTH, "\r\n", false, $message_charset);
610
611  $message_body = wordwrap($message_body, 998, "\r\n", true);
612
613  $MAIL_MIME->setTXTBody($message_body, false, true);
614}
615
616// add stored attachments, if any
617if (is_array($COMPOSE['attachments']))
618{
619  foreach ($COMPOSE['attachments'] as $id => $attachment) {
620    // This hook retrieves the attachment contents from the file storage backend
621    $attachment = $RCMAIL->plugins->exec_hook('attachment_get', $attachment);
622
623    $dispurl = '/\ssrc\s*=\s*[\'"]*\S+display-attachment\S+file=rcmfile' . preg_quote($attachment['id']) . '[\s\'"]*/';
624    $message_body = $MAIL_MIME->getHTMLBody();
625    if ($isHtml && (preg_match($dispurl, $message_body) > 0)) {
626      $message_body = preg_replace($dispurl, ' src="'.$attachment['name'].'" ', $message_body);
627      $MAIL_MIME->setHTMLBody($message_body);
628
629      if ($attachment['data'])
630        $MAIL_MIME->addHTMLImage($attachment['data'], $attachment['mimetype'], $attachment['name'], false);
631      else
632        $MAIL_MIME->addHTMLImage($attachment['path'], $attachment['mimetype'], $attachment['name'], true);
633    }
634    else {
635      $ctype = str_replace('image/pjpeg', 'image/jpeg', $attachment['mimetype']); // #1484914
636      $file = $attachment['data'] ? $attachment['data'] : $attachment['path'];
637
638      $MAIL_MIME->addAttachment($file,
639        $ctype,
640        $attachment['name'],
641        ($attachment['data'] ? false : true),
642        ($ctype == 'message/rfc822' ? '8bit' : 'base64'),
643        'attachment',
644        '', '', '',
645        $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
646        $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL,
647        '', RCMAIL_CHARSET
648      );
649    }
650  }
651}
652
653// choose transfer encoding for plain/text body
654if (preg_match('/[^\x00-\x7F]/', $MAIL_MIME->getTXTBody()))
655  $transfer_encoding = $RCMAIL->config->get('force_7bit') ? 'quoted-printable' : '8bit';
656else
657  $transfer_encoding = '7bit';
658
659// encoding settings for mail composing
660$MAIL_MIME->setParam('text_encoding', $transfer_encoding);
661$MAIL_MIME->setParam('html_encoding', 'quoted-printable');
662$MAIL_MIME->setParam('head_encoding', 'quoted-printable');
663$MAIL_MIME->setParam('head_charset', $message_charset);
664$MAIL_MIME->setParam('html_charset', $message_charset);
665$MAIL_MIME->setParam('text_charset', $message_charset . ($flowed ? ";\r\n format=flowed" : ''));
666
667// encoding subject header with mb_encode provides better results with asian characters
668if (function_exists('mb_encode_mimeheader')) {
669  mb_internal_encoding($message_charset);
670  $headers['Subject'] = mb_encode_mimeheader($headers['Subject'],
671    $message_charset, 'Q', "\r\n", 8);
672  mb_internal_encoding(RCMAIL_CHARSET);
673}
674
675// pass headers to message object
676$MAIL_MIME->headers($headers);
677
678// Begin SMTP Delivery Block
679if (!$savedraft)
680{
681  // check 'From' address (identity may be incomplete)
682  if (empty($from)) {
683    $OUTPUT->show_message('nofromaddress', 'error');
684    $OUTPUT->send('iframe');
685  }
686
687  // Handle Delivery Status Notification request
688  if (!empty($_POST['_dsn'])) {
689    $smtp_opts['dsn'] = true;
690  }
691
692  $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto,
693    $smtp_error, $mailbody_file, $smtp_opts);
694
695  // return to compose page if sending failed
696  if (!$sent) {
697    // remove temp file
698    if ($mailbody_file) {
699      unlink($mailbody_file);
700    }
701
702    if ($smtp_error)
703      $OUTPUT->show_message($smtp_error['label'], 'error', $smtp_error['vars']);
704    else
705      $OUTPUT->show_message('sendingfailed', 'error');
706    $OUTPUT->send('iframe');
707  }
708
709  // save message sent time
710  if (!empty($CONFIG['sendmail_delay']))
711    $RCMAIL->user->save_prefs(array('last_message_time' => time()));
712
713  // set replied/forwarded flag
714  if ($COMPOSE['reply_uid'])
715    $RCMAIL->storage->set_flag($COMPOSE['reply_uid'], 'ANSWERED', $COMPOSE['mailbox']);
716  else if ($COMPOSE['forward_uid'])
717    $RCMAIL->storage->set_flag($COMPOSE['forward_uid'], 'FORWARDED', $COMPOSE['mailbox']);
718
719} // End of SMTP Delivery Block
720
721
722// Determine which folder to save message
723if ($savedraft)
724  $store_target = $CONFIG['drafts_mbox'];
725else if (!$RCMAIL->config->get('no_save_sent_messages'))
726  $store_target = isset($_POST['_store_target']) ? get_input_value('_store_target', RCUBE_INPUT_POST) : $CONFIG['sent_mbox'];
727
728if ($store_target) {
729  // check if folder is subscribed
730  if ($RCMAIL->storage->folder_exists($store_target, true))
731    $store_folder = true;
732  // folder may be existing but not subscribed (#1485241)
733  else if (!$RCMAIL->storage->folder_exists($store_target))
734    $store_folder = $RCMAIL->storage->create_folder($store_target, true);
735  else if ($RCMAIL->storage->subscribe($store_target))
736    $store_folder = true;
737
738  // append message to sent box
739  if ($store_folder) {
740    // message body in file
741    if ($mailbody_file || $MAIL_MIME->getParam('delay_file_io')) {
742      $headers = $MAIL_MIME->txtHeaders();
743
744      // file already created
745      if ($mailbody_file)
746        $msg = $mailbody_file;
747      else {
748        $temp_dir = $RCMAIL->config->get('temp_dir');
749        $mailbody_file = tempnam($temp_dir, 'rcmMsg');
750        if (!PEAR::isError($msg = $MAIL_MIME->saveMessageBody($mailbody_file)))
751          $msg = $mailbody_file;
752      }
753    }
754    else {
755      $msg = $MAIL_MIME->getMessage();
756      $headers = '';
757    }
758
759    if (PEAR::isError($msg))
760      raise_error(array('code' => 650, 'type' => 'php',
761            'file' => __FILE__, 'line' => __LINE__,
762            'message' => "Could not create message: ".$msg->getMessage()),
763            TRUE, FALSE);
764    else {
765      $saved = $RCMAIL->storage->save_message($store_target, $msg, $headers,
766        $mailbody_file ? true : false, array('SEEN'));
767    }
768
769    if ($mailbody_file) {
770      unlink($mailbody_file);
771      $mailbody_file = null;
772    }
773  }
774
775  // raise error if saving failed
776  if (!$saved) {
777    raise_error(array('code' => 800, 'type' => 'imap',
778       'file' => __FILE__, 'line' => __LINE__,
779       'message' => "Could not save message in $store_target"), TRUE, FALSE);
780
781    if ($savedraft) {
782      $OUTPUT->show_message('errorsaving', 'error');
783      // start the auto-save timer again
784      $OUTPUT->command('auto_save_start');
785      $OUTPUT->send('iframe');
786    }
787  }
788
789  if ($olddraftmessageid) {
790    // delete previous saved draft
791    // @TODO: use message UID (remember to check UIDVALIDITY) to skip this SEARCH
792    $delete_idx = $RCMAIL->storage->search_once($CONFIG['drafts_mbox'],
793        'HEADER Message-ID '.$olddraftmessageid);
794
795    if ($del_uid = $delete_idx->get_element('FIRST')) {
796      $deleted = $RCMAIL->storage->delete_message($del_uid, $CONFIG['drafts_mbox']);
797
798      // raise error if deletion of old draft failed
799      if (!$deleted)
800        raise_error(array('code' => 800, 'type' => 'imap',
801          'file' => __FILE__, 'line' => __LINE__,
802          'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
803    }
804  }
805}
806// remove temp file
807else if ($mailbody_file) {
808  unlink($mailbody_file);
809}
810
811
812if ($savedraft) {
813  $msgid = strtr($message_id, array('>' => '', '<' => ''));
814
815  // remember new draft-uid ($saved could be an UID or TRUE here)
816  if (is_bool($saved)) {
817    $draft_idx = $RCMAIL->storage->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid);
818    $saved     = $draft_idx->get_element('FIRST');
819  }
820  $COMPOSE['param']['draft_uid'] = $saved;
821  $plugin = $RCMAIL->plugins->exec_hook('message_draftsaved', array('msgid' => $msgid, 'uid' => $saved, 'folder' => $store_target));
822
823  // display success
824  $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'messagesaved', 'confirmation');
825
826  // update "_draft_saveid" and the "cmp_hash" to prevent "Unsaved changes" warning
827  $OUTPUT->command('set_draft_id', $msgid);
828  $OUTPUT->command('compose_field_hash', true);
829
830  // start the auto-save timer again
831  $OUTPUT->command('auto_save_start');
832
833  $OUTPUT->send('iframe');
834}
835else {
836  rcmail_compose_cleanup($COMPOSE_ID);
837
838  if ($store_folder && !$saved)
839    $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'));
840  else
841    $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $store_target);
842  $OUTPUT->send('iframe');
843}
Note: See TracBrowser for help on using the repository browser.