source: github/program/steps/mail/func.inc @ 6d2714b

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since 6d2714b was 6d2714b, checked in by alecpl <alec@…>, 5 years ago

#1484972: optimization: mark as read in one action with preview, deleted redundant quota reads

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