Aleksander Machniak
2013-01-02 232535f76e50c08e77d8cba599fabe7fe8ca42d4
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                     |
a8a72e 8  | Copyright (C) 2005-2012, The Roundcube Dev Team                       |
7fe381 9  |                                                                       |
T 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.                     |
4e17e6 13  |                                                                       |
T 14  | PURPOSE:                                                              |
15  |   Provide webmail functionality and GUI objects                       |
16  |                                                                       |
17  +-----------------------------------------------------------------------+
18  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
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');
5c1dfb 25 $SEARCH_MODS_DEFAULT = array(
A 26     '*'         => array('subject'=>1, 'from'=>1),
27     $SENT_MBOX  => array('subject'=>1, 'to'=>1),
28     $DRAFTS_MBOX => array('subject'=>1, 'to'=>1)
29 );
39cd51 30
c321a9 31 // always instantiate storage object (but not connect to server yet)
T 32 $RCMAIL->storage_init();
39cd51 33
4e17e6 34 // set imap properties and session vars
b72e2f 35 if (strlen(trim($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC, true))))
c321a9 36   $RCMAIL->storage->set_folder(($_SESSION['mbox'] = $mbox));
T 37 else if ($RCMAIL->storage)
38   $_SESSION['mbox'] = $RCMAIL->storage->get_folder();
4e17e6 39
b3ce79 40 if (!empty($_GET['_page']))
c321a9 41   $RCMAIL->storage->set_page(($_SESSION['page'] = intval($_GET['_page'])));
4e17e6 42
6a35c8 43 // set default sort col/order to session
T 44 if (!isset($_SESSION['sort_col']))
2c052c 45   $_SESSION['sort_col'] = !empty($CONFIG['message_sort_col']) ? $CONFIG['message_sort_col'] : '';
6a35c8 46 if (!isset($_SESSION['sort_order']))
ae3d60 47   $_SESSION['sort_order'] = strtoupper($CONFIG['message_sort_order']) == 'ASC' ? 'ASC' : 'DESC';
2bca6e 48
f52c93 49 // set threads mode
T 50 $a_threading = $RCMAIL->config->get('message_threading', array());
51 if (isset($_GET['_threads'])) {
52   if ($_GET['_threads'])
53     $a_threading[$_SESSION['mbox']] = true;
54   else
55     unset($a_threading[$_SESSION['mbox']]);
56   $RCMAIL->user->save_prefs(array('message_threading' => $a_threading));
57 }
c321a9 58 $RCMAIL->storage->set_threading($a_threading[$_SESSION['mbox']]);
f52c93 59
2bca6e 60 // set message set for search result
f6aac3 61 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'])
A 62     && $_SESSION['search_request'] == $_REQUEST['_search']
63 ) {
c321a9 64   $RCMAIL->storage->set_search_set($_SESSION['search']);
1f020b 65   $OUTPUT->set_env('search_request', $_REQUEST['_search']);
S 66   $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
f6aac3 67 }
4e17e6 68
528514 69 // set main env variables, labels and page title
f6aac3 70 if (empty($RCMAIL->action) || $RCMAIL->action == 'list') {
464a0f 71   // connect to storage server and trigger error on failure
TB 72   $RCMAIL->storage_connect();
73
c321a9 74   $mbox_name = $RCMAIL->storage->get_folder();
8abda5 75
f6aac3 76   if (empty($RCMAIL->action)) {
8abda5 77     // initialize searching result if search_filter is used
f6aac3 78     if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL') {
8abda5 79       $search_request = md5($mbox_name.$_SESSION['search_filter']);
9800a8 80
e0efd8 81       $RCMAIL->storage->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, rcmail_sort_column());
c321a9 82       $_SESSION['search'] = $RCMAIL->storage->get_search_set();
f6aac3 83       $_SESSION['search_request'] = $search_request;
8abda5 84       $OUTPUT->set_env('search_request', $search_request);
40c45e 85     }
9800a8 86
e349a8 87     $search_mods = $RCMAIL->config->get('search_mods', $SEARCH_MODS_DEFAULT);
AM 88     $OUTPUT->set_env('search_mods', $search_mods);
f6aac3 89   }
9800a8 90
c321a9 91   $threading = (bool) $RCMAIL->storage->get_threading();
T 92
f52c93 93   // set current mailbox and some other vars in client environment
8abda5 94   $OUTPUT->set_env('mailbox', $mbox_name);
c321a9 95   $OUTPUT->set_env('pagesize', $RCMAIL->storage->get_pagesize());
T 96   $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
97   $OUTPUT->set_env('threading', $threading);
98   $OUTPUT->set_env('threads', $threading || $RCMAIL->storage->get_capability('THREAD'));
a509bb 99   $OUTPUT->set_env('preview_pane_mark_read', $RCMAIL->config->get('preview_pane_mark_read', 0));
46d001 100   if ($RCMAIL->storage->get_capability('QUOTA')) {
AM 101     $OUTPUT->set_env('quota', true);
102   }
528514 103
271efe 104   foreach (array('delete_junk','flag_for_deletion','read_when_deleted','skip_deleted','display_next','message_extwin','compose_extwin','forward_attachment') as $prop) {
TB 105     if ($CONFIG[$prop])
106       $OUTPUT->set_env($prop, true);
107   }
108
528514 109   if ($CONFIG['trash_mbox'])
A 110     $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
111   if ($CONFIG['drafts_mbox'])
112     $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
113   if ($CONFIG['junk_mbox'])
114     $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
115
e349a8 116   if (!empty($_SESSION['browser_caps']))
AM 117     $OUTPUT->set_env('browser_capabilities', $_SESSION['browser_caps']);
118
528514 119   if (!$OUTPUT->ajax_call)
9b3fdc 120     $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
c50d88 121       'movingmessage', 'copyingmessage', 'deletingmessage', 'markingmessage',
A 122       'copy', 'move', 'quota');
528514 123
c321a9 124   $OUTPUT->set_pagetitle(rcmail_localize_foldername($RCMAIL->storage->mod_folder($mbox_name)));
f6aac3 125 }
5eee00 126
e0efd8 127 /**
AM 128  * Returns 'to' if current folder is configured Sent or Drafts
129  * or their subfolders, otherwise returns 'from'.
130  *
131  * @return string Column name
132  */
133 function rcmail_message_list_smart_column_name()
134 {
135   global $RCMAIL;
136
137   $delim       = $RCMAIL->storage->get_hierarchy_delimiter();
138   $mbox        = $RCMAIL->storage->get_folder();
139   $sent_mbox   = $RCMAIL->config->get('sent_mbox');
140   $drafts_mbox = $RCMAIL->config->get('drafts_mbox');
141
142   if (strpos($mbox.$delim, $sent_mbox.$delim) === 0 || strpos($mbox.$delim, $drafts_mbox.$delim) === 0) {
143     return 'to';
144   }
145
146   return 'from';
147 }
148
149 /**
150  * Returns configured messages list sorting column name
151  * The name is context-sensitive, which means if sorting is set to 'fromto'
152  * it will return 'from' or 'to' according to current folder type.
153  *
154  * @return string Column name
155  */
156 function rcmail_sort_column()
157 {
158   global $RCMAIL;
159
160   if (isset($_SESSION['sort_col'])) {
161     $column = $_SESSION['sort_col'];
162   }
163   else {
164     $column = $RCMAIL->config->get('message_sort_col');
165   }
166
167   // get name of smart From/To column in folder context
168   if ($column == 'fromto') {
169     $column = rcmail_message_list_smart_column_name();
170   }
171
172   return $column;
173 }
174
175 /**
176  * Returns configured message list sorting order
177  *
178  * @return string Sorting order (ASC|DESC)
179  */
180 function rcmail_sort_order()
181 {
182   global $RCMAIL;
183
184   if (isset($_SESSION['sort_order'])) {
185     return $_SESSION['sort_order'];
186   }
187
188   return $RCMAIL->config->get('message_sort_order');
189 }
4e17e6 190
45f56c 191 /**
T 192  * return the message list as HTML table
193  */
4e17e6 194 function rcmail_message_list($attrib)
f52c93 195 {
e0efd8 196   global $CONFIG, $OUTPUT;
b076a4 197
24053e 198   // add some labels to client
112c91 199   $OUTPUT->add_label('from', 'to');
4e17e6 200
T 201   // add id to message list table if not specified
202   if (!strlen($attrib['id']))
203     $attrib['id'] = 'rcubemessagelist';
e0ddd4 204
d59aaa 205   // define list of cols to be displayed based on parameter or config
b62c48 206   if (empty($attrib['columns'])) {
A 207     $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
208     $OUTPUT->set_env('col_movable', !in_array('list_cols', (array)$CONFIG['dont_override']));
209   }
210   else {
211     $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
212     $attrib['columns'] = $a_show_cols;
213   }
d59aaa 214
f52c93 215   // save some variables for use in ajax list
T 216   $_SESSION['list_attrib'] = $attrib;
614c64 217   // make sure 'threads' and 'subject' columns are present
A 218   if (!in_array('subject', $a_show_cols))
219     array_unshift($a_show_cols, 'subject');
6c9d49 220   if (!in_array('threads', $a_show_cols))
A 221     array_unshift($a_show_cols, 'threads');
222
f52c93 223   $skin_path = $_SESSION['skin_path'] = $CONFIG['skin_path'];
9800a8 224
4e17e6 225   // set client env
f11541 226   $OUTPUT->add_gui_object('messagelist', $attrib['id']);
f52c93 227   $OUTPUT->set_env('autoexpand_threads', intval($CONFIG['autoexpand_threads']));
T 228   $OUTPUT->set_env('sort_col', $_SESSION['sort_col']);
229   $OUTPUT->set_env('sort_order', $_SESSION['sort_order']);
230   $OUTPUT->set_env('messages', array());
d24d20 231   $OUTPUT->set_env('coltypes', $a_show_cols);
9800a8 232
6b47de 233   $OUTPUT->include_script('list.js');
9800a8 234
f52c93 235   $thead = '';
T 236   foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell)
237     $thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
9800a8 238
f52c93 239   return html::tag('table',
T 240     $attrib,
241     html::tag('thead', null, html::tag('tr', null, $thead)) .
242       html::tag('tbody', null, ''),
b62c48 243         array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
f52c93 244 }
4e17e6 245
T 246
45f56c 247 /**
T 248  * return javascript commands to add rows to the message list
249  */
614c64 250 function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null)
f52c93 251 {
b6da0b 252   global $CONFIG, $RCMAIL, $OUTPUT;
4e17e6 253
614c64 254   if (empty($a_show_cols)) {
A 255     if (!empty($_SESSION['list_attrib']['columns']))
256       $a_show_cols = $_SESSION['list_attrib']['columns'];
257     else
258       $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
259   }
260   else {
261     if (!is_array($a_show_cols))
262       $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($a_show_cols));
263     $head_replace = true;
264   }
d59aaa 265
c321a9 266   $mbox = $RCMAIL->storage->get_folder();
614c64 267
A 268   // make sure 'threads' and 'subject' columns are present
269   if (!in_array('subject', $a_show_cols))
270     array_unshift($a_show_cols, 'subject');
271   if (!in_array('threads', $a_show_cols))
272     array_unshift($a_show_cols, 'threads');
273
274   $_SESSION['list_attrib']['columns'] = $a_show_cols;
4e17e6 275
614c64 276   // Make sure there are no duplicated columns (#1486999)
A 277   $a_show_cols = array_unique($a_show_cols);
6c9d49 278
0c2596 279   // Plugins may set header's list_cols/list_flags and other rcube_message_header variables
5bde17 280   // and list columns
A 281   $plugin = $RCMAIL->plugins->exec_hook('messages_list',
282     array('messages' => $a_headers, 'cols' => $a_show_cols));
283
284   $a_show_cols = $plugin['cols'];
285   $a_headers   = $plugin['messages'];
286
f52c93 287   $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : NULL;
9800a8 288
e0efd8 289   // get name of smart From/To column in folder context
AM 290   if (($f = array_search('fromto', $a_show_cols)) !== false) {
291     $smart_col = rcmail_message_list_smart_column_name();
292   }
293
294   $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead, $smart_col);
c4b819 295
f52c93 296   if (empty($a_headers))
T 297     return;
91354e 298
4438d6 299   // remove 'threads', 'attachment', 'flag', 'status' columns, we don't need them here
7a2bad 300   foreach (array('threads', 'attachment', 'flag', 'status', 'priority') as $col) {
4438d6 301     if (($key = array_search($col, $a_show_cols)) !== FALSE)
A 302       unset($a_show_cols[$key]);
303   }
5bde17 304
4438d6 305   // loop through message headers
A 306   foreach ($a_headers as $n => $header) {
ecd2e7 307     if (empty($header))
T 308       continue;
5bde17 309
A 310     $a_msg_cols = array();
311     $a_msg_flags = array();
f11541 312
4e17e6 313     // format each col; similar as in rcmail_message_list()
4438d6 314     foreach ($a_show_cols as $col) {
e0efd8 315       $col_name = $col == 'fromto' ? $smart_col : $col;
AM 316
317       if (in_array($col_name, array('from', 'to', 'cc', 'replyto')))
61512f 318         $cont = rcmail_address_string($header->$col_name, 3, false, null, $header->charset);
e0efd8 319       else if ($col == 'subject') {
1c4f23 320         $cont = trim(rcube_mime::decode_header($header->$col, $header->charset));
44385f 321         if (!$cont) $cont = rcube_label('nosubject');
f52c93 322         $cont = Q($cont);
4438d6 323       }
e0efd8 324       else if ($col == 'size')
4e17e6 325         $cont = show_bytes($header->$col);
e0efd8 326       else if ($col == 'date')
f11541 327         $cont = format_date($header->date);
4e17e6 328       else
2bca6e 329         $cont = Q($header->$col);
9800a8 330
4e17e6 331       $a_msg_cols[$col] = $cont;
4438d6 332     }
4e17e6 333
609d39 334     $a_msg_flags = array_change_key_case(array_map('intval', (array) $header->flags));
f52c93 335     if ($header->depth)
T 336       $a_msg_flags['depth'] = $header->depth;
0e7b66 337     else if ($header->has_children)
A 338       $roots[] = $header->uid;
f52c93 339     if ($header->parent_uid)
T 340       $a_msg_flags['parent_uid'] = $header->parent_uid;
341     if ($header->has_children)
342       $a_msg_flags['has_children'] = $header->has_children;
343     if ($header->unread_children)
344       $a_msg_flags['unread_children'] = $header->unread_children;
e25a35 345     if ($header->others['list-post'])
A 346       $a_msg_flags['ml'] = 1;
7a2bad 347     if ($header->priority)
A 348       $a_msg_flags['prio'] = (int) $header->priority;
6b4929 349
A 350     $a_msg_flags['ctype'] = Q($header->ctype);
f52c93 351     $a_msg_flags['mbox'] = $mbox;
T 352
609d39 353     // merge with plugin result (Deprecated, use $header->flags)
5bde17 354     if (!empty($header->list_flags) && is_array($header->list_flags))
A 355       $a_msg_flags = array_merge($a_msg_flags, $header->list_flags);
356     if (!empty($header->list_cols) && is_array($header->list_cols))
357       $a_msg_cols = array_merge($a_msg_cols, $header->list_cols);
358
f11541 359     $OUTPUT->command('add_message_row',
T 360       $header->uid,
361       $a_msg_cols,
362       $a_msg_flags,
363       $insert_top);
4438d6 364   }
0e7b66 365
c321a9 366   if ($RCMAIL->storage->get_threading()) {
bba252 367     $OUTPUT->command('init_threads', (array) $roots, $mbox);
4438d6 368   }
b62c48 369 }
f52c93 370
T 371
372 /*
373  * Creates <THEAD> for message list table
374  */
375 function rcmail_message_list_head($attrib, $a_show_cols)
376 {
f0affa 377   global $RCMAIL;
AM 378
f52c93 379   $skin_path = $_SESSION['skin_path'];
T 380   $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s"));
381
382   // check to see if we have some settings for sorting
383   $sort_col   = $_SESSION['sort_col'];
384   $sort_order = $_SESSION['sort_order'];
385
f0affa 386   $dont_override  = (array)$RCMAIL->config->get('dont_override');
AM 387   $disabled_sort  = in_array('message_sort_col', $dont_override);
388   $disabled_order = in_array('message_sort_order', $dont_override);
389
390   $RCMAIL->output->set_env('disabled_sort_col', $disabled_sort);
391   $RCMAIL->output->set_env('disabled_sort_order', $disabled_order);
392
f52c93 393   // define sortable columns
f0affa 394   if ($disabled_sort)
AM 395     $a_sort_cols = $sort_col && !$disabled_order ? array($sort_col) : array();
396   else
397     $a_sort_cols = array('subject', 'date', 'from', 'to', 'fromto', 'size', 'cc');
9800a8 398
1716d5 399   if (!empty($attrib['optionsmenuicon'])) {
A 400     $onclick = 'return ' . JS_OBJECT_NAME . ".command('menu-open', 'messagelistmenu')";
401     if ($attrib['optionsmenuicon'] === true || $attrib['optionsmenuicon'] == 'true')
402       $list_menu = html::div(array('onclick' => $onclick, 'class' => 'listmenu',
403         'id' => 'listmenulink', 'title' => rcube_label('listoptions')));
404     else
405       $list_menu = html::a(array('href' => '#', 'onclick' => $onclick),
406         html::img(array('src' => $skin_path . $attrib['optionsmenuicon'],
407           'id' => 'listmenulink', 'title' => rcube_label('listoptions')))
408       );
409   }
f52c93 410   else
T 411     $list_menu = '';
412
6c9d49 413   $cells = array();
f52c93 414
e0efd8 415   // get name of smart From/To column in folder context
AM 416   if (($f = array_search('fromto', $a_show_cols)) !== false) {
417     $smart_col = rcmail_message_list_smart_column_name();
418   }
419
f52c93 420   foreach ($a_show_cols as $col) {
T 421     // get column name
422     switch ($col) {
423       case 'flag':
e94706 424         $col_name = '<span class="flagged">&nbsp;</span>';
f52c93 425         break;
T 426       case 'attachment':
7a2bad 427       case 'priority':
4438d6 428       case 'status':
A 429         $col_name = '<span class="' . $col .'">&nbsp;</span>';
f52c93 430         break;
6c9d49 431       case 'threads':
A 432         $col_name = $list_menu;
433         break;
e0efd8 434       case 'fromto':
AM 435         $col_name = Q(rcube_label($smart_col));
436         break;
f52c93 437       default:
T 438         $col_name = Q(rcube_label($col));
439     }
440
441     // make sort links
442     if (in_array($col, $a_sort_cols))
443       $col_name = html::a(array('href'=>"./#sort", 'onclick' => 'return '.JS_OBJECT_NAME.".command('sort','".$col."',this)", 'title' => rcube_label('sortby')), $col_name);
f94e44 444     else if ($col_name[0] != '<')
T 445       $col_name = '<span class="' . $col .'">' . $col_name . '</span>';
f52c93 446
f0affa 447     $sort_class = $col == $sort_col && !$disabled_order ? " sorted$sort_order" : '';
e94706 448     $class_name = $col.$sort_class;
f52c93 449
T 450     // put it all together
451     $cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name);
452   }
453
454   return $cells;
455 }
4e17e6 456
T 457
45f56c 458 /**
T 459  * return an HTML iframe for loading mail content
460  */
b19097 461 function rcmail_messagecontent_frame($attrib)
b6da0b 462 {
ce06d3 463   global $OUTPUT, $RCMAIL;
9800a8 464
b19097 465   if (empty($attrib['id']))
T 466     $attrib['id'] = 'rcmailcontentwindow';
467
e2c610 468   $attrib['name'] = $attrib['id'];
b19097 469
ce06d3 470   if ($RCMAIL->config->get('preview_pane'))
A 471     $OUTPUT->set_env('contentframe', $attrib['id']);
cfc27c 472   $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/resources/blank.gif');
b19097 473
28de39 474   return $OUTPUT->frame($attrib, true);
b6da0b 475 }
b19097 476
4e17e6 477
T 478 function rcmail_messagecount_display($attrib)
b6da0b 479 {
29b397 480   global $RCMAIL;
9800a8 481
4e17e6 482   if (!$attrib['id'])
T 483     $attrib['id'] = 'rcmcountdisplay';
484
29b397 485   $RCMAIL->output->add_gui_object('countdisplay', $attrib['id']);
4e17e6 486
29b397 487   $content =  $RCMAIL->action != 'show' ? rcmail_get_messagecount_text() : rcube_label('loading');
A 488
489   return html::span($attrib, $content);
b6da0b 490 }
4e17e6 491
T 492
4647e1 493 function rcmail_get_messagecount_text($count=NULL, $page=NULL)
b6da0b 494 {
A 495   global $RCMAIL;
31b2ce 496
c321a9 497   if ($page === NULL) {
T 498     $page = $RCMAIL->storage->get_page();
499   }
9800a8 500
c321a9 501   $page_size = $RCMAIL->storage->get_pagesize();
T 502   $start_msg = ($page-1) * $page_size + 1;
9800a8 503
A 504   if ($count!==NULL)
505     $max = $count;
506   else if ($RCMAIL->action)
c321a9 507     $max = $RCMAIL->storage->count(NULL, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL');
4e17e6 508
T 509   if ($max==0)
510     $out = rcube_label('mailboxempty');
511   else
c321a9 512     $out = rcube_label(array('name' => $RCMAIL->storage->get_threading() ? 'threadsfromto' : 'messagesfromto',
f52c93 513             'vars' => array('from'  => $start_msg,
c321a9 514             'to'    => min($max, $start_msg + $page_size - 1),
f52c93 515             'count' => $max)));
4e17e6 516
2bca6e 517   return Q($out);
b6da0b 518 }
4e17e6 519
cbeea3 520
ac5d15 521 function rcmail_mailbox_name_display($attrib)
T 522 {
cbeea3 523   global $RCMAIL;
ac5d15 524
cbeea3 525   if (!$attrib['id'])
A 526     $attrib['id'] = 'rcmmailboxname';
ac5d15 527
cbeea3 528   $RCMAIL->output->add_gui_object('mailboxname', $attrib['id']);
ac5d15 529
cbeea3 530   return html::span($attrib, rcmail_get_mailbox_name_text());
ac5d15 531 }
T 532
f96ffd 533
ac5d15 534 function rcmail_get_mailbox_name_text()
T 535 {
cbeea3 536   global $RCMAIL;
c321a9 537   return rcmail_localize_foldername($RCMAIL->storage->get_folder());
ac5d15 538 }
T 539
cbeea3 540
636bd7 541 function rcmail_send_unread_count($mbox_name, $force=false, $count=null, $mark='')
cbeea3 542 {
A 543   global $RCMAIL;
9800a8 544
b46edc 545   $old_unseen = rcmail_get_unseen_count($mbox_name);
2144f9 546
A 547   if ($count === null)
c321a9 548     $unseen = $RCMAIL->storage->count($mbox_name, 'UNSEEN', $force);
2144f9 549   else
A 550     $unseen = $count;
cbeea3 551
7d1db8 552   if ($unseen != $old_unseen || ($mbox_name == 'INBOX'))
f4cfb1 553     $RCMAIL->output->command('set_unread_count', $mbox_name, $unseen,
A 554       ($mbox_name == 'INBOX'), $unseen && $mark ? $mark : '');
cbeea3 555
b46edc 556   rcmail_set_unseen_count($mbox_name, $unseen);
9800a8 557
cbeea3 558   return $unseen;
A 559 }
f96ffd 560
cbeea3 561
b46edc 562 function rcmail_set_unseen_count($mbox_name, $count)
A 563 {
564   // @TODO: this data is doubled (session and cache tables) if caching is enabled
565
566   // Make sure we have an array here (#1487066)
567   if (!is_array($_SESSION['unseen_count']))
568     $_SESSION['unseen_count'] = array();
569
570   $_SESSION['unseen_count'][$mbox_name] = $count;
571 }
572
573
574 function rcmail_get_unseen_count($mbox_name)
575 {
576   if (is_array($_SESSION['unseen_count']) && array_key_exists($mbox_name, $_SESSION['unseen_count']))
577     return $_SESSION['unseen_count'][$mbox_name];
578   else
579     return null;
580 }
581
582
ec603f 583 /**
A 584  * Sets message is_safe flag according to 'show_images' option value
585  *
586  * @param object rcube_message Message
587  */
588 function rcmail_check_safe(&$message)
589 {
590   global $RCMAIL;
591
592   if (!$message->is_safe
840b4d 593     && ($show_images = $RCMAIL->config->get('show_images'))
AM 594     && $message->has_html_part()
595   ) {
596     switch ($show_images) {
597       case 1: // known senders only
598         // get default addressbook, like in addcontact.inc
599         $CONTACTS = $RCMAIL->get_address_book(-1, true);
600
601         if ($CONTACTS) {
602           $result = $CONTACTS->search('email', $message->sender['mailto'], 1, false);
603           if ($result->count) {
604             $message->set_safe(true);
605           }
ec603f 606         }
840b4d 607         break;
AM 608
609       case 2: // always
ec603f 610         $message->set_safe(true);
840b4d 611         break;
ec603f 612     }
A 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  */
57486f 625 function rcmail_wash_html($html, $p, $cid_replaces)
ec603f 626 {
A 627   global $REMOTE_OBJECTS;
69a7d3 628
ec603f 629   $p += array('safe' => false, 'inline_html' => true);
2337a8 630
c321a9 631   // charset was converted to UTF-8 in rcube_storage::get_message_part(),
104e23 632   // change/add charset specification in HTML accordingly,
A 633   // washtml cannot work without that
634   $meta = '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />';
635
636   // remove old meta tag and add the new one, making sure
637   // that it is placed in the head (#1488093)
638   $html = preg_replace('/<meta[^>]+charset=[a-z0-9-_]+[^>]*>/Ui', '', $html);
639   $html = preg_replace('/(<head[^>]*>)/Ui', '\\1'.$meta, $html, -1, $rcount);
640   if (!$rcount) {
641     $html = '<head>' . $meta . '</head>' . $html;
269fb8 642   }
ec603f 643
A 644   // clean HTML with washhtml by Frederic Motte
645   $wash_opts = array(
646     'show_washed' => false,
647     'allow_remote' => $p['safe'],
cfc27c 648     'blocked_src' => "./program/resources/blocked.gif",
ec603f 649     'charset' => RCMAIL_CHARSET,
A 650     'cid_map' => $cid_replaces,
651     'html_elements' => array('body'),
652   );
ce4673 653
ec603f 654   if (!$p['inline_html']) {
A 655     $wash_opts['html_elements'] = array('html','head','title','body');
656   }
657   if ($p['safe']) {
658     $wash_opts['html_elements'][] = 'link';
659     $wash_opts['html_attribs'] = array('rel','type');
660   }
9800a8 661
33da0b 662   // overwrite washer options with options from plugins
A 663   if (isset($p['html_elements']))
664     $wash_opts['html_elements'] = $p['html_elements'];
665   if (isset($p['html_attribs']))
666     $wash_opts['html_attribs'] = $p['html_attribs'];
667
668   // initialize HTML washer
7ac944 669   $washer = new rcube_washtml($wash_opts);
33da0b 670
A 671   if (!$p['skip_washer_form_callback'])
672     $washer->add_callback('form', 'rcmail_washtml_callback');
ec603f 673
2337a8 674   // allow CSS styles, will be sanitized by rcmail_washtml_callback()
33da0b 675   if (!$p['skip_washer_style_callback'])
A 676     $washer->add_callback('style', 'rcmail_washtml_callback');
bf1b66 677
d61756 678   // Remove non-UTF8 characters (#1487813)
A 679   $html = rc_utf8_clean($html);
680
ec603f 681   $html = $washer->wash($html);
A 682   $REMOTE_OBJECTS = $washer->extlinks;
5b3ed5 683
ec603f 684   return $html;
A 685 }
686
4e17e6 687
45f56c 688 /**
65cc1c 689  * Convert the given message part to proper HTML
T 690  * which can be displayed the message view
45f56c 691  *
65cc1c 692  * @param object rcube_message_part Message part
ec603f 693  * @param array  Display parameters array 
65cc1c 694  * @return string Formatted HTML string
45f56c 695  */
21e724 696 function rcmail_print_body($part, $p = array())
45f56c 697 {
cc97ea 698   global $RCMAIL;
9800a8 699
cc97ea 700   // trigger plugin hook
T 701   $data = $RCMAIL->plugins->exec_hook('message_part_before',
c9f673 702     array('type' => $part->ctype_secondary, 'body' => $part->body, 'id' => $part->mime_id)
A 703         + $p + array('safe' => false, 'plain' => false, 'inline_html' => true));
ec603f 704
5cc4b1 705   // convert html to text/plain
cc97ea 706   if ($data['type'] == 'html' && $data['plain']) {
66afd7 707     $txt = new rcube_html2text($data['body'], false, true);
5cc4b1 708     $body = $txt->get_text();
T 709     $part->ctype_secondary = 'plain';
45f56c 710   }
4e17e6 711   // text/html
cc97ea 712   else if ($data['type'] == 'html') {
T 713     $body = rcmail_wash_html($data['body'], $data, $part->replaces);
714     $part->ctype_secondary = $data['type'];
45f56c 715   }
4e17e6 716   // text/enriched
cc97ea 717   else if ($data['type'] == 'enriched') {
0fa54d 718     $body = rcube_enriched::to_html($data['body']);
d15163 719     $body = rcmail_wash_html($body, $data, $part->replaces);
AM 720     $part->ctype_secondary = 'html';
45f56c 721   }
cc97ea 722   else {
T 723     // assert plaintext
45f56c 724     $body = $part->body;
cc97ea 725     $part->ctype_secondary = $data['type'] = 'plain';
45f56c 726   }
9800a8 727
cc97ea 728   // free some memory (hopefully)
T 729   unset($data['body']);
45f56c 730
cc97ea 731   // plaintext postprocessing
4f6932 732   if ($part->ctype_secondary == 'plain')
99b8c1 733     $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed');
88ed23 734
cc97ea 735   // allow post-processing of the message body
c9f673 736   $data = $RCMAIL->plugins->exec_hook('message_part_after',
A 737     array('type' => $part->ctype_secondary, 'body' => $body, 'id' => $part->mime_id) + $data);
cc97ea 738
T 739   return $data['type'] == 'html' ? $data['body'] : html::tag('pre', array(), $data['body']);
4f6932 740 }
A 741
f96ffd 742
4f6932 743 /**
A 744  * Handle links and citation marks in plain text message
745  *
99b8c1 746  * @param string  Plain text string
A 747  * @param boolean Text uses format=flowed
748  *
4f6932 749  * @return string Formatted HTML string
A 750  */
99b8c1 751 function rcmail_plain_body($body, $flowed=false)
4f6932 752 {
33dfdd 753   global $RCMAIL;
A 754
4f6932 755   // make links and email-addresses clickable
60226a 756   $replacer = new rcmail_string_replacer;
9800a8 757
22c67d 758   // search for patterns like links and e-mail addresses and replace with tokens
AM 759   $body = $replacer->replace($body);
4f6932 760
A 761   // split body into single lines
dd0ae6 762   $body = preg_split('/\r?\n/', $body);
4f6932 763   $quote_level = 0;
99b8c1 764   $last = -1;
4f6932 765
A 766   // find/mark quoted lines...
dd0ae6 767   for ($n=0, $cnt=count($body); $n < $cnt; $n++) {
e5e219 768     if ($body[$n][0] == '>' && preg_match('/^(>+ {0,1})+/', $body[$n], $regs)) {
AM 769       $q        = substr_count($regs[0], '>');
dd0ae6 770       $body[$n] = substr($body[$n], strlen($regs[0]));
4f6932 771
dd0ae6 772       if ($q > $quote_level) {
A 773         $body[$n] = $replacer->get_replacement($replacer->add(
774           str_repeat('<blockquote>', $q - $quote_level))) . $body[$n];
775       }
776       else if ($q < $quote_level) {
777         $body[$n] = $replacer->get_replacement($replacer->add(
778           str_repeat('</blockquote>', $quote_level - $q))) . $body[$n];
779       }
99b8c1 780       else if ($flowed) {
A 781         // previous line is flowed
dd0ae6 782         if (isset($body[$last]) && $body[$n]
A 783           && $body[$last][strlen($body[$last])-1] == ' ') {
33dfdd 784           // merge lines
dd0ae6 785           $body[$last] .= $body[$n];
A 786           unset($body[$n]);
99b8c1 787         }
dd0ae6 788         else {
99b8c1 789           $last = $n;
dd0ae6 790         }
99b8c1 791       }
4f6932 792     }
99b8c1 793     else {
A 794       $q = 0;
795       if ($flowed) {
796         // sig separator - line is fixed
dd0ae6 797         if ($body[$n] == '-- ') {
A 798           $last = $last_sig = $n;
99b8c1 799         }
A 800         else {
801           // remove space-stuffing
dd0ae6 802           if ($body[$n][0] == ' ')
A 803             $body[$n] = substr($body[$n], 1);
99b8c1 804
A 805           // previous line is flowed?
dd0ae6 806           if (isset($body[$last]) && $body[$n]
9aab5e 807             && $last !== $last_sig
dd0ae6 808             && $body[$last][strlen($body[$last])-1] == ' '
99b8c1 809           ) {
dd0ae6 810             $body[$last] .= $body[$n];
A 811             unset($body[$n]);
99b8c1 812           }
A 813           else {
814             $last = $n;
815           }
816         }
817         if ($quote_level > 0)
dd0ae6 818           $body[$last] = $replacer->get_replacement($replacer->add(
A 819             str_repeat('</blockquote>', $quote_level))) . $body[$last];
99b8c1 820       }
A 821       else if ($quote_level > 0)
dd0ae6 822         $body[$n] = $replacer->get_replacement($replacer->add(
A 823           str_repeat('</blockquote>', $quote_level))) . $body[$n];
99b8c1 824     }
4f6932 825
A 826     $quote_level = $q;
827   }
828
dd0ae6 829   $body = join("\n", $body);
a4c970 830
A 831   // quote plain text (don't use Q() here, to display entities "as is")
832   $table = get_html_translation_table(HTML_SPECIALCHARS);
833   unset($table['?']);
834   $body = strtr($body, $table);
4f6932 835
ba12c7 836   // colorize signature (up to <sig_max_lines> lines)
33dfdd 837   $len = strlen($body);
ba12c7 838   $sig_max_lines = $RCMAIL->config->get('sig_max_lines', 15);
33dfdd 839   while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
A 840     if ($sp == 0 || $body[$sp-1] == "\n") {
841       // do not touch blocks with more that X lines
ba12c7 842       if (substr_count($body, "\n", $sp) < $sig_max_lines)
99b8c1 843         $body = substr($body, 0, max(0, $sp))
A 844           .'<span class="sig">'.substr($body, $sp).'</span>';
33dfdd 845       break;
4f6932 846     }
99b8c1 847   }
4f6932 848
99b8c1 849   // insert url/mailto links and citation tags
A 850   $body = $replacer->resolve($body);
4f6932 851
A 852   return $body;
21e724 853 }
4e17e6 854
21e724 855
T 856 /**
857  * Callback function for washtml cleaning class
858  */
2b017e 859 function rcmail_washtml_callback($tagname, $attrib, $content, $washtml)
21e724 860 {
T 861   switch ($tagname) {
862     case 'form':
863       $out = html::div('form', $content);
f54a3a 864       break;
9800a8 865
1c499a 866     case 'style':
T 867       // decode all escaped entities and reduce to ascii strings
79e634 868       $stripped = preg_replace('/[^a-zA-Z\(:;]/', '', rcmail_xss_entity_decode($content));
9800a8 869
36c236 870       // now check for evil strings like expression, behavior or url()
854397 871       if (!preg_match('/expression|behavior|javascript:|import[^a]/i', $stripped)) {
T 872         if (!$washtml->get_config('allow_remote') && stripos($stripped, 'url('))
2b017e 873           $washtml->extlinks = true;
T 874         else
875           $out = html::tag('style', array('type' => 'text/css'), $content);
1c499a 876         break;
T 877       }
9800a8 878
21e724 879     default:
T 880       $out = '';
881   }
9800a8 882
21e724 883   return $out;
2337a8 884 }
A 885
886
887 /**
45f56c 888  * return table with message headers
T 889  */
a8a72e 890 function rcmail_message_headers($attrib, $headers=null)
4e17e6 891   {
b6da0b 892   global $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL;
4e17e6 893   static $sa_attrib;
9800a8 894
4e17e6 895   // keep header table attrib
a8a72e 896   if (is_array($attrib) && !$sa_attrib && !$attrib['valueof'])
4e17e6 897     $sa_attrib = $attrib;
T 898   else if (!is_array($attrib) && is_array($sa_attrib))
899     $attrib = $sa_attrib;
9800a8 900
4e17e6 901   if (!isset($MESSAGE))
T 902     return FALSE;
903
904   // get associative array of headers object
a8a72e 905   if (!$headers) {
TB 906     $headers_obj = $MESSAGE->headers;
907     $headers = get_object_vars($MESSAGE->headers);
908   }
dc67f3 909   else if (is_object($headers)) {
TB 910     $headers_obj = $headers;
911     $headers = get_object_vars($headers_obj);
912   }
a8a72e 913   else {
TB 914     $headers_obj = rcube_message_header::from_array($headers);
915   }
7c60ff 916
4e17e6 917   // show these headers
83370e 918   $standard_headers = array('subject', 'from', 'sender', 'to', 'cc', 'bcc', 'replyto',
a49a00 919     'mail-reply-to', 'mail-followup-to', 'date', 'priority');
faea23 920   $exclude_headers = $attrib['exclude'] ? explode(',', $attrib['exclude']) : array();
cc97ea 921   $output_headers = array();
e5686f 922
cc97ea 923   foreach ($standard_headers as $hkey) {
61512f 924     $ishtml = false;
TB 925
e25a35 926     if ($headers[$hkey])
A 927       $value = $headers[$hkey];
928     else if ($headers['others'][$hkey])
929       $value = $headers['others'][$hkey];
930     else
faea23 931       continue;
T 932
933     if (in_array($hkey, $exclude_headers))
4e17e6 934       continue;
T 935
765ecb 936     $header_title = rcube_label(preg_replace('/(^mail-|-)/', '', $hkey));
TB 937
cc97ea 938     if ($hkey == 'date') {
5b1de5 939       if ($PRINT_MODE)
e25a35 940         $header_value = format_date($value, $RCMAIL->config->get('date_long', 'x'));
5b1de5 941       else
e25a35 942         $header_value = format_date($value);
a49a00 943     }
567be6 944     else if ($hkey == 'priority') {
a49a00 945       if ($value) {
T 946         $header_value = html::span('prio' . $value, rcmail_localized_priority($value));
947       }
948       else
949         continue;
cc97ea 950     }
T 951     else if ($hkey == 'replyto') {
61512f 952       if ($headers['replyto'] != $headers['from']) {
765ecb 953         $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 954         $ishtml = true;
TB 955       }
700320 956       else
A 957         continue;
4e17e6 958     }
e25a35 959     else if ($hkey == 'mail-reply-to') {
A 960       if ($headers['mail-replyto'] != $headers['reply-to']
961         && $headers['reply-to'] != $headers['from']
61512f 962       ) {
765ecb 963         $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 964         $ishtml = true;
TB 965       }
e25a35 966       else
A 967         continue;
968     }
83370e 969     else if ($hkey == 'sender') {
AM 970       if ($headers['sender'] != $headers['from']) {
971         $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
972         $ishtml = true;
973       }
974       else
975         continue;
976     }
e25a35 977     else if ($hkey == 'mail-followup-to') {
765ecb 978       $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 979       $ishtml = true;
e25a35 980     }
61512f 981     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc'))) {
765ecb 982       $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 983       $ishtml = true;
TB 984     }
e25a35 985     else if ($hkey == 'subject' && empty($value))
cc97ea 986       $header_value = rcube_label('nosubject');
T 987     else
1c4f23 988       $header_value = trim(rcube_mime::decode_header($value, $headers['charset']));
9800a8 989
3ee5a7 990     $output_headers[$hkey] = array(
765ecb 991         'title' => $header_title,
61512f 992         'value' => $header_value,
TB 993         'raw' => $value,
994         'html' => $ishtml,
3ee5a7 995     );
cc97ea 996   }
9800a8 997
3ee5a7 998   $plugin = $RCMAIL->plugins->exec_hook('message_headers_output',
a8a72e 999     array('output' => $output_headers, 'headers' => $headers_obj, 'exclude' => $exclude_headers));
faea23 1000
T 1001   // single header value is requested
1002   if (!empty($attrib['valueof']))
54be5c 1003     return Q($plugin['output'][$attrib['valueof']]['value'], ($attrib['valueof'] == 'subject' ? 'strict' : 'show'));
9800a8 1004
cc97ea 1005   // compose html table
T 1006   $table = new html_table(array('cols' => 2));
9800a8 1007
cc97ea 1008   foreach ($plugin['output'] as $hkey => $row) {
T 1009     $table->add(array('class' => 'header-title'), Q($row['title']));
61512f 1010     $table->add(array('class' => 'header '.$hkey), $row['html'] ? $row['value'] : Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show')));
cc97ea 1011   }
4e17e6 1012
c6be45 1013   return $table->show($attrib);
T 1014 }
1015
a49a00 1016 /**
T 1017  * Convert Priority header value into a localized string
1018  */
1019 function rcmail_localized_priority($value)
1020 {
1021   $labels_map = array(
1022     '1' => 'highest',
1023     '2' => 'high',
1024     '3' => 'normal',
1025     '4' => 'low',
1026     '5' => 'lowest',
1027   );
297c1a 1028
a49a00 1029   if ($value && $labels_map[$value])
T 1030     return rcube_label($labels_map[$value]);
297c1a 1031
a49a00 1032   return '';
T 1033 }
c6be45 1034
T 1035 /**
1036  * return block to show full message headers
1037  */
1038 function rcmail_message_full_headers($attrib, $headers=NULL)
1039 {
1040   global $OUTPUT;
c08b18 1041
76248c 1042   $html = html::div(array('id' => "all-headers", 'class' => "all", 'style' => 'display:none'), html::div(array('id' => 'headers-source'), ''));
725704 1043   $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)", 'title' => rcube_label('togglefullheaders')), '');
c08b18 1044
e5686f 1045   $OUTPUT->add_gui_object('all_headers_row', 'all-headers');
A 1046   $OUTPUT->add_gui_object('all_headers_box', 'headers-source');
c08b18 1047
c23aad 1048   return html::div($attrib, $html);
c6be45 1049 }
4e17e6 1050
T 1051
45f56c 1052 /**
21605c 1053  * Handler for the 'messagebody' GUI object
45f56c 1054  *
21605c 1055  * @param array Named parameters
T 1056  * @return string HTML content showing the message body
45f56c 1057  */
4e17e6 1058 function rcmail_message_body($attrib)
e0960f 1059 {
b6da0b 1060   global $CONFIG, $OUTPUT, $MESSAGE, $RCMAIL, $REMOTE_OBJECTS;
5f8686 1061
8fa58e 1062   if (!is_array($MESSAGE->parts) && empty($MESSAGE->body))
4e17e6 1063     return '';
9800a8 1064
4e17e6 1065   if (!$attrib['id'])
T 1066     $attrib['id'] = 'rcmailMsgBody';
1067
8fa58e 1068   $safe_mode = $MESSAGE->is_safe || intval($_GET['_safe']);
21605c 1069   $out = '';
9800a8 1070
4e17e6 1071   $header_attrib = array();
T 1072   foreach ($attrib as $attr => $value)
1073     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
1074       $header_attrib[$regs[1]] = $value;
1075
e0960f 1076   if (!empty($MESSAGE->parts)) {
A 1077     foreach ($MESSAGE->parts as $i => $part) {
a8a72e 1078       if ($part->type == 'headers') {
TB 1079         $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
1080       }
0c2596 1081       else if ($part->type == 'content') {
A 1082         // unsapported
1083         if ($part->realtype) {
1084           if ($part->realtype == 'multipart/encrypted') {
1085             $out .= html::span('part-notice', rcube_label('encryptedmessage'));
1086           }
1087           continue;
1088         }
1089         else if (!$part->size) {
1090           continue;
1091         }
e0960f 1092         // Check if we have enough memory to handle the message in it
A 1093         // #1487424: we need up to 10x more memory than the body
0c2596 1094         else if (!rcmail_mem_check($part->size * 10)) {
e0960f 1095           $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
A 1096             . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part='.$part->mime_id
c321a9 1097               .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download')));
e0960f 1098           continue;
A 1099         }
1100
8d4bcd 1101         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
8fa58e 1102           $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
a0109c 1103
8d4bcd 1104         // fetch part if not available
T 1105         if (!isset($part->body))
8fa58e 1106           $part->body = $MESSAGE->get_part_content($part->mime_id);
a0109c 1107
a8a72e 1108         // extract headers from message/rfc822 parts
TB 1109         if ($part->mimetype == 'message/rfc822') {
dc67f3 1110           $msgpart = rcube_mime::parse_message($part->body);
TB 1111           if (!empty($msgpart->headers)) {
1112             $part = $msgpart;
1113             $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
a8a72e 1114           }
TB 1115         }
1116
64e3e8 1117         // message is cached but not exists (#1485443), or other error
A 1118         if ($part->body === false) {
1119           rcmail_message_error($MESSAGE->uid);
1120         }
1121
3c3433 1122         $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
A 1123           'part' => $part, 'prefix' => ''));
6b6f2e 1124
21e724 1125         $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
5f8686 1126
7b808b 1127         if ($part->ctype_secondary == 'html') {
2b017e 1128           $body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs, $safe_mode);
7b808b 1129           $div_attr = array('class' => 'message-htmlpart');
fb995a 1130           $style = array();
A 1131
cb3dfd 1132           if (!empty($attrs)) {
A 1133             foreach ($attrs as $a_idx => $a_val)
1134               $style[] = $a_idx . ': ' . $a_val;
1135             if (!empty($style))
1136               $div_attr['style'] = implode('; ', $style);
1137           }
7b808b 1138
3c3433 1139           $out .= html::div($div_attr, $plugin['prefix'] . $body);
7b808b 1140         }
a2f2c5 1141         else
3c3433 1142           $out .= html::div('message-part', $plugin['prefix'] . $body);
4e17e6 1143       }
T 1144     }
e0960f 1145   }
3c3433 1146   else {
e0960f 1147     // Check if we have enough memory to handle the message in it
A 1148     // #1487424: we need up to 10x more memory than the body
1149     if (!rcmail_mem_check(strlen($MESSAGE->body) * 10)) {
1150       $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
1151         . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part=0'
c321a9 1152           .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download')));
3c3433 1153     }
e0960f 1154     else {
A 1155       $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
1156         'part' => $MESSAGE, 'prefix' => ''));
1157
1158       $out .= html::div('message-part', $plugin['prefix'] . html::tag('pre', array(),
1159         rcmail_plain_body(Q($MESSAGE->body, 'strict', false))));
1160     }
1161   }
4e17e6 1162
T 1163   // list images after mail body
031491 1164   if ($RCMAIL->config->get('inline_images', true) && !empty($MESSAGE->attachments)) {
TB 1165     $thumbnail_size = $RCMAIL->config->get('image_thumbnail_size', 240);
d20e96 1166     $client_mimetypes = (array)$RCMAIL->config->get('client_mimetypes');
031491 1167
8fa58e 1168     foreach ($MESSAGE->attachments as $attach_prop) {
18ca0b 1169       // skip inline images
A 1170       if ($attach_prop->content_id && $attach_prop->disposition == 'inline') {
1171         continue;
1172       }
1173
47d06e 1174       // Content-Type: image/*...
b81e7e 1175       if ($mimetype = rcmail_part_image_type($attach_prop)) {
031491 1176         // display thumbnails
TB 1177         if ($thumbnail_size) {
1178           $show_link = array(
1179             'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false),
1180             'onclick' => sprintf(
1181               'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)',
1182               JS_OBJECT_NAME,
1183               $attach_prop->mime_id,
b81e7e 1184               $mimetype)
031491 1185           );
TB 1186           $out .= html::p('image-attachment',
d20e96 1187              html::a($show_link + array('class' => 'image-link', 'style' => sprintf('width:%dpx', $thumbnail_size)),
031491 1188                html::img(array(
TB 1189                 'class' => 'image-thumbnail',
1190                 'src'   => $MESSAGE->get_part_url($attach_prop->mime_id, true) . '&_thumb=1',
1191                 'title' => $attach_prop->filename,
1192                 'alt'   => $attach_prop->filename,
1193                 'style' => sprintf('max-width:%dpx; max-height:%dpx', $thumbnail_size, $thumbnail_size),
1194               ))
1195             ) .
1196             html::span('image-filename', Q($attach_prop->filename)) .
a9e136 1197             html::span('image-filesize', Q($RCMAIL->message_part_size($attach_prop))) .
031491 1198             html::span('attachment-links',
b81e7e 1199               (in_array($mimetype, $client_mimetypes) ? html::a($show_link, rcube_label('showattachment')) . '&nbsp;' : '') .
031491 1200               html::a($show_link['href'] . '&_download=1', rcube_label('download'))
TB 1201             ) .
1202             html::br(array('style' => 'clear:both'))
1203           );
1204         }
1205         else {
1206           $out .= html::tag('fieldset', 'image-attachment',
1207             html::tag('legend', 'image-filename', Q($attach_prop->filename)) .
1208             html::p(array('align' => "center"),
1209               html::img(array(
1210                 'src'   => $MESSAGE->get_part_url($attach_prop->mime_id, true),
1211                 'title' => $attach_prop->filename,
1212                 'alt'   => $attach_prop->filename,
1213               )))
1214           );
1215         }
e0960f 1216       }
4e17e6 1217     }
8fa58e 1218   }
9800a8 1219
4e17e6 1220   // tell client that there are blocked remote objects
T 1221   if ($REMOTE_OBJECTS && !$safe_mode)
f11541 1222     $OUTPUT->set_env('blockedobjects', true);
4e17e6 1223
21605c 1224   return html::div($attrib, $out);
e0960f 1225 }
4e17e6 1226
19cc5b 1227 function rcmail_part_image_type($part)
AM 1228 {
1229   $rcmail = rcmail::get_instance();
1230
1231   // Skip TIFF images if browser doesn't support this format...
1232   $tiff_support = !empty($_SESSION['browser_caps']) && !empty($_SESSION['browser_caps']['tif']);
1233   // until we can convert them to JPEG
1234   $tiff_support = $tiff_support || $rcmail->config->get('im_convert_path');
1235
1236   // Content-type regexp
1237   $mime_regex = $tiff_support ? '/^image\//i' : '/^image\/(?!tif)/i';
1238
1239   // Content-Type: image/*...
1240   if (preg_match($mime_regex, $part->mimetype)) {
090c49 1241     return rcmail_fix_mimetype($part->mimetype);
19cc5b 1242   }
AM 1243
1244   // Many clients use application/octet-stream, we'll detect mimetype
1245   // by checking filename extension
1246
1247   // Supported image filename extensions to image type map
1248   $types = array(
1249     'jpg'  => 'image/jpeg',
1250     'jpeg' => 'image/jpeg',
1251     'png'  => 'image/png',
1252     'gif'  => 'image/gif',
1253     'bmp'  => 'image/bmp',
1254   );
1255   if ($tiff_support) {
1256     $types['tif']  = 'image/tiff';
1257     $types['tiff'] = 'image/tiff';
1258   }
1259
1260   if ($part->filename
1261     && preg_match('/^application\/octet-stream$/i', $part->mimetype)
9ce239 1262     && preg_match('/\.([^.]+)$/i', $part->filename, $m)
19cc5b 1263     && ($extension = strtolower($m[1]))
AM 1264     && isset($types[$extension])
1265   ) {
1266     return $types[$extension];
1267   }
aa055c 1268 }
4e17e6 1269
f5d62f 1270
45f56c 1271 /**
T 1272  * modify a HTML message that it can be displayed inside a HTML page
1273  */
2b017e 1274 function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null, $allow_remote=false)
cb3dfd 1275 {
4e17e6 1276   $last_style_pos = 0;
cb3dfd 1277   $cont_id = $container_id.($body_id ? ' div.'.$body_id : '');
9800a8 1278
4e17e6 1279   // find STYLE tags
e4a4ca 1280   while (($pos = stripos($body, '<style', $last_style_pos)) && ($pos2 = stripos($body, '</style>', $pos)))
cb3dfd 1281   {
aede1f 1282     $pos = strpos($body, '>', $pos) + 1;
AM 1283     $len = $pos2 - $pos;
ea206d 1284
4e17e6 1285     // replace all css definitions with #container [def]
aede1f 1286     $styles = substr($body, $pos, $len);
AM 1287     $styles = rcmail_mod_css_styles($styles, $cont_id, $allow_remote);
ea206d 1288
aede1f 1289     $body = substr_replace($body, $styles, $pos, $len);
AM 1290     $last_style_pos = $pos2 + strlen($styles) - $len;
cb3dfd 1291   }
4e17e6 1292
fe79b1 1293   // modify HTML links to open a new window if clicked
115263 1294   $GLOBALS['rcmail_html_container_id'] = $container_id;
fb53c8 1295   $body = preg_replace_callback('/<(a|link|area)\s+([^>]+)>/Ui', 'rcmail_alter_html_link', $body);
115263 1296   unset($GLOBALS['rcmail_html_container_id']);
4e17e6 1297
cb3dfd 1298   $body = preg_replace(array(
b488c1 1299       // add comments arround html and other tags
d7a411 1300       '/(<!DOCTYPE[^>]*>)/i',
A 1301       '/(<\?xml[^>]*>)/i',
06895c 1302       '/(<\/?html[^>]*>)/i',
T 1303       '/(<\/?head[^>]*>)/i',
1304       '/(<title[^>]*>.*<\/title>)/Ui',
b488c1 1305       '/(<\/?meta[^>]*>)/i',
A 1306       // quote <? of php and xml files that are specified as text/html
1307       '/<\?/',
1308       '/\?>/',
1309       // replace <body> with <div>
1310       '/<body([^>]*)>/i',
1311       '/<\/body>/i',
1312       ),
1313     array(
1314       '<!--\\1-->',
1315       '<!--\\1-->',
1316       '<!--\\1-->',
1317       '<!--\\1-->',
1318       '<!--\\1-->',
1319       '<!--\\1-->',
1320       '&lt;?',
1321       '?&gt;',
7b808b 1322       '<div class="'.$body_id.'"\\1>',
b488c1 1323       '</div>',
A 1324       ),
06895c 1325     $body);
a0109c 1326
fb995a 1327   $attributes = array();
A 1328
1329   // Handle body attributes that doesn't play nicely with div elements
ceb708 1330   $regexp = '/<div class="' . preg_quote($body_id, '/') . '"([^>]*)/';
A 1331   if (preg_match($regexp, $body, $m)) {
fb995a 1332     $attrs = $m[0];
A 1333     // Get bgcolor, we'll set it as background-color of the message container
ceb708 1334     if ($m[1] && preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/', $attrs, $mb)) {
cb3dfd 1335       $attributes['background-color'] = $mb[1];
fb995a 1336       $attrs = preg_replace('/bgcolor=["\']*([a-z0-9#]+)["\']*/', '', $attrs);
7b808b 1337     }
fb995a 1338     // Get background, we'll set it as background-image of the message container
ceb708 1339     if ($m[1] && preg_match('/background=["\']*([^"\'>\s]+)["\']*/', $attrs, $mb)) {
cb3dfd 1340       $attributes['background-image'] = 'url('.$mb[1].')';
fb995a 1341       $attrs = preg_replace('/background=["\']*([^"\'>\s]+)["\']*/', '', $attrs);
A 1342     }
ceb708 1343     if (!empty($attributes)) {
A 1344       $body = preg_replace($regexp, rtrim($attrs), $body, 1);
1345     }
cb3dfd 1346
A 1347     // handle body styles related to background image
1348     if ($attributes['background-image']) {
1349       // get body style
1350       if (preg_match('/#'.preg_quote($cont_id, '/').'\s+\{([^}]+)}/i', $body, $m)) {
1351         // get background related style
1352         if (preg_match_all('/(background-position|background-repeat)\s*:\s*([^;]+);/i', $m[1], $ma, PREG_SET_ORDER)) {
1353           foreach ($ma as $style)
1354             $attributes[$style[1]] = $style[2];
1355         }
1356       }
1357     }
7b808b 1358   }
b488c1 1359   // make sure there's 'rcmBody' div, we need it for proper css modification
A 1360   // its name is hardcoded in rcmail_message_body() also
ceb708 1361   else {
cb3dfd 1362     $body = '<div class="' . $body_id . '">' . $body . '</div>';
ceb708 1363   }
86958f 1364
cb3dfd 1365   return $body;
A 1366 }
4e17e6 1367
T 1368
45f56c 1369 /**
T 1370  * parse link attributes and set correct target
1371  */
115263 1372 function rcmail_alter_html_link($matches)
e5af2f 1373 {
5c1dfb 1374   global $RCMAIL;
A 1375
1376   // Support unicode/punycode in top-level domain part
1377   $EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))';
9800a8 1378
115263 1379   $tag = $matches[1];
T 1380   $attrib = parse_attrib_string($matches[2]);
e5af2f 1381   $end = '>';
84f5b7 1382
29c542 1383   // Remove non-printable characters in URL (#1487805)
c8c53f 1384   if ($attrib['href'])
T 1385     $attrib['href'] = preg_replace('/[\x00-\x1F]/', '', $attrib['href']);
29c542 1386
e5af2f 1387   if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) {
3e0e91 1388     $tempurl = 'tmp-' . md5($attrib['href']) . '.css';
T 1389     $_SESSION['modcssurls'][$tempurl] = $attrib['href'];
1390     $attrib['href'] = $RCMAIL->url(array('task' => 'utils', 'action' => 'modcss', 'u' => $tempurl, 'c' => $GLOBALS['rcmail_html_container_id']));
e5af2f 1391     $end = ' />';
T 1392   }
5c1dfb 1393   else if (preg_match('/^mailto:'.$EMAIL_PATTERN.'(\?[^"\'>]+)?/i', $attrib['href'], $mailto)) {
115263 1394     $attrib['href'] = $mailto[0];
97bd2c 1395     $attrib['onclick'] = sprintf(
T 1396       "return %s.command('compose','%s',this)",
1397       JS_OBJECT_NAME,
5c1dfb 1398       JQ($mailto[1].$mailto[3]));
4e17e6 1399   }
c8c53f 1400   else if (empty($attrib['href']) && !$attrib['name']) {
T 1401     $attrib['href'] = './#NOP';
1402     $attrib['onclick'] = 'return false';
1403   }
e5af2f 1404   else if (!empty($attrib['href']) && $attrib['href'][0] != '#') {
T 1405     $attrib['target'] = '_blank';
1406   }
1407
fb53c8 1408   // allowed attributes for a|link|area tags
AM 1409   $allow = array('href','name','target','onclick','id','class','style','title',
1410     'rel','type','media','alt','coords','nohref','hreflang','shape');
1411
1412   return "<$tag" . html::attrib_string($attrib, $allow) . $end;
e5af2f 1413 }
4e17e6 1414
T 1415
45f56c 1416 /**
T 1417  * decode address string and re-format it as HTML links
1418  */
765ecb 1419 function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $default_charset=null, $title=null)
8e44f4 1420 {
b6da0b 1421   global $RCMAIL, $PRINT_MODE, $CONFIG;
1088d6 1422
1c4f23 1423   $a_parts = rcube_mime::decode_address_list($input, null, true, $default_charset);
4e17e6 1424
T 1425   if (!sizeof($a_parts))
1426     return $input;
1427
1428   $c = count($a_parts);
1429   $j = 0;
1430   $out = '';
765ecb 1431   $allvalues = array();
4e17e6 1432
ecb51c 1433   if ($addicon && !isset($_SESSION['writeable_abook'])) {
T 1434     $_SESSION['writeable_abook'] = $RCMAIL->get_address_sources(true) ? true : false;
55243b 1435   }
b579f4 1436
8e44f4 1437   foreach ($a_parts as $part) {
4e17e6 1438     $j++;
e99991 1439     $name   = $part['name'];
A 1440     $mailto = $part['mailto'];
1441     $string = $part['string'];
1442
1443     // IDNA ASCII to Unicode
1444     if ($name == $mailto)
e8d5bd 1445       $name = rcube_idn_to_utf8($name);
e99991 1446     if ($string == $mailto)
e8d5bd 1447       $string = rcube_idn_to_utf8($string);
A 1448     $mailto = rcube_idn_to_utf8($mailto);
e99991 1449
8e44f4 1450     if ($PRINT_MODE) {
297c1a 1451       $out .= ($out ? ', ' : '') . sprintf('%s &lt;%s&gt;', Q($name), $mailto);
AM 1452       // for printing we display all addresses
1453       continue;
8e44f4 1454     }
1baeb6 1455     else if (check_email($part['mailto'], false)) {
8e44f4 1456       if ($linked) {
768091 1457         $address = html::a(array(
e99991 1458             'href' => 'mailto:'.$mailto,
A 1459             'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)),
1460             'title' => $mailto,
8e44f4 1461             'class' => "rcmContactAddress",
T 1462           ),
e99991 1463         Q($name ? $name : $mailto));
4e17e6 1464       }
8e44f4 1465       else {
768091 1466         $address = html::span(array('title' => $mailto, 'class' => "rcmContactAddress"),
e99991 1467           Q($name ? $name : $mailto));
8e44f4 1468       }
T 1469
ecb51c 1470       if ($addicon && $_SESSION['writeable_abook']) {
61512f 1471         $address .= html::a(array(
8e44f4 1472             'href' => "#add",
c02080 1473             'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, JQ($string)),
8e44f4 1474             'title' => rcube_label('addtoaddressbook'),
67e0c9 1475             'class' => 'rcmaddcontact',
8e44f4 1476           ),
T 1477           html::img(array(
1478             'src' => $CONFIG['skin_path'] . $addicon,
1479             'alt' => "Add contact",
61512f 1480           )));
8e44f4 1481       }
T 1482     }
1483     else {
61512f 1484       $address = '';
e99991 1485       if ($name)
61512f 1486         $address .= Q($name);
e99991 1487       if ($mailto)
61512f 1488         $address .= (strlen($address) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($mailto));
8e44f4 1489     }
9800a8 1490
765ecb 1491     $address = html::span('adr', $address);
TB 1492     $allvalues[] = $address;
9800a8 1493
765ecb 1494     if (!$moreadrs)
TB 1495       $out .= ($out ? ', ' : '') . $address;
1496
1497     if ($max && $j == $max && $c > $j) {
1498       if ($linked) {
1499         $moreadrs = $c - $j;
1500       }
1501       else {
1502         $out .= '...';
1503         break;
1504       }
4e17e6 1505     }
8e44f4 1506   }
9800a8 1507
765ecb 1508   if ($moreadrs) {
TB 1509       $out .= ' ' . html::a(array(
1510           'href' => '#more',
1511           'class' => 'morelink',
1512           'onclick' => sprintf("return %s.show_popup_dialog('%s','%s')",
1513             JS_OBJECT_NAME,
1514             JQ(join(', ', $allvalues)),
1515             JQ($title))
1516         ),
1517         Q(rcube_label(array('name' => 'andnmore', 'vars' => array('nr' => $moreadrs)))));
1518   }
1519
4e17e6 1520   return $out;
8e44f4 1521 }
4e17e6 1522
T 1523
ccd63c 1524 /**
T 1525  * Wrap text to a given number of characters per line
6b6f2e 1526  * but respect the mail quotation of replies messages (>).
T 1527  * Finally add another quotation level by prpending the lines
1528  * with >
ccd63c 1529  *
T 1530  * @param string Text to wrap
1531  * @param int The line width
1532  * @return string The wrapped text
1533  */
6b6f2e 1534 function rcmail_wrap_and_quote($text, $length = 72)
ccd63c 1535 {
T 1536   // Rebuild the message body with a maximum of $max chars, while keeping quoted message.
248d78 1537   $max = max(75, $length + 8);
ccd63c 1538   $lines = preg_split('/\r?\n/', trim($text));
T 1539   $out = '';
1540
1541   foreach ($lines as $line) {
6b6f2e 1542     // don't wrap already quoted lines
T 1543     if ($line[0] == '>')
1544       $line = '>' . rtrim($line);
1545     else if (mb_strlen($line) > $max) {
1546       $newline = '';
1547       foreach(explode("\n", rc_wordwrap($line, $length - 2)) as $l) {
1548         if (strlen($l))
1549           $newline .= '> ' . $l . "\n";
1550         else
1551           $newline .= ">\n";
ccd63c 1552       }
6b6f2e 1553       $line = rtrim($newline);
ccd63c 1554     }
6b6f2e 1555     else
T 1556       $line = '> ' . $line;
ccd63c 1557
T 1558     // Append the line
1559     $out .= $line . "\n";
1560   }
9800a8 1561
4340d5 1562   return rtrim($out, "\n");
ccd63c 1563 }
T 1564
1565
bc404f 1566 function rcmail_draftinfo_encode($p)
T 1567 {
1568   $parts = array();
1569   foreach ($p as $key => $val)
1570     $parts[] = $key . '=' . ($key == 'folder' ? base64_encode($val) : $val);
9800a8 1571
bc404f 1572   return join('; ', $parts);
T 1573 }
1574
1575
1576 function rcmail_draftinfo_decode($str)
1577 {
1578   $info = array();
1579   foreach (preg_split('/;\s+/', $str) as $part) {
1580     list($key, $val) = explode('=', $part, 2);
1581     if ($key == 'folder')
1582       $val = base64_decode($val);
1583     $info[$key] = $val;
1584   }
9800a8 1585
bc404f 1586   return $info;
T 1587 }
1588
1589
c7dcb3 1590 function rcmail_message_part_controls($attrib)
e99991 1591 {
8749e9 1592   global $MESSAGE, $RCMAIL;
9800a8 1593
d5342a 1594   $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
8fa58e 1595   if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part])
4e17e6 1596     return '';
9800a8 1597
8749e9 1598   $part  = $MESSAGE->mime_parts[$part];
8fa58e 1599   $table = new html_table(array('cols' => 3));
9800a8 1600
be72fb 1601   $filename = rcmail_attachment_name($part);
0c2596 1602
A 1603   if (!empty($filename)) {
8fa58e 1604     $table->add('title', Q(rcube_label('filename')));
0c2596 1605     $table->add('header', Q($filename));
c7dcb3 1606     $table->add('download-link', html::a(array('href' => './?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING'])), Q(rcube_label('download'))));
8fa58e 1607   }
9800a8 1608
8749e9 1609   $table->add('title', Q(rcube_label('filesize')));
AM 1610   $table->add('header', Q($RCMAIL->message_part_size($part)));
9800a8 1611
8fa58e 1612   return $table->show($attrib);
e99991 1613 }
4e17e6 1614
T 1615
1616 function rcmail_message_part_frame($attrib)
e99991 1617 {
4e17e6 1618   global $MESSAGE;
9800a8 1619
8fa58e 1620   $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
4e17e6 1621   $ctype_primary = strtolower($part->ctype_primary);
T 1622
c14b33 1623   $attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary=='text' ? '_embed=' : '_preload='), $_SERVER['QUERY_STRING']);
4e17e6 1624
95fcc3 1625   return html::iframe($attrib);
e99991 1626 }
4e17e6 1627
T 1628
45f56c 1629 /**
T 1630  * clear message composing settings
1631  */
4591de 1632 function rcmail_compose_cleanup($id)
e99991 1633 {
06dc98 1634   if (!isset($_SESSION['compose_data_'.$id]))
4e17e6 1635     return;
70d4b9 1636
929a50 1637   $rcmail = rcmail::get_instance();
4591de 1638   $rcmail->plugins->exec_hook('attachments_cleanup', array('group' => $id));
06dc98 1639   $rcmail->session->remove('compose_data_'.$id);
e99991 1640 }
9800a8 1641
fba1f5 1642
T 1643 /**
a99968 1644  * Send the MDN response
A 1645  *
1646  * @param mixed $message    Original message object (rcube_message) or UID
1647  * @param array $smtp_error SMTP error array (reference)
1648  *
1649  * @return boolean Send status
1650  */
1651 function rcmail_send_mdn($message, &$smtp_error)
0ea884 1652 {
b6da0b 1653   global $RCMAIL;
8fa58e 1654
8c124b 1655   if (!is_object($message) || !is_a($message, 'rcube_message'))
a99968 1656     $message = new rcube_message($message);
9800a8 1657
609d39 1658   if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) &&
c321a9 1659     ($RCMAIL->storage->check_permflag('MDNSENT') || $RCMAIL->storage->check_permflag('*')))
0ea884 1660   {
a0e3dc 1661     $identity  = rcmail_identity_select($message);
AM 1662     $sender    = format_email_recipient($identity['email'], $identity['name']);
1c4f23 1663     $recipient = array_shift(rcube_mime::decode_address_list(
A 1664       $message->headers->mdn_to, 1, true, $message->headers->charset));
a0e3dc 1665     $mailto    = $recipient['mailto'];
0ea884 1666
ac8edb 1667     $compose = new Mail_mime("\r\n");
91790e 1668
A 1669     $compose->setParam('text_encoding', 'quoted-printable');
1670     $compose->setParam('html_encoding', 'quoted-printable');
1671     $compose->setParam('head_encoding', 'quoted-printable');
1672     $compose->setParam('head_charset', RCMAIL_CHARSET);
1673     $compose->setParam('html_charset', RCMAIL_CHARSET);
1674     $compose->setParam('text_charset', RCMAIL_CHARSET);
9800a8 1675
0ea884 1676     // compose headers array
T 1677     $headers = array(
2bf3cc 1678       'Date' => rcmail_user_date(),
0ea884 1679       'From' => $sender,
8fa58e 1680       'To'   => $message->headers->mdn_to,
T 1681       'Subject' => rcube_label('receiptread') . ': ' . $message->subject,
1d8cbc 1682       'Message-ID' => rcmail_gen_message_id(),
0ea884 1683       'X-Sender' => $identity['email'],
ea50e7 1684       'References' => trim($message->headers->references . ' ' . $message->headers->messageID),
0ea884 1685     );
9800a8 1686
83a763 1687     if ($agent = $RCMAIL->config->get('useragent'))
T 1688       $headers['User-Agent'] = $agent;
0ea884 1689
232535 1690     if ($RCMAIL->config->get('mdn_use_from'))
AM 1691       $options['mdn_use_from'] = true;
1692
0ea884 1693     $body = rcube_label("yourmessage") . "\r\n\r\n" .
1adc70 1694       "\t" . rcube_label("to") . ': ' . rcube_mime::decode_mime_string($message->headers->to, $message->headers->charset) . "\r\n" .
8fa58e 1695       "\t" . rcube_label("subject") . ': ' . $message->subject . "\r\n" .
83a763 1696       "\t" . rcube_label("sent") . ': ' . format_date($message->headers->date, $RCMAIL->config->get('date_long')) . "\r\n" .
0ea884 1697       "\r\n" . rcube_label("receiptnote") . "\r\n";
9800a8 1698
e019f2 1699     $ua = $RCMAIL->config->get('useragent', "Roundcube Webmail (Version ".RCMAIL_VERSION.")");
0ea884 1700     $report = "Reporting-UA: $ua\r\n";
9800a8 1701
8fa58e 1702     if ($message->headers->to)
T 1703         $report .= "Original-Recipient: {$message->headers->to}\r\n";
9800a8 1704
0ea884 1705     $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
8fa58e 1706                "Original-Message-ID: {$message->headers->messageID}\r\n" .
0ea884 1707                "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
9800a8 1708
8fa58e 1709     $compose->headers($headers);
6b0113 1710     $compose->setContentType('multipart/report', array('report-type'=> 'disposition-notification'));
7145e0 1711     $compose->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
0ea884 1712     $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
T 1713
232535 1714     $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file, $options);
0ea884 1715
T 1716     if ($sent)
1717     {
c321a9 1718       $RCMAIL->storage->set_flag($message->uid, 'MDNSENT');
0ea884 1719       return true;
T 1720     }
1721   }
9800a8 1722
0ea884 1723   return false;
T 1724 }
1725
a0e3dc 1726 /**
AM 1727  * Detect recipient identity from specified message
1728  */
1729 function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'reply')
1730 {
1731     $a_recipients = array();
1732     $a_names      = array();
1733
1734     if ($identities === null) {
1735         $identities = rcmail::get_instance()->user->list_identities(null, true);
1736     }
1737
1738     // extract all recipients of the reply-message
1739     if (is_object($MESSAGE->headers) && in_array($compose_mode, array('reply', 'forward'))) {
1740         $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset);
1741         foreach ($a_to as $addr) {
1742             if (!empty($addr['mailto'])) {
1743                 $a_recipients[] = format_email($addr['mailto']);
1744                 $a_names[]      = $addr['name'];
1745             }
1746         }
1747
1748         if (!empty($MESSAGE->headers->cc)) {
1749             $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset);
1750             foreach ($a_cc as $addr) {
1751                 if (!empty($addr['mailto'])) {
1752                     $a_recipients[] = format_email($addr['mailto']);
1753                     $a_names[]      = $addr['name'];
1754                 }
1755             }
1756         }
1757     }
1758
1759     $from_idx         = null;
1760     $found_idx        = null;
1761     $default_identity = 0; // default identity is always first on the list
1762
1763     // Select identity
1764     foreach ($identities as $idx => $ident) {
1765         // use From header
1766         if (in_array($compose_mode, array('draft', 'edit'))) {
1767             if ($MESSAGE->headers->from == $ident['ident']) {
1768                 $from_idx = $idx;
1769                 break;
1770             }
1771         }
1772         // reply to yourself
1773         else if ($compose_mode == 'reply' && $MESSAGE->headers->from == $ident['ident']) {
1774             $from_idx = $idx;
1775             break;
1776         }
1777         // use replied message recipients
1778         else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) {
1779             if ($found_idx === null) {
1780                 $found_idx = $idx;
1781             }
1782             // match identity name
1783             if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) {
1784                 $from_idx = $idx;
1785                 break;
1786             }
1787         }
1788     }
1789
1790     // If matching by name+address doesn't found any matches, get first found address (identity)
1791     if ($from_idx === null) {
1792         $from_idx = $found_idx;
1793     }
1794
1795     // Try Return-Path
1796     if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) {
1797         foreach ($identities as $idx => $ident) {
1798             if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) {
1799                 $from_idx = $idx;
1800                 break;
1801             }
1802         }
1803     }
1804
1805     // Fallback using Delivered-To
1806     if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) {
1807         foreach ($identities as $idx => $ident) {
1808             if (in_array($ident['email_ascii'], (array)$delivered_to)) {
1809                 $from_idx = $idx;
1810                 break;
1811             }
1812         }
1813     }
1814
1815     // Fallback using Envelope-To
1816     if ($from_idx === null && ($envelope_to = $MESSAGE->headers->others['envelope-to'])) {
1817         foreach ($identities as $idx => $ident) {
1818             if (in_array($ident['email_ascii'], (array)$envelope_to)) {
1819                 $from_idx = $idx;
1820                 break;
1821             }
1822         }
1823     }
1824
1825     return $identities[$from_idx !== null ? $from_idx : $default_identity];
1826 }
2bf3cc 1827
e0bd70 1828 // Fixes some content-type names
A 1829 function rcmail_fix_mimetype($name)
1830 {
1831   // Some versions of Outlook create garbage Content-Type:
1832   // application/pdf.A520491B_3BF7_494D_8855_7FAC2C6C0608
1833   if (preg_match('/^application\/pdf.+/', $name))
1834     $name = 'application/pdf';
1835
090c49 1836   // treat image/pjpeg as image/jpeg
TB 1837   else if (preg_match('/^image\/p?jpe?g$/', $name))
1838     $name = 'image/jpeg';
1839
e0bd70 1840   return $name;
A 1841 }
2bf3cc 1842
be72fb 1843 // return attachment filename, handle empty filename case
830fd2 1844 function rcmail_attachment_name($attachment, $display = false)
be72fb 1845 {
AM 1846     $filename = $attachment->filename;
1847
1848     if ($filename === null || $filename === '') {
1849         if ($attachment->mimetype == 'text/html') {
1850             $filename = rcube_label('htmlmessage');
1851         }
1852         else {
1853             $ext      = rcube_mime::get_mime_extensions($attachment->mimetype);
1854             $ext      = array_shift($ext);
1855             $filename = rcube_label('messagepart') . ' ' . $attachment->mime_id;
1856             if ($ext) {
1857                 $filename .= '.' . $ext;
1858             }
1859         }
1860     }
1861
1862     $filename = preg_replace('[\r\n]', '', $filename);
1863
830fd2 1864     // Display smart names for some known mimetypes
AM 1865     if ($display) {
1866         if (preg_match('/application\/(pgp|pkcs7)-signature/i', $attachment->mimetype)) {
1867             $filename = rcube_label('digitalsig');
1868         }
1869     }
1870
be72fb 1871     return $filename;
AM 1872 }
1873
e538b3 1874 function rcmail_search_filter($attrib)
A 1875 {
119cd1 1876   global $OUTPUT, $CONFIG;
e538b3 1877
A 1878   if (!strlen($attrib['id']))
1879     $attrib['id'] = 'rcmlistfilter';
1880
1881   $attrib['onchange'] = JS_OBJECT_NAME.'.filter_mailbox(this.value)';
9800a8 1882
e538b3 1883   /*
A 1884     RFC3501 (6.4.4): 'ALL', 'RECENT', 
1885     'ANSWERED', 'DELETED', 'FLAGGED', 'SEEN',
1886     'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNSEEN',
1887     'NEW', // = (RECENT UNSEEN)
1888     'OLD' // = NOT RECENT
1889   */
1890
1891   $select_filter = new html_select($attrib);
1892   $select_filter->add(rcube_label('all'), 'ALL');
1893   $select_filter->add(rcube_label('unread'), 'UNSEEN');
1894   $select_filter->add(rcube_label('flagged'), 'FLAGGED');
1895   $select_filter->add(rcube_label('unanswered'), 'UNANSWERED');
c8f35a 1896   if (!$CONFIG['skip_deleted']) {
119cd1 1897     $select_filter->add(rcube_label('deleted'), 'DELETED');
c8f35a 1898     $select_filter->add(rcube_label('undeleted'), 'UNDELETED');
AM 1899   }
4b21d2 1900   $select_filter->add(rcube_label('priority').': '.rcube_label('highest'), 'HEADER X-PRIORITY 1');
A 1901   $select_filter->add(rcube_label('priority').': '.rcube_label('high'), 'HEADER X-PRIORITY 2');
1902   $select_filter->add(rcube_label('priority').': '.rcube_label('normal'), 'NOT HEADER X-PRIORITY 1 NOT HEADER X-PRIORITY 2 NOT HEADER X-PRIORITY 4 NOT HEADER X-PRIORITY 5');
1903   $select_filter->add(rcube_label('priority').': '.rcube_label('low'), 'HEADER X-PRIORITY 4');
1904   $select_filter->add(rcube_label('priority').': '.rcube_label('lowest'), 'HEADER X-PRIORITY 5');
e538b3 1905
A 1906   $out = $select_filter->show($_SESSION['search_filter']);
1907
1908   $OUTPUT->add_gui_object('search_filter', $attrib['id']);
1909
9800a8 1910   return $out;
e538b3 1911 }
A 1912
64e3e8 1913 function rcmail_message_error($uid=null)
A 1914 {
1915   global $RCMAIL;
1916
1917   // Set env variables for messageerror.html template
1918   if ($RCMAIL->action == 'show') {
c321a9 1919     $mbox_name = $RCMAIL->storage->get_folder();
64e3e8 1920     $RCMAIL->output->set_env('mailbox', $mbox_name);
A 1921     $RCMAIL->output->set_env('uid', null);
1922   }
1923   // display error message
1924   $RCMAIL->output->show_message('messageopenerror', 'error');
1925   // ... display message error page
1926   $RCMAIL->output->send('messageerror');
1927 }
9b3fdc 1928
f11541 1929 // register UI objects
T 1930 $OUTPUT->add_handlers(array(
1931   'mailboxlist' => 'rcmail_mailbox_list',
1932   'messages' => 'rcmail_message_list',
1933   'messagecountdisplay' => 'rcmail_messagecount_display',
1934   'quotadisplay' => 'rcmail_quota_display',
ac5d15 1935   'mailboxname' => 'rcmail_mailbox_name_display',
f11541 1936   'messageheaders' => 'rcmail_message_headers',
c6be45 1937   'messagefullheaders' => 'rcmail_message_full_headers',
f11541 1938   'messagebody' => 'rcmail_message_body',
T 1939   'messagecontentframe' => 'rcmail_messagecontent_frame',
1940   'messagepartframe' => 'rcmail_message_part_frame',
1941   'messagepartcontrols' => 'rcmail_message_part_controls',
e538b3 1942   'searchfilter' => 'rcmail_search_filter',
47124c 1943   'searchform' => array($OUTPUT, 'search_form'),
f11541 1944 ));
T 1945
68d2d5 1946 // register action aliases
A 1947 $RCMAIL->register_action_map(array(
77de23 1948     'refresh' => 'check_recent.inc',
68d2d5 1949     'preview' => 'show.inc',
A 1950     'print'   => 'show.inc',
1951     'moveto'  => 'move_del.inc',
1952     'delete'  => 'move_del.inc',
1953     'send'    => 'sendmail.inc',
1954     'expunge' => 'folders.inc',
1955     'purge'   => 'folders.inc',
1956     'remove-attachment'  => 'attachments.inc',
1957     'display-attachment' => 'attachments.inc',
1958     'upload'             => 'attachments.inc',
1959     'group-expand'       => 'autocomplete.inc',
1960 ));