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