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