alecpl
2009-04-22 324f985e4784378d79b46263eea61846e706652c
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                     |
cbbef3 8  | Copyright (C) 2005-2009, 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
fba1f5 22 require_once('include/rcube_smtp.inc');
4e17e6 23
115263 24 $EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})';
39cd51 25
A 26 // actions that do not require imap connection
133bb0 27 $NOIMAP_ACTIONS = array('spell', 'addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment');
39cd51 28
A 29
30 // log in to imap server
31 if (!in_array($RCMAIL->action, $NOIMAP_ACTIONS) && !$RCMAIL->imap_connect()) {
32   $RCMAIL->kill_session();
33
34   if ($OUTPUT->ajax_call)
35     $OUTPUT->redirect(array(), 2000);
36
37   $OUTPUT->set_env('task', 'login');
38   $OUTPUT->send('login');
39 }
40
4e17e6 41
T 42 // set imap properties and session vars
b3ce79 43 if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
c57996 44   $IMAP->set_mailbox(($_SESSION['mbox'] = $mbox));
4e5b11 45 else
A 46   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
4e17e6 47
b3ce79 48 if (!empty($_GET['_page']))
c57996 49   $IMAP->set_page(($_SESSION['page'] = intval($_GET['_page'])));
4e17e6 50
6a35c8 51 // set default sort col/order to session
T 52 if (!isset($_SESSION['sort_col']))
53   $_SESSION['sort_col'] = $CONFIG['message_sort_col'];
54 if (!isset($_SESSION['sort_order']))
55   $_SESSION['sort_order'] = $CONFIG['message_sort_order'];
2bca6e 56
T 57 // set message set for search result
8d0758 58 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
1f020b 59   {
8d0758 60   $IMAP->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
1f020b 61   $OUTPUT->set_env('search_request', $_REQUEST['_search']);
S 62   $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
63   }
4e17e6 64
528514 65 // set main env variables, labels and page title
197601 66 if (empty($RCMAIL->action) || $RCMAIL->action == 'list')
528514 67   {
8abda5 68   $mbox_name = $IMAP->get_mailbox_name();
A 69
70   if (empty($RCMAIL->action))
71     {
72     // initialize searching result if search_filter is used
73     if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL')
74       {
75       $search_request = md5($mbox_name.$_SESSION['search_filter']);
76   
77       $IMAP->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $_SESSION['sort_col']);
78       $_SESSION['search'][$search_request] = $IMAP->get_search_set();
79       $OUTPUT->set_env('search_request', $search_request);
80       }
39cd51 81     
A 82       // make sure the message count is refreshed (for default view)
83       $IMAP->messagecount($mbox_name, 'ALL', true);
8abda5 84     }
A 85     
528514 86   // set current mailbox in client environment
8abda5 87   $OUTPUT->set_env('mailbox', $mbox_name);
528514 88   $OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
A 89   $OUTPUT->set_env('delimiter', $IMAP->get_hierarchy_delimiter());
90
91   if ($CONFIG['trash_mbox'])
92     $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
93   if ($CONFIG['drafts_mbox'])
94     $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
95   if ($CONFIG['junk_mbox'])
96     $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
97
98   if (!$OUTPUT->ajax_call)
112c91 99     $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage');
528514 100
4e5b11 101   $OUTPUT->set_pagetitle(rcmail_localize_foldername($mbox_name));
a86204 102   }
5eee00 103
4e17e6 104
45f56c 105 /**
T 106  * return the message list as HTML table
107  */
4e17e6 108 function rcmail_message_list($attrib)
T 109   {
f11541 110   global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT;
b076a4 111
4e17e6 112   $skin_path = $CONFIG['skin_path'];
e189a6 113   $image_tag = '<img src="%s%s" alt="%s" />';
b076a4 114
f3b659 115   // check to see if we have some settings for sorting
6a35c8 116   $sort_col   = $_SESSION['sort_col'];
T 117   $sort_order = $_SESSION['sort_order'];
24053e 118   
T 119   // add some labels to client
112c91 120   $OUTPUT->add_label('from', 'to');
f3b659 121
4e17e6 122   // get message headers
f3b659 123   $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
4e17e6 124
T 125   // add id to message list table if not specified
126   if (!strlen($attrib['id']))
127     $attrib['id'] = 'rcubemessagelist';
128
129   // allow the following attributes to be added to the <table> tag
130   $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
131
132   $out = '<table' . $attrib_str . ">\n";
e0ddd4 133
d59aaa 134   // define list of cols to be displayed based on parameter or config
A 135   if (empty($attrib['columns']))
136       $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
137   else
58687d 138       $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
d59aaa 139
A 140   // store column list in a session-variable
141   $_SESSION['list_columns'] = $a_show_cols;
142   
143   // define sortable columns
c8c1e0 144   $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
41bece 145
T 146   $mbox = $IMAP->get_mailbox_name();
4e17e6 147   
T 148   // show 'to' instead of from in sent messages
41bece 149   if (($mbox==$CONFIG['sent_mbox'] || $mbox==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
8c2e58 150       && !array_search('to', $a_show_cols))
4e17e6 151     $a_show_cols[$f] = 'to';
8c2e58 152   
e0ddd4 153   // add col definition
T 154   $out .= '<colgroup>';
01c86f 155   $out .= '<col class="icon" />';
e0ddd4 156
T 157   foreach ($a_show_cols as $col)
d59aaa 158     $out .= ($col!='attachment') ? sprintf('<col class="%s" />', $col) : '<col class="icon" />';
e0ddd4 159
T 160   $out .= "</colgroup>\n";
4e17e6 161
T 162   // add table title
163   $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
b076a4 164
f3b659 165   $javascript = '';
4e17e6 166   foreach ($a_show_cols as $col)
f3b659 167     {
T 168     // get column name
d59aaa 169     switch ($col)
A 170       {
171       case 'flag':
172         $col_name = sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], '');
173         break;
174       case 'attachment':
175         $col_name = sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '');
176         break;
177       default:
178         $col_name = Q(rcube_label($col));
179     }
f3b659 180
T 181     // make sort links
182     $sort = '';
2a9cb3 183     if (in_array($col, $a_sort_cols))
f3b659 184       {
1cded8 185       // have buttons configured
T 186       if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
187         {
188         $sort = '&nbsp;&nbsp;';
b076a4 189
1cded8 190         // asc link
T 191         if (!empty($attrib['sortascbutton']))
192           {
f11541 193           $sort .= $OUTPUT->button(array(
T 194             'command' => 'sort',
195             'prop' => $col.'_ASC',
196             'image' => $attrib['sortascbutton'],
197             'align' => 'absmiddle',
198             'title' => 'sortasc'));
1cded8 199           }       
b076a4 200         
1cded8 201         // desc link
T 202         if (!empty($attrib['sortdescbutton']))
203           {
f11541 204           $sort .= $OUTPUT->button(array(
T 205             'command' => 'sort',
206             'prop' => $col.'_DESC',
207             'image' => $attrib['sortdescbutton'],
208             'align' => 'absmiddle',
209             'title' => 'sortdesc'));
1cded8 210           }
T 211         }
212       // just add a link tag to the header
213       else
b076a4 214         {
41bece 215         $col_name = sprintf(
T 216           '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
217           JS_OBJECT_NAME,
218           $col,
219           rcube_label('sortby'),
220           $col_name);
b076a4 221         }
f3b659 222       }
b076a4 223       
T 224     $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
f3b659 225
T 226     // put it all together
d59aaa 227     if ($col!='attachment')
A 228       $out .= '<td class="'.$col.$sort_class.'" id="rcm'.$col.'">' . "$col_name$sort</td>\n";
229     else    
230       $out .= '<td class="icon" id="rcm'.$col.'">' . "$col_name$sort</td>\n";
f3b659 231     }
4e17e6 232
T 233   $out .= "</tr></thead>\n<tbody>\n";
234
235   // no messages in this mailbox
236   if (!sizeof($a_headers))
5eee00 237     $OUTPUT->show_message('nomessagesfound', 'notice');
4e17e6 238
T 239
240   $a_js_message_arr = array();
241
242   // create row for each message
243   foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
244     {
e189a6 245     $message_icon = $attach_icon = $flagged_icon = '';
4e17e6 246     $js_row_arr = array();
a164a2 247     $zebra_class = $i%2 ? ' even' : ' odd';
4e17e6 248
T 249     // set messag attributes to javascript array
6ec0a8 250     if ($header->deleted)
S 251       $js_row_arr['deleted'] = true;
4e17e6 252     if (!$header->seen)
T 253       $js_row_arr['unread'] = true;
254     if ($header->answered)
255       $js_row_arr['replied'] = true;
d73404 256     if ($header->forwarded)
A 257       $js_row_arr['forwarded'] = true;
e189a6 258     if ($header->flagged)
A 259       $js_row_arr['flagged'] = true;
260
6ec0a8 261     // set message icon  
S 262     if ($attrib['deletedicon'] && $header->deleted)
263       $message_icon = $attrib['deletedicon'];
d73404 264     else if ($attrib['repliedicon'] && $header->answered)
A 265       {
266       if ($attrib['forwardedrepliedicon'] && $header->forwarded)
267         $message_icon = $attrib['forwardedrepliedicon'];
268       else
269         $message_icon = $attrib['repliedicon'];
270       }
271     else if ($attrib['forwardedicon'] && $header->forwarded)
272       $message_icon = $attrib['forwardedicon'];
6ec0a8 273     else if ($attrib['unreadicon'] && !$header->seen)
4e17e6 274       $message_icon = $attrib['unreadicon'];
T 275     else if ($attrib['messageicon'])
276       $message_icon = $attrib['messageicon'];
e189a6 277
A 278     if ($attrib['flaggedicon'] && $header->flagged)
279       $flagged_icon = $attrib['flaggedicon'];
280     else if ($attrib['unflaggedicon'] && !$header->flagged)
281       $flagged_icon = $attrib['unflaggedicon'];
4e17e6 282     
f11541 283     // set attachment icon
7a2d79 284     if ($attrib['attachmenticon'] && preg_match("/multipart\/m/i", $header->ctype))
4e17e6 285       $attach_icon = $attrib['attachmenticon'];
T 286         
a164a2 287     $out .= sprintf('<tr id="rcmrow%d" class="message%s%s%s%s">'."\n",
15a9d1 288                     $header->uid,
T 289                     $header->seen ? '' : ' unread',
290                     $header->deleted ? ' deleted' : '',
e189a6 291                     $header->flagged ? ' flagged' : '',
a164a2 292                     $zebra_class);
15a9d1 293     
4e17e6 294     $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
e189a6 295
1088d6 296
5faac0 297     $IMAP->set_charset(!empty($header->charset) ? $header->charset : $CONFIG['default_charset']);
583850 298   
4e17e6 299     // format each col
T 300     foreach ($a_show_cols as $col)
301       {
302       if ($col=='from' || $col=='to')
8e44f4 303         $cont = Q(rcmail_address_string($header->$col, 3, false, $attrib['addicon']), 'show');
4e17e6 304       else if ($col=='subject')
b4b081 305         {
41bece 306         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
2b962c 307         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draft_uid' : '_uid';
44385f 308         $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160);
T 309         if (empty($cont)) $cont = rcube_label('nosubject');
310         $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), Q($cont));
b4b081 311         }
e189a6 312       else if ($col=='flag')
A 313         $cont = $flagged_icon ? sprintf($image_tag, $skin_path, $flagged_icon, '') : '';
4e17e6 314       else if ($col=='size')
T 315         $cont = show_bytes($header->$col);
316       else if ($col=='date')
f11541 317         $cont = format_date($header->date);
4e17e6 318       else
2bca6e 319         $cont = Q($header->$col);
4e17e6 320         
d59aaa 321       if ($col!='attachment')
A 322         $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
323       else
de2e0b 324         $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '&nbsp;');
4e17e6 325       }
T 326
327     $out .= "</tr>\n";
328     
329     if (sizeof($js_row_arr))
330       $a_js_message_arr[$header->uid] = $js_row_arr;
331     }
332   
333   // complete message table
334   $out .= "</tbody></table>\n";
335   
336   $message_count = $IMAP->messagecount();
337   
338   // set client env
f11541 339   $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
T 340   $OUTPUT->add_gui_object('messagelist', $attrib['id']);
341   $OUTPUT->set_env('messagecount', $message_count);
342   $OUTPUT->set_env('current_page', $IMAP->list_page);
343   $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
344   $OUTPUT->set_env('sort_col', $sort_col);
345   $OUTPUT->set_env('sort_order', $sort_order);
4e17e6 346   
T 347   if ($attrib['messageicon'])
f11541 348     $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
6ec0a8 349   if ($attrib['deletedicon'])
f11541 350     $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
4e17e6 351   if ($attrib['unreadicon'])
f11541 352     $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
4e17e6 353   if ($attrib['repliedicon'])
f11541 354     $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
d73404 355   if ($attrib['forwardedicon'])
A 356     $OUTPUT->set_env('forwardedicon', $skin_path . $attrib['forwardedicon']);
357   if ($attrib['forwardedrepliedicon'])
358     $OUTPUT->set_env('forwardedrepliedicon', $skin_path . $attrib['forwardedrepliedicon']);
4e17e6 359   if ($attrib['attachmenticon'])
f11541 360     $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
e189a6 361   if ($attrib['flaggedicon'])
A 362     $OUTPUT->set_env('flaggedicon', $skin_path . $attrib['flaggedicon']);
363   if ($attrib['unflaggedicon'])
364     $OUTPUT->set_env('unflaggedicon', $skin_path . $attrib['unflaggedicon']);
4e17e6 365   
ae895a 366   $OUTPUT->set_env('messages', $a_js_message_arr);
d24d20 367   $OUTPUT->set_env('coltypes', $a_show_cols);
f11541 368   
6b47de 369   $OUTPUT->include_script('list.js');
4e17e6 370   
T 371   return $out;
372   }
373
374
45f56c 375 /**
T 376  * return javascript commands to add rows to the message list
377  */
4e17e6 378 function rcmail_js_message_list($a_headers, $insert_top=FALSE)
T 379   {
f11541 380   global $CONFIG, $IMAP, $OUTPUT;
4e17e6 381
d59aaa 382   if (empty($_SESSION['list_columns']))
A 383     $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
384   else
385     $a_show_cols = $_SESSION['list_columns'];
386
41bece 387   $mbox = $IMAP->get_mailbox_name();
4e17e6 388
T 389   // show 'to' instead of from in sent messages
41bece 390   if (($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox'])
f11541 391       && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
4e17e6 392     $a_show_cols[$f] = 'to';
T 393
c4b819 394   $browser = new rcube_browser;
A 395
f11541 396   $OUTPUT->command('set_message_coltypes', $a_show_cols);
324f98 397   if ($browser->ie && !$insert_top)
c4b819 398     $OUTPUT->command('offline_message_list', true);
25d8ba 399
4e17e6 400   // loop through message headers
ecd2e7 401   foreach ($a_headers as $n => $header)
4e17e6 402     {
T 403     $a_msg_cols = array();
404     $a_msg_flags = array();
ecd2e7 405     
T 406     if (empty($header))
407       continue;
f11541 408
5faac0 409     $IMAP->set_charset(!empty($header->charset) ? $header->charset : $CONFIG['default_charset']);
583850 410
d59aaa 411     // remove 'attachment' and 'flag' columns, we don't need them here
A 412     if(($key = array_search('attachment', $a_show_cols)) !== FALSE)
413       unset($a_show_cols[$key]);
414     if(($key = array_search('flag', $a_show_cols)) !== FALSE)
415       unset($a_show_cols[$key]);
416
4e17e6 417     // format each col; similar as in rcmail_message_list()
T 418     foreach ($a_show_cols as $col)
419       {
420       if ($col=='from' || $col=='to')
583850 421         $cont = Q(rcmail_address_string($header->$col, 3), 'show');
4e17e6 422       else if ($col=='subject')
7bbd5f 423         {
41bece 424         $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
2b962c 425         $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draft_uid' : '_uid';
44385f 426         $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160);
T 427         if (!$cont) $cont = rcube_label('nosubject');
428         $cont = sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), Q($cont));
7bbd5f 429         }
4e17e6 430       else if ($col=='size')
T 431         $cont = show_bytes($header->$col);
432       else if ($col=='date')
f11541 433         $cont = format_date($header->date);
4e17e6 434       else
2bca6e 435         $cont = Q($header->$col);
4e17e6 436           
T 437       $a_msg_cols[$col] = $cont;
438       }
439
c4b819 440     if ($header->deleted)
A 441       $a_msg_flags['deleted'] = 1;
442     if (!$header->seen)
443       $a_msg_flags['unread'] = 1;
444     if ($header->answered)
445       $a_msg_flags['replied'] = 1;
446     if ($header->forwarded)
447       $a_msg_flags['forwarded'] = 1;
448     if ($header->flagged)
449       $a_msg_flags['flagged'] = 1;
e189a6 450     
f11541 451     $OUTPUT->command('add_message_row',
T 452       $header->uid,
453       $a_msg_cols,
454       $a_msg_flags,
455       preg_match("/multipart\/m/i", $header->ctype),
456       $insert_top);
4e17e6 457     }
c4b819 458
324f98 459     if ($browser->ie && !$insert_top)
c4b819 460       $OUTPUT->command('offline_message_list', false);
4e17e6 461   }
T 462
463
45f56c 464 /**
T 465  * return an HTML iframe for loading mail content
466  */
b19097 467 function rcmail_messagecontent_frame($attrib)
T 468   {
f11541 469   global $OUTPUT;
b19097 470   
T 471   if (empty($attrib['id']))
472     $attrib['id'] = 'rcmailcontentwindow';
473
e2c610 474   $attrib['name'] = $attrib['id'];
b19097 475
e2c610 476   $OUTPUT->set_env('contentframe', $attrib['id']);
f11541 477   $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
b19097 478
95fcc3 479   return html::iframe($attrib);
b19097 480   }
T 481
4e17e6 482
45f56c 483 /**
T 484  *
485  */
4e17e6 486 function rcmail_messagecount_display($attrib)
T 487   {
f11541 488   global $IMAP, $OUTPUT;
4e17e6 489   
T 490   if (!$attrib['id'])
491     $attrib['id'] = 'rcmcountdisplay';
492
f11541 493   $OUTPUT->add_gui_object('countdisplay', $attrib['id']);
4e17e6 494
e2c610 495   return html::span($attrib, rcmail_get_messagecount_text());
4e17e6 496   }
T 497
498
45f56c 499 /**
T 500  *
501  */
58e360 502 function rcmail_quota_display($attrib)
T 503   {
f11541 504   global $OUTPUT, $COMM_PATH;
58e360 505
T 506   if (!$attrib['id'])
507     $attrib['id'] = 'rcmquotadisplay';
508
6d2714 509   if(isset($attrib['display']))
A 510     $_SESSION['quota_display'] = $attrib['display'];
511
f11541 512   $OUTPUT->add_gui_object('quotadisplay', $attrib['id']);
58e360 513
6d20d0 514   return html::span($attrib, rcmail_quota_content(NULL, $attrib));
23796e 515   }
S 516
517
45f56c 518 /**
T 519  *
520  */
6cd009 521 function rcmail_quota_content($quota=NULL, $attrib=NULL)
23796e 522   {
876b15 523   global $IMAP, $COMM_PATH, $RCMAIL;
3ea0e3 524
6d2714 525   $display = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
A 526
527   if (is_array($quota) && !empty($quota['used']) && !empty($quota['total']))
3ea0e3 528     {
6d2714 529       if (!isset($quota['percent']))
A 530         $quota['percent'] = $quota['used'] / $quota['total'];
531     }
532   elseif (!$IMAP->get_capability('QUOTA'))
533     return rcube_label('unknown');
534   else
535     $quota = $IMAP->get_quota();
536
876b15 537   if ($quota && !($quota['total']==0 && $RCMAIL->config->get('quota_zero_as_unlimited')))
6d2714 538     {
A 539     $quota_text = sprintf('%s / %s (%.0f%%)',
540                           show_bytes($quota['used'] * 1024),
541                           show_bytes($quota['total'] * 1024),
542                           $quota['percent']);
3ea0e3 543
T 544     // show quota as image (by Brett Patterson)
23796e 545     if ($display == 'image' && function_exists('imagegif'))
3ea0e3 546       {
6cd009 547       if (!$attrib['width'])
A 548         $attrib['width'] = isset($_SESSION['quota_width']) ? $_SESSION['quota_width'] : 100;
549       else
550     $_SESSION['quota_width'] = $attrib['width'];
551
552       if (!$attrib['height'])
553         $attrib['height'] = isset($_SESSION['quota_height']) ? $_SESSION['quota_height'] : 14;
554       else
555     $_SESSION['quota_height'] = $attrib['height'];
556         
f11541 557       $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 558                             $quota['used'], $quota['total'],
fda695 559                             $attrib['width'], $attrib['height'],
T 560                             $attrib['width'], $attrib['height'],
561                             $quota_text,
6cd009 562                             show_bytes($quota['used'] * 1024),
A 563                             show_bytes($quota['total'] * 1024));
3ea0e3 564       }
T 565     }
566   else
4647e1 567     $quota_text = rcube_label('unlimited');
58e360 568
23796e 569   return $quota_text;
58e360 570   }
T 571
4e17e6 572
45f56c 573 /**
T 574  *
575  */
4647e1 576 function rcmail_get_messagecount_text($count=NULL, $page=NULL)
4e17e6 577   {
T 578   global $IMAP, $MESSAGE;
579   
8fa58e 580   if (isset($MESSAGE->index))
4e17e6 581     {
T 582     return rcube_label(array('name' => 'messagenrof',
8fa58e 583                              'vars' => array('nr'  => $MESSAGE->index+1,
4647e1 584                                              'count' => $count!==NULL ? $count : $IMAP->messagecount())));
4e17e6 585     }
31b2ce 586
4647e1 587   if ($page===NULL)
T 588     $page = $IMAP->list_page;
589     
590   $start_msg = ($page-1) * $IMAP->page_size + 1;
591   $max = $count!==NULL ? $count : $IMAP->messagecount();
4e17e6 592
T 593   if ($max==0)
594     $out = rcube_label('mailboxempty');
595   else
596     $out = rcube_label(array('name' => 'messagesfromto',
597                               'vars' => array('from'  => $start_msg,
598                                               'to'    => min($max, $start_msg + $IMAP->page_size - 1),
599                                               'count' => $max)));
600
2bca6e 601   return Q($out);
4e17e6 602   }
T 603
ac5d15 604 /**
T 605  *
606  */
607 function rcmail_mailbox_name_display($attrib)
608 {
609     global $RCMAIL;
610
611     if (!$attrib['id'])
612         $attrib['id'] = 'rcmmailboxname';
613
614     $RCMAIL->output->add_gui_object('mailboxname', $attrib['id']);
615
616     return html::span($attrib, rcmail_get_mailbox_name_text());
617 }
618
619 function rcmail_get_mailbox_name_text()
620 {
621     global $RCMAIL;
622     return rcmail_localize_foldername($RCMAIL->imap->get_mailbox_name());
623 }
624
ec603f 625 /**
A 626  * Sets message is_safe flag according to 'show_images' option value
627  *
628  * @param object rcube_message Message
629  */
630 function rcmail_check_safe(&$message)
631 {
632   global $RCMAIL;
633
634   $show_images = $RCMAIL->config->get('show_images');
635   if (!$message->is_safe
636     && !empty($show_images)
637     && $message->has_html_part())
638   {
639     switch($show_images) {
640       case '1': // known senders only
8a78a1 641         $CONTACTS = new rcube_contacts($RCMAIL->db, $_SESSION['user_id']);
ec603f 642         if ($CONTACTS->search('email', $message->sender['mailto'], true, false)->count) {
A 643           $message->set_safe(true);
644         }
645       break;
646       case '2': // always
647         $message->set_safe(true);
648       break;
649     }
650   }
651 }
652
653 /**
654  * Cleans up the given message HTML Body (for displaying)
655  *
656  * @param string HTML
657  * @param array  Display parameters 
658  * @param array  CID map replaces (inline images)
659  * @return string Clean HTML
660  */
661 function rcmail_wash_html($html, $p = array(), $cid_replaces)
662 {
663   global $REMOTE_OBJECTS;
664   
665   $p += array('safe' => false, 'inline_html' => true);
666   
667   // special replacements (not properly handled by washtml class)
668   $html_search = array(
669     '/(<\/nobr>)(\s+)(<nobr>)/i',    // space(s) between <NOBR>
670     '/(<[\/]*st1:[^>]+>)/i',        // Microsoft's Smart Tags <ST1>
671     '/<\/?rte_text>/i',            // Rich Text Editor tags (#1485647)
672     '/<title>.*<\/title>/i',        // PHP bug #32547 workaround: remove title tag
673     '/<html[^>]*>/im',            // malformed html: remove html tags (#1485139)
674     '/<\/html>/i',            // malformed html: remove html tags (#1485139)
3bde30 675     '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/',    // byte-order mark (only outlook?)
ec603f 676   );
A 677   $html_replace = array(
678     '\\1'.' &nbsp; '.'\\3',
679     '',
680     '',
681     '',
682     '',
683     '',
3bde30 684     '',
ec603f 685   );
A 686   $html = preg_replace($html_search, $html_replace, $html);
687
688   // charset was converted to UTF-8 in rcube_imap::get_message_part() -> change charset specification in HTML accordingly
689   $charset_pattern = '/(\s+content=[\'"]?\w+\/\w+;\s*charset)=([a-z0-9-_]+)/i';
690   if (preg_match($charset_pattern, $html)) {
691     $html = preg_replace($charset_pattern, '\\1='.RCMAIL_CHARSET, $html);
692   }
693   else {
694     // add head for malformed messages, washtml cannot work without that
695     if (!preg_match('/<head[^>]*>(.*)<\/head>/Uims', $html))
696       $html = '<head></head>'. $html;
697     $html = substr_replace($html, '<meta http-equiv="content-type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0);
698   }
699     
700   // turn relative into absolute urls
701   $html = rcmail_resolve_base($html);
702
703   // clean HTML with washhtml by Frederic Motte
704   $wash_opts = array(
705     'show_washed' => false,
706     'allow_remote' => $p['safe'],
707     'blocked_src' => "./program/blocked.gif",
708     'charset' => RCMAIL_CHARSET,
709     'cid_map' => $cid_replaces,
710     'html_elements' => array('body'),
711   );
712     
713   if (!$p['inline_html']) {
714     $wash_opts['html_elements'] = array('html','head','title','body');
715   }
716   if ($p['safe']) {
717     $wash_opts['html_elements'][] = 'link';
718     $wash_opts['html_attribs'] = array('rel','type');
719   }
720     
721   $washer = new washtml($wash_opts);
722   $washer->add_callback('form', 'rcmail_washtml_callback');
723
724   if ($p['safe']) {  // allow CSS styles, will be sanitized by rcmail_washtml_callback()
725     $washer->add_callback('style', 'rcmail_washtml_callback');
726   }
727     
728   $html = $washer->wash($html);
729   $REMOTE_OBJECTS = $washer->extlinks;
730   
731   return $html;
732 }
733
4e17e6 734
45f56c 735 /**
65cc1c 736  * Convert the given message part to proper HTML
T 737  * which can be displayed the message view
45f56c 738  *
65cc1c 739  * @param object rcube_message_part Message part
ec603f 740  * @param array  Display parameters array 
65cc1c 741  * @return string Formatted HTML string
45f56c 742  */
21e724 743 function rcmail_print_body($part, $p = array())
45f56c 744 {
cc97ea 745   global $RCMAIL;
T 746   
747   // trigger plugin hook
748   $data = $RCMAIL->plugins->exec_hook('message_part_before',
749     array('type' => $part->ctype_secondary, 'body' => $part->body) + $p + array('safe' => false, 'plain' => false, 'inline_html' => true));
ec603f 750
5cc4b1 751   // convert html to text/plain
cc97ea 752   if ($data['type'] == 'html' && $data['plain']) {
T 753     $txt = new html2text($data['body'], false, true);
5cc4b1 754     $body = $txt->get_text();
T 755     $part->ctype_secondary = 'plain';
45f56c 756   }
4e17e6 757   // text/html
cc97ea 758   else if ($data['type'] == 'html') {
T 759     $body = rcmail_wash_html($data['body'], $data, $part->replaces);
760     $part->ctype_secondary = $data['type'];
45f56c 761   }
4e17e6 762   // text/enriched
cc97ea 763   else if ($data['type'] == 'enriched') {
cfe4a6 764     $part->ctype_secondary = 'html';
ec603f 765     require_once('lib/enriched.inc');
cc97ea 766     $body = Q(enriched_to_html($data['body']), 'show');
45f56c 767   }
cc97ea 768   else {
T 769     // assert plaintext
45f56c 770     $body = $part->body;
cc97ea 771     $part->ctype_secondary = $data['type'] = 'plain';
45f56c 772   }
cc97ea 773   
T 774   // free some memory (hopefully)
775   unset($data['body']);
45f56c 776
278064 777
cc97ea 778   // plaintext postprocessing
T 779   if ($part->ctype_secondary == 'plain') {
780     // make links and email-addresses clickable
781     $replacements = new rcube_string_replacer;
782     
783     $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
784     $url_chars_within = '\?\.~,!';
785
786     // search for patterns like links and e-mail addresses
787     $body = preg_replace_callback("/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
788     $body = preg_replace_callback("/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
789     $body = preg_replace_callback('/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i', array($replacements, 'mailto_callback'), $body);
790
791     // split body into single lines
792     $a_lines = preg_split('/\r?\n/', $body);
793     $quote_level = 0;
794
795     // colorize quoted parts
796     for ($n=0; $n < count($a_lines); $n++) {
797       $line = $a_lines[$n];
798       $quotation = '';
799       $q = 0;
800     
801       if (preg_match('/^(>+\s*)+/', $line, $regs)) {
802         $q    = strlen(preg_replace('/\s/', '', $regs[0]));
803         $line = substr($line, strlen($regs[0]));
804
805         if ($q > $quote_level)
806           $quotation = str_repeat('<blockquote>', $q - $quote_level);
807         else if ($q < $quote_level)
808           $quotation = str_repeat("</blockquote>", $quote_level - $q);
809       }
810       else if ($quote_level > 0)
811         $quotation = str_repeat("</blockquote>", $quote_level);
812
813       $quote_level = $q;
814       $a_lines[$n] = $quotation . Q($line, 'replace', false);  // htmlquote plaintext
815     }
816
817     // insert the links for urls and mailtos
818     $body = $replacements->resolve(join("\n", $a_lines));
819   }
820   
821   // allow post-processing of the message body
822   $data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body) + $data);
823
824   return $data['type'] == 'html' ? $data['body'] : html::tag('pre', array(), $data['body']);
21e724 825 }
4e17e6 826
ec603f 827
45f56c 828 /**
T 829  * add a string to the replacement array and return a replacement string
830  */
4e17e6 831 function rcmail_str_replacement($str, &$rep)
21e724 832 {
4e17e6 833   static $count = 0;
T 834   $rep[$count] = stripslashes($str);
835   return "##string_replacement{".($count++)."}##";
21e724 836 }
4e17e6 837
21e724 838
T 839 /**
840  * Callback function for washtml cleaning class
841  */
842 function rcmail_washtml_callback($tagname, $attrib, $content)
843 {
844   switch ($tagname) {
845     case 'form':
846       $out = html::div('form', $content);
f54a3a 847       break;
T 848       
1c499a 849     case 'style':
T 850       // decode all escaped entities and reduce to ascii strings
36c236 851       $stripped = preg_replace('/[^a-zA-Z\(:]/', '', rcmail_xss_entitiy_decode($content));
1c499a 852       
36c236 853       // now check for evil strings like expression, behavior or url()
T 854       if (!preg_match('/expression|behavior|url\(|import/', $stripped)) {
1c499a 855         $out = html::tag('style', array('type' => 'text/css'), $content);
T 856         break;
857       }
858     
21e724 859     default:
T 860       $out = '';
861   }
862   
863   return $out;
864 }
4e17e6 865
T 866
45f56c 867 /**
T 868  * return table with message headers
869  */
4e17e6 870 function rcmail_message_headers($attrib, $headers=NULL)
T 871   {
cc97ea 872   global $IMAP, $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL;
4e17e6 873   static $sa_attrib;
T 874   
875   // keep header table attrib
876   if (is_array($attrib) && !$sa_attrib)
877     $sa_attrib = $attrib;
878   else if (!is_array($attrib) && is_array($sa_attrib))
879     $attrib = $sa_attrib;
880   
881   if (!isset($MESSAGE))
882     return FALSE;
883
884   // get associative array of headers object
885   if (!$headers)
8fa58e 886     $headers = is_object($MESSAGE->headers) ? get_object_vars($MESSAGE->headers) : $MESSAGE->headers;
cfe4a6 887     
4e17e6 888   // show these headers
591381 889   $standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto', 'date');
cc97ea 890   $output_headers = array();
e5686f 891
cc97ea 892   foreach ($standard_headers as $hkey) {
4e17e6 893     if (!$headers[$hkey])
T 894       continue;
895
cc97ea 896     if ($hkey == 'date') {
5b1de5 897       if ($PRINT_MODE)
cc97ea 898         $header_value = format_date($headers[$hkey], $RCMAIL->config->get('date_long', 'x'));
5b1de5 899       else
A 900         $header_value = format_date($headers[$hkey]);
cc97ea 901     }
T 902     else if ($hkey == 'replyto') {
700320 903       if ($headers['replyto'] != $headers['from'])
cc97ea 904         $header_value = rcmail_address_string($headers['replyto'], null, true, $attrib['addicon']);
700320 905       else
A 906         continue;
4e17e6 907     }
cc97ea 908     else if (in_array($hkey, array('from', 'to', 'cc', 'bcc')))
T 909       $header_value = rcmail_address_string($headers[$hkey], null, true, $attrib['addicon']);
910     else if ($hkey == 'subject' && empty($headers[$hkey]))
911       $header_value = rcube_label('nosubject');
912     else
913       $header_value = trim($IMAP->decode_header($headers[$hkey]));
914       
915     $output_headers[$hkey] = array('title' => rcube_label($hkey), 'value' => $header_value, 'raw' => $headers[$hkey]);
916   }
917     
918   $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', array('output' => $output_headers, 'headers' => $MESSAGE->headers));
919   
920   // compose html table
921   $table = new html_table(array('cols' => 2));
922   
923   foreach ($plugin['output'] as $hkey => $row) {
924     $table->add(array('class' => 'header-title'), Q($row['title']));
925     $table->add(array('class' => $hkey, 'width' => "90%"), Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show')));
926   }
4e17e6 927
e5686f 928   // all headers division
cc97ea 929   $table->add(array('colspan' => 2, 'class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('load-headers','',this)"), '');
T 930   $table->add_row(array('id' => "all-headers"));
931   $table->add(array('colspan' => 2, 'class' => "all"), html::div(array('id' => 'headers-source'), ''));
932   
e5686f 933   $OUTPUT->add_gui_object('all_headers_row', 'all-headers');
A 934   $OUTPUT->add_gui_object('all_headers_box', 'headers-source');
935
cc97ea 936   return $table->show($attrib);
4e17e6 937   }
T 938
939
45f56c 940 /**
21605c 941  * Handler for the 'messagebody' GUI object
45f56c 942  *
21605c 943  * @param array Named parameters
T 944  * @return string HTML content showing the message body
45f56c 945  */
4e17e6 946 function rcmail_message_body($attrib)
T 947   {
8fa58e 948   global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $REMOTE_OBJECTS;
5f8686 949
8fa58e 950   if (!is_array($MESSAGE->parts) && empty($MESSAGE->body))
4e17e6 951     return '';
T 952     
953   if (!$attrib['id'])
954     $attrib['id'] = 'rcmailMsgBody';
955
8fa58e 956   $safe_mode = $MESSAGE->is_safe || intval($_GET['_safe']);
21605c 957   $out = '';
4e17e6 958   
T 959   $header_attrib = array();
960   foreach ($attrib as $attr => $value)
961     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
962       $header_attrib[$regs[1]] = $value;
963
8fa58e 964   if (!empty($MESSAGE->parts))
4e17e6 965     {
8fa58e 966     foreach ($MESSAGE->parts as $i => $part)
4e17e6 967       {
8fa58e 968       if ($part->type == 'headers')
8d4bcd 969         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
8fa58e 970       else if ($part->type == 'content')
4e17e6 971         {
8d4bcd 972         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
8fa58e 973           $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
a0109c 974
8d4bcd 975         // fetch part if not available
T 976         if (!isset($part->body))
8fa58e 977           $part->body = $MESSAGE->get_part_content($part->mime_id);
a0109c 978
21e724 979         $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
5f8686 980
cfe4a6 981         if ($part->ctype_secondary == 'html')
21605c 982           $out .= html::div('message-htmlpart', rcmail_html4inline($body, $attrib['id']));
a2f2c5 983         else
21605c 984           $out .= html::div('message-part', $body);
4e17e6 985         }
T 986       }
987     }
988   else
278064 989     $out .= html::div('message-part', html::tag('pre', array(), Q($MESSAGE->body)));
4e17e6 990
T 991
8fa58e 992   $ctype_primary = strtolower($MESSAGE->structure->ctype_primary);
T 993   $ctype_secondary = strtolower($MESSAGE->structure->ctype_secondary);
166b61 994
4e17e6 995   // list images after mail body
be5f03 996   if ($CONFIG['inline_images']
2da368 997       && $ctype_primary == 'multipart'
T 998       && !empty($MESSAGE->attachments) 
999       && !strstr($message_body, '<html'))
166b61 1000     {
8fa58e 1001     foreach ($MESSAGE->attachments as $attach_prop) {
T 1002       if (strpos($attach_prop->mimetype, 'image/') === 0) {
1003         $out .= html::tag('hr') . html::p(array('align' => "center"),
1004           html::img(array(
1005             'src' => $MESSAGE->get_part_url($attach_prop->mime_id),
1006             'title' => $attach_prop->filename,
1007             'alt' => $attach_prop->filename,
1008           )));
1009         }
4e17e6 1010     }
8fa58e 1011   }
4e17e6 1012   
T 1013   // tell client that there are blocked remote objects
1014   if ($REMOTE_OBJECTS && !$safe_mode)
f11541 1015     $OUTPUT->set_env('blockedobjects', true);
4e17e6 1016
21605c 1017   return html::div($attrib, $out);
4e17e6 1018   }
T 1019
1020
aa055c 1021 /**
T 1022  * Convert all relative URLs according to a <base> in HTML
1023  */
1024 function rcmail_resolve_base($body)
1025 {
1026   // check for <base href=...>
1027   if (preg_match('!(<base.*href=["\']?)([hftps]{3,5}://[a-z0-9/.%-]+)!i', $body, $regs)) {
1028     $replacer = new rcube_base_replacer($regs[2]);
1029
1030     // replace all relative paths
1031     $body = preg_replace_callback('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Ui', array($replacer, 'callback'), $body);
1032     $body = preg_replace_callback('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Ui', array($replacer, 'callback'), $body);
1033   }
1034
1035   return $body;
1036 }
4e17e6 1037
45f56c 1038 /**
T 1039  * modify a HTML message that it can be displayed inside a HTML page
1040  */
1041 function rcmail_html4inline($body, $container_id)
4e17e6 1042   {
T 1043   $last_style_pos = 0;
1044   $body_lc = strtolower($body);
1045   
1046   // find STYLE tags
1047   while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
1048     {
ea206d 1049     $pos = strpos($body_lc, '>', $pos)+1;
T 1050
4e17e6 1051     // replace all css definitions with #container [def]
aa055c 1052     $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id);
ea206d 1053
3b12ae 1054     $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
S 1055     $body_lc = strtolower($body);
4e17e6 1056     $last_style_pos = $pos2;
5e98e1 1057     }
4e17e6 1058
fe79b1 1059   // modify HTML links to open a new window if clicked
115263 1060   $GLOBALS['rcmail_html_container_id'] = $container_id;
T 1061   $body = preg_replace_callback('/<(a|link)\s+([^>]+)>/Ui', 'rcmail_alter_html_link', $body);
1062   unset($GLOBALS['rcmail_html_container_id']);
4e17e6 1063
T 1064   // add comments arround html and other tags
06895c 1065   $out = preg_replace(array(
d7a411 1066       '/(<!DOCTYPE[^>]*>)/i',
A 1067       '/(<\?xml[^>]*>)/i',
06895c 1068       '/(<\/?html[^>]*>)/i',
T 1069       '/(<\/?head[^>]*>)/i',
1070       '/(<title[^>]*>.*<\/title>)/Ui',
1071       '/(<\/?meta[^>]*>)/i'),
1072     '<!--\\1-->',
1073     $body);
a0109c 1074
97bd2c 1075   $out = preg_replace(
45f56c 1076     array('/<body([^>]*)>/i', '/<\/body>/i'),
T 1077     array('<div class="rcmBody"\\1>', '</div>'),
97bd2c 1078     $out);
a0109c 1079
86958f 1080   // quote <? of php and xml files that are specified as text/html
T 1081   $out = preg_replace(array('/<\?/', '/\?>/'), array('&lt;?', '?&gt;'), $out);
1082
4e17e6 1083   return $out;
T 1084   }
1085
1086
45f56c 1087 /**
T 1088  * parse link attributes and set correct target
1089  */
115263 1090 function rcmail_alter_html_link($matches)
e5af2f 1091 {
115263 1092   global $EMAIL_ADDRESS_PATTERN;
T 1093   
1094   $tag = $matches[1];
1095   $attrib = parse_attrib_string($matches[2]);
e5af2f 1096   $end = '>';
84f5b7 1097
e5af2f 1098   if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) {
115263 1099     $attrib['href'] = "./bin/modcss.php?u=" . urlencode($attrib['href']) . "&amp;c=" . urlencode($GLOBALS['rcmail_html_container_id']);
e5af2f 1100     $end = ' />';
T 1101   }
115263 1102   else if (preg_match("/^mailto:$EMAIL_ADDRESS_PATTERN/i", $attrib['href'], $mailto)) {
T 1103     $attrib['href'] = $mailto[0];
97bd2c 1104     $attrib['onclick'] = sprintf(
T 1105       "return %s.command('compose','%s',this)",
1106       JS_OBJECT_NAME,
115263 1107       JQ($mailto[1]));
4e17e6 1108   }
e5af2f 1109   else if (!empty($attrib['href']) && $attrib['href'][0] != '#') {
T 1110     $attrib['target'] = '_blank';
1111   }
1112
1113   return "<$tag" . html::attrib_string($attrib, array('href','name','target','onclick','id','class','style','title','rel','type','media')) . $end;
1114 }
4e17e6 1115
T 1116
45f56c 1117 /**
T 1118  * decode address string and re-format it as HTML links
1119  */
8e44f4 1120 function rcmail_address_string($input, $max=null, $linked=false, $addicon=null)
T 1121 {
f11541 1122   global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $EMAIL_ADDRESS_PATTERN;
1088d6 1123
4e17e6 1124   $a_parts = $IMAP->decode_address_list($input);
T 1125
1126   if (!sizeof($a_parts))
1127     return $input;
1128
1129   $c = count($a_parts);
1130   $j = 0;
1131   $out = '';
1132
8e44f4 1133   foreach ($a_parts as $part) {
4e17e6 1134     $j++;
8e44f4 1135     if ($PRINT_MODE) {
90cd45 1136       $out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
8e44f4 1137     }
115263 1138     else if (preg_match("/$EMAIL_ADDRESS_PATTERN/i", $part['mailto'])) {
8e44f4 1139       if ($linked) {
T 1140         $out .= html::a(array(
1141             'href' => 'mailto:'.$part['mailto'],
1142             'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($part['mailto'])),
1143             'title' => $part['mailto'],
1144             'class' => "rcmContactAddress",
1145           ),
1146         Q($part['name']));
4e17e6 1147       }
8e44f4 1148       else {
T 1149         $out .= html::span(array('title' => $part['mailto'], 'class' => "rcmContactAddress"), Q($part['name']));
1150       }
1151
1152       if ($addicon) {
1153         $out .= '&nbsp;' . html::a(array(
1154             'href' => "#add",
1155             'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, urlencode($part['string'])),
1156             'title' => rcube_label('addtoaddressbook'),
1157           ),
1158           html::img(array(
1159             'src' => $CONFIG['skin_path'] . $addicon,
1160             'alt' => "Add contact",
1161           )));
1162       }
1163     }
1164     else {
4e17e6 1165       if ($part['name'])
2bca6e 1166         $out .= Q($part['name']);
4e17e6 1167       if ($part['mailto'])
28bfe4 1168         $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($part['mailto']));
8e44f4 1169     }
4e17e6 1170       
T 1171     if ($c>$j)
1172       $out .= ','.($max ? '&nbsp;' : ' ');
1173         
8e44f4 1174     if ($max && $j==$max && $c>$j) {
4e17e6 1175       $out .= '...';
T 1176       break;
1177     }
8e44f4 1178   }
4e17e6 1179     
T 1180   return $out;
8e44f4 1181 }
4e17e6 1182
T 1183
ccd63c 1184 /**
T 1185  * Wrap text to a given number of characters per line
1186  * but respect the mail quotation of replies messages (>)
1187  *
1188  * @param string Text to wrap
1189  * @param int The line width
1190  * @return string The wrapped text
1191  */
1192 function rcmail_wrap_quoted($text, $max = 76)
1193 {
1194   // Rebuild the message body with a maximum of $max chars, while keeping quoted message.
1195   $lines = preg_split('/\r?\n/', trim($text));
1196   $out = '';
1197
1198   foreach ($lines as $line) {
1199     if (strlen($line) > $max) {
1200       if (preg_match('/^([>\s]+)/', $line, $regs)) {
1201         $length = strlen($regs[0]);
1202         $prefix = substr($line, 0, $length);
1203
1204         // Remove '> ' from the line, then wordwrap() the line
7145e0 1205         $line = rc_wordwrap(substr($line, $length), $max - $length);
ccd63c 1206
T 1207         // Rebuild the line with '> ' at the beginning of each 'subline'
1208         $newline = '';
1209         foreach (explode("\n", $line) as $l) {
1210           $newline .= $prefix . $l . "\n";
1211         }
1212
1213         // Remove the righest newline char
1214         $line = rtrim($newline);
1215       }
1216       else {
7145e0 1217         $line = rc_wordwrap($line, $max);
ccd63c 1218       }
T 1219     }
1220
1221     // Append the line
1222     $out .= $line . "\n";
1223   }
1224   
1225   return $out;
1226 }
1227
1228
4e17e6 1229 function rcmail_message_part_controls()
T 1230   {
8fa58e 1231   global $MESSAGE;
4e17e6 1232   
d5342a 1233   $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
8fa58e 1234   if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part])
4e17e6 1235     return '';
T 1236     
8fa58e 1237   $part = $MESSAGE->mime_parts[$part];
T 1238   $table = new html_table(array('cols' => 3));
4e17e6 1239   
8fa58e 1240   if (!empty($part->filename)) {
T 1241     $table->add('title', Q(rcube_label('filename')));
1242     $table->add(null, Q($part->filename));
8dc048 1243     $table->add(null, '[' . html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']');
8fa58e 1244   }
4e17e6 1245   
8fa58e 1246   if (!empty($part->size)) {
T 1247     $table->add('title', Q(rcube_label('filesize')));
1248     $table->add(null, Q(show_bytes($part->size)));
1249   }
4e17e6 1250   
8fa58e 1251   return $table->show($attrib);
4e17e6 1252   }
T 1253
1254
1255
1256 function rcmail_message_part_frame($attrib)
1257   {
1258   global $MESSAGE;
1259   
8fa58e 1260   $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
4e17e6 1261   $ctype_primary = strtolower($part->ctype_primary);
T 1262
95fcc3 1263   $attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
4e17e6 1264
95fcc3 1265   return html::iframe($attrib);
4e17e6 1266   }
T 1267
1268
45f56c 1269 /**
T 1270  * clear message composing settings
1271  */
4e17e6 1272 function rcmail_compose_cleanup()
T 1273   {
1274   if (!isset($_SESSION['compose']))
1275     return;
70d4b9 1276
cc97ea 1277   rcmail::get_instance()->plugins->exec_hook('cleanup_attachments',array());
4e17e6 1278   
T 1279   unset($_SESSION['compose']);
1280   }
fba1f5 1281   
T 1282
1283 /**
1284  * Send the given message compose object using the configured method
1285  */
1286 function rcmail_deliver_message(&$message, $from, $mailto)
1287 {
a22cb6 1288   global $CONFIG, $RCMAIL;
fba1f5 1289
T 1290   $msg_body = $message->get();
8fa58e 1291   $headers = $message->headers();
79aeb3 1292
fba1f5 1293   // send thru SMTP server using custom SMTP library
T 1294   if ($CONFIG['smtp_server'])
1295     {
1296     // generate list of recipients
1297     $a_recipients = array($mailto);
1298   
1299     if (strlen($headers['Cc']))
1300       $a_recipients[] = $headers['Cc'];
1301     if (strlen($headers['Bcc']))
1302       $a_recipients[] = $headers['Bcc'];
1303   
1304     // clean Bcc from header for recipients
1305     $send_headers = $headers;
1306     unset($send_headers['Bcc']);
7ec922 1307     // here too, it because txtHeaders() below use $message->_headers not only $send_headers
A 1308     unset($message->_headers['Bcc']);
fba1f5 1309
T 1310     // send message
1311     $smtp_response = array();
7ec922 1312     $sent = smtp_mail($from, $a_recipients, ($foo = $message->txtHeaders($send_headers, true)), $msg_body, $smtp_response);
fba1f5 1313
T 1314     // log error
1315     if (!$sent)
1316       raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
1317                         'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
1318     }
1319   
1320   // send mail using PHP's mail() function
1321   else
1322     {
1323     // unset some headers because they will be added by the mail() function
1324     $headers_enc = $message->headers($headers);
1325     $headers_php = $message->_headers;
1326     unset($headers_php['To'], $headers_php['Subject']);
1327     
1328     // reset stored headers and overwrite
1329     $message->_headers = array();
1330     $header_str = $message->txtHeaders($headers_php);
5d1101 1331     
A 1332     // #1485779
1333     if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1334       if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) {
1335         $headers_enc['To'] = implode(', ', $m[1]);
1336         }
1337       }
1338        
fba1f5 1339     if (ini_get('safe_mode'))
T 1340       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
1341     else
1342       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
1343     }
1344   
79aeb3 1345   if ($sent)
A 1346   {
1347     // remove MDN headers after sending
ae8f19 1348     unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
79aeb3 1349     
A 1350     if ($CONFIG['smtp_log'])
146977 1351       write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s",
a22cb6 1352         $RCMAIL->user->get_username(),
79aeb3 1353         $_SERVER['REMOTE_ADDR'],
A 1354         $mailto,
1355         !empty($smtp_response) ? join('; ', $smtp_response) : ''));
1356   }
fba1f5 1357   
T 1358   $message->_headers = array();
1359   $message->headers($headers);
1360   
1361   return $sent;
1362 }
f11541 1363
T 1364
0ea884 1365 function rcmail_send_mdn($uid)
T 1366 {
83a763 1367   global $RCMAIL, $IMAP;
8fa58e 1368
T 1369   $message = new rcube_message($uid);
0ea884 1370   
5c771c 1371   if ($message->headers->mdn_to && !$message->headers->mdn_sent &&
A 1372     ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*')))
0ea884 1373   {
83a763 1374     $identity = $RCMAIL->user->get_identity();
0ea884 1375     $sender = format_email_recipient($identity['email'], $identity['name']);
8fa58e 1376     $recipient = array_shift($IMAP->decode_address_list($message->headers->mdn_to));
0ea884 1377     $mailto = $recipient['mailto'];
T 1378
83a763 1379     $compose = new rcube_mail_mime($RCMAIL->config->header_delimiter());
0ea884 1380     $compose->setParam(array(
T 1381       'text_encoding' => 'quoted-printable',
1382       'html_encoding' => 'quoted-printable',
1383       'head_encoding' => 'quoted-printable',
1384       'head_charset'  => RCMAIL_CHARSET,
1385       'html_charset'  => RCMAIL_CHARSET,
1386       'text_charset'  => RCMAIL_CHARSET,
1387     ));
1388     
1389     // compose headers array
1390     $headers = array(
1391       'Date' => date('r'),
1392       'From' => $sender,
8fa58e 1393       'To'   => $message->headers->mdn_to,
T 1394       'Subject' => rcube_label('receiptread') . ': ' . $message->subject,
83a763 1395       'Message-ID' => sprintf('<%s@%s>', md5(uniqid('rcmail'.rand(),true)), $RCMAIL->config->mail_domain($_SESSION['imap_host'])),
0ea884 1396       'X-Sender' => $identity['email'],
T 1397       'Content-Type' => 'multipart/report; report-type=disposition-notification',
1398     );
1399     
83a763 1400     if ($agent = $RCMAIL->config->get('useragent'))
T 1401       $headers['User-Agent'] = $agent;
0ea884 1402
T 1403     $body = rcube_label("yourmessage") . "\r\n\r\n" .
8fa58e 1404       "\t" . rcube_label("to") . ': ' . rcube_imap::decode_mime_string($message->headers->to, $message->headers->charset) . "\r\n" .
T 1405       "\t" . rcube_label("subject") . ': ' . $message->subject . "\r\n" .
83a763 1406       "\t" . rcube_label("sent") . ': ' . format_date($message->headers->date, $RCMAIL->config->get('date_long')) . "\r\n" .
0ea884 1407       "\r\n" . rcube_label("receiptnote") . "\r\n";
T 1408     
83a763 1409     $ua = $RCMAIL->config->get('useragent', "RoundCube Webmail (Version ".RCMAIL_VERSION.")");
0ea884 1410     $report = "Reporting-UA: $ua\r\n";
T 1411     
8fa58e 1412     if ($message->headers->to)
T 1413         $report .= "Original-Recipient: {$message->headers->to}\r\n";
0ea884 1414     
T 1415     $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
8fa58e 1416                "Original-Message-ID: {$message->headers->messageID}\r\n" .
0ea884 1417                "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
T 1418     
8fa58e 1419     $compose->headers($headers);
7145e0 1420     $compose->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
0ea884 1421     $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
T 1422
1423     $sent = rcmail_deliver_message($compose, $identity['email'], $mailto);
1424
1425     if ($sent)
1426     {
8fa58e 1427       $IMAP->set_flag($message->uid, 'MDNSENT');
0ea884 1428       return true;
T 1429     }
1430   }
1431   
1432   return false;
1433 }
1434
1435
e538b3 1436 function rcmail_search_filter($attrib)
A 1437 {
1438   global $OUTPUT;
1439
1440   if (!strlen($attrib['id']))
1441     $attrib['id'] = 'rcmlistfilter';
1442
1443   $attrib['onchange'] = JS_OBJECT_NAME.'.filter_mailbox(this.value)';
1444   
1445   /*
1446     RFC3501 (6.4.4): 'ALL', 'RECENT', 
1447     'ANSWERED', 'DELETED', 'FLAGGED', 'SEEN',
1448     'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNSEEN',
1449     'NEW', // = (RECENT UNSEEN)
1450     'OLD' // = NOT RECENT
1451   */
1452
1453   $select_filter = new html_select($attrib);
1454   $select_filter->add(rcube_label('all'), 'ALL');
1455   $select_filter->add(rcube_label('unread'), 'UNSEEN');
1456   $select_filter->add(rcube_label('flagged'), 'FLAGGED');
1457   $select_filter->add(rcube_label('unanswered'), 'UNANSWERED');
1458
1459   $out = $select_filter->show($_SESSION['search_filter']);
1460
1461   $OUTPUT->add_gui_object('search_filter', $attrib['id']);
1462
1463   return $out;                                        
1464 }
1465
f11541 1466 // register UI objects
T 1467 $OUTPUT->add_handlers(array(
1468   'mailboxlist' => 'rcmail_mailbox_list',
1469   'messages' => 'rcmail_message_list',
1470   'messagecountdisplay' => 'rcmail_messagecount_display',
1471   'quotadisplay' => 'rcmail_quota_display',
ac5d15 1472   'mailboxname' => 'rcmail_mailbox_name_display',
f11541 1473   'messageheaders' => 'rcmail_message_headers',
T 1474   'messagebody' => 'rcmail_message_body',
1475   'messagecontentframe' => 'rcmail_messagecontent_frame',
1476   'messagepartframe' => 'rcmail_message_part_frame',
1477   'messagepartcontrols' => 'rcmail_message_part_controls',
e538b3 1478   'searchfilter' => 'rcmail_search_filter',
47124c 1479   'searchform' => array($OUTPUT, 'search_form'),
f11541 1480 ));
T 1481
93be5b 1482 ?>