source: subversion/trunk/roundcubemail/program/steps/mail/func.inc @ 3258

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