alecpl
2008-04-23 d91fedb8d45fafa2c16859771fdb958aa10ad469
commit | author | age
4e17e6 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/steps/mail/func.inc                                           |
6  |                                                                       |
7  | This file is part of the RoundCube Webmail client                     |
f11541 8  | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
30233b 9  | Licensed under the GNU GPL                                            |
4e17e6 10  |                                                                       |
T 11  | PURPOSE:                                                              |
12  |   Provide webmail functionality and GUI objects                       |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id$
19
20 */
21
22 require_once('lib/enriched.inc');
fba1f5 23 require_once('include/rcube_smtp.inc');
4e17e6 24
T 25
26 $EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
27
f11541 28 if (empty($_SESSION['mbox']))
c5ac07 29   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
S 30
4e17e6 31 // set imap properties and session vars
b3ce79 32 if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
c57996 33   $IMAP->set_mailbox(($_SESSION['mbox'] = $mbox));
4e17e6 34
b3ce79 35 if (!empty($_GET['_page']))
c57996 36   $IMAP->set_page(($_SESSION['page'] = intval($_GET['_page'])));
4e17e6 37
fecb03 38 // set mailbox to INBOX if not set
T 39 if (empty($_SESSION['mbox']))
40   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
4e17e6 41
6a35c8 42 // set default sort col/order to session
T 43 if (!isset($_SESSION['sort_col']))
44   $_SESSION['sort_col'] = $CONFIG['message_sort_col'];
45 if (!isset($_SESSION['sort_order']))
46   $_SESSION['sort_order'] = $CONFIG['message_sort_order'];
2bca6e 47
T 48 // set message set for search result
8d0758 49 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
1f020b 50   {
8d0758 51   $IMAP->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
1f020b 52   $OUTPUT->set_env('search_request', $_REQUEST['_search']);
S 53   $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
54   }
2bca6e 55
6a35c8 56
4e17e6 57 // define url for getting message parts
T 58 if (strlen($_GET['_uid']))
41bece 59   $GET_URL = rcmail_url('get', array('_mbox'=>$IMAP->get_mailbox_name(), '_uid'=>get_input_value('_uid', RCUBE_INPUT_GET)));
4e17e6 60
T 61
62 // set current mailbox in client environment
f11541 63 $OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
719804 64 $OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
4e17e6 65
T 66 if ($CONFIG['trash_mbox'])
f11541 67   $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
1966c5 68 if ($CONFIG['drafts_mbox'])
f11541 69   $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
b4b081 70 if ($CONFIG['junk_mbox'])
f11541 71   $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
T 72
73 if (!$OUTPUT->ajax_call)
eb6842 74   rcube_add_label('checkingmail', 'deletemessage', 'movemessagetotrash');
f11541 75
5eee00 76 // set page title
T 77 if (empty($_action) || $_action == 'list')
fed22f 78   $OUTPUT->set_pagetitle(rcmail_localize_foldername($IMAP->get_mailbox_name()));
5eee00 79
4e17e6 80
T 81
82 // return the message list as HTML table
83 function rcmail_message_list($attrib)
84   {
f11541 85   global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT;
b076a4 86
4e17e6 87   $skin_path = $CONFIG['skin_path'];
T 88   $image_tag = '<img src="%s%s" alt="%s" border="0" />';
b076a4 89
f3b659 90   // check to see if we have some settings for sorting
6a35c8 91   $sort_col   = $_SESSION['sort_col'];
T 92   $sort_order = $_SESSION['sort_order'];
24053e 93   
T 94   // add some labels to client
95   rcube_add_label('from', 'to');
f3b659 96
4e17e6 97   // get message headers
f3b659 98   $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
4e17e6 99
T 100   // add id to message list table if not specified
101   if (!strlen($attrib['id']))
102     $attrib['id'] = 'rcubemessagelist';
103
104   // allow the following attributes to be added to the <table> tag
105   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
106
107   $out = '<table' . $attrib_str . ">\n";
e0ddd4 108
T 109
4e17e6 110   // define list of cols to be displayed
T 111   $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
c8c1e0 112   $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
41bece 113
T 114   $mbox = $IMAP->get_mailbox_name();
4e17e6 115   
T 116   // show 'to' instead of from in sent messages
41bece 117   if (($mbox==$CONFIG['sent_mbox'] || $mbox==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
8c2e58 118       && !array_search('to', $a_show_cols))
4e17e6 119     $a_show_cols[$f] = 'to';
8c2e58 120   
e0ddd4 121   // add col definition
T 122   $out .= '<colgroup>';
01c86f 123   $out .= '<col class="icon" />';
e0ddd4 124
T 125   foreach ($a_show_cols as $col)
01c86f 126     $out .= sprintf('<col class="%s" />', $col);
e0ddd4 127
01c86f 128   $out .= '<col class="icon" />';
e0ddd4 129   $out .= "</colgroup>\n";
4e17e6 130
T 131   // add table title
132   $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
b076a4 133
f3b659 134   $javascript = '';
4e17e6 135   foreach ($a_show_cols as $col)
f3b659 136     {
T 137     // get column name
2bca6e 138     $col_name = Q(rcube_label($col));
f3b659 139
T 140     // make sort links
141     $sort = '';
1cded8 142     if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
f3b659 143       {
1cded8 144       // have buttons configured
T 145       if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
146         {
147         $sort = '&nbsp;&nbsp;';
b076a4 148
1cded8 149         // asc link
T 150         if (!empty($attrib['sortascbutton']))
151           {
f11541 152           $sort .= $OUTPUT->button(array(
T 153             'command' => 'sort',
154             'prop' => $col.'_ASC',
155             'image' => $attrib['sortascbutton'],
156             'align' => 'absmiddle',
157             'title' => 'sortasc'));
1cded8 158           }       
b076a4 159         
1cded8 160         // desc link
T 161         if (!empty($attrib['sortdescbutton']))
162           {
f11541 163           $sort .= $OUTPUT->button(array(
T 164             'command' => 'sort',
165             'prop' => $col.'_DESC',
166             'image' => $attrib['sortdescbutton'],
167             'align' => 'absmiddle',
168             'title' => 'sortdesc'));
1cded8 169           }
T 170         }
171       // just add a link tag to the header
172       else
b076a4 173         {
41bece 174         $col_name = sprintf(
T 175           '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
176           JS_OBJECT_NAME,
177           $col,
178           rcube_label('sortby'),
179           $col_name);
b076a4 180         }
f3b659 181       }
b076a4 182       
T 183     $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
f3b659 184
T 185     // put it all together
b076a4 186     $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";    
f3b659 187     }
4e17e6 188
T 189   $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
190   $out .= "</tr></thead>\n<tbody>\n";
191
192   // no messages in this mailbox
193   if (!sizeof($a_headers))
5eee00 194     $OUTPUT->show_message('nomessagesfound', 'notice');
4e17e6 195
T 196
197   $a_js_message_arr = array();
198
199   // create row for each message
200   foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
201     {
202     $message_icon = $attach_icon = '';
203     $js_row_arr = array();
204     $zebra_class = $i%2 ? 'even' : 'odd';
205
206     // set messag attributes to javascript array
6ec0a8 207     if ($header->deleted)
S 208       $js_row_arr['deleted'] = true;
4e17e6 209     if (!$header->seen)
T 210       $js_row_arr['unread'] = true;
211     if ($header->answered)
212       $js_row_arr['replied'] = true;
6ec0a8 213     // set message icon  
S 214     if ($attrib['deletedicon'] && $header->deleted)
215       $message_icon = $attrib['deletedicon'];
216     else if ($attrib['unreadicon'] && !$header->seen)
4e17e6 217       $message_icon = $attrib['unreadicon'];
T 218     else if ($attrib['repliedicon'] && $header->answered)
219       $message_icon = $attrib['repliedicon'];
220     else if ($attrib['messageicon'])
221       $message_icon = $attrib['messageicon'];
222     
f11541 223     // set attachment icon
8d4bcd 224     if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
4e17e6 225       $attach_icon = $attrib['attachmenticon'];
T 226         
15a9d1 227     $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
T 228                     $header->uid,
229                     $header->seen ? '' : ' unread',
230                     $header->deleted ? ' deleted' : '',
231                     $zebra_class);    
232     
4e17e6 233     $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
f11541 234     
4e17e6 235     // format each col
T 236     foreach ($a_show_cols as $col)
237       {
238       if ($col=='from' || $col=='to')
2bca6e 239         $cont = Q(rcmail_address_string($header->$col, 3, $attrib['addicon']), 'show');
4e17e6 240       else if ($col=='subject')
b4b081 241         {
41bece 242         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
T 243         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
f11541 244         $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
T 245         if (empty($cont)) $cont = Q(rcube_label('nosubject'));
b2fb95 246         $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
b4b081 247         }
4e17e6 248       else if ($col=='size')
T 249         $cont = show_bytes($header->$col);
250       else if ($col=='date')
f11541 251         $cont = format_date($header->date);
4e17e6 252       else
2bca6e 253         $cont = Q($header->$col);
4e17e6 254         
2bca6e 255       $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
4e17e6 256       }
T 257
258     $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
259     $out .= "</tr>\n";
260     
261     if (sizeof($js_row_arr))
262       $a_js_message_arr[$header->uid] = $js_row_arr;
263     }
264   
265   // complete message table
266   $out .= "</tbody></table>\n";
267   
268   
269   $message_count = $IMAP->messagecount();
270   
271   // set client env
f11541 272   $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
T 273   $OUTPUT->add_gui_object('messagelist', $attrib['id']);
274   $OUTPUT->set_env('messagecount', $message_count);
275   $OUTPUT->set_env('current_page', $IMAP->list_page);
276   $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
277   $OUTPUT->set_env('sort_col', $sort_col);
278   $OUTPUT->set_env('sort_order', $sort_order);
4e17e6 279   
T 280   if ($attrib['messageicon'])
f11541 281     $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
6ec0a8 282   if ($attrib['deletedicon'])
f11541 283     $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
4e17e6 284   if ($attrib['unreadicon'])
f11541 285     $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
4e17e6 286   if ($attrib['repliedicon'])
f11541 287     $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
4e17e6 288   if ($attrib['attachmenticon'])
f11541 289     $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
4e17e6 290   
ae895a 291   $OUTPUT->set_env('messages', $a_js_message_arr);
d24d20 292   $OUTPUT->set_env('coltypes', $a_show_cols);
f11541 293   
6b47de 294   $OUTPUT->include_script('list.js');
4e17e6 295   
T 296   return $out;
297   }
298
299
300 // return javascript commands to add rows to the message list
301 function rcmail_js_message_list($a_headers, $insert_top=FALSE)
302   {
f11541 303   global $CONFIG, $IMAP, $OUTPUT;
4e17e6 304
T 305   $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
41bece 306   $mbox = $IMAP->get_mailbox_name();
4e17e6 307
T 308   // show 'to' instead of from in sent messages
41bece 309   if (($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox'])
f11541 310       && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
4e17e6 311     $a_show_cols[$f] = 'to';
T 312
f11541 313   $OUTPUT->command('set_message_coltypes', $a_show_cols);
25d8ba 314
4e17e6 315   // loop through message headers
ecd2e7 316   foreach ($a_headers as $n => $header)
4e17e6 317     {
T 318     $a_msg_cols = array();
319     $a_msg_flags = array();
ecd2e7 320     
T 321     if (empty($header))
322       continue;
f11541 323
4e17e6 324     // format each col; similar as in rcmail_message_list()
T 325     foreach ($a_show_cols as $col)
326       {
327       if ($col=='from' || $col=='to')
2bca6e 328         $cont = Q(rcmail_address_string($header->$col, 3), 'show');
4e17e6 329       else if ($col=='subject')
7bbd5f 330         {
41bece 331         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
T 332         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
f11541 333         $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
7bbd5f 334         if (!$cont) $cont = Q(rcube_label('nosubject'));
b2fb95 335         $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), $cont);
7bbd5f 336         }
4e17e6 337       else if ($col=='size')
T 338         $cont = show_bytes($header->$col);
339       else if ($col=='date')
f11541 340         $cont = format_date($header->date);
4e17e6 341       else
2bca6e 342         $cont = Q($header->$col);
4e17e6 343           
T 344       $a_msg_cols[$col] = $cont;
345       }
346
6ec0a8 347     $a_msg_flags['deleted'] = $header->deleted ? 1 : 0;
4e17e6 348     $a_msg_flags['unread'] = $header->seen ? 0 : 1;
T 349     $a_msg_flags['replied'] = $header->answered ? 1 : 0;
f11541 350     $OUTPUT->command('add_message_row',
T 351       $header->uid,
352       $a_msg_cols,
353       $a_msg_flags,
354       preg_match("/multipart\/m/i", $header->ctype),
355       $insert_top);
4e17e6 356     }
T 357   }
358
359
b19097 360 // return an HTML iframe for loading mail content
T 361 function rcmail_messagecontent_frame($attrib)
362   {
f11541 363   global $OUTPUT;
b19097 364   
T 365   if (empty($attrib['id']))
366     $attrib['id'] = 'rcmailcontentwindow';
367
368   // allow the following attributes to be added to the <iframe> tag
369   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
370   $framename = $attrib['id'];
371
372   $out = sprintf('<iframe name="%s"%s></iframe>'."\n",
373          $framename,
374          $attrib_str);
375
f11541 376   $OUTPUT->set_env('contentframe', $framename);
T 377   $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
b19097 378
T 379   return $out;
380   }
381
4e17e6 382
T 383 function rcmail_messagecount_display($attrib)
384   {
f11541 385   global $IMAP, $OUTPUT;
4e17e6 386   
T 387   if (!$attrib['id'])
388     $attrib['id'] = 'rcmcountdisplay';
389
f11541 390   $OUTPUT->add_gui_object('countdisplay', $attrib['id']);
4e17e6 391
T 392   // allow the following attributes to be added to the <span> tag
393   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
394
395   
396   $out = '<span' . $attrib_str . '>';
397   $out .= rcmail_get_messagecount_text();
398   $out .= '</span>';
399   return $out;
400   }
401
402
58e360 403 function rcmail_quota_display($attrib)
T 404   {
f11541 405   global $OUTPUT, $COMM_PATH;
58e360 406
T 407   if (!$attrib['id'])
408     $attrib['id'] = 'rcmquotadisplay';
409
6d2714 410   if(isset($attrib['display']))
A 411     $_SESSION['quota_display'] = $attrib['display'];
412
f11541 413   $OUTPUT->add_gui_object('quotadisplay', $attrib['id']);
58e360 414
T 415   // allow the following attributes to be added to the <span> tag
97f57d 416   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'display'));
23796e 417
S 418   $out = '<span' . $attrib_str . '>';
6d2714 419   $out .= rcmail_quota_content();
23796e 420   $out .= '</span>';
S 421   return $out;
422   }
423
424
6d2714 425 function rcmail_quota_content($quota=NULL)
23796e 426   {
S 427   global $IMAP, $COMM_PATH;
3ea0e3 428
6d2714 429   $display = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
A 430
431   if (is_array($quota) && !empty($quota['used']) && !empty($quota['total']))
3ea0e3 432     {
6d2714 433       if (!isset($quota['percent']))
A 434         $quota['percent'] = $quota['used'] / $quota['total'];
435     }
436   elseif (!$IMAP->get_capability('QUOTA'))
437     return rcube_label('unknown');
438   else
439     $quota = $IMAP->get_quota();
440
441   if ($quota)
442     {
443     $quota_text = sprintf('%s / %s (%.0f%%)',
444                           show_bytes($quota['used'] * 1024),
445                           show_bytes($quota['total'] * 1024),
446                           $quota['percent']);
3ea0e3 447
T 448     // show quota as image (by Brett Patterson)
23796e 449     if ($display == 'image' && function_exists('imagegif'))
3ea0e3 450       {
23796e 451       $attrib = array('width' => 100, 'height' => 14);
f11541 452       $quota_text = sprintf('<img src="./bin/quotaimg.php?u=%s&amp;q=%d&amp;w=%d&amp;h=%d" width="%d" height="%d" alt="%s" title="%s / %s" />',
3ea0e3 453                             $quota['used'], $quota['total'],
fda695 454                             $attrib['width'], $attrib['height'],
T 455                             $attrib['width'], $attrib['height'],
456                             $quota_text,
457                             show_bytes($quota["used"] * 1024),
458                             show_bytes($quota["total"] * 1024));
3ea0e3 459       }
T 460     }
461   else
4647e1 462     $quota_text = rcube_label('unlimited');
58e360 463
23796e 464   return $quota_text;
58e360 465   }
T 466
4e17e6 467
4647e1 468 function rcmail_get_messagecount_text($count=NULL, $page=NULL)
4e17e6 469   {
T 470   global $IMAP, $MESSAGE;
471   
472   if (isset($MESSAGE['index']))
473     {
474     return rcube_label(array('name' => 'messagenrof',
475                              'vars' => array('nr'  => $MESSAGE['index']+1,
4647e1 476                                              'count' => $count!==NULL ? $count : $IMAP->messagecount())));
4e17e6 477     }
31b2ce 478
4647e1 479   if ($page===NULL)
T 480     $page = $IMAP->list_page;
481     
482   $start_msg = ($page-1) * $IMAP->page_size + 1;
483   $max = $count!==NULL ? $count : $IMAP->messagecount();
4e17e6 484
T 485   if ($max==0)
486     $out = rcube_label('mailboxempty');
487   else
488     $out = rcube_label(array('name' => 'messagesfromto',
489                               'vars' => array('from'  => $start_msg,
490                                               'to'    => min($max, $start_msg + $IMAP->page_size - 1),
491                                               'count' => $max)));
492
2bca6e 493   return Q($out);
4e17e6 494   }
T 495
496
356f74 497 /* Stolen from Squirrelmail */
S 498 function sq_deent(&$attvalue, $regex, $hex=false)
499   {
500   $ret_match = false;
501   preg_match_all($regex, $attvalue, $matches);
502   if (is_array($matches) && sizeof($matches[0]) > 0)
503     {
504     $repl = Array();
505     for ($i = 0; $i < sizeof($matches[0]); $i++)
506       {
507       $numval = $matches[1][$i];
508       if ($hex)
509         $numval = hexdec($numval);
510       $repl{$matches[0][$i]} = chr($numval);
511       }
512     $attvalue = strtr($attvalue, $repl);
513     return true;
514     }
515   else
516     return false;
517   }
518
519
520 /* Stolen verbatim from Squirrelmail */
521 function sq_defang(&$attvalue)
522   {
523   /* Skip this if there aren't ampersands or backslashes. */
524   if ((strpos($attvalue, '&') === false) &&
525       (strpos($attvalue, '\\') === false))
526     return;
527   $m = false;
528   do
529     {
530     $m = false;
531     $m = $m || sq_deent($attvalue, '/\&#0*(\d+);*/s');
532     $m = $m || sq_deent($attvalue, '/\&#x0*((\d|[a-f])+);*/si', true);
533     $m = $m || sq_deent($attvalue, '/\\\\(\d+)/s', true);
534     } while ($m == true);
535   $attvalue = stripslashes($attvalue);
536   }
537
538
539 function rcmail_html_filter($html)
540   {
541   preg_match_all('/<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|\'.*?\'|[^\'">\s]+))?)+\s*|\s*)\/?>/', $html, $tags);
542
543   /* From Squirrelmail: Translate all dangerous Unicode or Shift_JIS characters which are accepted by
544    * IE as regular characters. */
545   $replace = array(array('&#x029F;', '&#0671;',  /* L UNICODE IPA Extension */
546                          '&#x0280;', '&#0640;',  /* R UNICODE IPA Extension */
547                          '&#x0274;', '&#0628;',  /* N UNICODE IPA Extension */
548                          '&#xFF25;', '&#65317;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER E */
549                          '&#xFF45;', '&#65349;', /* Unicode FULLWIDTH LATIN SMALL LETTER E */
550                          '&#xFF38;', '&#65336;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER X */
551                          '&#xFF58;', '&#65368;', /* Unicode FULLWIDTH LATIN SMALL LETTER X */
552                          '&#xFF30;', '&#65328;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER P */
553                          '&#xFF50;', '&#65360;', /* Unicode FULLWIDTH LATIN SMALL LETTER P */
554                          '&#xFF32;', '&#65330;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER R */
555                          '&#xFF52;', '&#65362;', /* Unicode FULLWIDTH LATIN SMALL LETTER R */
556                          '&#xFF33;', '&#65331;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER S */
557                          '&#xFF53;', '&#65363;', /* Unicode FULLWIDTH LATIN SMALL LETTER S */
558                          '&#xFF29;', '&#65321;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER I */
559                          '&#xFF49;', '&#65353;', /* Unicode FULLWIDTH LATIN SMALL LETTER I */
560                          '&#xFF2F;', '&#65327;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER O */
561                          '&#xFF4F;', '&#65359;', /* Unicode FULLWIDTH LATIN SMALL LETTER O */
562                          '&#xFF2E;', '&#65326;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER N */
563                          '&#xFF4E;', '&#65358;', /* Unicode FULLWIDTH LATIN SMALL LETTER N */
564                          '&#xFF2C;', '&#65324;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER L */
565                          '&#xFF4C;', '&#65356;', /* Unicode FULLWIDTH LATIN SMALL LETTER L */
566                          '&#xFF35;', '&#65333;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER U */
567                          '&#xFF55;', '&#65365;', /* Unicode FULLWIDTH LATIN SMALL LETTER U */
568                          '&#x207F;', '&#8319;' , /* Unicode SUPERSCRIPT LATIN SMALL LETTER N */
569                          "\xEF\xBC\xA5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER E */
570                                          /* in unicode this is some Chinese char range */
571                          "\xEF\xBD\x85", /* Shift JIS FULLWIDTH LATIN SMALL LETTER E */
572                          "\xEF\xBC\xB8", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER X */
573                          "\xEF\xBD\x98", /* Shift JIS FULLWIDTH LATIN SMALL LETTER X */
574                          "\xEF\xBC\xB0", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER P */
575                          "\xEF\xBD\x90", /* Shift JIS FULLWIDTH LATIN SMALL LETTER P */
576                          "\xEF\xBC\xB2", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER R */
577                          "\xEF\xBD\x92", /* Shift JIS FULLWIDTH LATIN SMALL LETTER R */
578                          "\xEF\xBC\xB3", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER S */
579                          "\xEF\xBD\x93", /* Shift JIS FULLWIDTH LATIN SMALL LETTER S */
580                          "\xEF\xBC\xA9", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER I */
581                          "\xEF\xBD\x89", /* Shift JIS FULLWIDTH LATIN SMALL LETTER I */
582                          "\xEF\xBC\xAF", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER O */
583                          "\xEF\xBD\x8F", /* Shift JIS FULLWIDTH LATIN SMALL LETTER O */
584                          "\xEF\xBC\xAE", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER N */
585                          "\xEF\xBD\x8E", /* Shift JIS FULLWIDTH LATIN SMALL LETTER N */
586                          "\xEF\xBC\xAC", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER L */
587                          "\xEF\xBD\x8C", /* Shift JIS FULLWIDTH LATIN SMALL LETTER L */
588                          "\xEF\xBC\xB5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER U */
589                          "\xEF\xBD\x95", /* Shift JIS FULLWIDTH LATIN SMALL LETTER U */
590                          "\xE2\x81\xBF", /* Shift JIS FULLWIDTH SUPERSCRIPT N */
591                          "\xCA\x9F",   /* L UNICODE IPA Extension */
592                          "\xCA\x80",   /* R UNICODE IPA Extension */
593                          "\xC9\xB4"),  /* N UNICODE IPA Extension */
594                    array('l', 'l', 'r', 'r', 'n', 'n', 'E', 'E', 'e', 'e', 'X', 'X', 'x', 'x',
595                          'P', 'P', 'p', 'p', 'R', 'R', 'r', 'r', 'S', 'S', 's', 's', 'I', 'I',
596                          'i', 'i', 'O', 'O', 'o', 'o', 'N', 'N', 'n', 'n', 'L', 'L', 'l', 'l',
597                          'U', 'U', 'u', 'u', 'n', 'n', 'E', 'e', 'X', 'x', 'P', 'p', 'R', 'r',
598                          'S', 's', 'I', 'i', 'O', 'o', 'N', 'n', 'L', 'l', 'U', 'u', 'n', 'l', 'r', 'n'));
599   if ((count($tags)>3) && (count($tags[3])>0))
600     foreach ($tags[3] as $nr=>$value)
601       {
602       /* Remove comments */
603       $newvalue = preg_replace('/(\/\*.*\*\/)/','$2',$value);
604       /* Translate dangerous characters */
605       $newvalue = str_replace($replace[0], $replace[1], $newvalue);
606       sq_defang($newvalue);
607       /* Rename dangerous CSS */
608       $newvalue = preg_replace('/expression/i', 'idiocy', $newvalue);
609       $newvalue = preg_replace('/url/i', 'idiocy', $newvalue);
610       $newattrs = preg_replace('/'.preg_quote($value, '/').'$/', $newvalue, $tags[1][$nr]);
611       $newtag = preg_replace('/'.preg_quote($tags[1][$nr], '/').'/', $newattrs, $tags[0][$nr]);
612       $html = preg_replace('/'.preg_quote($tags[0][$nr], '/').'/', $newtag, $html);
613       }
614   return $html;
615   }
616
617
8d4bcd 618 function rcmail_print_body($part, $safe=FALSE, $plain=FALSE)
4e17e6 619   {
f11541 620   global $IMAP, $REMOTE_OBJECTS;
4e17e6 621   
8d4bcd 622   $body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body;
a0109c 623
5cc4b1 624   // convert html to text/plain
T 625   if ($part->ctype_secondary=='html' && $plain)
626     {
627     $txt = new html2text($body, false, true);
628     $body = $txt->get_text();
629     $part->ctype_secondary = 'plain';
630     }
631     
4e17e6 632   // text/html
8d4bcd 633   if ($part->ctype_secondary=='html')
4e17e6 634     {
f7bfec 635     // remove charset specification in HTML message
T 636     $body = preg_replace('/charset=[a-z0-9\-]+/i', '', $body);
637
4e17e6 638     if (!$safe)  // remove remote images and scripts
T 639       {
ea206d 640       $remote_patterns = array('/<img\s+(.*)src=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
T 641                                '/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
4e17e6 642                                '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
T 643                                '/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
644                                '/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
645                                '/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
646                                '/<script.+<\/script>/Umis');
647
ee883a 648       $remote_replaces = array('<img \\1src=\\2./program/blocked.gif\\4',
4e17e6 649                                '',
ea206d 650                                '',
T 651                                '',
4e17e6 652                                'none',
T 653                                'none',
654                                '');
655       
656       // set flag if message containes remote obejcts that where blocked
657       foreach ($remote_patterns as $pattern)
658         {
659         if (preg_match($pattern, $body))
660           {
661           $REMOTE_OBJECTS = TRUE;
662           break;
663           }
664         }
665
666       $body = preg_replace($remote_patterns, $remote_replaces, $body);
667       }
668
356f74 669     return Q(rcmail_html_filter($body), 'show', FALSE);
4e17e6 670     }
T 671
672   // text/enriched
8d4bcd 673   if ($part->ctype_secondary=='enriched')
4e17e6 674     {
2bca6e 675     return Q(enriched_to_html($body), 'show');
4e17e6 676     }
T 677   else
678     {
679     // make links and email-addresses clickable
680     $convert_patterns = $convert_replaces = $replace_strings = array();
681     
949dea 682     $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
4e17e6 683     $url_chars_within = '\?\.~,!';
T 684
685     $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
20a1b3 686     $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
4e17e6 687
T 688     $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
20a1b3 689     $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
4e17e6 690     
T 691     $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
f11541 692     $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return ".JS_OBJECT_NAME.".command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
10c92b 693     
379050 694 //    if ($part->ctype_parameters['format'] != 'flowed')
A 695 //      $body = wordwrap(trim($body), 80);
4e17e6 696
T 697     $body = preg_replace($convert_patterns, $convert_replaces, $body);
698
699     // split body into single lines
700     $a_lines = preg_split('/\r?\n/', $body);
10c92b 701     $quote_level = 0;
4e17e6 702
T 703     // colorize quoted parts
704     for($n=0; $n<sizeof($a_lines); $n++)
705       {
706       $line = $a_lines[$n];
10c92b 707       $quotation = '';
T 708       $q = 0;
709       
0b1727 710       if (preg_match('/^(>+\s*)+/', $line, $regs))
10c92b 711         {
0b1727 712         $q    = strlen(preg_replace('/\s/', '', $regs[0]));
T 713         $line = substr($line, strlen($regs[0]));
4e17e6 714
10c92b 715         if ($q > $quote_level)
T 716           $quotation = str_repeat('<blockquote>', $q - $quote_level);
717         else if ($q < $quote_level)
718           $quotation = str_repeat("</blockquote>", $quote_level - $q);
719         }
720       else if ($quote_level > 0)
721         $quotation = str_repeat("</blockquote>", $quote_level);
4e17e6 722
10c92b 723       $quote_level = $q;
2bca6e 724       $a_lines[$n] = $quotation . Q($line, 'replace', FALSE);
4e17e6 725       }
T 726
727     // insert the links for urls and mailtos
728     $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
729     
ea206d 730     return "<div class=\"pre\">".$body."\n</div>";
4e17e6 731     }
T 732   }
733
734
735
736 // add a string to the replacement array and return a replacement string
737 function rcmail_str_replacement($str, &$rep)
738   {
739   static $count = 0;
740   $rep[$count] = stripslashes($str);
741   return "##string_replacement{".($count++)."}##";
742   }
743
744
8d4bcd 745 function rcmail_parse_message(&$structure, $arg=array(), $recursive=FALSE)
4e17e6 746   {
T 747   global $IMAP;
748   static $sa_inline_objects = array();
749
750   // arguments are: (bool)$prefer_html, (string)$get_url
751   extract($arg);
752
753   $a_attachments = array();
754   $a_return_parts = array();
755   $out = '';
756
757   $message_ctype_primary = strtolower($structure->ctype_primary);
758   $message_ctype_secondary = strtolower($structure->ctype_secondary);
759
760   // show message headers
761   if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
8d4bcd 762     {
T 763     $c = new stdClass;
764     $c->type = 'headers';
765     $c->headers = &$structure->headers;
766     $a_return_parts[] = $c;
767     }
4e17e6 768
T 769   // print body if message doesn't have multiple parts
770   if ($message_ctype_primary=='text')
771     {
8d4bcd 772     $structure->type = 'content';
T 773     $a_return_parts[] = &$structure;
4e17e6 774     }
6726f0 775     
4e17e6 776   // message contains alternative parts
T 777   else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
778     {
779     // get html/plaintext parts
780     $plain_part = $html_part = $print_part = $related_part = NULL;
781     
782     foreach ($structure->parts as $p => $sub_part)
783       {
719a25 784       $rel_parts = $attachmnts = null;
4e17e6 785       $sub_ctype_primary = strtolower($sub_part->ctype_primary);
T 786       $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
787
788       // check if sub part is 
789       if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
790         $plain_part = $p;
791       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
792         $html_part = $p;
793       else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
794         $enriched_part = $p;
719a25 795       else if ($sub_ctype_primary=='multipart' && ($sub_ctype_secondary=='related' || $sub_ctype_secondary=='mixed'))
4e17e6 796         $related_part = $p;
T 797       }
719a25 798       
4e17e6 799     // parse related part (alternative part could be in here)
719a25 800     if ($related_part!==NULL)
T 801     {
802       list($rel_parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
4e17e6 803       $a_attachments = array_merge($a_attachments, $attachmnts);
719a25 804     }
T 805     
806     // merge related parts if any
807     if ($rel_parts && $prefer_html && !$html_part)
808       $a_return_parts = array_merge($a_return_parts, $rel_parts);
4e17e6 809
719a25 810     // choose html/plain part to print
4e17e6 811     else if ($html_part!==NULL && $prefer_html)
8d4bcd 812       $print_part = &$structure->parts[$html_part];
4e17e6 813     else if ($enriched_part!==NULL)
8d4bcd 814       $print_part = &$structure->parts[$enriched_part];
4e17e6 815     else if ($plain_part!==NULL)
8d4bcd 816       $print_part = &$structure->parts[$plain_part];
4e17e6 817
T 818     // show message body
819     if (is_object($print_part))
8d4bcd 820       {
T 821       $print_part->type = 'content';
822       $a_return_parts[] = $print_part;
823       }
4e17e6 824     // show plaintext warning
719a25 825     else if ($html_part!==NULL && empty($a_return_parts))
8d4bcd 826       {
T 827       $c = new stdClass;
828       $c->type = 'content';
829       $c->body = rcube_label('htmlmessage');
830       $c->ctype_primary = 'text';
831       $c->ctype_secondary = 'plain';
832       
833       $a_return_parts[] = $c;
834       }
4e17e6 835                                 
T 836     // add html part as attachment
837     if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
838       {
8d4bcd 839       $html_part = &$structure->parts[$html_part];
T 840       $html_part->filename = rcube_label('htmlmessage');
841       $html_part->mimetype = 'text/html';
842       
843       $a_attachments[] = $html_part;
4e17e6 844       }
T 845     }
846
847   // message contains multiple parts
8d4bcd 848   else if (is_array($structure->parts) && !empty($structure->parts))
4e17e6 849     {
8d4bcd 850     for ($i=0; $i<count($structure->parts); $i++)
4e17e6 851       {
8d4bcd 852       $mail_part = &$structure->parts[$i];
4e17e6 853       $primary_type = strtolower($mail_part->ctype_primary);
T 854       $secondary_type = strtolower($mail_part->ctype_secondary);
855
856       // multipart/alternative
8d4bcd 857       if ($primary_type=='multipart')
4e17e6 858         {
T 859         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
860
861         $a_return_parts = array_merge($a_return_parts, $parts);
862         $a_attachments = array_merge($a_attachments, $attachmnts);
863         }
864
865       // part text/[plain|html] OR message/delivery-status
58e360 866       else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
fba1f5 867                ($primary_type=='message' && ($secondary_type=='delivery-status' || $secondary_type=='disposition-notification')))
4e17e6 868         {
8d4bcd 869         $mail_part->type = 'content';
T 870         $a_return_parts[] = $mail_part;
4e17e6 871         }
T 872
873       // part message/*
874       else if ($primary_type=='message')
875         {
8d4bcd 876         list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
T 877           
4e17e6 878         $a_return_parts = array_merge($a_return_parts, $parts);
T 879         $a_attachments = array_merge($a_attachments, $attachmnts);
880         }
6726f0 881         
T 882       // ignore "virtual" protocol parts
883       else if ($primary_type=='protocol')
884         continue;
4e17e6 885
T 886       // part is file/attachment
b595c9 887       else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] ||
5cc4b1 888                (empty($mail_part->disposition) && $mail_part->filename))
4e17e6 889         {
bb8562 890         // skip apple resource forks
8d4bcd 891         if ($message_ctype_secondary=='appledouble' && $secondary_type=='applefile')
T 892           continue;
4e17e6 893
8d4bcd 894         // part belongs to a related message
T 895         if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
896           {
897           $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
898           $sa_inline_objects[] = $mail_part;
899           }
900         // is regular attachment
fd3934 901         else
T 902           {
903           if (!$mail_part->filename)
fba1f5 904             $mail_part->filename = 'Part '.$mail_part->mime_id;
8d4bcd 905           $a_attachments[] = $mail_part;
fd3934 906           }
4e17e6 907         }
T 908       }
909
910     // if this was a related part try to resolve references
911     if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
912       {
8d4bcd 913       $a_replaces = array();
4e17e6 914         
T 915       foreach ($sa_inline_objects as $inline_object)
ea206d 916         $a_replaces['cid:'.$inline_object->content_id] = htmlspecialchars(sprintf($get_url, $inline_object->mime_id));
4e17e6 917       
8d4bcd 918       // add replace array to each content part
T 919       // (will be applied later when part body is available)
920       for ($i=0; $i<count($a_return_parts); $i++)
4e17e6 921         {
8d4bcd 922         if ($a_return_parts[$i]->type=='content')
T 923           $a_return_parts[$i]->replaces = $a_replaces;
4e17e6 924         }
T 925       }
926     }
927
04d630 928   // message is single part non-text
5cc4b1 929   else if ($structure->filename)
T 930     $a_attachments[] = $structure;
04d630 931
4e17e6 932   return array($a_return_parts, $a_attachments);
T 933   }
934
935
936
937
938 // return table with message headers
939 function rcmail_message_headers($attrib, $headers=NULL)
940   {
941   global $IMAP, $OUTPUT, $MESSAGE;
942   static $sa_attrib;
943   
944   // keep header table attrib
945   if (is_array($attrib) && !$sa_attrib)
946     $sa_attrib = $attrib;
947   else if (!is_array($attrib) && is_array($sa_attrib))
948     $attrib = $sa_attrib;
949   
950   
951   if (!isset($MESSAGE))
952     return FALSE;
953
954   // get associative array of headers object
955   if (!$headers)
956     $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
f11541 957   
4e17e6 958   $header_count = 0;
T 959   
960   // allow the following attributes to be added to the <table> tag
961   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
962   $out = '<table' . $attrib_str . ">\n";
963
964   // show these headers
bde645 965   $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'reply-to', 'date');
4e17e6 966   
T 967   foreach ($standard_headers as $hkey)
968     {
969     if (!$headers[$hkey])
970       continue;
971
b076a4 972     if ($hkey=='date' && !empty($headers[$hkey]))
ea090c 973       $header_value = format_date($headers[$hkey]);
bde645 974     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc', 'reply-to')))
2bca6e 975       $header_value = Q(rcmail_address_string($headers[$hkey], NULL, $attrib['addicon']), 'show');
4e17e6 976     else
f11541 977       $header_value = Q(rcube_imap::decode_mime_string($headers[$hkey], $headers['charset']));
4e17e6 978
T 979     $out .= "\n<tr>\n";
2bca6e 980     $out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
4e17e6 981     $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
T 982     $header_count++;
983     }
984
985   $out .= "\n</table>\n\n";
986
987   return $header_count ? $out : '';  
988   }
989
990
991
992 function rcmail_message_body($attrib)
993   {
f11541 994   global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $GET_URL, $REMOTE_OBJECTS;
4e17e6 995   
T 996   if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
997     return '';
998     
999   if (!$attrib['id'])
1000     $attrib['id'] = 'rcmailMsgBody';
1001
6726f0 1002   $safe_mode = $MESSAGE['is_safe'] || intval($_GET['_safe']);
4e17e6 1003   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
T 1004   $out = '<div '. $attrib_str . ">\n";
1005   
1006   $header_attrib = array();
1007   foreach ($attrib as $attr => $value)
1008     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
1009       $header_attrib[$regs[1]] = $value;
1010
1011
1012   // this is an ecrypted message
1013   // -> create a plaintext body with the according message
1014   if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
1015     {
8d4bcd 1016     $p = new stdClass;
T 1017     $p->type = 'content';
1018     $p->ctype_primary = 'text';
1019     $p->ctype_secondary = 'plain';
1020     $p->body = rcube_label('encryptedmessage');
1021     $MESSAGE['parts'][0] = $p;
4e17e6 1022     }
T 1023   
1024   if ($MESSAGE['parts'])
1025     {
1026     foreach ($MESSAGE['parts'] as $i => $part)
1027       {
8d4bcd 1028       if ($part->type=='headers')
T 1029         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
1030       else if ($part->type=='content')
4e17e6 1031         {
8d4bcd 1032         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
2c9298 1033           $part->ctype_parameters['charset'] = $MESSAGE['headers']->charset;
a0109c 1034
8d4bcd 1035         // fetch part if not available
T 1036         if (!isset($part->body))
1037           $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
a0109c 1038
5cc4b1 1039         $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']);
4e17e6 1040         $out .= '<div class="message-part">';
a2f2c5 1041         
8d4bcd 1042         if ($part->ctype_secondary != 'plain')
5cc4b1 1043           $out .= rcmail_sanitize_html($body, $attrib['id']);
a2f2c5 1044         else
T 1045           $out .= $body;
1046
4e17e6 1047         $out .= "</div>\n";
T 1048         }
1049       }
1050     }
1051   else
1052     $out .= $MESSAGE['body'];
1053
1054
1055   $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
1056   $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
1057   
1058   // list images after mail body
719a25 1059   if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' &&
T 1060       !empty($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
4e17e6 1061     {
T 1062     foreach ($MESSAGE['attachments'] as $attach_prop)
1063       {
8d4bcd 1064       if (strpos($attach_prop->mimetype, 'image/')===0)
ea206d 1065         $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&amp;_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
T 1066                         htmlspecialchars($GET_URL), $attach_prop->mime_id,
8d4bcd 1067                         $attach_prop->filename,
T 1068                         $attach_prop->filename);
4e17e6 1069       }
T 1070     }
1071   
1072   // tell client that there are blocked remote objects
1073   if ($REMOTE_OBJECTS && !$safe_mode)
f11541 1074     $OUTPUT->set_env('blockedobjects', true);
4e17e6 1075
T 1076   $out .= "\n</div>";
1077   return $out;
1078   }
1079
1080
1081
1082 // modify a HTML message that it can be displayed inside a HTML page
5cc4b1 1083 function rcmail_sanitize_html($body, $container_id)
4e17e6 1084   {
749b07 1085   // remove any null-byte characters before parsing
T 1086   $body = preg_replace('/\x00/', '', $body);
1087   
97bd2c 1088   $base_url = "";
4e17e6 1089   $last_style_pos = 0;
T 1090   $body_lc = strtolower($body);
97bd2c 1091   
T 1092   // check for <base href>
1093   if (preg_match(($base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i'), $body, $base_regs))
1094     $base_url = $base_regs[2];
4e17e6 1095   
T 1096   // find STYLE tags
1097   while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
1098     {
ea206d 1099     $pos = strpos($body_lc, '>', $pos)+1;
T 1100
4e17e6 1101     // replace all css definitions with #container [def]
97bd2c 1102     $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id, $base_url);
ea206d 1103
3b12ae 1104     $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
S 1105     $body_lc = strtolower($body);
4e17e6 1106     $last_style_pos = $pos2;
T 1107     }
1108
1109
1110   // remove SCRIPT tags
6a35c8 1111   foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
4e17e6 1112     {
a08a60 1113     while (($pos = strpos($body_lc, '<'.$tag)) && (($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)) || ($pos3 = strpos($body_lc, '>', $pos))))
6a35c8 1114       {
a08a60 1115       $end = $pos2 ? $pos2 + strlen('</'.$tag.'>') : $pos3 + 1;
T 1116       $body = substr($body, 0, $pos) . substr($body, $end, strlen($body)-$end);
6a35c8 1117       $body_lc = strtolower($body);
T 1118       }
4e17e6 1119     }
6a35c8 1120
T 1121   // replace event handlers on any object
5e98e1 1122   while ($body != $prev_body)
S 1123     {
1124     $prev_body = $body;
2f93b0 1125     $body = preg_replace('/(<[^!][^>]*\s)on(?:load|unload|click|dblclick|mousedown|mouseup|mouseover|mousemove|mouseout|focus|blur|keypress|keydown|keyup|submit|reset|select|change)=([^>]+>)/im', '$1__removed=$2', $body);
18e2a3 1126     $body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
5e98e1 1127     }
4e17e6 1128
T 1129   // resolve <base href>
97bd2c 1130   if ($base_url)
4e17e6 1131     {
T 1132     $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
1133     $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
1134     $body = preg_replace($base_reg, '', $body);
1135     }
fe79b1 1136     
T 1137   // modify HTML links to open a new window if clicked
97bd2c 1138   $body = preg_replace('/<(a|link)\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1','\\2', '$container_id');", $body);
4e17e6 1139
T 1140   // add comments arround html and other tags
06895c 1141   $out = preg_replace(array(
d7a411 1142       '/(<!DOCTYPE[^>]*>)/i',
A 1143       '/(<\?xml[^>]*>)/i',
06895c 1144       '/(<\/?html[^>]*>)/i',
T 1145       '/(<\/?head[^>]*>)/i',
1146       '/(<title[^>]*>.*<\/title>)/Ui',
1147       '/(<\/?meta[^>]*>)/i'),
1148     '<!--\\1-->',
1149     $body);
a0109c 1150
97bd2c 1151   $out = preg_replace(
T 1152     array(
1153       '/<body([^>]*)>/i',
1154       '/<\/body>/i',
1155     ),
1156     array(
1157       '<div class="rcmBody"\\1>',
1158       '</div>',
1159     ),
1160     $out);
a0109c 1161
86958f 1162   // quote <? of php and xml files that are specified as text/html
T 1163   $out = preg_replace(array('/<\?/', '/\?>/'), array('&lt;?', '?&gt;'), $out);
1164
4e17e6 1165   return $out;
T 1166   }
1167
1168
fe79b1 1169 // parse link attributes and set correct target
97bd2c 1170 function rcmail_alter_html_link($tag, $attrs, $container_id)
fe79b1 1171   {
3cf664 1172   $in = preg_replace('/=([^("|\'|\s)]+)(\s|$)/', '="\1"', $in);
97bd2c 1173   $attrib = parse_attrib_string($attrs);
T 1174   
1175   if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href']))
1176     $attrib['href'] = "./bin/modcss.php?u=" . urlencode($attrib['href']) . "&amp;c=" . urlencode($container_id);
fe79b1 1177
97bd2c 1178   else if (stristr((string)$attrib['href'], 'mailto:'))
T 1179     $attrib['onclick'] = sprintf(
1180       "return %s.command('compose','%s',this)",
1181       JS_OBJECT_NAME,
1182       JQ(substr($attrib['href'], 7)));
1183   
fe79b1 1184   else if (!empty($attrib['href']) && $attrib['href']{0}!='#')
T 1185     $attrib['target'] = '_blank';
1186
97bd2c 1187   return "<$tag" . create_attrib_string($attrib, array('href','name','target','onclick','id','class','style','title','rel','type','media')) . ' />';
4e17e6 1188   }
T 1189
1190
a0109c 1191 function rcmail_has_html_part($message_parts)
S 1192 {
1193    if (!is_array($message_parts))
1194       return FALSE;
1195
1196    // check all message parts
1197    foreach ($message_parts as $pid => $part)
1198    {
1199       $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1200       if ($mimetype=='text/html')
1201       {
1202          return TRUE;
1203       }
1204    }
1205     
1206    return FALSE;
1207 }
1208
1209 // return first HTML part of a message
1210 function rcmail_first_html_part($message_struct)
1211   {
1212   global $IMAP;
1213
1214   if (!is_array($message_struct['parts']))
1215     return FALSE;
1216     
1217   $html_part = NULL;
1218
1219   // check all message parts
1220   foreach ($message_struct['parts'] as $pid => $part)
1221     {
1222     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
1223     if ($mimetype=='text/html')
1224       {
1225       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
1226       }
1227     }
1228
1229   if ($html_part)
1230     {
1231     // remove special chars encoding
1232     //$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1233     //$html_part = strtr($html_part, $trans);
1234
1235     return $html_part;
1236     }
1237
1238   return FALSE;
1239 }
1240
4e17e6 1241
T 1242 // return first text part of a message
8d4bcd 1243 function rcmail_first_text_part($message_struct)
4e17e6 1244   {
8d4bcd 1245   global $IMAP;
T 1246
e170b4 1247   if (empty($message_struct['parts']))
T 1248     return $message_struct['UID'] ? $IMAP->get_body($message_struct['UID']) : false;
1249
4e17e6 1250   // check all message parts
8d4bcd 1251   foreach ($message_struct['parts'] as $pid => $part)
4e17e6 1252     {
T 1253     $mimetype = strtolower($part->ctype_primary.'/'.$part->ctype_secondary);
8d4bcd 1254
4e17e6 1255     if ($mimetype=='text/plain')
8d4bcd 1256       return $IMAP->get_message_part($message_struct['UID'], $pid, $part);
T 1257
4e17e6 1258     else if ($mimetype=='text/html')
T 1259       {
8d4bcd 1260       $html_part = $IMAP->get_message_part($message_struct['UID'], $pid, $part);
T 1261       
1262       // remove special chars encoding
1263       $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
1264       $html_part = strtr($html_part, $trans);
1265
1266       // create instance of html2text class
1267       $txt = new html2text($html_part);
1268       return $txt->get_text();
4e17e6 1269       }
T 1270     }
1271
1272   return FALSE;
1273   }
1274
1275
1276 // decode address string and re-format it as HTML links
1277 function rcmail_address_string($input, $max=NULL, $addicon=NULL)
1278   {
f11541 1279   global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $EMAIL_ADDRESS_PATTERN;
4e17e6 1280   
T 1281   $a_parts = $IMAP->decode_address_list($input);
1282
1283   if (!sizeof($a_parts))
1284     return $input;
1285
1286   $c = count($a_parts);
1287   $j = 0;
1288   $out = '';
1289
1290   foreach ($a_parts as $part)
1291     {
1292     $j++;
1293     if ($PRINT_MODE)
2bca6e 1294       $out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
4e17e6 1295     else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
T 1296       {
1297       $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
28bfe4 1298                       Q($part['mailto']),
f11541 1299                       JS_OBJECT_NAME,
28bfe4 1300                       JQ($part['mailto']),
T 1301                       Q($part['mailto']),
2bca6e 1302                       Q($part['name']));
4e17e6 1303                       
T 1304       if ($addicon)
1305         $out .= sprintf('&nbsp;<a href="#add" onclick="return %s.command(\'add-contact\',\'%s\',this)" title="%s"><img src="%s%s" alt="add" border="0" /></a>',
f11541 1306                         JS_OBJECT_NAME,
4e17e6 1307                         urlencode($part['string']),
T 1308                         rcube_label('addtoaddressbook'),
1309                         $CONFIG['skin_path'],
1310                         $addicon);
1311       }
1312     else
1313       {
1314       if ($part['name'])
2bca6e 1315         $out .= Q($part['name']);
4e17e6 1316       if ($part['mailto'])
28bfe4 1317         $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($part['mailto']));
4e17e6 1318       }
T 1319       
1320     if ($c>$j)
1321       $out .= ','.($max ? '&nbsp;' : ' ');
1322         
1323     if ($max && $j==$max && $c>$j)
1324       {
1325       $out .= '...';
1326       break;
1327       }        
1328     }
1329     
1330   return $out;
1331   }
1332
1333
1334 function rcmail_message_part_controls()
1335   {
1336   global $CONFIG, $IMAP, $MESSAGE;
1337   
d5342a 1338   $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
c57996 1339   if (!is_array($MESSAGE) || !is_array($MESSAGE['parts']) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE['parts'][$part])
4e17e6 1340     return '';
T 1341     
29bee0 1342   $part = $MESSAGE['parts'][$part];
4e17e6 1343   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary'));
T 1344   $out = '<table '. $attrib_str . ">\n";
1345   
29bee0 1346   if ($part->filename)
4e17e6 1347     {
T 1348     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n",
2bca6e 1349                     Q(rcube_label('filename')),
5cc4b1 1350                     Q($part->filename),
4e17e6 1351                     str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']),
2bca6e 1352                     Q(rcube_label('download')));
4e17e6 1353     }
T 1354     
5cc4b1 1355   if ($part->size)
4e17e6 1356     $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n",
2bca6e 1357                     Q(rcube_label('filesize')),
5cc4b1 1358                     show_bytes($part->size));
4e17e6 1359   
T 1360   $out .= "\n</table>";
1361   
1362   return $out;
1363   }
1364
1365
1366
1367 function rcmail_message_part_frame($attrib)
1368   {
1369   global $MESSAGE;
1370   
d5342a 1371   $part = $MESSAGE['parts'][asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
4e17e6 1372   $ctype_primary = strtolower($part->ctype_primary);
T 1373
719a25 1374   $attrib['src'] = Q('./?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']));
4e17e6 1375
T 1376   $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
90022e 1377   $out = '<iframe '. $attrib_str . "></iframe>";
4e17e6 1378     
T 1379   return $out;
1380   }
1381
1382
1383 // clear message composing settings
1384 function rcmail_compose_cleanup()
1385   {
1386   if (!isset($_SESSION['compose']))
1387     return;
70d4b9 1388
4e17e6 1389   // remove attachment files from temp dir
T 1390   if (is_array($_SESSION['compose']['attachments']))
1391     foreach ($_SESSION['compose']['attachments'] as $attachment)
15a9d1 1392       @unlink($attachment['path']);
4e17e6 1393   
T 1394   unset($_SESSION['compose']);
1395   }
fba1f5 1396   
T 1397
1398 /**
1399  * Send the given message compose object using the configured method
1400  */
1401 function rcmail_deliver_message(&$message, $from, $mailto)
1402 {
1403   global $CONFIG;
1404
1405   $headers = $message->headers();
1406   $msg_body = $message->get();
1407   
1408   // send thru SMTP server using custom SMTP library
1409   if ($CONFIG['smtp_server'])
1410     {
1411     // generate list of recipients
1412     $a_recipients = array($mailto);
1413   
1414     if (strlen($headers['Cc']))
1415       $a_recipients[] = $headers['Cc'];
1416     if (strlen($headers['Bcc']))
1417       $a_recipients[] = $headers['Bcc'];
1418   
1419     // clean Bcc from header for recipients
1420     $send_headers = $headers;
1421     unset($send_headers['Bcc']);
1422
1423     // send message
1424     $smtp_response = array();
1425     $sent = smtp_mail($from, $a_recipients, ($foo = $message->txtHeaders($send_headers)), $msg_body, $smtp_response);
1426
1427     // log error
1428     if (!$sent)
1429       raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
1430                         'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
1431     }
1432   
1433   // send mail using PHP's mail() function
1434   else
1435     {
1436     // unset some headers because they will be added by the mail() function
1437     $headers_enc = $message->headers($headers);
1438     $headers_php = $message->_headers;
1439     unset($headers_php['To'], $headers_php['Subject']);
1440     
1441     // reset stored headers and overwrite
1442     $message->_headers = array();
1443     $header_str = $message->txtHeaders($headers_php);
1444   
1445     if (ini_get('safe_mode'))
1446       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
1447     else
1448       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
1449     }
1450   
ae8f19 1451   if ($sent)  // remove MDN headers after sending
T 1452     unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
fba1f5 1453   
T 1454   $message->_headers = array();
1455   $message->headers($headers);
1456   
1457   return $sent;
1458 }
f11541 1459
T 1460
0ea884 1461 function rcmail_send_mdn($uid)
T 1462 {
1463   global $CONFIG, $USER, $IMAP;
1464   
1465   $message = array('UID' => $uid);
1466   $message['headers'] = $IMAP->get_headers($message['UID']);
1467   $message['subject'] = rcube_imap::decode_mime_string($message['headers']->subject, $message['headers']->charset);
1468   
1469   if ($message['headers']->mdn_to && !$message['headers']->mdn_sent)
1470   {
1471     $identity = $USER->get_identity();
1472     $sender = format_email_recipient($identity['email'], $identity['name']);
1473     $recipient = array_shift($IMAP->decode_address_list($message['headers']->mdn_to));
1474     $mailto = $recipient['mailto'];
1475
d91fed 1476     $compose = new rcube_mail_mime(rcmail_header_delm());
0ea884 1477     $compose->setParam(array(
T 1478       'text_encoding' => 'quoted-printable',
1479       'html_encoding' => 'quoted-printable',
1480       'head_encoding' => 'quoted-printable',
1481       'head_charset'  => RCMAIL_CHARSET,
1482       'html_charset'  => RCMAIL_CHARSET,
1483       'text_charset'  => RCMAIL_CHARSET,
1484     ));
1485     
1486     // compose headers array
1487     $headers = array(
1488       'Date' => date('r'),
1489       'From' => $sender,
1490       'To'   => $message['headers']->mdn_to,
1491       'Subject' => rcube_label('receiptread') . ': ' . $message['subject'],
1492       'Message-ID' => sprintf('<%s@%s>', md5(uniqid('rcmail'.rand(),true)), rcmail_mail_domain($_SESSION['imap_host'])),
1493       'X-Sender' => $identity['email'],
1494       'Content-Type' => 'multipart/report; report-type=disposition-notification',
1495     );
1496     
1497     if (!empty($CONFIG['useragent']))
1498       $headers['User-Agent'] = $CONFIG['useragent'];
1499
1500     $body = rcube_label("yourmessage") . "\r\n\r\n" .
1501       "\t" . rcube_label("to") . ': ' . rcube_imap::decode_mime_string($message['headers']->to, $message['headers']->charset) . "\r\n" .
1502       "\t" . rcube_label("subject") . ': ' . $message['subject'] . "\r\n" .
ea090c 1503       "\t" . rcube_label("sent") . ': ' . format_date($message['headers']->date, $CONFIG['date_long']) . "\r\n" .
0ea884 1504       "\r\n" . rcube_label("receiptnote") . "\r\n";
T 1505     
1506     $ua = !empty($CONFIG['useragent']) ? $CONFIG['useragent'] : "RoundCube Webmail (Version ".RCMAIL_VERSION.")";
1507     $report = "Reporting-UA: $ua\r\n";
1508     
1509     if ($message['headers']->to)
1510         $report .= "Original-Recipient: {$message['headers']->to}\r\n";
1511     
1512     $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
1513                "Original-Message-ID: {$message['headers']->messageID}\r\n" .
1514                "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
1515     
1516     $compose->headers($headers, true);
1517     $compose->setTXTBody($body);
1518     $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
1519
1520     $sent = rcmail_deliver_message($compose, $identity['email'], $mailto);
1521
1522     if ($sent)
1523     {
1524       $IMAP->set_flag($message['UID'], 'MDNSENT');
1525       return true;
1526     }
1527   }
1528   
1529   return false;
1530 }
1531
1532
f11541 1533 // register UI objects
T 1534 $OUTPUT->add_handlers(array(
1535   'mailboxlist' => 'rcmail_mailbox_list',
1536   'messages' => 'rcmail_message_list',
1537   'messagecountdisplay' => 'rcmail_messagecount_display',
1538   'quotadisplay' => 'rcmail_quota_display',
1539   'messageheaders' => 'rcmail_message_headers',
1540   'messagebody' => 'rcmail_message_body',
1541   'messagecontentframe' => 'rcmail_messagecontent_frame',
1542   'messagepartframe' => 'rcmail_message_part_frame',
1543   'messagepartcontrols' => 'rcmail_message_part_controls',
47124c 1544   'searchform' => array($OUTPUT, 'search_form'),
f11541 1545 ));
T 1546
93be5b 1547 ?>