Thomas Bruederli
2013-06-16 73c702c4d1391ac37ed37669aa07adb79b768e98
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];
71ec1b 775         $last = $n;
dd0ae6 776       }
A 777       else if ($q < $quote_level) {
778         $body[$n] = $replacer->get_replacement($replacer->add(
779           str_repeat('</blockquote>', $quote_level - $q))) . $body[$n];
71ec1b 780         $last = $n;
dd0ae6 781       }
99b8c1 782       else if ($flowed) {
A 783         // previous line is flowed
dd0ae6 784         if (isset($body[$last]) && $body[$n]
A 785           && $body[$last][strlen($body[$last])-1] == ' ') {
33dfdd 786           // merge lines
dd0ae6 787           $body[$last] .= $body[$n];
A 788           unset($body[$n]);
99b8c1 789         }
dd0ae6 790         else {
99b8c1 791           $last = $n;
dd0ae6 792         }
99b8c1 793       }
4f6932 794     }
99b8c1 795     else {
A 796       $q = 0;
797       if ($flowed) {
798         // sig separator - line is fixed
dd0ae6 799         if ($body[$n] == '-- ') {
A 800           $last = $last_sig = $n;
99b8c1 801         }
A 802         else {
803           // remove space-stuffing
dd0ae6 804           if ($body[$n][0] == ' ')
A 805             $body[$n] = substr($body[$n], 1);
99b8c1 806
A 807           // previous line is flowed?
dd0ae6 808           if (isset($body[$last]) && $body[$n]
9aab5e 809             && $last !== $last_sig
dd0ae6 810             && $body[$last][strlen($body[$last])-1] == ' '
99b8c1 811           ) {
dd0ae6 812             $body[$last] .= $body[$n];
A 813             unset($body[$n]);
99b8c1 814           }
A 815           else {
816             $last = $n;
817           }
818         }
819         if ($quote_level > 0)
dd0ae6 820           $body[$last] = $replacer->get_replacement($replacer->add(
A 821             str_repeat('</blockquote>', $quote_level))) . $body[$last];
99b8c1 822       }
A 823       else if ($quote_level > 0)
dd0ae6 824         $body[$n] = $replacer->get_replacement($replacer->add(
A 825           str_repeat('</blockquote>', $quote_level))) . $body[$n];
99b8c1 826     }
4f6932 827
A 828     $quote_level = $q;
829   }
830
dd0ae6 831   $body = join("\n", $body);
a4c970 832
A 833   // quote plain text (don't use Q() here, to display entities "as is")
834   $table = get_html_translation_table(HTML_SPECIALCHARS);
835   unset($table['?']);
836   $body = strtr($body, $table);
4f6932 837
ba12c7 838   // colorize signature (up to <sig_max_lines> lines)
33dfdd 839   $len = strlen($body);
ba12c7 840   $sig_max_lines = $RCMAIL->config->get('sig_max_lines', 15);
33dfdd 841   while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
A 842     if ($sp == 0 || $body[$sp-1] == "\n") {
843       // do not touch blocks with more that X lines
ba12c7 844       if (substr_count($body, "\n", $sp) < $sig_max_lines)
99b8c1 845         $body = substr($body, 0, max(0, $sp))
A 846           .'<span class="sig">'.substr($body, $sp).'</span>';
33dfdd 847       break;
4f6932 848     }
99b8c1 849   }
4f6932 850
99b8c1 851   // insert url/mailto links and citation tags
A 852   $body = $replacer->resolve($body);
4f6932 853
A 854   return $body;
21e724 855 }
4e17e6 856
21e724 857
T 858 /**
859  * Callback function for washtml cleaning class
860  */
2b017e 861 function rcmail_washtml_callback($tagname, $attrib, $content, $washtml)
21e724 862 {
T 863   switch ($tagname) {
864     case 'form':
865       $out = html::div('form', $content);
f54a3a 866       break;
9800a8 867
1c499a 868     case 'style':
T 869       // decode all escaped entities and reduce to ascii strings
79e634 870       $stripped = preg_replace('/[^a-zA-Z\(:;]/', '', rcmail_xss_entity_decode($content));
9800a8 871
36c236 872       // now check for evil strings like expression, behavior or url()
854397 873       if (!preg_match('/expression|behavior|javascript:|import[^a]/i', $stripped)) {
T 874         if (!$washtml->get_config('allow_remote') && stripos($stripped, 'url('))
2b017e 875           $washtml->extlinks = true;
T 876         else
877           $out = html::tag('style', array('type' => 'text/css'), $content);
1c499a 878         break;
T 879       }
9800a8 880
21e724 881     default:
T 882       $out = '';
883   }
9800a8 884
21e724 885   return $out;
2337a8 886 }
A 887
888
889 /**
45f56c 890  * return table with message headers
T 891  */
a8a72e 892 function rcmail_message_headers($attrib, $headers=null)
6f4b50 893 {
b6da0b 894   global $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL;
4e17e6 895   static $sa_attrib;
9800a8 896
4e17e6 897   // keep header table attrib
a8a72e 898   if (is_array($attrib) && !$sa_attrib && !$attrib['valueof'])
4e17e6 899     $sa_attrib = $attrib;
T 900   else if (!is_array($attrib) && is_array($sa_attrib))
901     $attrib = $sa_attrib;
9800a8 902
4e17e6 903   if (!isset($MESSAGE))
T 904     return FALSE;
905
906   // get associative array of headers object
a8a72e 907   if (!$headers) {
TB 908     $headers_obj = $MESSAGE->headers;
909     $headers = get_object_vars($MESSAGE->headers);
910   }
dc67f3 911   else if (is_object($headers)) {
TB 912     $headers_obj = $headers;
913     $headers = get_object_vars($headers_obj);
914   }
a8a72e 915   else {
TB 916     $headers_obj = rcube_message_header::from_array($headers);
917   }
7c60ff 918
4e17e6 919   // show these headers
83370e 920   $standard_headers = array('subject', 'from', 'sender', 'to', 'cc', 'bcc', 'replyto',
a49a00 921     'mail-reply-to', 'mail-followup-to', 'date', 'priority');
faea23 922   $exclude_headers = $attrib['exclude'] ? explode(',', $attrib['exclude']) : array();
cc97ea 923   $output_headers = array();
e5686f 924
cc97ea 925   foreach ($standard_headers as $hkey) {
61512f 926     $ishtml = false;
TB 927
e25a35 928     if ($headers[$hkey])
A 929       $value = $headers[$hkey];
930     else if ($headers['others'][$hkey])
931       $value = $headers['others'][$hkey];
73c702 932     else if (!$attrib['valueof'])
faea23 933       continue;
T 934
935     if (in_array($hkey, $exclude_headers))
4e17e6 936       continue;
T 937
765ecb 938     $header_title = rcube_label(preg_replace('/(^mail-|-)/', '', $hkey));
TB 939
cc97ea 940     if ($hkey == 'date') {
5b1de5 941       if ($PRINT_MODE)
e25a35 942         $header_value = format_date($value, $RCMAIL->config->get('date_long', 'x'));
5b1de5 943       else
e25a35 944         $header_value = format_date($value);
a49a00 945     }
567be6 946     else if ($hkey == 'priority') {
a49a00 947       if ($value) {
T 948         $header_value = html::span('prio' . $value, rcmail_localized_priority($value));
949       }
950       else
951         continue;
cc97ea 952     }
T 953     else if ($hkey == 'replyto') {
61512f 954       if ($headers['replyto'] != $headers['from']) {
765ecb 955         $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 956         $ishtml = true;
TB 957       }
700320 958       else
A 959         continue;
4e17e6 960     }
e25a35 961     else if ($hkey == 'mail-reply-to') {
A 962       if ($headers['mail-replyto'] != $headers['reply-to']
963         && $headers['reply-to'] != $headers['from']
61512f 964       ) {
765ecb 965         $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 966         $ishtml = true;
TB 967       }
e25a35 968       else
A 969         continue;
970     }
83370e 971     else if ($hkey == 'sender') {
AM 972       if ($headers['sender'] != $headers['from']) {
973         $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
974         $ishtml = true;
975       }
976       else
977         continue;
978     }
e25a35 979     else if ($hkey == 'mail-followup-to') {
765ecb 980       $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 981       $ishtml = true;
e25a35 982     }
61512f 983     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc'))) {
765ecb 984       $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
61512f 985       $ishtml = true;
TB 986     }
e25a35 987     else if ($hkey == 'subject' && empty($value))
cc97ea 988       $header_value = rcube_label('nosubject');
T 989     else
1c4f23 990       $header_value = trim(rcube_mime::decode_header($value, $headers['charset']));
9800a8 991
3ee5a7 992     $output_headers[$hkey] = array(
765ecb 993         'title' => $header_title,
61512f 994         'value' => $header_value,
TB 995         'raw' => $value,
996         'html' => $ishtml,
3ee5a7 997     );
cc97ea 998   }
9800a8 999
3ee5a7 1000   $plugin = $RCMAIL->plugins->exec_hook('message_headers_output',
a8a72e 1001     array('output' => $output_headers, 'headers' => $headers_obj, 'exclude' => $exclude_headers));
faea23 1002
T 1003   // single header value is requested
1004   if (!empty($attrib['valueof']))
54be5c 1005     return Q($plugin['output'][$attrib['valueof']]['value'], ($attrib['valueof'] == 'subject' ? 'strict' : 'show'));
9800a8 1006
cc97ea 1007   // compose html table
T 1008   $table = new html_table(array('cols' => 2));
9800a8 1009
cc97ea 1010   foreach ($plugin['output'] as $hkey => $row) {
T 1011     $table->add(array('class' => 'header-title'), Q($row['title']));
61512f 1012     $table->add(array('class' => 'header '.$hkey), $row['html'] ? $row['value'] : Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show')));
cc97ea 1013   }
4e17e6 1014
c6be45 1015   return $table->show($attrib);
T 1016 }
1017
a49a00 1018 /**
T 1019  * Convert Priority header value into a localized string
1020  */
1021 function rcmail_localized_priority($value)
1022 {
1023   $labels_map = array(
1024     '1' => 'highest',
1025     '2' => 'high',
1026     '3' => 'normal',
1027     '4' => 'low',
1028     '5' => 'lowest',
1029   );
297c1a 1030
a49a00 1031   if ($value && $labels_map[$value])
T 1032     return rcube_label($labels_map[$value]);
297c1a 1033
a49a00 1034   return '';
T 1035 }
c6be45 1036
T 1037 /**
1038  * return block to show full message headers
1039  */
1040 function rcmail_message_full_headers($attrib, $headers=NULL)
1041 {
1042   global $OUTPUT;
c08b18 1043
76248c 1044   $html = html::div(array('id' => "all-headers", 'class' => "all", 'style' => 'display:none'), html::div(array('id' => 'headers-source'), ''));
725704 1045   $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)", 'title' => rcube_label('togglefullheaders')), '');
c08b18 1046
e5686f 1047   $OUTPUT->add_gui_object('all_headers_row', 'all-headers');
A 1048   $OUTPUT->add_gui_object('all_headers_box', 'headers-source');
c08b18 1049
c23aad 1050   return html::div($attrib, $html);
c6be45 1051 }
4e17e6 1052
T 1053
45f56c 1054 /**
21605c 1055  * Handler for the 'messagebody' GUI object
45f56c 1056  *
21605c 1057  * @param array Named parameters
T 1058  * @return string HTML content showing the message body
45f56c 1059  */
4e17e6 1060 function rcmail_message_body($attrib)
e0960f 1061 {
b6da0b 1062   global $CONFIG, $OUTPUT, $MESSAGE, $RCMAIL, $REMOTE_OBJECTS;
5f8686 1063
8fa58e 1064   if (!is_array($MESSAGE->parts) && empty($MESSAGE->body))
4e17e6 1065     return '';
9800a8 1066
4e17e6 1067   if (!$attrib['id'])
T 1068     $attrib['id'] = 'rcmailMsgBody';
1069
8fa58e 1070   $safe_mode = $MESSAGE->is_safe || intval($_GET['_safe']);
21605c 1071   $out = '';
9800a8 1072
4e17e6 1073   $header_attrib = array();
T 1074   foreach ($attrib as $attr => $value)
1075     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
1076       $header_attrib[$regs[1]] = $value;
1077
e0960f 1078   if (!empty($MESSAGE->parts)) {
A 1079     foreach ($MESSAGE->parts as $i => $part) {
a8a72e 1080       if ($part->type == 'headers') {
TB 1081         $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
1082       }
0c2596 1083       else if ($part->type == 'content') {
fddb9f 1084         // unsupported (e.g. encrypted)
0c2596 1085         if ($part->realtype) {
fddb9f 1086           if ($part->realtype == 'multipart/encrypted' || $part->realtype == 'application/pkcs7-mime') {
0c2596 1087             $out .= html::span('part-notice', rcube_label('encryptedmessage'));
A 1088           }
1089           continue;
1090         }
1091         else if (!$part->size) {
1092           continue;
1093         }
e0960f 1094         // Check if we have enough memory to handle the message in it
A 1095         // #1487424: we need up to 10x more memory than the body
0c2596 1096         else if (!rcmail_mem_check($part->size * 10)) {
e0960f 1097           $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
A 1098             . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part='.$part->mime_id
c321a9 1099               .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download')));
e0960f 1100           continue;
A 1101         }
1102
8d4bcd 1103         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
8fa58e 1104           $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
a0109c 1105
8d4bcd 1106         // fetch part if not available
T 1107         if (!isset($part->body))
8fa58e 1108           $part->body = $MESSAGE->get_part_content($part->mime_id);
a0109c 1109
a8a72e 1110         // extract headers from message/rfc822 parts
TB 1111         if ($part->mimetype == 'message/rfc822') {
dc67f3 1112           $msgpart = rcube_mime::parse_message($part->body);
TB 1113           if (!empty($msgpart->headers)) {
1114             $part = $msgpart;
1115             $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
a8a72e 1116           }
TB 1117         }
1118
64e3e8 1119         // message is cached but not exists (#1485443), or other error
A 1120         if ($part->body === false) {
1121           rcmail_message_error($MESSAGE->uid);
1122         }
1123
3c3433 1124         $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
A 1125           'part' => $part, 'prefix' => ''));
6b6f2e 1126
21e724 1127         $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
5f8686 1128
7b808b 1129         if ($part->ctype_secondary == 'html') {
2b017e 1130           $body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs, $safe_mode);
7b808b 1131           $div_attr = array('class' => 'message-htmlpart');
fb995a 1132           $style = array();
A 1133
cb3dfd 1134           if (!empty($attrs)) {
A 1135             foreach ($attrs as $a_idx => $a_val)
1136               $style[] = $a_idx . ': ' . $a_val;
1137             if (!empty($style))
1138               $div_attr['style'] = implode('; ', $style);
1139           }
7b808b 1140
3c3433 1141           $out .= html::div($div_attr, $plugin['prefix'] . $body);
7b808b 1142         }
a2f2c5 1143         else
3c3433 1144           $out .= html::div('message-part', $plugin['prefix'] . $body);
4e17e6 1145       }
T 1146     }
e0960f 1147   }
3c3433 1148   else {
e0960f 1149     // Check if we have enough memory to handle the message in it
A 1150     // #1487424: we need up to 10x more memory than the body
1151     if (!rcmail_mem_check(strlen($MESSAGE->body) * 10)) {
1152       $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
1153         . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part=0'
c321a9 1154           .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download')));
3c3433 1155     }
e0960f 1156     else {
A 1157       $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
1158         'part' => $MESSAGE, 'prefix' => ''));
1159
1160       $out .= html::div('message-part', $plugin['prefix'] . html::tag('pre', array(),
1161         rcmail_plain_body(Q($MESSAGE->body, 'strict', false))));
1162     }
1163   }
4e17e6 1164
T 1165   // list images after mail body
031491 1166   if ($RCMAIL->config->get('inline_images', true) && !empty($MESSAGE->attachments)) {
TB 1167     $thumbnail_size = $RCMAIL->config->get('image_thumbnail_size', 240);
d20e96 1168     $client_mimetypes = (array)$RCMAIL->config->get('client_mimetypes');
031491 1169
8fa58e 1170     foreach ($MESSAGE->attachments as $attach_prop) {
18ca0b 1171       // skip inline images
A 1172       if ($attach_prop->content_id && $attach_prop->disposition == 'inline') {
1173         continue;
1174       }
1175
47d06e 1176       // Content-Type: image/*...
b81e7e 1177       if ($mimetype = rcmail_part_image_type($attach_prop)) {
031491 1178         // display thumbnails
TB 1179         if ($thumbnail_size) {
1180           $show_link = array(
1181             'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false),
1182             'onclick' => sprintf(
1183               'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)',
1184               JS_OBJECT_NAME,
1185               $attach_prop->mime_id,
b81e7e 1186               $mimetype)
031491 1187           );
TB 1188           $out .= html::p('image-attachment',
d20e96 1189              html::a($show_link + array('class' => 'image-link', 'style' => sprintf('width:%dpx', $thumbnail_size)),
031491 1190                html::img(array(
TB 1191                 'class' => 'image-thumbnail',
706d3f 1192                 'src'   => $MESSAGE->get_part_url($attach_prop->mime_id, 'image') . '&_thumb=1',
031491 1193                 'title' => $attach_prop->filename,
TB 1194                 'alt'   => $attach_prop->filename,
1195                 'style' => sprintf('max-width:%dpx; max-height:%dpx', $thumbnail_size, $thumbnail_size),
1196               ))
1197             ) .
1198             html::span('image-filename', Q($attach_prop->filename)) .
a9e136 1199             html::span('image-filesize', Q($RCMAIL->message_part_size($attach_prop))) .
031491 1200             html::span('attachment-links',
b81e7e 1201               (in_array($mimetype, $client_mimetypes) ? html::a($show_link, rcube_label('showattachment')) . '&nbsp;' : '') .
031491 1202               html::a($show_link['href'] . '&_download=1', rcube_label('download'))
TB 1203             ) .
1204             html::br(array('style' => 'clear:both'))
1205           );
1206         }
1207         else {
1208           $out .= html::tag('fieldset', 'image-attachment',
1209             html::tag('legend', 'image-filename', Q($attach_prop->filename)) .
1210             html::p(array('align' => "center"),
1211               html::img(array(
706d3f 1212                 'src'   => $MESSAGE->get_part_url($attach_prop->mime_id, 'image'),
031491 1213                 'title' => $attach_prop->filename,
TB 1214                 'alt'   => $attach_prop->filename,
1215               )))
1216           );
1217         }
e0960f 1218       }
4e17e6 1219     }
8fa58e 1220   }
9800a8 1221
4e17e6 1222   // tell client that there are blocked remote objects
T 1223   if ($REMOTE_OBJECTS && !$safe_mode)
f11541 1224     $OUTPUT->set_env('blockedobjects', true);
4e17e6 1225
21605c 1226   return html::div($attrib, $out);
e0960f 1227 }
4e17e6 1228
19cc5b 1229 function rcmail_part_image_type($part)
AM 1230 {
1231   $rcmail = rcmail::get_instance();
1232
1233   // Skip TIFF images if browser doesn't support this format...
1234   $tiff_support = !empty($_SESSION['browser_caps']) && !empty($_SESSION['browser_caps']['tif']);
1235   // until we can convert them to JPEG
1236   $tiff_support = $tiff_support || $rcmail->config->get('im_convert_path');
1237
1238   // Content-type regexp
1239   $mime_regex = $tiff_support ? '/^image\//i' : '/^image\/(?!tif)/i';
1240
1241   // Content-Type: image/*...
1242   if (preg_match($mime_regex, $part->mimetype)) {
090c49 1243     return rcmail_fix_mimetype($part->mimetype);
19cc5b 1244   }
AM 1245
1246   // Many clients use application/octet-stream, we'll detect mimetype
1247   // by checking filename extension
1248
1249   // Supported image filename extensions to image type map
1250   $types = array(
1251     'jpg'  => 'image/jpeg',
1252     'jpeg' => 'image/jpeg',
1253     'png'  => 'image/png',
1254     'gif'  => 'image/gif',
1255     'bmp'  => 'image/bmp',
1256   );
1257   if ($tiff_support) {
1258     $types['tif']  = 'image/tiff';
1259     $types['tiff'] = 'image/tiff';
1260   }
1261
1262   if ($part->filename
1263     && preg_match('/^application\/octet-stream$/i', $part->mimetype)
9ce239 1264     && preg_match('/\.([^.]+)$/i', $part->filename, $m)
19cc5b 1265     && ($extension = strtolower($m[1]))
AM 1266     && isset($types[$extension])
1267   ) {
1268     return $types[$extension];
1269   }
aa055c 1270 }
4e17e6 1271
f5d62f 1272
45f56c 1273 /**
T 1274  * modify a HTML message that it can be displayed inside a HTML page
1275  */
2b017e 1276 function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null, $allow_remote=false)
cb3dfd 1277 {
4e17e6 1278   $last_style_pos = 0;
cb3dfd 1279   $cont_id = $container_id.($body_id ? ' div.'.$body_id : '');
9800a8 1280
4e17e6 1281   // find STYLE tags
e4a4ca 1282   while (($pos = stripos($body, '<style', $last_style_pos)) && ($pos2 = stripos($body, '</style>', $pos)))
cb3dfd 1283   {
aede1f 1284     $pos = strpos($body, '>', $pos) + 1;
AM 1285     $len = $pos2 - $pos;
ea206d 1286
4e17e6 1287     // replace all css definitions with #container [def]
aede1f 1288     $styles = substr($body, $pos, $len);
AM 1289     $styles = rcmail_mod_css_styles($styles, $cont_id, $allow_remote);
ea206d 1290
aede1f 1291     $body = substr_replace($body, $styles, $pos, $len);
AM 1292     $last_style_pos = $pos2 + strlen($styles) - $len;
cb3dfd 1293   }
4e17e6 1294
fe79b1 1295   // modify HTML links to open a new window if clicked
115263 1296   $GLOBALS['rcmail_html_container_id'] = $container_id;
fb53c8 1297   $body = preg_replace_callback('/<(a|link|area)\s+([^>]+)>/Ui', 'rcmail_alter_html_link', $body);
115263 1298   unset($GLOBALS['rcmail_html_container_id']);
4e17e6 1299
cb3dfd 1300   $body = preg_replace(array(
b488c1 1301       // add comments arround html and other tags
d7a411 1302       '/(<!DOCTYPE[^>]*>)/i',
A 1303       '/(<\?xml[^>]*>)/i',
06895c 1304       '/(<\/?html[^>]*>)/i',
T 1305       '/(<\/?head[^>]*>)/i',
1306       '/(<title[^>]*>.*<\/title>)/Ui',
b488c1 1307       '/(<\/?meta[^>]*>)/i',
A 1308       // quote <? of php and xml files that are specified as text/html
1309       '/<\?/',
1310       '/\?>/',
1311       // replace <body> with <div>
1312       '/<body([^>]*)>/i',
1313       '/<\/body>/i',
1314       ),
1315     array(
1316       '<!--\\1-->',
1317       '<!--\\1-->',
1318       '<!--\\1-->',
1319       '<!--\\1-->',
1320       '<!--\\1-->',
1321       '<!--\\1-->',
1322       '&lt;?',
1323       '?&gt;',
7b808b 1324       '<div class="'.$body_id.'"\\1>',
b488c1 1325       '</div>',
A 1326       ),
06895c 1327     $body);
a0109c 1328
fb995a 1329   $attributes = array();
A 1330
1331   // Handle body attributes that doesn't play nicely with div elements
ceb708 1332   $regexp = '/<div class="' . preg_quote($body_id, '/') . '"([^>]*)/';
A 1333   if (preg_match($regexp, $body, $m)) {
fb995a 1334     $attrs = $m[0];
A 1335     // Get bgcolor, we'll set it as background-color of the message container
ceb708 1336     if ($m[1] && preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/', $attrs, $mb)) {
cb3dfd 1337       $attributes['background-color'] = $mb[1];
fb995a 1338       $attrs = preg_replace('/bgcolor=["\']*([a-z0-9#]+)["\']*/', '', $attrs);
7b808b 1339     }
fb995a 1340     // Get background, we'll set it as background-image of the message container
ceb708 1341     if ($m[1] && preg_match('/background=["\']*([^"\'>\s]+)["\']*/', $attrs, $mb)) {
cb3dfd 1342       $attributes['background-image'] = 'url('.$mb[1].')';
fb995a 1343       $attrs = preg_replace('/background=["\']*([^"\'>\s]+)["\']*/', '', $attrs);
A 1344     }
ceb708 1345     if (!empty($attributes)) {
A 1346       $body = preg_replace($regexp, rtrim($attrs), $body, 1);
1347     }
cb3dfd 1348
A 1349     // handle body styles related to background image
1350     if ($attributes['background-image']) {
1351       // get body style
1352       if (preg_match('/#'.preg_quote($cont_id, '/').'\s+\{([^}]+)}/i', $body, $m)) {
1353         // get background related style
1354         if (preg_match_all('/(background-position|background-repeat)\s*:\s*([^;]+);/i', $m[1], $ma, PREG_SET_ORDER)) {
1355           foreach ($ma as $style)
1356             $attributes[$style[1]] = $style[2];
1357         }
1358       }
1359     }
7b808b 1360   }
b488c1 1361   // make sure there's 'rcmBody' div, we need it for proper css modification
A 1362   // its name is hardcoded in rcmail_message_body() also
ceb708 1363   else {
cb3dfd 1364     $body = '<div class="' . $body_id . '">' . $body . '</div>';
ceb708 1365   }
86958f 1366
cb3dfd 1367   return $body;
A 1368 }
4e17e6 1369
T 1370
45f56c 1371 /**
T 1372  * parse link attributes and set correct target
1373  */
115263 1374 function rcmail_alter_html_link($matches)
e5af2f 1375 {
5c1dfb 1376   global $RCMAIL;
A 1377
1378   // Support unicode/punycode in top-level domain part
1379   $EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))';
9800a8 1380
115263 1381   $tag = $matches[1];
T 1382   $attrib = parse_attrib_string($matches[2]);
e5af2f 1383   $end = '>';
84f5b7 1384
29c542 1385   // Remove non-printable characters in URL (#1487805)
c8c53f 1386   if ($attrib['href'])
T 1387     $attrib['href'] = preg_replace('/[\x00-\x1F]/', '', $attrib['href']);
29c542 1388
e5af2f 1389   if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) {
3e0e91 1390     $tempurl = 'tmp-' . md5($attrib['href']) . '.css';
T 1391     $_SESSION['modcssurls'][$tempurl] = $attrib['href'];
1392     $attrib['href'] = $RCMAIL->url(array('task' => 'utils', 'action' => 'modcss', 'u' => $tempurl, 'c' => $GLOBALS['rcmail_html_container_id']));
e5af2f 1393     $end = ' />';
T 1394   }
5c1dfb 1395   else if (preg_match('/^mailto:'.$EMAIL_PATTERN.'(\?[^"\'>]+)?/i', $attrib['href'], $mailto)) {
115263 1396     $attrib['href'] = $mailto[0];
97bd2c 1397     $attrib['onclick'] = sprintf(
T 1398       "return %s.command('compose','%s',this)",
1399       JS_OBJECT_NAME,
5c1dfb 1400       JQ($mailto[1].$mailto[3]));
4e17e6 1401   }
c8c53f 1402   else if (empty($attrib['href']) && !$attrib['name']) {
T 1403     $attrib['href'] = './#NOP';
1404     $attrib['onclick'] = 'return false';
1405   }
e5af2f 1406   else if (!empty($attrib['href']) && $attrib['href'][0] != '#') {
T 1407     $attrib['target'] = '_blank';
1408   }
1409
fb53c8 1410   // allowed attributes for a|link|area tags
AM 1411   $allow = array('href','name','target','onclick','id','class','style','title',
1412     'rel','type','media','alt','coords','nohref','hreflang','shape');
1413
1414   return "<$tag" . html::attrib_string($attrib, $allow) . $end;
e5af2f 1415 }
4e17e6 1416
T 1417
45f56c 1418 /**
T 1419  * decode address string and re-format it as HTML links
1420  */
765ecb 1421 function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $default_charset=null, $title=null)
8e44f4 1422 {
b6da0b 1423   global $RCMAIL, $PRINT_MODE, $CONFIG;
1088d6 1424
1c4f23 1425   $a_parts = rcube_mime::decode_address_list($input, null, true, $default_charset);
4e17e6 1426
T 1427   if (!sizeof($a_parts))
1428     return $input;
1429
1430   $c = count($a_parts);
1431   $j = 0;
1432   $out = '';
765ecb 1433   $allvalues = array();
4e17e6 1434
ecb51c 1435   if ($addicon && !isset($_SESSION['writeable_abook'])) {
T 1436     $_SESSION['writeable_abook'] = $RCMAIL->get_address_sources(true) ? true : false;
55243b 1437   }
b579f4 1438
8e44f4 1439   foreach ($a_parts as $part) {
4e17e6 1440     $j++;
e99991 1441     $name   = $part['name'];
A 1442     $mailto = $part['mailto'];
1443     $string = $part['string'];
bde854 1444     $valid  = check_email($mailto, false);
e99991 1445
097c54 1446     // phishing email prevention (#1488981), e.g. "valid@email.addr <phishing@email.addr>"
bde854 1447     if ($name && $valid && $name != $mailto && strpos($name, '@')) {
097c54 1448       $name = '';
AM 1449     }
1450
e99991 1451     // IDNA ASCII to Unicode
A 1452     if ($name == $mailto)
e8d5bd 1453       $name = rcube_idn_to_utf8($name);
e99991 1454     if ($string == $mailto)
e8d5bd 1455       $string = rcube_idn_to_utf8($string);
A 1456     $mailto = rcube_idn_to_utf8($mailto);
e99991 1457
8e44f4 1458     if ($PRINT_MODE) {
297c1a 1459       $out .= ($out ? ', ' : '') . sprintf('%s &lt;%s&gt;', Q($name), $mailto);
AM 1460       // for printing we display all addresses
1461       continue;
8e44f4 1462     }
bde854 1463     else if ($valid) {
8e44f4 1464       if ($linked) {
768091 1465         $address = html::a(array(
e99991 1466             'href' => 'mailto:'.$mailto,
A 1467             'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)),
1468             'title' => $mailto,
8e44f4 1469             'class' => "rcmContactAddress",
T 1470           ),
e99991 1471         Q($name ? $name : $mailto));
4e17e6 1472       }
8e44f4 1473       else {
768091 1474         $address = html::span(array('title' => $mailto, 'class' => "rcmContactAddress"),
e99991 1475           Q($name ? $name : $mailto));
8e44f4 1476       }
T 1477
ecb51c 1478       if ($addicon && $_SESSION['writeable_abook']) {
61512f 1479         $address .= html::a(array(
8e44f4 1480             'href' => "#add",
c02080 1481             'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, JQ($string)),
8e44f4 1482             'title' => rcube_label('addtoaddressbook'),
67e0c9 1483             'class' => 'rcmaddcontact',
8e44f4 1484           ),
T 1485           html::img(array(
1486             'src' => $CONFIG['skin_path'] . $addicon,
1487             'alt' => "Add contact",
61512f 1488           )));
8e44f4 1489       }
T 1490     }
1491     else {
61512f 1492       $address = '';
e99991 1493       if ($name)
61512f 1494         $address .= Q($name);
e99991 1495       if ($mailto)
bde854 1496         $address = trim($address . ' ' . Q($name ? sprintf('<%s>', $mailto) : $mailto));
8e44f4 1497     }
9800a8 1498
765ecb 1499     $address = html::span('adr', $address);
TB 1500     $allvalues[] = $address;
9800a8 1501
765ecb 1502     if (!$moreadrs)
TB 1503       $out .= ($out ? ', ' : '') . $address;
1504
1505     if ($max && $j == $max && $c > $j) {
1506       if ($linked) {
1507         $moreadrs = $c - $j;
1508       }
1509       else {
1510         $out .= '...';
1511         break;
1512       }
4e17e6 1513     }
8e44f4 1514   }
9800a8 1515
765ecb 1516   if ($moreadrs) {
TB 1517       $out .= ' ' . html::a(array(
1518           'href' => '#more',
1519           'class' => 'morelink',
1520           'onclick' => sprintf("return %s.show_popup_dialog('%s','%s')",
1521             JS_OBJECT_NAME,
1522             JQ(join(', ', $allvalues)),
1523             JQ($title))
1524         ),
1525         Q(rcube_label(array('name' => 'andnmore', 'vars' => array('nr' => $moreadrs)))));
1526   }
1527
4e17e6 1528   return $out;
8e44f4 1529 }
4e17e6 1530
T 1531
ccd63c 1532 /**
T 1533  * Wrap text to a given number of characters per line
6b6f2e 1534  * but respect the mail quotation of replies messages (>).
20df06 1535  * Finally add another quotation level by prepending the lines
6b6f2e 1536  * with >
ccd63c 1537  *
T 1538  * @param string Text to wrap
20df06 1539  * @param int    The line width
ccd63c 1540  * @return string The wrapped text
T 1541  */
6b6f2e 1542 function rcmail_wrap_and_quote($text, $length = 72)
ccd63c 1543 {
T 1544   // Rebuild the message body with a maximum of $max chars, while keeping quoted message.
248d78 1545   $max = max(75, $length + 8);
ccd63c 1546   $lines = preg_split('/\r?\n/', trim($text));
T 1547   $out = '';
1548
1549   foreach ($lines as $line) {
6b6f2e 1550     // don't wrap already quoted lines
T 1551     if ($line[0] == '>')
1552       $line = '>' . rtrim($line);
1553     else if (mb_strlen($line) > $max) {
1554       $newline = '';
20df06 1555       foreach (explode("\n", rc_wordwrap($line, $length - 2)) as $l) {
6b6f2e 1556         if (strlen($l))
T 1557           $newline .= '> ' . $l . "\n";
1558         else
1559           $newline .= ">\n";
ccd63c 1560       }
6b6f2e 1561       $line = rtrim($newline);
ccd63c 1562     }
6b6f2e 1563     else
T 1564       $line = '> ' . $line;
ccd63c 1565
T 1566     // Append the line
1567     $out .= $line . "\n";
1568   }
9800a8 1569
4340d5 1570   return rtrim($out, "\n");
ccd63c 1571 }
T 1572
1573
bc404f 1574 function rcmail_draftinfo_encode($p)
T 1575 {
1576   $parts = array();
1577   foreach ($p as $key => $val)
1578     $parts[] = $key . '=' . ($key == 'folder' ? base64_encode($val) : $val);
9800a8 1579
bc404f 1580   return join('; ', $parts);
T 1581 }
1582
1583
1584 function rcmail_draftinfo_decode($str)
1585 {
1586   $info = array();
1587   foreach (preg_split('/;\s+/', $str) as $part) {
1588     list($key, $val) = explode('=', $part, 2);
1589     if ($key == 'folder')
1590       $val = base64_decode($val);
1591     $info[$key] = $val;
1592   }
9800a8 1593
bc404f 1594   return $info;
T 1595 }
1596
1597
c7dcb3 1598 function rcmail_message_part_controls($attrib)
e99991 1599 {
8749e9 1600   global $MESSAGE, $RCMAIL;
9800a8 1601
d5342a 1602   $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
8fa58e 1603   if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part])
4e17e6 1604     return '';
9800a8 1605
8749e9 1606   $part  = $MESSAGE->mime_parts[$part];
8fa58e 1607   $table = new html_table(array('cols' => 3));
9800a8 1608
be72fb 1609   $filename = rcmail_attachment_name($part);
0c2596 1610
A 1611   if (!empty($filename)) {
8fa58e 1612     $table->add('title', Q(rcube_label('filename')));
0c2596 1613     $table->add('header', Q($filename));
c7dcb3 1614     $table->add('download-link', html::a(array('href' => './?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING'])), Q(rcube_label('download'))));
8fa58e 1615   }
9800a8 1616
8749e9 1617   $table->add('title', Q(rcube_label('filesize')));
AM 1618   $table->add('header', Q($RCMAIL->message_part_size($part)));
9800a8 1619
8fa58e 1620   return $table->show($attrib);
e99991 1621 }
4e17e6 1622
T 1623
1624 function rcmail_message_part_frame($attrib)
e99991 1625 {
4e17e6 1626   global $MESSAGE;
9800a8 1627
8fa58e 1628   $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
4e17e6 1629   $ctype_primary = strtolower($part->ctype_primary);
T 1630
c14b33 1631   $attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary=='text' ? '_embed=' : '_preload='), $_SERVER['QUERY_STRING']);
4e17e6 1632
95fcc3 1633   return html::iframe($attrib);
e99991 1634 }
4e17e6 1635
T 1636
45f56c 1637 /**
T 1638  * clear message composing settings
1639  */
4591de 1640 function rcmail_compose_cleanup($id)
e99991 1641 {
06dc98 1642   if (!isset($_SESSION['compose_data_'.$id]))
4e17e6 1643     return;
70d4b9 1644
929a50 1645   $rcmail = rcmail::get_instance();
4591de 1646   $rcmail->plugins->exec_hook('attachments_cleanup', array('group' => $id));
06dc98 1647   $rcmail->session->remove('compose_data_'.$id);
e99991 1648 }
9800a8 1649
fba1f5 1650
T 1651 /**
a99968 1652  * Send the MDN response
A 1653  *
1654  * @param mixed $message    Original message object (rcube_message) or UID
1655  * @param array $smtp_error SMTP error array (reference)
1656  *
1657  * @return boolean Send status
1658  */
1659 function rcmail_send_mdn($message, &$smtp_error)
0ea884 1660 {
b6da0b 1661   global $RCMAIL;
8fa58e 1662
8c124b 1663   if (!is_object($message) || !is_a($message, 'rcube_message'))
a99968 1664     $message = new rcube_message($message);
9800a8 1665
609d39 1666   if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) &&
c321a9 1667     ($RCMAIL->storage->check_permflag('MDNSENT') || $RCMAIL->storage->check_permflag('*')))
0ea884 1668   {
a0e3dc 1669     $identity  = rcmail_identity_select($message);
AM 1670     $sender    = format_email_recipient($identity['email'], $identity['name']);
1c4f23 1671     $recipient = array_shift(rcube_mime::decode_address_list(
A 1672       $message->headers->mdn_to, 1, true, $message->headers->charset));
a0e3dc 1673     $mailto    = $recipient['mailto'];
0ea884 1674
ac8edb 1675     $compose = new Mail_mime("\r\n");
91790e 1676
A 1677     $compose->setParam('text_encoding', 'quoted-printable');
1678     $compose->setParam('html_encoding', 'quoted-printable');
1679     $compose->setParam('head_encoding', 'quoted-printable');
1680     $compose->setParam('head_charset', RCMAIL_CHARSET);
1681     $compose->setParam('html_charset', RCMAIL_CHARSET);
1682     $compose->setParam('text_charset', RCMAIL_CHARSET);
9800a8 1683
0ea884 1684     // compose headers array
T 1685     $headers = array(
2bf3cc 1686       'Date' => rcmail_user_date(),
0ea884 1687       'From' => $sender,
8fa58e 1688       'To'   => $message->headers->mdn_to,
T 1689       'Subject' => rcube_label('receiptread') . ': ' . $message->subject,
1d8cbc 1690       'Message-ID' => rcmail_gen_message_id(),
0ea884 1691       'X-Sender' => $identity['email'],
ea50e7 1692       'References' => trim($message->headers->references . ' ' . $message->headers->messageID),
0ea884 1693     );
9800a8 1694
83a763 1695     if ($agent = $RCMAIL->config->get('useragent'))
T 1696       $headers['User-Agent'] = $agent;
0ea884 1697
232535 1698     if ($RCMAIL->config->get('mdn_use_from'))
AM 1699       $options['mdn_use_from'] = true;
1700
0ea884 1701     $body = rcube_label("yourmessage") . "\r\n\r\n" .
1adc70 1702       "\t" . rcube_label("to") . ': ' . rcube_mime::decode_mime_string($message->headers->to, $message->headers->charset) . "\r\n" .
8fa58e 1703       "\t" . rcube_label("subject") . ': ' . $message->subject . "\r\n" .
83a763 1704       "\t" . rcube_label("sent") . ': ' . format_date($message->headers->date, $RCMAIL->config->get('date_long')) . "\r\n" .
0ea884 1705       "\r\n" . rcube_label("receiptnote") . "\r\n";
9800a8 1706
e019f2 1707     $ua = $RCMAIL->config->get('useragent', "Roundcube Webmail (Version ".RCMAIL_VERSION.")");
0ea884 1708     $report = "Reporting-UA: $ua\r\n";
9800a8 1709
8fa58e 1710     if ($message->headers->to)
T 1711         $report .= "Original-Recipient: {$message->headers->to}\r\n";
9800a8 1712
0ea884 1713     $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
8fa58e 1714                "Original-Message-ID: {$message->headers->messageID}\r\n" .
0ea884 1715                "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
9800a8 1716
8fa58e 1717     $compose->headers($headers);
6b0113 1718     $compose->setContentType('multipart/report', array('report-type'=> 'disposition-notification'));
7145e0 1719     $compose->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
0ea884 1720     $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
T 1721
232535 1722     $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file, $options);
0ea884 1723
T 1724     if ($sent)
1725     {
c321a9 1726       $RCMAIL->storage->set_flag($message->uid, 'MDNSENT');
0ea884 1727       return true;
T 1728     }
1729   }
9800a8 1730
0ea884 1731   return false;
T 1732 }
1733
a0e3dc 1734 /**
AM 1735  * Detect recipient identity from specified message
1736  */
1737 function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'reply')
1738 {
1739     $a_recipients = array();
1740     $a_names      = array();
1741
1742     if ($identities === null) {
1743         $identities = rcmail::get_instance()->user->list_identities(null, true);
1744     }
1745
1746     // extract all recipients of the reply-message
1747     if (is_object($MESSAGE->headers) && in_array($compose_mode, array('reply', 'forward'))) {
1748         $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset);
1749         foreach ($a_to as $addr) {
1750             if (!empty($addr['mailto'])) {
1751                 $a_recipients[] = format_email($addr['mailto']);
1752                 $a_names[]      = $addr['name'];
1753             }
1754         }
1755
1756         if (!empty($MESSAGE->headers->cc)) {
1757             $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset);
1758             foreach ($a_cc as $addr) {
1759                 if (!empty($addr['mailto'])) {
1760                     $a_recipients[] = format_email($addr['mailto']);
1761                     $a_names[]      = $addr['name'];
1762                 }
1763             }
1764         }
1765     }
1766
1767     $from_idx         = null;
1768     $found_idx        = null;
1769     $default_identity = 0; // default identity is always first on the list
1770
1771     // Select identity
1772     foreach ($identities as $idx => $ident) {
1773         // use From header
1774         if (in_array($compose_mode, array('draft', 'edit'))) {
1775             if ($MESSAGE->headers->from == $ident['ident']) {
1776                 $from_idx = $idx;
1777                 break;
1778             }
1779         }
1780         // reply to yourself
1781         else if ($compose_mode == 'reply' && $MESSAGE->headers->from == $ident['ident']) {
1782             $from_idx = $idx;
1783             break;
1784         }
1785         // use replied message recipients
1786         else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) {
1787             if ($found_idx === null) {
1788                 $found_idx = $idx;
1789             }
1790             // match identity name
1791             if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) {
1792                 $from_idx = $idx;
1793                 break;
1794             }
1795         }
1796     }
1797
1798     // If matching by name+address doesn't found any matches, get first found address (identity)
1799     if ($from_idx === null) {
1800         $from_idx = $found_idx;
1801     }
1802
1803     // Try Return-Path
1804     if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) {
1805         foreach ($identities as $idx => $ident) {
f5fac8 1806             $ident = str_replace('@', '=', $ident['email_ascii']) . '@';
AM 1807             foreach ((array)$return_path as $path) {
1808                 if (strpos($path, $ident) !== false) {
1809                     $from_idx = $idx;
1810                     break 2;
1811                 }
a0e3dc 1812             }
AM 1813         }
1814     }
1815
1816     // Fallback using Delivered-To
1817     if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) {
1818         foreach ($identities as $idx => $ident) {
1819             if (in_array($ident['email_ascii'], (array)$delivered_to)) {
1820                 $from_idx = $idx;
1821                 break;
1822             }
1823         }
1824     }
1825
1826     // Fallback using Envelope-To
1827     if ($from_idx === null && ($envelope_to = $MESSAGE->headers->others['envelope-to'])) {
1828         foreach ($identities as $idx => $ident) {
1829             if (in_array($ident['email_ascii'], (array)$envelope_to)) {
1830                 $from_idx = $idx;
1831                 break;
1832             }
1833         }
1834     }
1835
1836     return $identities[$from_idx !== null ? $from_idx : $default_identity];
1837 }
2bf3cc 1838
e0bd70 1839 // Fixes some content-type names
A 1840 function rcmail_fix_mimetype($name)
1841 {
1842   // Some versions of Outlook create garbage Content-Type:
1843   // application/pdf.A520491B_3BF7_494D_8855_7FAC2C6C0608
1844   if (preg_match('/^application\/pdf.+/', $name))
1845     $name = 'application/pdf';
05b5f9 1846   // treat image/pjpeg (image/pjpg, image/jpg) as image/jpeg (#1489097)
090c49 1847   else if (preg_match('/^image\/p?jpe?g$/', $name))
TB 1848     $name = 'image/jpeg';
1849
e0bd70 1850   return $name;
A 1851 }
2bf3cc 1852
be72fb 1853 // return attachment filename, handle empty filename case
830fd2 1854 function rcmail_attachment_name($attachment, $display = false)
be72fb 1855 {
AM 1856     $filename = $attachment->filename;
1857
1858     if ($filename === null || $filename === '') {
1859         if ($attachment->mimetype == 'text/html') {
1860             $filename = rcube_label('htmlmessage');
1861         }
1862         else {
fd777a 1863             $ext      = (array) rcube_mime::get_mime_extensions($attachment->mimetype);
be72fb 1864             $ext      = array_shift($ext);
AM 1865             $filename = rcube_label('messagepart') . ' ' . $attachment->mime_id;
1866             if ($ext) {
1867                 $filename .= '.' . $ext;
1868             }
1869         }
1870     }
1871
1872     $filename = preg_replace('[\r\n]', '', $filename);
1873
830fd2 1874     // Display smart names for some known mimetypes
AM 1875     if ($display) {
1876         if (preg_match('/application\/(pgp|pkcs7)-signature/i', $attachment->mimetype)) {
1877             $filename = rcube_label('digitalsig');
1878         }
1879     }
1880
be72fb 1881     return $filename;
AM 1882 }
1883
e538b3 1884 function rcmail_search_filter($attrib)
A 1885 {
119cd1 1886   global $OUTPUT, $CONFIG;
e538b3 1887
A 1888   if (!strlen($attrib['id']))
1889     $attrib['id'] = 'rcmlistfilter';
1890
1891   $attrib['onchange'] = JS_OBJECT_NAME.'.filter_mailbox(this.value)';
9800a8 1892
e538b3 1893   /*
A 1894     RFC3501 (6.4.4): 'ALL', 'RECENT', 
1895     'ANSWERED', 'DELETED', 'FLAGGED', 'SEEN',
1896     'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNSEEN',
1897     'NEW', // = (RECENT UNSEEN)
1898     'OLD' // = NOT RECENT
1899   */
1900
1901   $select_filter = new html_select($attrib);
1902   $select_filter->add(rcube_label('all'), 'ALL');
1903   $select_filter->add(rcube_label('unread'), 'UNSEEN');
1904   $select_filter->add(rcube_label('flagged'), 'FLAGGED');
1905   $select_filter->add(rcube_label('unanswered'), 'UNANSWERED');
c8f35a 1906   if (!$CONFIG['skip_deleted']) {
119cd1 1907     $select_filter->add(rcube_label('deleted'), 'DELETED');
c8f35a 1908     $select_filter->add(rcube_label('undeleted'), 'UNDELETED');
AM 1909   }
4b21d2 1910   $select_filter->add(rcube_label('priority').': '.rcube_label('highest'), 'HEADER X-PRIORITY 1');
A 1911   $select_filter->add(rcube_label('priority').': '.rcube_label('high'), 'HEADER X-PRIORITY 2');
1912   $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');
1913   $select_filter->add(rcube_label('priority').': '.rcube_label('low'), 'HEADER X-PRIORITY 4');
1914   $select_filter->add(rcube_label('priority').': '.rcube_label('lowest'), 'HEADER X-PRIORITY 5');
e538b3 1915
A 1916   $out = $select_filter->show($_SESSION['search_filter']);
1917
1918   $OUTPUT->add_gui_object('search_filter', $attrib['id']);
1919
9800a8 1920   return $out;
e538b3 1921 }
A 1922
64e3e8 1923 function rcmail_message_error($uid=null)
A 1924 {
1925   global $RCMAIL;
1926
1927   // Set env variables for messageerror.html template
1928   if ($RCMAIL->action == 'show') {
c321a9 1929     $mbox_name = $RCMAIL->storage->get_folder();
64e3e8 1930     $RCMAIL->output->set_env('mailbox', $mbox_name);
A 1931     $RCMAIL->output->set_env('uid', null);
1932   }
1933   // display error message
1934   $RCMAIL->output->show_message('messageopenerror', 'error');
1935   // ... display message error page
1936   $RCMAIL->output->send('messageerror');
1937 }
9b3fdc 1938
f11541 1939 // register UI objects
T 1940 $OUTPUT->add_handlers(array(
1941   'mailboxlist' => 'rcmail_mailbox_list',
1942   'messages' => 'rcmail_message_list',
1943   'messagecountdisplay' => 'rcmail_messagecount_display',
1944   'quotadisplay' => 'rcmail_quota_display',
ac5d15 1945   'mailboxname' => 'rcmail_mailbox_name_display',
f11541 1946   'messageheaders' => 'rcmail_message_headers',
c6be45 1947   'messagefullheaders' => 'rcmail_message_full_headers',
f11541 1948   'messagebody' => 'rcmail_message_body',
T 1949   'messagecontentframe' => 'rcmail_messagecontent_frame',
1950   'messagepartframe' => 'rcmail_message_part_frame',
1951   'messagepartcontrols' => 'rcmail_message_part_controls',
e538b3 1952   'searchfilter' => 'rcmail_search_filter',
47124c 1953   'searchform' => array($OUTPUT, 'search_form'),
f11541 1954 ));
T 1955
68d2d5 1956 // register action aliases
A 1957 $RCMAIL->register_action_map(array(
77de23 1958     'refresh' => 'check_recent.inc',
68d2d5 1959     'preview' => 'show.inc',
A 1960     'print'   => 'show.inc',
1961     'moveto'  => 'move_del.inc',
1962     'delete'  => 'move_del.inc',
1963     'send'    => 'sendmail.inc',
1964     'expunge' => 'folders.inc',
1965     'purge'   => 'folders.inc',
1966     'remove-attachment'  => 'attachments.inc',
1967     'display-attachment' => 'attachments.inc',
1968     'upload'             => 'attachments.inc',
1969     'group-expand'       => 'autocomplete.inc',
1970 ));