thomascube
2008-09-18 7dfb1fba5001299300736e6b5d95d9400575e3e7
commit | author | age
4e17e6 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/steps/mail/sendmail.inc                                       |
6  |                                                                       |
7  | This file is part of the RoundCube Webmail client                     |
47124c 8  | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland                 |
30233b 9  | Licensed under the GNU GPL                                            |
4e17e6 10  |                                                                       |
T 11  | PURPOSE:                                                              |
12  |   Compose a new mail message with all headers and attachments         |
e8f8fe 13  |   and send it using the PEAR::Net_SMTP class or with PHP mail()       |
4e17e6 14  |                                                                       |
T 15  +-----------------------------------------------------------------------+
16  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
17  +-----------------------------------------------------------------------+
18
19  $Id$
20
21 */
22
23
0b6c1c 24 // remove all scripts and act as called in frame
T 25 $OUTPUT->reset();
26 $OUTPUT->framed = TRUE;
27
acb08f 28 $savedraft = !empty($_POST['_draft']) ? TRUE : FALSE;
A 29
30 /****** checks ********/
0b6c1c 31
T 32 if (!isset($_SESSION['compose']['id'])) {
9d55d2 33   raise_error(array('code' => 500, 'file' => __FILE__, 'message' => "Invalid compose ID"), true, false);
0b6c1c 34   console("Sendmail error", $_SESSION['compose']);
T 35   $OUTPUT->show_message("An internal error occured. Please try again.", 'error');
36   $OUTPUT->send('iframe');
37 }
4e17e6 38
acb08f 39 if (!$savedraft && empty($_POST['_to']) && empty($_POST['_cc']) && empty($_POST['_bcc']) && empty($_POST['_subject']) && $_POST['_message']) {
A 40   $OUTPUT->show_message('sendingfailed', 'error');
41   $OUTPUT->send('iframe');
42 }
43
44 if(!$savedraft && !empty($CONFIG['sendmail_delay'])) {
45   $wait_sec = time() - intval($CONFIG['sendmail_delay']) - intval($_SESSION['last_message_time']);
46   if($wait_sec < 0)
47     {
48     $OUTPUT->show_message('senttooquickly', 'error', array('sec' => $wait_sec * -1));
49     $OUTPUT->send('iframe');
50     }
51 }
52
4e17e6 53
T 54 /****** message sending functions ********/
55
fba1f5 56 // get identity record
4e17e6 57 function rcmail_get_identity($id)
T 58   {
fba1f5 59   global $USER, $OUTPUT;
4e17e6 60   
fba1f5 61   if ($sql_arr = $USER->get_identity($id))
4e17e6 62     {
T 63     $out = $sql_arr;
fba1f5 64     $out['mailto'] = $sql_arr['email'];
abb32e 65     $name = strpos($sql_arr['name'], ",") ? '"'.$sql_arr['name'].'"' : $sql_arr['name'];
13c1af 66     $out['string'] = sprintf('%s <%s>',
f11541 67                              rcube_charset_convert($name, RCMAIL_CHARSET, $OUTPUT->get_charset()),
fba1f5 68                              $sql_arr['email']);
4e17e6 69     return $out;
T 70     }
71
72   return FALSE;  
73   }
74
a0109c 75 /**
S 76  * go from this:
77  * <img src=".../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
78  *
79  * to this:
80  *
81  * <IMG src="cid:smiley-cool.gif"/>
82  * ...
83  * ------part...
84  * Content-Type: image/gif
85  * Content-Transfer-Encoding: base64
86  * Content-ID: <smiley-cool.gif>
87  */
88 function rcmail_attach_emoticons(&$mime_message)
89 {
47124c 90   global $CONFIG;
a0109c 91
S 92   $htmlContents = $mime_message->getHtmlBody();
93
94   // remove any null-byte characters before parsing
95   $body = preg_replace('/\x00/', '', $htmlContents);
96   
97   $last_img_pos = 0;
98
33ca14 99   $searchstr = 'program/js/tiny_mce/plugins/emotions/img/';
a0109c 100
ed6592 101   // keep track of added images, so they're only added once
S 102   $included_images = array();
103
a0109c 104   // find emoticon image tags
S 105   while ($pos = strpos($body, $searchstr, $last_img_pos))
106     {
107     $pos2 = strpos($body, '"', $pos);
108     $body_pre = substr($body, 0, $pos);
ed6592 109     $image_name = substr($body,
S 110                          $pos + strlen($searchstr),
111                          $pos2 - ($pos + strlen($searchstr)));
ee883a 112     // sanitize image name so resulting attachment doesn't leave images dir
T 113     $image_name = preg_replace('/[^a-zA-Z0-9_\.\-]/i','',$image_name);
a0109c 114
4e6eb1 115     $body_post = substr($body, $pos2);
S 116
ed6592 117     if (! in_array($image_name, $included_images))
a0109c 118       {
ed6592 119       // add the image to the MIME message
47124c 120       $img_file = INSTALL_PATH . '/' . $searchstr . $image_name;
33ca14 121       if(! $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name))
f11541 122         $OUTPUT->show_message("emoticonerror", 'error');
T 123
ed6592 124       array_push($included_images, $image_name);
S 125       }
a0109c 126
33ca14 127     $body = $body_pre . $img_file . $body_post;
4e6eb1 128
a0109c 129     $last_img_pos = $pos2;
S 130     }
131    
132   $mime_message->setHTMLBody($body);
133 }
41fa0b 134
acb08f 135
A 136 /****** compose message ********/
137
b068a0 138 if (strlen($_POST['_draft_saveid']) > 3)
T 139   $olddraftmessageid = get_input_value('_draft_saveid', RCUBE_INPUT_POST);
140
83a763 141 $message_id = sprintf('<%s@%s>', md5(uniqid('rcmail'.rand(),true)), $RCMAIL->config->mail_domain($_SESSION['imap_host']));
4e17e6 142
5bc8cb 143 // set default charset
13c1af 144 $input_charset = $OUTPUT->get_charset();
c03095 145 $message_charset = isset($_POST['_charset']) ? $_POST['_charset'] : $input_charset;
T 146
5a6ad2 147 $mailto_regexp = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m', '/;/');
T 148 $mailto_replace = array(', ', ', ', '', ',');
4e17e6 149
abb32e 150 // replace new lines and strip ending ', '
ea7c46 151 $mailto = preg_replace($mailto_regexp, $mailto_replace, get_input_value('_to', RCUBE_INPUT_POST, TRUE, $message_charset));
e8f8fe 152 $mailcc = preg_replace($mailto_regexp, $mailto_replace, get_input_value('_cc', RCUBE_INPUT_POST, TRUE, $message_charset));
T 153 $mailbcc = preg_replace($mailto_regexp, $mailto_replace, get_input_value('_bcc', RCUBE_INPUT_POST, TRUE, $message_charset));
4e17e6 154
e8f8fe 155 if (empty($mailto) && !empty($mailcc)) {
T 156   $mailto = $mailcc;
157   $mailcc = null;
158 }
159 else if (empty($mailto))
160   $mailto = 'undisclosed-recipients:;';
161
162 // get sender name and address
b068a0 163 $identity_arr = rcmail_get_identity(get_input_value('_from', RCUBE_INPUT_POST));
4e17e6 164 $from = $identity_arr['mailto'];
T 165
b068a0 166 if (empty($identity_arr['string']))
T 167   $identity_arr['string'] = $from;
4e17e6 168
T 169 // compose headers array
fba1f5 170 $headers = array('Date' => date('r'),
f11541 171                  'From' => rcube_charset_convert($identity_arr['string'], RCMAIL_CHARSET, $message_charset),
5a6ad2 172                  'To'   => $mailto);
4e17e6 173
T 174 // additional recipients
e8f8fe 175 if (!empty($mailcc))
T 176   $headers['Cc'] = $mailcc;
4e17e6 177
e8f8fe 178 if (!empty($mailbcc))
T 179   $headers['Bcc'] = $mailbcc;
4e17e6 180   
ea7c46 181 if (!empty($identity_arr['bcc']))
4e17e6 182   $headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc'];
T 183
184 // add subject
ea7c46 185 $headers['Subject'] = trim(get_input_value('_subject', RCUBE_INPUT_POST, FALSE, $message_charset));
4e17e6 186
ea7c46 187 if (!empty($identity_arr['organization']))
4e17e6 188   $headers['Organization'] = $identity_arr['organization'];
T 189
7984ec 190 if (!empty($_POST['_replyto']))
T 191   $headers['Reply-To'] = preg_replace($mailto_regexp, $mailto_replace, get_input_value('_replyto', RCUBE_INPUT_POST, TRUE, $message_charset));
192 else if (!empty($identity_arr['reply-to']))
4e17e6 193   $headers['Reply-To'] = $identity_arr['reply-to'];
T 194
f88d41 195 if (!empty($_SESSION['compose']['reply_msgid']))
4e17e6 196   $headers['In-Reply-To'] = $_SESSION['compose']['reply_msgid'];
T 197
f88d41 198 if (!empty($_SESSION['compose']['references']))
T 199   $headers['References'] = $_SESSION['compose']['references'];
4e17e6 200
ea7c46 201 if (!empty($_POST['_priority']))
4e17e6 202   {
c57996 203   $priority = intval($_POST['_priority']);
3287e8 204   $a_priorities = array(1=>'highest', 2=>'high', 4=>'low', 5=>'lowest');
4e17e6 205   if ($str_priority = $a_priorities[$priority])
T 206     $headers['X-Priority'] = sprintf("%d (%s)", $priority, ucfirst($str_priority));
207   }
208
620439 209 if (!empty($_POST['_receipt']))
T 210   {
211   $headers['Return-Receipt-To'] = $identity_arr['string'];
212   $headers['Disposition-Notification-To'] = $identity_arr['string'];
213   }
4e17e6 214
T 215 // additional headers
5ec113 216 if ($CONFIG['http_received_header'])
T 217 {
83a763 218   $nldlm = $RCMAIL->config->header_delimiter() . "\t";
5ec113 219   $headers['Received'] =  wordwrap('from ' . (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
T 220       gethostbyaddr($_SERVER['HTTP_X_FORWARDED_FOR']).' ['.$_SERVER['HTTP_X_FORWARDED_FOR'].']'.$nldlm.' via ' : '') .
221     gethostbyaddr($_SERVER['REMOTE_ADDR']).' ['.$_SERVER['REMOTE_ADDR'].']'.$nldlm.'with ' .
222     $_SERVER['SERVER_PROTOCOL'].' ('.$_SERVER['REQUEST_METHOD'].'); ' . date('r'),
223     69, $nldlm);
224 }
4e17e6 225
53e79d 226 $headers['Message-ID'] = $message_id;
A 227 $headers['X-Sender'] = $from;
228
ea7c46 229 if (!empty($CONFIG['useragent']))
4e17e6 230   $headers['User-Agent'] = $CONFIG['useragent'];
T 231
940fc1 232 $isHtmlVal = strtolower(get_input_value('_is_html', RCUBE_INPUT_POST));
A 233 $isHtml = ($isHtmlVal == "1");
234
c03095 235 // fetch message body
ea7c46 236 $message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset);
940fc1 237
A 238 // remove signature's div ID
239 if (!$savedraft && $isHtml)
240   $message_body = preg_replace('/\s*id="_rc_sig"/', '', $message_body);
4e17e6 241
4b0f65 242 // append generic footer to all messages
63f9d3 243 if (!$savedraft && !empty($CONFIG['generic_message_footer']) && ($footer = file_get_contents(realpath($CONFIG['generic_message_footer']))))
T 244   $message_body .= "\r\n" . rcube_charset_convert($footer, 'UTF-8', $message_charset);
a0109c 245
ab6f80 246 // create extended PEAR::Mail_mime instance
83a763 247 $MAIL_MIME = new rcube_mail_mime($RCMAIL->config->header_delimiter());
a0109c 248
S 249 // For HTML-formatted messages, construct the MIME message with both
250 // the HTML part and the plain-text part
251
252 if ($isHtml)
253   {
254   $MAIL_MIME->setHTMLBody($message_body);
255
256   // add a plain text version of the e-mail as an alternative part.
257   $h2t = new html2text($message_body);
a23884 258   $plainTextPart = wordwrap($h2t->get_text(), 998, "\r\n", true);
212352 259   if (!strlen($plainTextPart)) 
T 260     { 
261     // empty message body breaks attachment handling in drafts 
262     $plainTextPart = "\r\n"; 
263     }
606fc0 264   $MAIL_MIME->setTXTBody(html_entity_decode($plainTextPart, ENT_COMPAT, 'utf-8'));
a0109c 265
S 266   // look for "emoticon" images from TinyMCE and copy into message as attachments
267   rcmail_attach_emoticons($MAIL_MIME);
268   }
269 else
270   {
a23884 271   $message_body = wordwrap($message_body, 75, "\r\n");
T 272   $message_body = wordwrap($message_body, 998, "\r\n", true);
212352 273   if (!strlen($message_body))  
T 274     { 
275     // empty message body breaks attachment handling in drafts 
276     $message_body = "\r\n"; 
277     } 
a0109c 278   $MAIL_MIME->setTXTBody($message_body, FALSE, TRUE);
S 279   }
4e17e6 280
33ca14 281 // chose transfer encoding
A 282 $charset_7bit = array('ASCII', 'ISO-2022-JP', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-15');
283 $transfer_encoding = in_array(strtoupper($message_charset), $charset_7bit) ? '7bit' : '8bit';
4e17e6 284
T 285 // add stored attachments, if any
286 if (is_array($_SESSION['compose']['attachments']))
4315b0 287   foreach ($_SESSION['compose']['attachments'] as $id => $attachment)
S 288   {
289     $dispurl = '/\ssrc\s*=\s*[\'"]?\S+display-attachment\S+file=rcmfile' . $id . '[\'"]?/';
290     $match = preg_match($dispurl, $message_body);
291     if ($isHtml && ($match > 0))
292     {
293       $message_body = preg_replace($dispurl, ' src="'.$attachment['name'].'"', $message_body);
294       $MAIL_MIME->setHTMLBody($message_body);
295       $MAIL_MIME->addHTMLImage($attachment['path'], $attachment['mimetype'], $attachment['name']);
296     }
297     else
298     {
6d5dba 299       $ctype = str_replace('image/pjpeg', 'image/jpeg', $attachment['mimetype']); // #1484914
33ca14 300
A 301       // .eml attachments send inline
734584 302       $MAIL_MIME->addAttachment($attachment['path'],
8b36d4 303         $ctype, 
33ca14 304         $attachment['name'], true, 
8b36d4 305         ($ctype == 'message/rfc822' ? $transfer_encoding : 'base64'),
33ca14 306         ($ctype == 'message/rfc822' ? 'inline' : 'attachment'),
8b36d4 307         $message_charset);
4315b0 308     }
S 309   }
4e17e6 310
T 311 // add submitted attachments
312 if (is_array($_FILES['_attachments']['tmp_name']))
313   foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath)
33ca14 314     {
A 315     $ctype = $files['type'][$i];
316     $ctype = str_replace('image/pjpeg', 'image/jpeg', $ctype); // #1484914
317     
318     $MAIL_MIME->addAttachment($filepath, $ctype, $files['name'][$i], true,
319     ($ctype == 'message/rfc822' ? $transfer_encoding : 'base64'),
320     'attachment', $message_charset);
321     }
4e17e6 322
f88d41 323
a95e0e 324 // encoding settings for mail composing
fba1f5 325 $MAIL_MIME->setParam(array(
a23884 326   'text_encoding' => $transfer_encoding,
T 327   'html_encoding' => 'quoted-printable',
328   'head_encoding' => 'quoted-printable',
329   'head_charset'  => $message_charset,
330   'html_charset'  => $message_charset,
331   'text_charset'  => $message_charset,
fba1f5 332 ));
4e17e6 333
5a6ad2 334 // encoding subject header with mb_encode provides better results with asian characters
197601 335 if (function_exists("mb_encode_mimeheader"))
b517af 336 {
24fe97 337   mb_internal_encoding($message_charset);
fba1f5 338   $headers['Subject'] = mb_encode_mimeheader($headers['Subject'], $message_charset, 'Q');
f11541 339   mb_internal_encoding(RCMAIL_CHARSET);
b517af 340 }
4e17e6 341
fba1f5 342 // pass headers to message object
T 343 $MAIL_MIME->headers($headers);
4e17e6 344
fba1f5 345 // Begin SMTP Delivery Block 
T 346 if (!$savedraft)
347 {
348   $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto);
1966c5 349   
S 350   // return to compose page if sending failed
968bdc 351   if (!$sent)
4e17e6 352     {
f11541 353     $OUTPUT->show_message("sendingfailed", 'error'); 
T 354     $OUTPUT->send('iframe');
4e17e6 355     }
acb08f 356
A 357   // save message sent time
358   if (!empty($CONFIG['sendmail_delay']))
359     $_SESSION['last_message_time'] = time();
1966c5 360   
4dae73 361   // set replied/forwarded flag
1966c5 362   if ($_SESSION['compose']['reply_uid'])
S 363     $IMAP->set_flag($_SESSION['compose']['reply_uid'], 'ANSWERED');
4dae73 364   else if ($_SESSION['compose']['forward_uid'])
T 365     $IMAP->set_flag($_SESSION['compose']['forward_uid'], 'FORWARDED');
c03095 366
4dae73 367 } // End of SMTP Delivery Block
41fa0b 368
T 369
4e17e6 370
1966c5 371 // Determine which folder to save message
b068a0 372 if ($savedraft)
faf876 373   $store_target = $CONFIG['drafts_mbox'];
d583bc 374 else    
faf876 375   $store_target = isset($_POST['_store_target']) ? get_input_value('_store_target', RCUBE_INPUT_POST) : $CONFIG['sent_mbox'];
c03095 376
faf876 377 if ($store_target)
4e17e6 378   {
T 379   // check if mailbox exists
faf876 380   if (!in_array_nocase($store_target, $IMAP->list_mailboxes()))
d583bc 381     {
A 382       // folder may be existing but not subscribed (#1485241)
383       if (!in_array_nocase($store_target, $IMAP->list_unsubscribed()))
8b36d4 384         $store_folder = $IMAP->create_mailbox($store_target, TRUE);
d583bc 385       else if ($IMAP->subscribe($store_target))
8b36d4 386         $store_folder = TRUE;
d583bc 387     }
520c36 388   else
1966c5 389     $store_folder = TRUE;
b068a0 390   
4e17e6 391   // append message to sent box
1966c5 392   if ($store_folder)
faf876 393     $saved = $IMAP->save_message($store_target, $MAIL_MIME->getMessage());
520c36 394
T 395   // raise error if saving failed
396   if (!$saved)
f0f98f 397     {
f11541 398     raise_error(array('code' => 800, 'type' => 'imap', 'file' => __FILE__,
faf876 399                       'message' => "Could not save message in $store_target"), TRUE, FALSE);
41fa0b 400     
9a5762 401     if ($savedraft) {
A 402       $OUTPUT->show_message('errorsaving', 'error');
403       $OUTPUT->send('iframe');
404       }
f0f98f 405     }
4e17e6 406
b068a0 407   if ($olddraftmessageid)
T 408     {
1966c5 409     // delete previous saved draft
c719f3 410     $a_deleteid = $IMAP->search($CONFIG['drafts_mbox'], 'HEADER Message-ID', $olddraftmessageid);
T 411     $deleted = $IMAP->delete_message($IMAP->get_uid($a_deleteid[0], $CONFIG['drafts_mbox']), $CONFIG['drafts_mbox']);
4e17e6 412
f0f98f 413     // raise error if deletion of old draft failed
1966c5 414     if (!$deleted)
f11541 415       raise_error(array('code' => 800, 'type' => 'imap', 'file' => __FILE__,
1966c5 416                         'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
4e17e6 417     }
T 418   }
419
1966c5 420 if ($savedraft)
S 421   {
c719f3 422   $msgid = strtr($message_id, array('>' => '', '<' => ''));
T 423   
424   // remember new draft-uid
425   $draftids = $IMAP->search($CONFIG['drafts_mbox'], 'HEADER Message-ID', $msgid);
426   $_SESSION['compose']['param']['_draft_uid'] = $IMAP->get_uid($draftids[0], $CONFIG['drafts_mbox']);
427
f11541 428   // display success
T 429   $OUTPUT->show_message('messagesaved', 'confirmation');
f0f98f 430
f11541 431   // update "_draft_saveid" and the "cmp_hash" to prevent "Unsaved changes" warning
c719f3 432   $OUTPUT->command('set_draft_id', $msgid);
f11541 433   $OUTPUT->command('compose_field_hash', true);
41fa0b 434
f0f98f 435   // start the auto-save timer again
f11541 436   $OUTPUT->command('auto_save_start');
f0f98f 437
f11541 438   $OUTPUT->send('iframe');
1966c5 439   }
S 440 else
441   {
442   rcmail_compose_cleanup();
9a5762 443
A 444   if ($store_folder && !$saved)
445     $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'));
446   else
447     $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'));
f11541 448   $OUTPUT->send('iframe');
1966c5 449   }
4e17e6 450
1966c5 451 ?>