alecpl
2010-09-29 33da0b48b343609a46e200afc814d75fba589057
commit | author | age
4e17e6 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/steps/mail/func.inc                                           |
6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail client                     |
A 8  | Copyright (C) 2005-2010, Roundcube Dev. - Switzerland                 |
30233b 9  | Licensed under the GNU GPL                                            |
4e17e6 10  |                                                                       |
T 11  | PURPOSE:                                                              |
12  |   Provide webmail functionality and GUI objects                       |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id$
19
20 */
21
7910c0 22 // setup some global vars used by mail steps
T 23 $SENT_MBOX = $RCMAIL->config->get('sent_mbox');
24 $DRAFTS_MBOX = $RCMAIL->config->get('drafts_mbox');
25 $SEARCH_MODS_DEFAULT = array('*' => array('subject'=>1, 'from'=>1), $SENT_MBOX => array('subject'=>1, 'to'=>1), $DRAFTS_MBOX => array('subject'=>1, 'to'=>1));
26
e99991 27 // Simplified for IDN in Unicode
A 28 //$EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9][a-z0-9\-\.]*\\.[a-z]{2,5})';
29 $EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^.].*\\.[a-z]{2,5})';
39cd51 30
431234 31 // actions that do not require imap connection here
A 32 $NOIMAP_ACTIONS = array('addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment', 'get');
39cd51 33
47d8d3 34 // always instantiate imap object (but not yet connect to server)
T 35 $RCMAIL->imap_init();
36
39cd51 37 // log in to imap server
A 38 if (!in_array($RCMAIL->action, $NOIMAP_ACTIONS) && !$RCMAIL->imap_connect()) {
39   $RCMAIL->kill_session();
40
41   if ($OUTPUT->ajax_call)
42     $OUTPUT->redirect(array(), 2000);
43
44   $OUTPUT->set_env('task', 'login');
45   $OUTPUT->send('login');
46 }
47
4e17e6 48 // set imap properties and session vars
b3ce79 49 if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
c57996 50   $IMAP->set_mailbox(($_SESSION['mbox'] = $mbox));
48bc52 51 else if ($IMAP)
4e5b11 52   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
4e17e6 53
b3ce79 54 if (!empty($_GET['_page']))
c57996 55   $IMAP->set_page(($_SESSION['page'] = intval($_GET['_page'])));
4e17e6 56
6a35c8 57 // set default sort col/order to session
T 58 if (!isset($_SESSION['sort_col']))
59   $_SESSION['sort_col'] = $CONFIG['message_sort_col'];
60 if (!isset($_SESSION['sort_order']))
61   $_SESSION['sort_order'] = $CONFIG['message_sort_order'];
2bca6e 62
f52c93 63 // set threads mode
T 64 $a_threading = $RCMAIL->config->get('message_threading', array());
65 if (isset($_GET['_threads'])) {
66   if ($_GET['_threads'])
67     $a_threading[$_SESSION['mbox']] = true;
68   else
69     unset($a_threading[$_SESSION['mbox']]);
70   $RCMAIL->user->save_prefs(array('message_threading' => $a_threading));
71 }
72 $IMAP->set_threading($a_threading[$_SESSION['mbox']]);
73
2bca6e 74 // set message set for search result
8d0758 75 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
1f020b 76   {
8d0758 77   $IMAP->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
1f020b 78   $OUTPUT->set_env('search_request', $_REQUEST['_search']);
S 79   $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
80   }
4e17e6 81
528514 82 // set main env variables, labels and page title
197601 83 if (empty($RCMAIL->action) || $RCMAIL->action == 'list')
528514 84   {
8abda5 85   $mbox_name = $IMAP->get_mailbox_name();
A 86
87   if (empty($RCMAIL->action))
88     {
89     // initialize searching result if search_filter is used
90     if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL')
91       {
92       $search_request = md5($mbox_name.$_SESSION['search_filter']);
9800a8 93
8abda5 94       $IMAP->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $_SESSION['sort_col']);
A 95       $_SESSION['search'][$search_request] = $IMAP->get_search_set();
96       $OUTPUT->set_env('search_request', $search_request);
97       }
9800a8 98
7910c0 99       $search_mods = $RCMAIL->config->get('search_mods', $SEARCH_MODS_DEFAULT);
T 100       $OUTPUT->set_env('search_mods', $search_mods);
8abda5 101     }
9800a8 102
f52c93 103   // set current mailbox and some other vars in client environment
8abda5 104   $OUTPUT->set_env('mailbox', $mbox_name);
f52c93 105   $OUTPUT->set_env('pagesize', $IMAP->page_size);
528514 106   $OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
A 107   $OUTPUT->set_env('delimiter', $IMAP->get_hierarchy_delimiter());
f52c93 108   $OUTPUT->set_env('threading', (bool) $IMAP->threading);
T 109   $OUTPUT->set_env('threads', $IMAP->threading
110     || $IMAP->get_capability('thread=references')
111         || $IMAP->get_capability('thread=orderedsubject')
112         || $IMAP->get_capability('thread=refs')  
113   );
528514 114
0b2ce9 115   if ($CONFIG['flag_for_deletion'])
A 116     $OUTPUT->set_env('flag_for_deletion', true);
117   if ($CONFIG['read_when_deleted'])
118     $OUTPUT->set_env('read_when_deleted', true);
119   if ($CONFIG['skip_deleted'])
120     $OUTPUT->set_env('skip_deleted', true);
e54bb7 121   if ($CONFIG['display_next'])
A 122     $OUTPUT->set_env('display_next', true);
bc4960 123
T 124   $OUTPUT->set_env('preview_pane_mark_read', $RCMAIL->config->get('preview_pane_mark_read', 0));
125
528514 126   if ($CONFIG['trash_mbox'])
A 127     $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
128   if ($CONFIG['drafts_mbox'])
129     $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
130   if ($CONFIG['junk_mbox'])
131     $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
132
133   if (!$OUTPUT->ajax_call)
9b3fdc 134     $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
a2e817 135       'movingmessage', 'copyingmessage', 'copy', 'move', 'quota');
528514 136
4e5b11 137   $OUTPUT->set_pagetitle(rcmail_localize_foldername($mbox_name));
a86204 138   }
5eee00 139
4e17e6 140
45f56c 141 /**
T 142  * return the message list as HTML table
143  */
4e17e6 144 function rcmail_message_list($attrib)
f52c93 145 {
T 146   global $IMAP, $CONFIG, $OUTPUT;
b076a4 147
24053e 148   // add some labels to client
112c91 149   $OUTPUT->add_label('from', 'to');
4e17e6 150
T 151   // add id to message list table if not specified
152   if (!strlen($attrib['id']))
153     $attrib['id'] = 'rcubemessagelist';
e0ddd4 154
d59aaa 155   // define list of cols to be displayed based on parameter or config
b62c48 156   if (empty($attrib['columns'])) {
A 157     $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
158     $OUTPUT->set_env('col_movable', !in_array('list_cols', (array)$CONFIG['dont_override']));
159   }
160   else {
161     $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
162     $attrib['columns'] = $a_show_cols;
163   }
d59aaa 164
f52c93 165   // save some variables for use in ajax list
T 166   $_SESSION['list_attrib'] = $attrib;
9800a8 167
41bece 168   $mbox = $IMAP->get_mailbox_name();
4906eb 169   $delim = $IMAP->get_hierarchy_delimiter();
A 170
171   // show 'to' instead of 'from' in sent/draft messages
172   if ((strpos($mbox.$delim, $CONFIG['sent_mbox'].$delim)===0 || strpos($mbox.$delim, $CONFIG['drafts_mbox'].$delim)===0)
ffaea6 173       && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
4e17e6 174     $a_show_cols[$f] = 'to';
e0ddd4 175
614c64 176   // make sure 'threads' and 'subject' columns are present
A 177   if (!in_array('subject', $a_show_cols))
178     array_unshift($a_show_cols, 'subject');
6c9d49 179   if (!in_array('threads', $a_show_cols))
A 180     array_unshift($a_show_cols, 'threads');
181
f52c93 182   $skin_path = $_SESSION['skin_path'] = $CONFIG['skin_path'];
9800a8 183
4e17e6 184   // set client env
f11541 185   $OUTPUT->add_gui_object('messagelist', $attrib['id']);
f52c93 186   $OUTPUT->set_env('autoexpand_threads', intval($CONFIG['autoexpand_threads']));
T 187   $OUTPUT->set_env('sort_col', $_SESSION['sort_col']);
188   $OUTPUT->set_env('sort_order', $_SESSION['sort_order']);
9800a8 189
4e17e6 190   if ($attrib['messageicon'])
f11541 191     $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
6ec0a8 192   if ($attrib['deletedicon'])
f11541 193     $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
4e17e6 194   if ($attrib['unreadicon'])
f11541 195     $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
4e17e6 196   if ($attrib['repliedicon'])
f11541 197     $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
d73404 198   if ($attrib['forwardedicon'])
A 199     $OUTPUT->set_env('forwardedicon', $skin_path . $attrib['forwardedicon']);
200   if ($attrib['forwardedrepliedicon'])
201     $OUTPUT->set_env('forwardedrepliedicon', $skin_path . $attrib['forwardedrepliedicon']);
4e17e6 202   if ($attrib['attachmenticon'])
f11541 203     $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
e189a6 204   if ($attrib['flaggedicon'])
A 205     $OUTPUT->set_env('flaggedicon', $skin_path . $attrib['flaggedicon']);
206   if ($attrib['unflaggedicon'])
207     $OUTPUT->set_env('unflaggedicon', $skin_path . $attrib['unflaggedicon']);
f52c93 208   if ($attrib['unreadchildrenicon'])
T 209     $OUTPUT->set_env('unreadchildrenicon', $skin_path . $attrib['unreadchildrenicon']);
9800a8 210
f52c93 211   $OUTPUT->set_env('messages', array());
d24d20 212   $OUTPUT->set_env('coltypes', $a_show_cols);
9800a8 213
6b47de 214   $OUTPUT->include_script('list.js');
9800a8 215
f52c93 216   $thead = '';
T 217   foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell)
218     $thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
9800a8 219
f52c93 220   return html::tag('table',
T 221     $attrib,
222     html::tag('thead', null, html::tag('tr', null, $thead)) .
223       html::tag('tbody', null, ''),
b62c48 224         array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
f52c93 225 }
4e17e6 226
T 227
45f56c 228 /**
T 229  * return javascript commands to add rows to the message list
230  */
614c64 231 function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null)
f52c93 232 {
5bde17 233   global $CONFIG, $IMAP, $RCMAIL, $OUTPUT;
4e17e6 234
614c64 235   if (empty($a_show_cols)) {
A 236     if (!empty($_SESSION['list_attrib']['columns']))
237       $a_show_cols = $_SESSION['list_attrib']['columns'];
238     else
239       $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
240   }
241   else {
242     if (!is_array($a_show_cols))
243       $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($a_show_cols));
244     $head_replace = true;
245   }
d59aaa 246
41bece 247   $mbox = $IMAP->get_mailbox_name();
4906eb 248   $delim = $IMAP->get_hierarchy_delimiter();
614c64 249
A 250   // make sure 'threads' and 'subject' columns are present
251   if (!in_array('subject', $a_show_cols))
252     array_unshift($a_show_cols, 'subject');
253   if (!in_array('threads', $a_show_cols))
254     array_unshift($a_show_cols, 'threads');
255
256   $_SESSION['list_attrib']['columns'] = $a_show_cols;
9800a8 257
4906eb 258   // show 'to' instead of 'from' in sent/draft messages
A 259   if ((strpos($mbox.$delim, $CONFIG['sent_mbox'].$delim)===0 || strpos($mbox.$delim, $CONFIG['drafts_mbox'].$delim)===0)
f11541 260       && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
4e17e6 261     $a_show_cols[$f] = 'to';
T 262
614c64 263   // Make sure there are no duplicated columns (#1486999)
A 264   $a_show_cols = array_unique($a_show_cols);
6c9d49 265
5bde17 266   // Plugins may set header's list_cols/list_flags and other rcube_mail_header variables
A 267   // and list columns
268   $plugin = $RCMAIL->plugins->exec_hook('messages_list',
269     array('messages' => $a_headers, 'cols' => $a_show_cols));
270
271   $a_show_cols = $plugin['cols'];
272   $a_headers   = $plugin['messages'];
273
f52c93 274   $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : NULL;
9800a8 275
f52c93 276   $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead);
c4b819 277
f52c93 278   if (empty($a_headers))
T 279     return;
91354e 280
6c9d49 281   // remove 'threads', 'attachment', 'flag' columns, we don't need them here
A 282   if (($key = array_search('attachment', $a_show_cols)) !== FALSE)
91354e 283     unset($a_show_cols[$key]);
6c9d49 284   if (($key = array_search('flag', $a_show_cols)) !== FALSE)
A 285     unset($a_show_cols[$key]);
286   if (($key = array_search('threads', $a_show_cols)) !== FALSE)
91354e 287     unset($a_show_cols[$key]);
4e17e6 288   // loop through message headers
5bde17 289
ecd2e7 290   foreach ($a_headers as $n => $header)
4e17e6 291     {
ecd2e7 292     if (empty($header))
T 293       continue;
5bde17 294
A 295     $a_msg_cols = array();
296     $a_msg_flags = array();
f11541 297
5faac0 298     $IMAP->set_charset(!empty($header->charset) ? $header->charset : $CONFIG['default_charset']);
d59aaa 299
4e17e6 300     // format each col; similar as in rcmail_message_list()
T 301     foreach ($a_show_cols as $col)
302       {
5cef5b 303       if (in_array($col, array('from', 'to', 'cc', 'replyto')))
583850 304         $cont = Q(rcmail_address_string($header->$col, 3), 'show');
4e17e6 305       else if ($col=='subject')
7bbd5f 306         {
f52c93 307         $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160);
44385f 308         if (!$cont) $cont = rcube_label('nosubject');
f52c93 309         $cont = Q($cont);
7bbd5f 310         }
4e17e6 311       else if ($col=='size')
T 312         $cont = show_bytes($header->$col);
313       else if ($col=='date')
f11541 314         $cont = format_date($header->date);
4e17e6 315       else
2bca6e 316         $cont = Q($header->$col);
9800a8 317
4e17e6 318       $a_msg_cols[$col] = $cont;
T 319       }
320
f52c93 321     if ($header->depth)
T 322       $a_msg_flags['depth'] = $header->depth;
0e7b66 323     else if ($header->has_children)
A 324       $roots[] = $header->uid;
f52c93 325     if ($header->parent_uid)
T 326       $a_msg_flags['parent_uid'] = $header->parent_uid;
327     if ($header->has_children)
328       $a_msg_flags['has_children'] = $header->has_children;
329     if ($header->unread_children)
330       $a_msg_flags['unread_children'] = $header->unread_children;
c4b819 331     if ($header->deleted)
A 332       $a_msg_flags['deleted'] = 1;
333     if (!$header->seen)
334       $a_msg_flags['unread'] = 1;
335     if ($header->answered)
336       $a_msg_flags['replied'] = 1;
337     if ($header->forwarded)
338       $a_msg_flags['forwarded'] = 1;
339     if ($header->flagged)
340       $a_msg_flags['flagged'] = 1;
761894 341     if (preg_match("/(application\/|multipart\/m)/i", $header->ctype))
f52c93 342       $a_msg_flags['attachment'] = 1;
T 343     $a_msg_flags['mbox'] = $mbox;
344
5bde17 345     // merge with plugin result
A 346     if (!empty($header->list_flags) && is_array($header->list_flags))
347       $a_msg_flags = array_merge($a_msg_flags, $header->list_flags);
348     if (!empty($header->list_cols) && is_array($header->list_cols))
349       $a_msg_cols = array_merge($a_msg_cols, $header->list_cols);
350
f11541 351     $OUTPUT->command('add_message_row',
T 352       $header->uid,
353       $a_msg_cols,
354       $a_msg_flags,
355       $insert_top);
4e17e6 356     }
0e7b66 357
5bde17 358     if ($IMAP->threading) {
0e7b66 359       $OUTPUT->command('init_threads', (array) $roots);
A 360     }
b62c48 361 }
f52c93 362
T 363
364 /*
365  * Creates <THEAD> for message list table
366  */
367 function rcmail_message_list_head($attrib, $a_show_cols)
368 {
369   global $CONFIG;
370
371   $skin_path = $_SESSION['skin_path'];
372   $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s"));
373
374   // check to see if we have some settings for sorting
375   $sort_col   = $_SESSION['sort_col'];
376   $sort_order = $_SESSION['sort_order'];
377
378   // define sortable columns
379   $a_sort_cols = array('subject', 'date', 'from', 'to', 'size', 'cc');
9800a8 380
f52c93 381   if (!empty($attrib['optionsmenuicon']))
T 382     $list_menu = html::a(
383       array('href' => '#', 'onclick' => 'return '.JS_OBJECT_NAME.".command('menu-open', 'messagelistmenu')"),
384       html::img(array('src' => $skin_path . $attrib['optionsmenuicon'], 'id' => 'listmenulink', 'title' => rcube_label('listoptions')))
385     );
386   else
387     $list_menu = '';
388
6c9d49 389   $cells = array();
f52c93 390
T 391   foreach ($a_show_cols as $col) {
392     // get column name
393     switch ($col) {
394       case 'flag':
395         $col_name = sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], '');
396         break;
397       case 'attachment':
398         $col_name = sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '');
399         break;
6c9d49 400       case 'threads':
A 401         $col_name = $list_menu;
402         break;
f52c93 403       default:
T 404         $col_name = Q(rcube_label($col));
405     }
406
407     // make sort links
408     if (in_array($col, $a_sort_cols))
409       $col_name = html::a(array('href'=>"./#sort", 'onclick' => 'return '.JS_OBJECT_NAME.".command('sort','".$col."',this)", 'title' => rcube_label('sortby')), $col_name);
410
411     $sort_class = $col == $sort_col ? " sorted$sort_order" : '';
412     $class_name = $col == 'attachment' ? 'icon' : $col.$sort_class;
413
414     // put it all together
415     $cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name);
416   }
417
418   return $cells;
419 }
4e17e6 420
T 421
45f56c 422 /**
T 423  * return an HTML iframe for loading mail content
424  */
b19097 425 function rcmail_messagecontent_frame($attrib)
T 426   {
ce06d3 427   global $OUTPUT, $RCMAIL;
9800a8 428
b19097 429   if (empty($attrib['id']))
T 430     $attrib['id'] = 'rcmailcontentwindow';
431
e2c610 432   $attrib['name'] = $attrib['id'];
b19097 433
ce06d3 434   if ($RCMAIL->config->get('preview_pane'))
A 435     $OUTPUT->set_env('contentframe', $attrib['id']);
f11541 436   $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
b19097 437
95fcc3 438   return html::iframe($attrib);
b19097 439   }
T 440
4e17e6 441
T 442 function rcmail_messagecount_display($attrib)
443   {
f11541 444   global $IMAP, $OUTPUT;
9800a8 445
4e17e6 446   if (!$attrib['id'])
T 447     $attrib['id'] = 'rcmcountdisplay';
448
f11541 449   $OUTPUT->add_gui_object('countdisplay', $attrib['id']);
4e17e6 450
e2c610 451   return html::span($attrib, rcmail_get_messagecount_text());
4e17e6 452   }
T 453
454
58e360 455 function rcmail_quota_display($attrib)
T 456   {
f52c93 457   global $OUTPUT;
58e360 458
T 459   if (!$attrib['id'])
460     $attrib['id'] = 'rcmquotadisplay';
461
6d2714 462   if(isset($attrib['display']))
A 463     $_SESSION['quota_display'] = $attrib['display'];
464
f11541 465   $OUTPUT->add_gui_object('quotadisplay', $attrib['id']);
9800a8 466
5b3ed5 467   $quota = rcmail_quota_content($attrib);
9800a8 468
a2e817 469   $OUTPUT->add_script('$(document).ready(function(){
2717f9 470     rcmail.set_quota('.json_serialize($quota).')});', 'foot');
9800a8 471
a2e817 472   return html::span($attrib, '');
23796e 473   }
S 474
475
5b3ed5 476 function rcmail_quota_content($attrib=NULL)
23796e 477   {
5b3ed5 478   global $COMM_PATH, $RCMAIL;
3ea0e3 479
5b3ed5 480   $quota = $RCMAIL->imap->get_quota();
A 481   $quota = $RCMAIL->plugins->exec_hook('quota', $quota);
6d2714 482
a2e817 483   $quota_result = (array) $quota;
A 484   $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
5b3ed5 485
a2e817 486   if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited')) {
A 487     $quota_result['title'] = rcube_label('unlimited');
488     $quota_result['percent'] = 0;
489     }
490   else if ($quota['total']) {
5b3ed5 491     if (!isset($quota['percent']))
a2e817 492       $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100));
9800a8 493
a2e817 494     $title = sprintf('%s / %s (%.0f%%)',
7415c0 495         show_bytes($quota['used'] * 1024), show_bytes($quota['total'] * 1024),
a2e817 496         $quota_result['percent']);
3ea0e3 497
a2e817 498     $quota_result['title'] = $title;
5b3ed5 499
a2e817 500     if ($attrib['width'])
A 501       $quota_result['width'] = $attrib['width'];
502     if ($attrib['height'])
503       $quota_result['height']    = $attrib['height'];
504     }
505   else {
506     $quota_result['title'] = rcube_label('unknown');
507     $quota_result['percent'] = 0;
3978d2 508     }
A 509
a2e817 510   return $quota_result;
58e360 511   }
T 512
4e17e6 513
4647e1 514 function rcmail_get_messagecount_text($count=NULL, $page=NULL)
4e17e6 515   {
9800a8 516   global $RCMAIL, $IMAP, $MESSAGE;
A 517
8fa58e 518   if (isset($MESSAGE->index))
4e17e6 519     {
T 520     return rcube_label(array('name' => 'messagenrof',
f52c93 521         'vars' => array('nr'  => $MESSAGE->index+1,
T 522         'count' => $count!==NULL ? $count : $IMAP->messagecount(NULL, 'ALL')))); // Only messages, no threads here
4e17e6 523     }
31b2ce 524
4647e1 525   if ($page===NULL)
T 526     $page = $IMAP->list_page;
9800a8 527
4647e1 528   $start_msg = ($page-1) * $IMAP->page_size + 1;
9800a8 529
A 530   if ($count!==NULL)
531     $max = $count;
532   else if ($RCMAIL->action)
533     $max = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL');
4e17e6 534
T 535   if ($max==0)
536     $out = rcube_label('mailboxempty');
537   else
f52c93 538     $out = rcube_label(array('name' => $IMAP->threading ? 'threadsfromto' : 'messagesfromto',
T 539             'vars' => array('from'  => $start_msg,
540             'to'    => min($max, $start_msg + $IMAP->page_size - 1),
541             'count' => $max)));
4e17e6 542
2bca6e 543   return Q($out);
4e17e6 544   }
T 545
cbeea3 546
ac5d15 547 function rcmail_mailbox_name_display($attrib)
T 548 {
cbeea3 549   global $RCMAIL;
ac5d15 550
cbeea3 551   if (!$attrib['id'])
A 552     $attrib['id'] = 'rcmmailboxname';
ac5d15 553
cbeea3 554   $RCMAIL->output->add_gui_object('mailboxname', $attrib['id']);
ac5d15 555
cbeea3 556   return html::span($attrib, rcmail_get_mailbox_name_text());
ac5d15 557 }
T 558
f96ffd 559
ac5d15 560 function rcmail_get_mailbox_name_text()
T 561 {
cbeea3 562   global $RCMAIL;
A 563   return rcmail_localize_foldername($RCMAIL->imap->get_mailbox_name());
ac5d15 564 }
T 565
cbeea3 566
2144f9 567 function rcmail_send_unread_count($mbox_name, $force=false, $count=null)
cbeea3 568 {
A 569   global $RCMAIL;
9800a8 570
cbeea3 571   $old_unseen = $_SESSION['unseen_count'][$mbox_name];
2144f9 572
A 573   if ($count === null)
574     $unseen = $RCMAIL->imap->messagecount($mbox_name, 'UNSEEN', $force);
575   else
576     $unseen = $count;
cbeea3 577
7d1db8 578   if ($unseen != $old_unseen || ($mbox_name == 'INBOX'))
cbeea3 579     $RCMAIL->output->command('set_unread_count', $mbox_name, $unseen, ($mbox_name == 'INBOX'));
A 580
581   // @TODO: this data is doubled (session and cache tables) if caching is enabled
582   $_SESSION['unseen_count'][$mbox_name] = $unseen;
9800a8 583
cbeea3 584   return $unseen;
A 585 }
f96ffd 586
cbeea3 587
ec603f 588 /**
A 589  * Sets message is_safe flag according to 'show_images' option value
590  *
591  * @param object rcube_message Message
592  */
593 function rcmail_check_safe(&$message)
594 {
595   global $RCMAIL;
596
597   $show_images = $RCMAIL->config->get('show_images');
598   if (!$message->is_safe
599     && !empty($show_images)
600     && $message->has_html_part())
601   {
602     switch($show_images) {
603       case '1': // known senders only
8a78a1 604         $CONTACTS = new rcube_contacts($RCMAIL->db, $_SESSION['user_id']);
ec603f 605         if ($CONTACTS->search('email', $message->sender['mailto'], true, false)->count) {
A 606           $message->set_safe(true);
607         }
608       break;
609       case '2': // always
610         $message->set_safe(true);
611       break;
612     }
613   }
614 }
f96ffd 615
ec603f 616
A 617 /**
618  * Cleans up the given message HTML Body (for displaying)
619  *
620  * @param string HTML
621  * @param array  Display parameters 
622  * @param array  CID map replaces (inline images)
623  * @return string Clean HTML
624  */
625 function rcmail_wash_html($html, $p = array(), $cid_replaces)
626 {
627   global $REMOTE_OBJECTS;
69a7d3 628
ec603f 629   $p += array('safe' => false, 'inline_html' => true);
2337a8 630
ec603f 631   // special replacements (not properly handled by washtml class)
A 632   $html_search = array(
633     '/(<\/nobr>)(\s+)(<nobr>)/i',    // space(s) between <NOBR>
69a7d3 634     '/<title[^>]*>.*<\/title>/i',    // PHP bug #32547 workaround: remove title tag
3bde30 635     '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/',    // byte-order mark (only outlook?)
4f7aa8 636     '/<html\s[^>]+>/i',            // washtml/DOMDocument cannot handle xml namespaces
ec603f 637   );
A 638   $html_replace = array(
639     '\\1'.' &nbsp; '.'\\3',
640     '',
641     '',
6c2d7e 642     '<html>',
ec603f 643   );
A 644   $html = preg_replace($html_search, $html_replace, $html);
2337a8 645
b9ec2b 646   // PCRE errors handling (#1486856), should we use something like for every preg_* use?
A 647   if ($html === null && ($preg_error = preg_last_error()) != PREG_NO_ERROR) {
648     $errstr = "Could not clean up HTML message! PCRE Error: $preg_error.";
649
650     if ($preg_error == PREG_BACKTRACK_LIMIT_ERROR)
651       $errstr .= " Consider raising pcre.backtrack_limit!";
652     if ($preg_error == PREG_RECURSION_LIMIT_ERROR)
653       $errstr .= " Consider raising pcre.recursion_limit!";
654
655     raise_error(array('code' => 600, 'type' => 'php',
656         'line' => __LINE__, 'file' => __FILE__,
657         'message' => $errstr), true, false);
658     return '';
659   }
660
2337a8 661   // fix (unknown/malformed) HTML tags before "wash"
b6673c 662   $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', 'rcmail_html_tag_callback', $html);
ec603f 663
a80b7d 664   // charset was converted to UTF-8 in rcube_imap::get_message_part(),
269fb8 665   // -> change charset specification in HTML accordingly
69a7d3 666   $charset_pattern = '(<meta\s+[^>]* content=)[\'"]?(\w+\/\w+;\s*charset=)([a-z0-9-_]+[\'"]?)';
f4bf20 667   if (preg_match("/$charset_pattern/Ui", $html)) {
69a7d3 668     $html = preg_replace("/$charset_pattern/i", '\\1"\\2'.RCMAIL_CHARSET.'"', $html);
ec603f 669   }
269fb8 670   else {
A 671     // add meta content-type to malformed messages, washtml cannot work without that
672     if (!preg_match('/<head[^>]*>(.*)<\/head>/Uims', $html))
673       $html = '<head></head>'. $html;
674     $html = substr_replace($html, '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0);
675   }
10d0e3 676
ec603f 677   // turn relative into absolute urls
A 678   $html = rcmail_resolve_base($html);
679
680   // clean HTML with washhtml by Frederic Motte
681   $wash_opts = array(
682     'show_washed' => false,
683     'allow_remote' => $p['safe'],
684     'blocked_src' => "./program/blocked.gif",
685     'charset' => RCMAIL_CHARSET,
686     'cid_map' => $cid_replaces,
687     'html_elements' => array('body'),
688   );
ce4673 689
ec603f 690   if (!$p['inline_html']) {
A 691     $wash_opts['html_elements'] = array('html','head','title','body');
692   }
693   if ($p['safe']) {
694     $wash_opts['html_elements'][] = 'link';
695     $wash_opts['html_attribs'] = array('rel','type');
696   }
9800a8 697
33da0b 698   // overwrite washer options with options from plugins
A 699   if (isset($p['html_elements']))
700     $wash_opts['html_elements'] = $p['html_elements'];
701   if (isset($p['html_attribs']))
702     $wash_opts['html_attribs'] = $p['html_attribs'];
703
704   // initialize HTML washer
ec603f 705   $washer = new washtml($wash_opts);
33da0b 706
A 707   if (!$p['skip_washer_form_callback'])
708     $washer->add_callback('form', 'rcmail_washtml_callback');
ec603f 709
2337a8 710   // allow CSS styles, will be sanitized by rcmail_washtml_callback()
33da0b 711   if (!$p['skip_washer_style_callback'])
A 712     $washer->add_callback('style', 'rcmail_washtml_callback');
bf1b66 713
ec603f 714   $html = $washer->wash($html);
A 715   $REMOTE_OBJECTS = $washer->extlinks;
5b3ed5 716
ec603f 717   return $html;
A 718 }
719
4e17e6 720
45f56c 721 /**
65cc1c 722  * Convert the given message part to proper HTML
T 723  * which can be displayed the message view
45f56c 724  *
65cc1c 725  * @param object rcube_message_part Message part
ec603f 726  * @param array  Display parameters array 
65cc1c 727  * @return string Formatted HTML string
45f56c 728  */
21e724 729 function rcmail_print_body($part, $p = array())
45f56c 730 {
cc97ea 731   global $RCMAIL;
9800a8 732
cc97ea 733   // trigger plugin hook
T 734   $data = $RCMAIL->plugins->exec_hook('message_part_before',
735     array('type' => $part->ctype_secondary, 'body' => $part->body) + $p + array('safe' => false, 'plain' => false, 'inline_html' => true));
ec603f 736
5cc4b1 737   // convert html to text/plain
cc97ea 738   if ($data['type'] == 'html' && $data['plain']) {
T 739     $txt = new html2text($data['body'], false, true);
5cc4b1 740     $body = $txt->get_text();
T 741     $part->ctype_secondary = 'plain';
45f56c 742   }
4e17e6 743   // text/html
cc97ea 744   else if ($data['type'] == 'html') {
T 745     $body = rcmail_wash_html($data['body'], $data, $part->replaces);
746     $part->ctype_secondary = $data['type'];
45f56c 747   }
4e17e6 748   // text/enriched
cc97ea 749   else if ($data['type'] == 'enriched') {
cfe4a6 750     $part->ctype_secondary = 'html';
ec603f 751     require_once('lib/enriched.inc');
cc97ea 752     $body = Q(enriched_to_html($data['body']), 'show');
45f56c 753   }
cc97ea 754   else {
T 755     // assert plaintext
45f56c 756     $body = $part->body;
cc97ea 757     $part->ctype_secondary = $data['type'] = 'plain';
45f56c 758   }
9800a8 759
cc97ea 760   // free some memory (hopefully)
T 761   unset($data['body']);
45f56c 762
cc97ea 763   // plaintext postprocessing
4f6932 764   if ($part->ctype_secondary == 'plain')
99b8c1 765     $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed');
88ed23 766
cc97ea 767   // allow post-processing of the message body
T 768   $data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body) + $data);
769
770   return $data['type'] == 'html' ? $data['body'] : html::tag('pre', array(), $data['body']);
4f6932 771 }
A 772
f96ffd 773
4f6932 774 /**
A 775  * Handle links and citation marks in plain text message
776  *
99b8c1 777  * @param string  Plain text string
A 778  * @param boolean Text uses format=flowed
779  *
4f6932 780  * @return string Formatted HTML string
A 781  */
99b8c1 782 function rcmail_plain_body($body, $flowed=false)
4f6932 783 {
33dfdd 784   global $RCMAIL;
A 785
4f6932 786   // make links and email-addresses clickable
99b8c1 787   $replacer = new rcube_string_replacer;
9800a8 788
4f6932 789   // search for patterns like links and e-mail addresses
99b8c1 790   $body = preg_replace_callback($replacer->link_pattern, array($replacer, 'link_callback'), $body);
A 791   $body = preg_replace_callback($replacer->mailto_pattern, array($replacer, 'mailto_callback'), $body);
4f6932 792
A 793   // split body into single lines
794   $a_lines = preg_split('/\r?\n/', $body);
795   $quote_level = 0;
99b8c1 796   $last = -1;
4f6932 797
A 798   // find/mark quoted lines...
799   for ($n=0, $cnt=count($a_lines); $n < $cnt; $n++) {
800     if ($a_lines[$n][0] == '>' && preg_match('/^(>+\s*)+/', $a_lines[$n], $regs)) {
801       $q = strlen(preg_replace('/\s/', '', $regs[0]));
99b8c1 802       $a_lines[$n] = substr($a_lines[$n], strlen($regs[0]));
4f6932 803
A 804       if ($q > $quote_level)
99b8c1 805         $a_lines[$n] = $replacer->get_replacement($replacer->add(
A 806           str_repeat('<blockquote>', $q - $quote_level))) . $a_lines[$n];
4f6932 807       else if ($q < $quote_level)
99b8c1 808         $a_lines[$n] = $replacer->get_replacement($replacer->add(
A 809           str_repeat('</blockquote>', $quote_level - $q))) . $a_lines[$n];
810       else if ($flowed) {
811         // previous line is flowed
33dfdd 812         if (isset($a_lines[$last]) && $a_lines[$n]
99b8c1 813           && $a_lines[$last][strlen($a_lines[$last])-1] == ' ') {
33dfdd 814           // merge lines
99b8c1 815           $a_lines[$last] .= $a_lines[$n];
A 816           unset($a_lines[$n]);
817         }
818         else
819           $last = $n;
820       }
4f6932 821     }
99b8c1 822     else {
A 823       $q = 0;
824       if ($flowed) {
825         // sig separator - line is fixed
826         if ($a_lines[$n] == '-- ') {
827           $last = $n;
828         }
829         else {
830           // remove space-stuffing
831           if ($a_lines[$n][0] == ' ')
832             $a_lines[$n] = substr($a_lines[$n], 1);
833
834           // previous line is flowed?
33dfdd 835           if (isset($a_lines[$last]) && $a_lines[$n]
99b8c1 836             && $a_lines[$last] != '-- '
A 837             && $a_lines[$last][strlen($a_lines[$last])-1] == ' '
838           ) {
839             $a_lines[$last] .= $a_lines[$n];
840             unset($a_lines[$n]);
841           }
842           else {
843             $last = $n;
844           }
845         }
846         if ($quote_level > 0)
847           $a_lines[$last] = $replacer->get_replacement($replacer->add(
848             str_repeat('</blockquote>', $quote_level))) . $a_lines[$last];
849       }
850       else if ($quote_level > 0)
851         $a_lines[$n] = $replacer->get_replacement($replacer->add(
852           str_repeat('</blockquote>', $quote_level))) . $a_lines[$n];
853     }
4f6932 854
A 855     $quote_level = $q;
856   }
857
858   // quote plain text
2753a4 859   $body = Q(join("\n", $a_lines), 'dummy', false);
4f6932 860
A 861   // colorize signature
33dfdd 862   $len = strlen($body);
A 863   while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
864     if ($sp == 0 || $body[$sp-1] == "\n") {
865       // do not touch blocks with more that X lines
866       if (substr_count($body, "\n", $sp) < $RCMAIL->config->get('sig_max_lines', 15))
99b8c1 867         $body = substr($body, 0, max(0, $sp))
A 868           .'<span class="sig">'.substr($body, $sp).'</span>';
33dfdd 869       break;
4f6932 870     }
99b8c1 871   }
4f6932 872
99b8c1 873   // insert url/mailto links and citation tags
A 874   $body = $replacer->resolve($body);
4f6932 875
A 876   return $body;
21e724 877 }
4e17e6 878
21e724 879
T 880 /**
881  * Callback function for washtml cleaning class
882  */
883 function rcmail_washtml_callback($tagname, $attrib, $content)
884 {
885   switch ($tagname) {
886     case 'form':
887       $out = html::div('form', $content);
f54a3a 888       break;
9800a8 889
1c499a 890     case 'style':
T 891       // decode all escaped entities and reduce to ascii strings
c5ee03 892       $stripped = preg_replace('/[^a-zA-Z\(:]/', '', rcmail_xss_entity_decode($content));
9800a8 893
36c236 894       // now check for evil strings like expression, behavior or url()
T 895       if (!preg_match('/expression|behavior|url\(|import/', $stripped)) {
1c499a 896         $out = html::tag('style', array('type' => 'text/css'), $content);
T 897         break;
898       }
9800a8 899
21e724 900     default:
T 901       $out = '';
902   }
9800a8 903
21e724 904   return $out;
T 905 }
4e17e6 906
T 907
45f56c 908 /**
2337a8 909  * Callback function for HTML tags fixing
A 910  */
911 function rcmail_html_tag_callback($matches)
912 {
913   $tagname = $matches[2];
914
915   $tagname = preg_replace(array(
f96ffd 916     '/:.*$/',            // Microsoft's Smart Tags <st1:xxxx>
A 917     '/[^a-z0-9_\[\]\!-]/i',    // forbidden characters
2337a8 918     ), '', $tagname);
A 919
920   return $matches[1].$tagname;
921 }
922
923
924 /**
45f56c 925  * return table with message headers
T 926  */
4e17e6 927 function rcmail_message_headers($attrib, $headers=NULL)
T 928   {
cc97ea 929   global $IMAP, $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL;
4e17e6 930   static $sa_attrib;
9800a8 931
4e17e6 932   // keep header table attrib
T 933   if (is_array($attrib) && !$sa_attrib)
934     $sa_attrib = $attrib;
935   else if (!is_array($attrib) && is_array($sa_attrib))
936     $attrib = $sa_attrib;
9800a8 937
4e17e6 938   if (!isset($MESSAGE))
T 939     return FALSE;
940
941   // get associative array of headers object
942   if (!$headers)
8fa58e 943     $headers = is_object($MESSAGE->headers) ? get_object_vars($MESSAGE->headers) : $MESSAGE->headers;
7c60ff 944
4e17e6 945   // show these headers
591381 946   $standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto', 'date');
cc97ea 947   $output_headers = array();
e5686f 948
cc97ea 949   foreach ($standard_headers as $hkey) {
4e17e6 950     if (!$headers[$hkey])
T 951       continue;
952
cc97ea 953     if ($hkey == 'date') {
5b1de5 954       if ($PRINT_MODE)
cc97ea 955         $header_value = format_date($headers[$hkey], $RCMAIL->config->get('date_long', 'x'));
5b1de5 956       else
A 957         $header_value = format_date($headers[$hkey]);
cc97ea 958     }
T 959     else if ($hkey == 'replyto') {
700320 960       if ($headers['replyto'] != $headers['from'])
cc97ea 961         $header_value = rcmail_address_string($headers['replyto'], null, true, $attrib['addicon']);
700320 962       else
A 963         continue;
4e17e6 964     }
cc97ea 965     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc')))
T 966       $header_value = rcmail_address_string($headers[$hkey], null, true, $attrib['addicon']);
967     else if ($hkey == 'subject' && empty($headers[$hkey]))
968       $header_value = rcube_label('nosubject');
969     else
970       $header_value = trim($IMAP->decode_header($headers[$hkey]));
9800a8 971
cc97ea 972     $output_headers[$hkey] = array('title' => rcube_label($hkey), 'value' => $header_value, 'raw' => $headers[$hkey]);
T 973   }
9800a8 974
cc97ea 975   $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', array('output' => $output_headers, 'headers' => $MESSAGE->headers));
9800a8 976
cc97ea 977   // compose html table
T 978   $table = new html_table(array('cols' => 2));
9800a8 979
cc97ea 980   foreach ($plugin['output'] as $hkey => $row) {
T 981     $table->add(array('class' => 'header-title'), Q($row['title']));
982     $table->add(array('class' => $hkey, 'width' => "90%"), Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show')));
983   }
4e17e6 984
e5686f 985   // all headers division
cc97ea 986   $table->add(array('colspan' => 2, 'class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('load-headers','',this)"), '');
T 987   $table->add_row(array('id' => "all-headers"));
988   $table->add(array('colspan' => 2, 'class' => "all"), html::div(array('id' => 'headers-source'), ''));
989   
e5686f 990   $OUTPUT->add_gui_object('all_headers_row', 'all-headers');
A 991   $OUTPUT->add_gui_object('all_headers_box', 'headers-source');
992
cc97ea 993   return $table->show($attrib);
4e17e6 994   }
T 995
996
45f56c 997 /**
21605c 998  * Handler for the 'messagebody' GUI object
45f56c 999  *
21605c 1000  * @param array Named parameters
T 1001  * @return string HTML content showing the message body
45f56c 1002  */
4e17e6 1003 function rcmail_message_body($attrib)
T 1004   {
3c3433 1005   global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $RCMAIL, $REMOTE_OBJECTS;
5f8686 1006
8fa58e 1007   if (!is_array($MESSAGE->parts) && empty($MESSAGE->body))
4e17e6 1008     return '';
9800a8 1009
4e17e6 1010   if (!$attrib['id'])
T 1011     $attrib['id'] = 'rcmailMsgBody';
1012
8fa58e 1013   $safe_mode = $MESSAGE->is_safe || intval($_GET['_safe']);
21605c 1014   $out = '';
9800a8 1015
4e17e6 1016   $header_attrib = array();
T 1017   foreach ($attrib as $attr => $value)
1018     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
1019       $header_attrib[$regs[1]] = $value;
1020
8fa58e 1021   if (!empty($MESSAGE->parts))
4e17e6 1022     {
8fa58e 1023     foreach ($MESSAGE->parts as $i => $part)
4e17e6 1024       {
8fa58e 1025       if ($part->type == 'headers')
8d4bcd 1026         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
3b7e00 1027       else if ($part->type == 'content' && $part->size)
4e17e6 1028         {
8d4bcd 1029         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
8fa58e 1030           $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
a0109c 1031
8d4bcd 1032         // fetch part if not available
T 1033         if (!isset($part->body))
8fa58e 1034           $part->body = $MESSAGE->get_part_content($part->mime_id);
a0109c 1035
64e3e8 1036         // message is cached but not exists (#1485443), or other error
A 1037         if ($part->body === false) {
1038           rcmail_message_error($MESSAGE->uid);
1039         }
1040
3c3433 1041         $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
A 1042           'part' => $part, 'prefix' => ''));
6b6f2e 1043
21e724 1044         $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
5f8686 1045
7b808b 1046         if ($part->ctype_secondary == 'html') {
fb995a 1047           $body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs);
7b808b 1048           $div_attr = array('class' => 'message-htmlpart');
fb995a 1049           $style = array();
A 1050
cb3dfd 1051           if (!empty($attrs)) {
A 1052             foreach ($attrs as $a_idx => $a_val)
1053               $style[] = $a_idx . ': ' . $a_val;
1054             if (!empty($style))
1055               $div_attr['style'] = implode('; ', $style);
1056           }
7b808b 1057
3c3433 1058           $out .= html::div($div_attr, $plugin['prefix'] . $body);
7b808b 1059         }
a2f2c5 1060         else
3c3433 1061           $out .= html::div('message-part', $plugin['prefix'] . $body);
4e17e6 1062         }
T 1063       }
1064     }
3c3433 1065   else {
A 1066     $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
1067       'part' => $MESSAGE, 'prefix' => ''));
1068
1069     $out .= html::div('message-part', $plugin['prefix'] . html::tag('pre', array(),
4f6932 1070       rcmail_plain_body(Q($MESSAGE->body, 'strict', false))));
3c3433 1071     }
4e17e6 1072
8fa58e 1073   $ctype_primary = strtolower($MESSAGE->structure->ctype_primary);
T 1074   $ctype_secondary = strtolower($MESSAGE->structure->ctype_secondary);
166b61 1075
4e17e6 1076   // list images after mail body
be5f03 1077   if ($CONFIG['inline_images']
2da368 1078       && $ctype_primary == 'multipart'
38d930 1079       && !empty($MESSAGE->attachments))
166b61 1080     {
8fa58e 1081     foreach ($MESSAGE->attachments as $attach_prop) {
47d06e 1082       // Content-Type: image/*...
A 1083       if (preg_match('/^image\//i', $attach_prop->mimetype) ||
1084         // ...or known file extension: many clients are using application/octet-stream
1085         ($attach_prop->filename &&
1086           preg_match('/^application\/octet-stream$/i', $attach_prop->mimetype) &&
1087           preg_match('/\.(jpg|jpeg|png|gif|bmp)$/i', $attach_prop->filename))
1088       ) {
8fa58e 1089         $out .= html::tag('hr') . html::p(array('align' => "center"),
T 1090           html::img(array(
1091             'src' => $MESSAGE->get_part_url($attach_prop->mime_id),
1092             'title' => $attach_prop->filename,
1093             'alt' => $attach_prop->filename,
1094           )));
1095         }
4e17e6 1096     }
8fa58e 1097   }
9800a8 1098
4e17e6 1099   // tell client that there are blocked remote objects
T 1100   if ($REMOTE_OBJECTS && !$safe_mode)
f11541 1101     $OUTPUT->set_env('blockedobjects', true);
4e17e6 1102
21605c 1103   return html::div($attrib, $out);
4e17e6 1104   }
T 1105
1106
aa055c 1107 /**
T 1108  * Convert all relative URLs according to a <base> in HTML
1109  */
1110 function rcmail_resolve_base($body)
1111 {
1112   // check for <base href=...>
1113   if (preg_match('!(<base.*href=["\']?)([hftps]{3,5}://[a-z0-9/.%-]+)!i', $body, $regs)) {
1114     $replacer = new rcube_base_replacer($regs[2]);
1115
1116     // replace all relative paths
1117     $body = preg_replace_callback('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Ui', array($replacer, 'callback'), $body);
1118     $body = preg_replace_callback('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Ui', array($replacer, 'callback'), $body);
1119   }
1120
1121   return $body;
1122 }
4e17e6 1123
45f56c 1124 /**
T 1125  * modify a HTML message that it can be displayed inside a HTML page
1126  */
fb995a 1127 function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null)
cb3dfd 1128 {
4e17e6 1129   $last_style_pos = 0;
T 1130   $body_lc = strtolower($body);
cb3dfd 1131   $cont_id = $container_id.($body_id ? ' div.'.$body_id : '');
9800a8 1132
4e17e6 1133   // find STYLE tags
T 1134   while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
cb3dfd 1135   {
ea206d 1136     $pos = strpos($body_lc, '>', $pos)+1;
T 1137
4e17e6 1138     // replace all css definitions with #container [def]
cb3dfd 1139     $styles = rcmail_mod_css_styles(
A 1140       substr($body, $pos, $pos2-$pos), $cont_id);
ea206d 1141
3b12ae 1142     $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
S 1143     $body_lc = strtolower($body);
4e17e6 1144     $last_style_pos = $pos2;
cb3dfd 1145   }
4e17e6 1146
fe79b1 1147   // modify HTML links to open a new window if clicked
115263 1148   $GLOBALS['rcmail_html_container_id'] = $container_id;
T 1149   $body = preg_replace_callback('/<(a|link)\s+([^>]+)>/Ui', 'rcmail_alter_html_link', $body);
1150   unset($GLOBALS['rcmail_html_container_id']);
4e17e6 1151
cb3dfd 1152   $body = preg_replace(array(
b488c1 1153       // add comments arround html and other tags
d7a411 1154       '/(<!DOCTYPE[^>]*>)/i',
A 1155       '/(<\?xml[^>]*>)/i',
06895c 1156       '/(<\/?html[^>]*>)/i',
T 1157       '/(<\/?head[^>]*>)/i',
1158       '/(<title[^>]*>.*<\/title>)/Ui',
b488c1 1159       '/(<\/?meta[^>]*>)/i',
A 1160       // quote <? of php and xml files that are specified as text/html
1161       '/<\?/',
1162       '/\?>/',
1163       // replace <body> with <div>
1164       '/<body([^>]*)>/i',
1165       '/<\/body>/i',
1166       ),
1167     array(
1168       '<!--\\1-->',
1169       '<!--\\1-->',
1170       '<!--\\1-->',
1171       '<!--\\1-->',
1172       '<!--\\1-->',
1173       '<!--\\1-->',
1174       '&lt;?',
1175       '?&gt;',
7b808b 1176       '<div class="'.$body_id.'"\\1>',
b488c1 1177       '</div>',
A 1178       ),
06895c 1179     $body);
a0109c 1180
fb995a 1181   $attributes = array();
A 1182
1183   // Handle body attributes that doesn't play nicely with div elements
cb3dfd 1184   if (preg_match('/<div class="' . preg_quote($body_id, '/') . '" ([^>]+)/', $body, $m)) {
fb995a 1185     $attrs = $m[0];
A 1186     // Get bgcolor, we'll set it as background-color of the message container
1187     if (preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/', $attrs, $mb)) {
cb3dfd 1188       $attributes['background-color'] = $mb[1];
fb995a 1189       $attrs = preg_replace('/bgcolor=["\']*([a-z0-9#]+)["\']*/', '', $attrs);
7b808b 1190     }
fb995a 1191     // Get background, we'll set it as background-image of the message container
A 1192     if (preg_match('/background=["\']*([^"\'>\s]+)["\']*/', $attrs, $mb)) {
cb3dfd 1193       $attributes['background-image'] = 'url('.$mb[1].')';
fb995a 1194       $attrs = preg_replace('/background=["\']*([^"\'>\s]+)["\']*/', '', $attrs);
A 1195     }
1196     if (!empty($attributes))
cb3dfd 1197       $body = preg_replace('/<div class="' . preg_quote($body_id, '/') . '" [^>]+/', rtrim($attrs), $body, 1);
A 1198
1199     // handle body styles related to background image
1200     if ($attributes['background-image']) {
1201       // get body style
1202       if (preg_match('/#'.preg_quote($cont_id, '/').'\s+\{([^}]+)}/i', $body, $m)) {
1203         // get background related style
1204         if (preg_match_all('/(background-position|background-repeat)\s*:\s*([^;]+);/i', $m[1], $ma, PREG_SET_ORDER)) {
1205           foreach ($ma as $style)
1206             $attributes[$style[1]] = $style[2];
1207         }
1208       }
1209     }
7b808b 1210   }
b488c1 1211   // make sure there's 'rcmBody' div, we need it for proper css modification
A 1212   // its name is hardcoded in rcmail_message_body() also
7b808b 1213   else
cb3dfd 1214     $body = '<div class="' . $body_id . '">' . $body . '</div>';
86958f 1215
cb3dfd 1216   return $body;
A 1217 }
4e17e6 1218
T 1219
45f56c 1220 /**
T 1221  * parse link attributes and set correct target
1222  */
115263 1223 function rcmail_alter_html_link($matches)
e5af2f 1224 {
115263 1225   global $EMAIL_ADDRESS_PATTERN;
9800a8 1226
115263 1227   $tag = $matches[1];
T 1228   $attrib = parse_attrib_string($matches[2]);
e5af2f 1229   $end = '>';
84f5b7 1230
e5af2f 1231   if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) {
677e1f 1232     $attrib['href'] = "?_task=utils&amp;_action=modcss&amp;u=" . urlencode($attrib['href'])
A 1233         . "&amp;c=" . urlencode($GLOBALS['rcmail_html_container_id']);
e5af2f 1234     $end = ' />';
T 1235   }
ad18d6 1236   else if (preg_match('/^mailto:'.$EMAIL_ADDRESS_PATTERN.'(\?[^"\'>]+)?/i', $attrib['href'], $mailto)) {
115263 1237     $attrib['href'] = $mailto[0];
97bd2c 1238     $attrib['onclick'] = sprintf(
T 1239       "return %s.command('compose','%s',this)",
1240       JS_OBJECT_NAME,
ad18d6 1241       JQ($mailto[1].$mailto[2]));
4e17e6 1242   }
e5af2f 1243   else if (!empty($attrib['href']) && $attrib['href'][0] != '#') {
T 1244     $attrib['target'] = '_blank';
1245   }
1246
1247   return "<$tag" . html::attrib_string($attrib, array('href','name','target','onclick','id','class','style','title','rel','type','media')) . $end;
1248 }
4e17e6 1249
T 1250
45f56c 1251 /**
T 1252  * decode address string and re-format it as HTML links
1253  */
8e44f4 1254 function rcmail_address_string($input, $max=null, $linked=false, $addicon=null)
T 1255 {
e99991 1256   global $IMAP, $RCMAIL, $PRINT_MODE, $CONFIG;
55243b 1257   static $got_writable_abook = null;
1088d6 1258
4e17e6 1259   $a_parts = $IMAP->decode_address_list($input);
T 1260
1261   if (!sizeof($a_parts))
1262     return $input;
1263
1264   $c = count($a_parts);
1265   $j = 0;
1266   $out = '';
1267
55243b 1268   if ($got_writable_abook === null && $books = $RCMAIL->get_address_sources(true)) {
A 1269     $got_writable_abook = true;
1270   }
b579f4 1271
8e44f4 1272   foreach ($a_parts as $part) {
4e17e6 1273     $j++;
e99991 1274
A 1275     $name   = $part['name'];
1276     $mailto = $part['mailto'];
1277     $string = $part['string'];
1278
1279     // IDNA ASCII to Unicode
1280     if ($name == $mailto)
1281       $name = idn_to_utf8($name);
1282     if ($string == $mailto)
1283       $string = idn_to_utf8($string);
1284     $mailto = idn_to_utf8($mailto);
1285
8e44f4 1286     if ($PRINT_MODE) {
e99991 1287       $out .= sprintf('%s &lt;%s&gt;', Q($name), $mailto);
8e44f4 1288     }
1baeb6 1289     else if (check_email($part['mailto'], false)) {
8e44f4 1290       if ($linked) {
T 1291         $out .= html::a(array(
e99991 1292             'href' => 'mailto:'.$mailto,
A 1293             'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)),
1294             'title' => $mailto,
8e44f4 1295             'class' => "rcmContactAddress",
T 1296           ),
e99991 1297         Q($name ? $name : $mailto));
4e17e6 1298       }
8e44f4 1299       else {
e99991 1300         $out .= html::span(array('title' => $mailto, 'class' => "rcmContactAddress"),
A 1301           Q($name ? $name : $mailto));
8e44f4 1302       }
T 1303
55243b 1304       if ($addicon && $got_writable_abook) {
8e44f4 1305         $out .= '&nbsp;' . html::a(array(
T 1306             'href' => "#add",
e99991 1307             'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, urlencode($string)),
8e44f4 1308             'title' => rcube_label('addtoaddressbook'),
T 1309           ),
1310           html::img(array(
1311             'src' => $CONFIG['skin_path'] . $addicon,
1312             'alt' => "Add contact",
1313           )));
1314       }
1315     }
1316     else {
e99991 1317       if ($name)
A 1318         $out .= Q($name);
1319       if ($mailto)
1320         $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($mailto));
8e44f4 1321     }
9800a8 1322
4e17e6 1323     if ($c>$j)
T 1324       $out .= ','.($max ? '&nbsp;' : ' ');
9800a8 1325
8e44f4 1326     if ($max && $j==$max && $c>$j) {
4e17e6 1327       $out .= '...';
T 1328       break;
1329     }
8e44f4 1330   }
9800a8 1331
4e17e6 1332   return $out;
8e44f4 1333 }
4e17e6 1334
T 1335
ccd63c 1336 /**
T 1337  * Wrap text to a given number of characters per line
6b6f2e 1338  * but respect the mail quotation of replies messages (>).
T 1339  * Finally add another quotation level by prpending the lines
1340  * with >
ccd63c 1341  *
T 1342  * @param string Text to wrap
1343  * @param int The line width
1344  * @return string The wrapped text
1345  */
6b6f2e 1346 function rcmail_wrap_and_quote($text, $length = 72)
ccd63c 1347 {
T 1348   // Rebuild the message body with a maximum of $max chars, while keeping quoted message.
dffcaa 1349   $max = min(77, $length + 8);
ccd63c 1350   $lines = preg_split('/\r?\n/', trim($text));
T 1351   $out = '';
1352
1353   foreach ($lines as $line) {
6b6f2e 1354     // don't wrap already quoted lines
T 1355     if ($line[0] == '>')
1356       $line = '>' . rtrim($line);
1357     else if (mb_strlen($line) > $max) {
1358       $newline = '';
1359       foreach(explode("\n", rc_wordwrap($line, $length - 2)) as $l) {
1360         if (strlen($l))
1361           $newline .= '> ' . $l . "\n";
1362         else
1363           $newline .= ">\n";
ccd63c 1364       }
6b6f2e 1365       $line = rtrim($newline);
ccd63c 1366     }
6b6f2e 1367     else
T 1368       $line = '> ' . $line;
ccd63c 1369
T 1370     // Append the line
1371     $out .= $line . "\n";
1372   }
9800a8 1373
ccd63c 1374   return $out;
T 1375 }
1376
1377
bc404f 1378 function rcmail_draftinfo_encode($p)
T 1379 {
1380   $parts = array();
1381   foreach ($p as $key => $val)
1382     $parts[] = $key . '=' . ($key == 'folder' ? base64_encode($val) : $val);
9800a8 1383
bc404f 1384   return join('; ', $parts);
T 1385 }
1386
1387
1388 function rcmail_draftinfo_decode($str)
1389 {
1390   $info = array();
1391   foreach (preg_split('/;\s+/', $str) as $part) {
1392     list($key, $val) = explode('=', $part, 2);
1393     if ($key == 'folder')
1394       $val = base64_decode($val);
1395     $info[$key] = $val;
1396   }
9800a8 1397
bc404f 1398   return $info;
T 1399 }
1400
1401
4e17e6 1402 function rcmail_message_part_controls()
e99991 1403 {
8fa58e 1404   global $MESSAGE;
9800a8 1405
d5342a 1406   $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
8fa58e 1407   if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part])
4e17e6 1408     return '';
9800a8 1409
8fa58e 1410   $part = $MESSAGE->mime_parts[$part];
T 1411   $table = new html_table(array('cols' => 3));
9800a8 1412
8fa58e 1413   if (!empty($part->filename)) {
T 1414     $table->add('title', Q(rcube_label('filename')));
1415     $table->add(null, Q($part->filename));
8dc048 1416     $table->add(null, '[' . html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']');
8fa58e 1417   }
9800a8 1418
8fa58e 1419   if (!empty($part->size)) {
T 1420     $table->add('title', Q(rcube_label('filesize')));
1421     $table->add(null, Q(show_bytes($part->size)));
1422   }
9800a8 1423
8fa58e 1424   return $table->show($attrib);
e99991 1425 }
4e17e6 1426
T 1427
1428
1429 function rcmail_message_part_frame($attrib)
e99991 1430 {
4e17e6 1431   global $MESSAGE;
9800a8 1432
8fa58e 1433   $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
4e17e6 1434   $ctype_primary = strtolower($part->ctype_primary);
T 1435
95fcc3 1436   $attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
4e17e6 1437
95fcc3 1438   return html::iframe($attrib);
e99991 1439 }
4e17e6 1440
T 1441
45f56c 1442 /**
T 1443  * clear message composing settings
1444  */
4e17e6 1445 function rcmail_compose_cleanup()
e99991 1446 {
4e17e6 1447   if (!isset($_SESSION['compose']))
T 1448     return;
70d4b9 1449
929a50 1450   $rcmail = rcmail::get_instance();
e6ce00 1451   $rcmail->plugins->exec_hook('attachments_cleanup', array());
929a50 1452   $rcmail->session->remove('compose');
e99991 1453 }
9800a8 1454
fba1f5 1455
T 1456 /**
91790e 1457  * Send the given message using the configured method
A 1458  *
1459  * @param object $message    Reference to Mail_MIME object
1460  * @param string $from       Sender address string
1461  * @param array  $mailto     Array of recipient address strings
1462  * @param array  $smtp_error SMTP error array (reference)
1463  * @param string $body_file  Location of file with saved message body (reference)
1464  *
1465  * @return boolean Send status.
fba1f5 1466  */
91790e 1467 function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file)
fba1f5 1468 {
a22cb6 1469   global $CONFIG, $RCMAIL;
fba1f5 1470
8fa58e 1471   $headers = $message->headers();
79aeb3 1472
fba1f5 1473   // send thru SMTP server using custom SMTP library
e54f6c 1474   if ($CONFIG['smtp_server']) {
fba1f5 1475     // generate list of recipients
T 1476     $a_recipients = array($mailto);
9800a8 1477
fba1f5 1478     if (strlen($headers['Cc']))
T 1479       $a_recipients[] = $headers['Cc'];
1480     if (strlen($headers['Bcc']))
1481       $a_recipients[] = $headers['Bcc'];
9800a8 1482
fba1f5 1483     // clean Bcc from header for recipients
T 1484     $send_headers = $headers;
1485     unset($send_headers['Bcc']);
7ec922 1486     // here too, it because txtHeaders() below use $message->_headers not only $send_headers
A 1487     unset($message->_headers['Bcc']);
fba1f5 1488
91790e 1489     $smtp_headers = $message->txtHeaders($send_headers, true);
A 1490
1491     if ($message->getParam('delay_file_io')) {
1492       // use common temp dir
1493       $temp_dir = $RCMAIL->config->get('temp_dir');
1494       $body_file = tempnam($temp_dir, 'rcmMsg');
1495       if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) {
1496         raise_error(array('code' => 600, 'type' => 'php',
1497             'file' => __FILE__, 'line' => __LINE__,
1498             'message' => "Could not create message: ".$mime_result->getMessage()),
1499             TRUE, FALSE);
1500         return false;
1501       }
1502       $msg_body = fopen($body_file, 'r');
1503     } else {
1504       $msg_body = $message->get();
1505     }
1506
fba1f5 1507     // send message
2c3d81 1508     if (!is_object($RCMAIL->smtp))
A 1509       $RCMAIL->smtp_init(true);
9800a8 1510
91790e 1511     $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body);
2c3d81 1512     $smtp_response = $RCMAIL->smtp->get_response();
A 1513     $smtp_error = $RCMAIL->smtp->get_error();
91790e 1514
fba1f5 1515     // log error
T 1516     if (!$sent)
1517       raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
1518                         'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
e54f6c 1519   }
fba1f5 1520   // send mail using PHP's mail() function
e54f6c 1521   else {
fba1f5 1522     // unset some headers because they will be added by the mail() function
T 1523     $headers_enc = $message->headers($headers);
1524     $headers_php = $message->_headers;
1525     unset($headers_php['To'], $headers_php['Subject']);
9800a8 1526
fba1f5 1527     // reset stored headers and overwrite
T 1528     $message->_headers = array();
1529     $header_str = $message->txtHeaders($headers_php);
9800a8 1530
5d1101 1531     // #1485779
A 1532     if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1533       if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) {
1534         $headers_enc['To'] = implode(', ', $m[1]);
1535       }
ac8edb 1536     }
9800a8 1537
91790e 1538     $msg_body = $message->get();
A 1539
1540     if (PEAR::isError($msg_body))
1541       raise_error(array('code' => 600, 'type' => 'php',
1542             'file' => __FILE__, 'line' => __LINE__,
1543             'message' => "Could not create message: ".$msg_body->getMessage()),
1544             TRUE, FALSE);
ac8edb 1545     else {
A 1546       $delim   = $RCMAIL->config->header_delimiter();
1547       $to      = $headers_enc['To'];
1548       $subject = $headers_enc['Subject'];
1549
1550       if ($delim != "\r\n") {
1551         $header_str = str_replace("\r\n", $delim, $header_str);
1552         $msg_body   = str_replace("\r\n", $delim, $msg_body);
1553         $to         = str_replace("\r\n", $delim, $to);
1554         $subject    = str_replace("\r\n", $delim, $subject);
1555       }
1556
1557       if (ini_get('safe_mode'))
1558         $sent = mail($to, $subject, $msg_body, $header_str);
1559       else
1560         $sent = mail($to, $subject, $msg_body, $header_str, "-f$from");
1561     }
e54f6c 1562   }
9800a8 1563
e54f6c 1564   if ($sent) {
T 1565     $RCMAIL->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $msg_body));
9800a8 1566
79aeb3 1567     // remove MDN headers after sending
ae8f19 1568     unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
9800a8 1569
db1f1e 1570     // get all recipients
A 1571     if ($headers['Cc'])
1572       $mailto .= $headers['Cc'];
1573     if ($headers['Bcc'])
1574       $mailto .= $headers['Bcc'];
1575     if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m))
1576       $mailto = implode(', ', array_unique($m[1]));
1577
e54f6c 1578     if ($CONFIG['smtp_log']) {
146977 1579       write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s",
e54f6c 1580         $RCMAIL->user->get_username(),
T 1581         $_SERVER['REMOTE_ADDR'],
1582         $mailto,
1583         !empty($smtp_response) ? join('; ', $smtp_response) : ''));
1584     }
79aeb3 1585   }
91790e 1586
4eb849 1587   if (is_resource($msg_body)) {
A 1588     fclose($msg_body);
1589   }
1590
fba1f5 1591   $message->_headers = array();
T 1592   $message->headers($headers);
9800a8 1593
fba1f5 1594   return $sent;
T 1595 }
f11541 1596
a99968 1597 /**
A 1598  * Send the MDN response
1599  *
1600  * @param mixed $message    Original message object (rcube_message) or UID
1601  * @param array $smtp_error SMTP error array (reference)
1602  *
1603  * @return boolean Send status
1604  */
1605 function rcmail_send_mdn($message, &$smtp_error)
0ea884 1606 {
83a763 1607   global $RCMAIL, $IMAP;
8fa58e 1608
a99968 1609   if (!is_a($message, rcube_message))
A 1610     $message = new rcube_message($message);
9800a8 1611
5c771c 1612   if ($message->headers->mdn_to && !$message->headers->mdn_sent &&
A 1613     ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*')))
0ea884 1614   {
83a763 1615     $identity = $RCMAIL->user->get_identity();
0ea884 1616     $sender = format_email_recipient($identity['email'], $identity['name']);
8fa58e 1617     $recipient = array_shift($IMAP->decode_address_list($message->headers->mdn_to));
0ea884 1618     $mailto = $recipient['mailto'];
T 1619
ac8edb 1620     $compose = new Mail_mime("\r\n");
91790e 1621
A 1622     $compose->setParam('text_encoding', 'quoted-printable');
1623     $compose->setParam('html_encoding', 'quoted-printable');
1624     $compose->setParam('head_encoding', 'quoted-printable');
1625     $compose->setParam('head_charset', RCMAIL_CHARSET);
1626     $compose->setParam('html_charset', RCMAIL_CHARSET);
1627     $compose->setParam('text_charset', RCMAIL_CHARSET);
9800a8 1628
0ea884 1629     // compose headers array
T 1630     $headers = array(
2bf3cc 1631       'Date' => rcmail_user_date(),
0ea884 1632       'From' => $sender,
8fa58e 1633       'To'   => $message->headers->mdn_to,
T 1634       'Subject' => rcube_label('receiptread') . ': ' . $message->subject,
1d8cbc 1635       'Message-ID' => rcmail_gen_message_id(),
0ea884 1636       'X-Sender' => $identity['email'],
ea50e7 1637       'References' => trim($message->headers->references . ' ' . $message->headers->messageID),
0ea884 1638     );
9800a8 1639
83a763 1640     if ($agent = $RCMAIL->config->get('useragent'))
T 1641       $headers['User-Agent'] = $agent;
0ea884 1642
T 1643     $body = rcube_label("yourmessage") . "\r\n\r\n" .
8fa58e 1644       "\t" . rcube_label("to") . ': ' . rcube_imap::decode_mime_string($message->headers->to, $message->headers->charset) . "\r\n" .
T 1645       "\t" . rcube_label("subject") . ': ' . $message->subject . "\r\n" .
83a763 1646       "\t" . rcube_label("sent") . ': ' . format_date($message->headers->date, $RCMAIL->config->get('date_long')) . "\r\n" .
0ea884 1647       "\r\n" . rcube_label("receiptnote") . "\r\n";
9800a8 1648
e019f2 1649     $ua = $RCMAIL->config->get('useragent', "Roundcube Webmail (Version ".RCMAIL_VERSION.")");
0ea884 1650     $report = "Reporting-UA: $ua\r\n";
9800a8 1651
8fa58e 1652     if ($message->headers->to)
T 1653         $report .= "Original-Recipient: {$message->headers->to}\r\n";
9800a8 1654
0ea884 1655     $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
8fa58e 1656                "Original-Message-ID: {$message->headers->messageID}\r\n" .
0ea884 1657                "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
9800a8 1658
8fa58e 1659     $compose->headers($headers);
6b0113 1660     $compose->setContentType('multipart/report', array('report-type'=> 'disposition-notification'));
7145e0 1661     $compose->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
0ea884 1662     $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
T 1663
91790e 1664     $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file);
0ea884 1665
T 1666     if ($sent)
1667     {
8fa58e 1668       $IMAP->set_flag($message->uid, 'MDNSENT');
0ea884 1669       return true;
T 1670     }
1671   }
9800a8 1672
0ea884 1673   return false;
T 1674 }
1675
1d8cbc 1676 // Returns unique Message-ID
A 1677 function rcmail_gen_message_id()
1678 {
1679   global $RCMAIL;
1680
1681   $local_part  = md5(uniqid('rcmail'.mt_rand(),true));
1682   $domain_part = $RCMAIL->user->get_username('domain');
1683
1684   // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924)
1685   if (!preg_match('/\.[a-z]+$/i', $domain_part)) {
1686     if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']))
1687       && preg_match('/\.[a-z]+$/i', $host)) {
1688         $domain_part = $host;
1689     }
1690     else if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['SERVER_NAME']))
1691       && preg_match('/\.[a-z]+$/i', $host)) {
1692         $domain_part = $host;
1693     }
1694   }
1695
1696   return sprintf('<%s@%s>', $local_part, $domain_part);
1697 }
1698
2bf3cc 1699 // Returns RFC2822 formatted current date in user's timezone
A 1700 function rcmail_user_date()
1701 {
1702   global $CONFIG;
1703
1704   // get user's timezone
1705   if ($CONFIG['timezone'] === 'auto') {
1706     $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600;
1707   }
1708   else {
1709     $tz = $CONFIG['timezone'];
1710     if ($CONFIG['dst_active'])
1711       $tz++;
1712   }
1713
1714   $date = time() + $tz * 60 * 60;
1715   $date = gmdate('r', $date);
64233d 1716   $tz   = sprintf('%+05d', intval($tz) * 100 + ($tz - intval($tz)) * 60);
A 1717   $date = preg_replace('/[+-][0-9]{4}$/', $tz, $date);
2bf3cc 1718
A 1719   return $date;
1720 }
1721
1722
e538b3 1723 function rcmail_search_filter($attrib)
A 1724 {
119cd1 1725   global $OUTPUT, $CONFIG;
e538b3 1726
A 1727   if (!strlen($attrib['id']))
1728     $attrib['id'] = 'rcmlistfilter';
1729
1730   $attrib['onchange'] = JS_OBJECT_NAME.'.filter_mailbox(this.value)';
9800a8 1731
e538b3 1732   /*
A 1733     RFC3501 (6.4.4): 'ALL', 'RECENT', 
1734     'ANSWERED', 'DELETED', 'FLAGGED', 'SEEN',
1735     'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNSEEN',
1736     'NEW', // = (RECENT UNSEEN)
1737     'OLD' // = NOT RECENT
1738   */
1739
1740   $select_filter = new html_select($attrib);
1741   $select_filter->add(rcube_label('all'), 'ALL');
1742   $select_filter->add(rcube_label('unread'), 'UNSEEN');
1743   $select_filter->add(rcube_label('flagged'), 'FLAGGED');
1744   $select_filter->add(rcube_label('unanswered'), 'UNANSWERED');
119cd1 1745   if (!$CONFIG['skip_deleted'])
A 1746     $select_filter->add(rcube_label('deleted'), 'DELETED');
e538b3 1747
A 1748   $out = $select_filter->show($_SESSION['search_filter']);
1749
1750   $OUTPUT->add_gui_object('search_filter', $attrib['id']);
1751
9800a8 1752   return $out;
e538b3 1753 }
A 1754
64e3e8 1755 function rcmail_message_error($uid=null)
A 1756 {
1757   global $RCMAIL;
1758
1759   // Set env variables for messageerror.html template
1760   if ($RCMAIL->action == 'show') {
1761     $mbox_name = $RCMAIL->imap->get_mailbox_name();
1762     $RCMAIL->output->set_env('mailbox', $mbox_name);
1763     $RCMAIL->output->set_env('uid', null);
1764   }
1765   // display error message
1766   $RCMAIL->output->show_message('messageopenerror', 'error');
1767   // ... display message error page
1768   $RCMAIL->output->send('messageerror');
1769 }
9b3fdc 1770
f11541 1771 // register UI objects
T 1772 $OUTPUT->add_handlers(array(
1773   'mailboxlist' => 'rcmail_mailbox_list',
1774   'messages' => 'rcmail_message_list',
1775   'messagecountdisplay' => 'rcmail_messagecount_display',
1776   'quotadisplay' => 'rcmail_quota_display',
ac5d15 1777   'mailboxname' => 'rcmail_mailbox_name_display',
f11541 1778   'messageheaders' => 'rcmail_message_headers',
T 1779   'messagebody' => 'rcmail_message_body',
1780   'messagecontentframe' => 'rcmail_messagecontent_frame',
1781   'messagepartframe' => 'rcmail_message_part_frame',
1782   'messagepartcontrols' => 'rcmail_message_part_controls',
e538b3 1783   'searchfilter' => 'rcmail_search_filter',
47124c 1784   'searchform' => array($OUTPUT, 'search_form'),
f11541 1785 ));
T 1786
b25dfd 1787