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

Last change on this file since 1400 was 1400, checked in by thomasb, 5 years ago

New class rcube_message representing a mail message; changed global $MESSAGE from array to object

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.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-2008, 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/enriched.inc');
23require_once('include/rcube_smtp.inc');
24
25
26$EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
27
28if (empty($_SESSION['mbox']))
29  $_SESSION['mbox'] = $IMAP->get_mailbox_name();
30
31// set imap properties and session vars
32if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
33  $IMAP->set_mailbox(($_SESSION['mbox'] = $mbox));
34
35if (!empty($_GET['_page']))
36  $IMAP->set_page(($_SESSION['page'] = intval($_GET['_page'])));
37
38// set mailbox to INBOX if not set
39if (empty($_SESSION['mbox']))
40  $_SESSION['mbox'] = $IMAP->get_mailbox_name();
41
42// set default sort col/order to session
43if (!isset($_SESSION['sort_col']))
44  $_SESSION['sort_col'] = $CONFIG['message_sort_col'];
45if (!isset($_SESSION['sort_order']))
46  $_SESSION['sort_order'] = $CONFIG['message_sort_order'];
47
48// set message set for search result
49if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']]))
50  {
51  $IMAP->set_search_set($_SESSION['search'][$_REQUEST['_search']]);
52  $OUTPUT->set_env('search_request', $_REQUEST['_search']);
53  $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
54  }
55
56
57// set current mailbox in client environment
58$OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
59$OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
60
61if ($CONFIG['trash_mbox'])
62  $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
63if ($CONFIG['drafts_mbox'])
64  $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
65if ($CONFIG['junk_mbox'])
66  $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
67
68if (!$OUTPUT->ajax_call)
69  rcube_add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage');
70
71// set page title
72if (empty($RCMAIL->action) || $RCMAIL->action == 'list')
73  $OUTPUT->set_pagetitle(rcmail_localize_foldername($IMAP->get_mailbox_name()));
74
75
76
77// return the message list as HTML table
78function rcmail_message_list($attrib)
79  {
80  global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT;
81
82  $skin_path = $CONFIG['skin_path'];
83  $image_tag = '<img src="%s%s" alt="%s" border="0" />';
84
85  // check to see if we have some settings for sorting
86  $sort_col   = $_SESSION['sort_col'];
87  $sort_order = $_SESSION['sort_order'];
88 
89  // add some labels to client
90  rcube_add_label('from', 'to');
91
92  // get message headers
93  $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
94
95  // add id to message list table if not specified
96  if (!strlen($attrib['id']))
97    $attrib['id'] = 'rcubemessagelist';
98
99  // allow the following attributes to be added to the <table> tag
100  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
101
102  $out = '<table' . $attrib_str . ">\n";
103
104
105  // define list of cols to be displayed
106  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
107  $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
108
109  $mbox = $IMAP->get_mailbox_name();
110 
111  // show 'to' instead of from in sent messages
112  if (($mbox==$CONFIG['sent_mbox'] || $mbox==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
113      && !array_search('to', $a_show_cols))
114    $a_show_cols[$f] = 'to';
115 
116  // add col definition
117  $out .= '<colgroup>';
118  $out .= '<col class="icon" />';
119
120  foreach ($a_show_cols as $col)
121    $out .= sprintf('<col class="%s" />', $col);
122
123  $out .= '<col class="icon" />';
124  $out .= "</colgroup>\n";
125
126  // add table title
127  $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
128
129  $javascript = '';
130  foreach ($a_show_cols as $col)
131    {
132    // get column name
133    $col_name = Q(rcube_label($col));
134
135    // make sort links
136    $sort = '';
137    if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
138      {
139      // have buttons configured
140      if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
141        {
142        $sort = '&nbsp;&nbsp;';
143
144        // asc link
145        if (!empty($attrib['sortascbutton']))
146          {
147          $sort .= $OUTPUT->button(array(
148            'command' => 'sort',
149            'prop' => $col.'_ASC',
150            'image' => $attrib['sortascbutton'],
151            'align' => 'absmiddle',
152            'title' => 'sortasc'));
153          }       
154       
155        // desc link
156        if (!empty($attrib['sortdescbutton']))
157          {
158          $sort .= $OUTPUT->button(array(
159            'command' => 'sort',
160            'prop' => $col.'_DESC',
161            'image' => $attrib['sortdescbutton'],
162            'align' => 'absmiddle',
163            'title' => 'sortdesc'));
164          }
165        }
166      // just add a link tag to the header
167      else
168        {
169        $col_name = sprintf(
170          '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
171          JS_OBJECT_NAME,
172          $col,
173          rcube_label('sortby'),
174          $col_name);
175        }
176      }
177     
178    $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
179
180    // put it all together
181    $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";   
182    }
183
184  $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
185  $out .= "</tr></thead>\n<tbody>\n";
186
187  // no messages in this mailbox
188  if (!sizeof($a_headers))
189    $OUTPUT->show_message('nomessagesfound', 'notice');
190
191
192  $a_js_message_arr = array();
193
194  // create row for each message
195  foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
196    {
197    $message_icon = $attach_icon = '';
198    $js_row_arr = array();
199    $zebra_class = $i%2 ? 'even' : 'odd';
200
201    // set messag attributes to javascript array
202    if ($header->deleted)
203      $js_row_arr['deleted'] = true;
204    if (!$header->seen)
205      $js_row_arr['unread'] = true;
206    if ($header->answered)
207      $js_row_arr['replied'] = true;
208    // set message icon 
209    if ($attrib['deletedicon'] && $header->deleted)
210      $message_icon = $attrib['deletedicon'];
211    else if ($attrib['unreadicon'] && !$header->seen)
212      $message_icon = $attrib['unreadicon'];
213    else if ($attrib['repliedicon'] && $header->answered)
214      $message_icon = $attrib['repliedicon'];
215    else if ($attrib['messageicon'])
216      $message_icon = $attrib['messageicon'];
217   
218    // set attachment icon
219    if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
220      $attach_icon = $attrib['attachmenticon'];
221       
222    $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
223                    $header->uid,
224                    $header->seen ? '' : ' unread',
225                    $header->deleted ? ' deleted' : '',
226                    $zebra_class);   
227   
228    $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
229
230    if (!empty($header->charset))
231      $IMAP->set_charset($header->charset);
232 
233    // format each col
234    foreach ($a_show_cols as $col)
235      {
236      if ($col=='from' || $col=='to')
237        $cont = Q(rcmail_address_string($header->$col, 3, $attrib['addicon']), 'show');
238      else if ($col=='subject')
239        {
240        $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
241        $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
242        $cont = Q($IMAP->decode_header($header->$col));
243        if (empty($cont)) $cont = Q(rcube_label('nosubject'));
244        $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);
245        }
246      else if ($col=='size')
247        $cont = show_bytes($header->$col);
248      else if ($col=='date')
249        $cont = format_date($header->date);
250      else
251        $cont = Q($header->$col);
252       
253      $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
254      }
255
256    $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
257    $out .= "</tr>\n";
258   
259    if (sizeof($js_row_arr))
260      $a_js_message_arr[$header->uid] = $js_row_arr;
261    }
262 
263  // complete message table
264  $out .= "</tbody></table>\n";
265 
266 
267  $message_count = $IMAP->messagecount();
268 
269  // set client env
270  $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
271  $OUTPUT->add_gui_object('messagelist', $attrib['id']);
272  $OUTPUT->set_env('messagecount', $message_count);
273  $OUTPUT->set_env('current_page', $IMAP->list_page);
274  $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
275  $OUTPUT->set_env('sort_col', $sort_col);
276  $OUTPUT->set_env('sort_order', $sort_order);
277 
278  if ($attrib['messageicon'])
279    $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
280  if ($attrib['deletedicon'])
281    $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
282  if ($attrib['unreadicon'])
283    $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
284  if ($attrib['repliedicon'])
285    $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
286  if ($attrib['attachmenticon'])
287    $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
288 
289  $OUTPUT->set_env('messages', $a_js_message_arr);
290  $OUTPUT->set_env('coltypes', $a_show_cols);
291 
292  $OUTPUT->include_script('list.js');
293 
294  return $out;
295  }
296
297
298// return javascript commands to add rows to the message list
299function rcmail_js_message_list($a_headers, $insert_top=FALSE)
300  {
301  global $CONFIG, $IMAP, $OUTPUT;
302
303  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
304  $mbox = $IMAP->get_mailbox_name();
305
306  // show 'to' instead of from in sent messages
307  if (($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox'])
308      && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
309    $a_show_cols[$f] = 'to';
310
311  $OUTPUT->command('set_message_coltypes', $a_show_cols);
312
313  // loop through message headers
314  foreach ($a_headers as $n => $header)
315    {
316    $a_msg_cols = array();
317    $a_msg_flags = array();
318   
319    if (empty($header))
320      continue;
321
322    if (!empty($header->charset))
323      $IMAP->set_charset($header->charset);
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($IMAP->decode_header($header->$col));
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($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($IMAP->decode_header($headers[$hkey]));
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, $REMOTE_OBJECTS;
996 
997  if (!is_array($MESSAGE->parts) && empty($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  if (!empty($MESSAGE->parts))
1013    {
1014    foreach ($MESSAGE->parts as $i => $part)
1015      {
1016      if ($part->type == 'headers')
1017        $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
1018      else if ($part->type == 'content')
1019        {
1020        if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
1021          $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
1022
1023        // fetch part if not available
1024        if (!isset($part->body))
1025          $part->body = $MESSAGE->get_part_content($part->mime_id);
1026
1027        $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']);
1028        $out .= '<div class="message-part">';
1029       
1030        if ($part->ctype_secondary != 'plain')
1031          $out .= rcmail_sanitize_html($body, $attrib['id']);
1032        else
1033          $out .= $body;
1034
1035        $out .= "</div>\n";
1036        }
1037      }
1038    }
1039  else
1040    $out .= $MESSAGE->body;
1041
1042
1043  $ctype_primary = strtolower($MESSAGE->structure->ctype_primary);
1044  $ctype_secondary = strtolower($MESSAGE->structure->ctype_secondary);
1045 
1046  // list images after mail body
1047  if (get_boolean($attrib['showimages']) && $ctype_primary == 'multipart' &&
1048      !empty($MESSAGE->attachments) && !strstr($message_body, '<html')) {
1049    foreach ($MESSAGE->attachments as $attach_prop) {
1050      if (strpos($attach_prop->mimetype, 'image/') === 0) {
1051        $out .= html::tag('hr') . html::p(array('align' => "center"),
1052          html::img(array(
1053            'src' => $MESSAGE->get_part_url($attach_prop->mime_id),
1054            'title' => $attach_prop->filename,
1055            'alt' => $attach_prop->filename,
1056          )));
1057        }
1058    }
1059  }
1060 
1061  // tell client that there are blocked remote objects
1062  if ($REMOTE_OBJECTS && !$safe_mode)
1063    $OUTPUT->set_env('blockedobjects', true);
1064
1065  $out .= "\n</div>";
1066  return $out;
1067  }
1068
1069
1070
1071// modify a HTML message that it can be displayed inside a HTML page
1072function rcmail_sanitize_html($body, $container_id)
1073  {
1074  // remove any null-byte characters before parsing
1075  $body = preg_replace('/\x00/', '', $body);
1076 
1077  $base_url = "";
1078  $last_style_pos = 0;
1079  $body_lc = strtolower($body);
1080 
1081  // check for <base href>
1082  if (preg_match(($base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i'), $body, $base_regs))
1083    $base_url = $base_regs[2];
1084 
1085  // find STYLE tags
1086  while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
1087    {
1088    $pos = strpos($body_lc, '>', $pos)+1;
1089
1090    // replace all css definitions with #container [def]
1091    $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id, $base_url);
1092
1093    $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
1094    $body_lc = strtolower($body);
1095    $last_style_pos = $pos2;
1096    }
1097
1098
1099  // remove SCRIPT tags
1100  foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
1101    {
1102    while (($pos = strpos($body_lc, '<'.$tag)) && (($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)) || ($pos3 = strpos($body_lc, '>', $pos))))
1103      {
1104      $end = $pos2 ? $pos2 + strlen('</'.$tag.'>') : $pos3 + 1;
1105      $body = substr($body, 0, $pos) . substr($body, $end, strlen($body)-$end);
1106      $body_lc = strtolower($body);
1107      }
1108    }
1109
1110  // replace event handlers on any object
1111  while ($body != $prev_body)
1112    {
1113    $prev_body = $body;
1114    $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);
1115    $body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
1116    }
1117
1118  // resolve <base href>
1119  if ($base_url)
1120    {
1121    $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
1122    $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
1123    $body = preg_replace($base_reg, '', $body);
1124    }
1125   
1126  // modify HTML links to open a new window if clicked
1127  $body = preg_replace('/<(a|link)\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1','\\2', '$container_id');", $body);
1128
1129  // add comments arround html and other tags
1130  $out = preg_replace(array(
1131      '/(<!DOCTYPE[^>]*>)/i',
1132      '/(<\?xml[^>]*>)/i',
1133      '/(<\/?html[^>]*>)/i',
1134      '/(<\/?head[^>]*>)/i',
1135      '/(<title[^>]*>.*<\/title>)/Ui',
1136      '/(<\/?meta[^>]*>)/i'),
1137    '<!--\\1-->',
1138    $body);
1139
1140  $out = preg_replace(
1141    array(
1142      '/<body([^>]*)>/i',
1143      '/<\/body>/i',
1144    ),
1145    array(
1146      '<div class="rcmBody"\\1>',
1147      '</div>',
1148    ),
1149    $out);
1150
1151  // quote <? of php and xml files that are specified as text/html
1152  $out = preg_replace(array('/<\?/', '/\?>/'), array('&lt;?', '?&gt;'), $out);
1153
1154  return $out;
1155  }
1156
1157
1158// parse link attributes and set correct target
1159function rcmail_alter_html_link($tag, $attrs, $container_id)
1160  {
1161  $attrib = parse_attrib_string($attrs);
1162
1163  if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href']))
1164    $attrib['href'] = "./bin/modcss.php?u=" . urlencode($attrib['href']) . "&amp;c=" . urlencode($container_id);
1165
1166  else if (stristr((string)$attrib['href'], 'mailto:'))
1167    $attrib['onclick'] = sprintf(
1168      "return %s.command('compose','%s',this)",
1169      JS_OBJECT_NAME,
1170      JQ(substr($attrib['href'], 7)));
1171
1172  else if (!empty($attrib['href']) && $attrib['href']{0}!='#')
1173    $attrib['target'] = '_blank';
1174
1175  return "<$tag" . create_attrib_string($attrib, array('href','name','target','onclick','id','class','style','title','rel','type','media')) . ' />';
1176  }
1177
1178
1179// decode address string and re-format it as HTML links
1180function rcmail_address_string($input, $max=NULL, $addicon=NULL)
1181  {
1182  global $IMAP, $PRINT_MODE, $CONFIG, $OUTPUT, $EMAIL_ADDRESS_PATTERN;
1183
1184  $a_parts = $IMAP->decode_address_list($input);
1185
1186  if (!sizeof($a_parts))
1187    return $input;
1188
1189  $c = count($a_parts);
1190  $j = 0;
1191  $out = '';
1192
1193  foreach ($a_parts as $part)
1194    {
1195    $j++;
1196    if ($PRINT_MODE)
1197      $out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
1198    else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto']))
1199      {
1200      $out .= sprintf('<a href="mailto:%s" onclick="return %s.command(\'compose\',\'%s\',this)" class="rcmContactAddress" title="%s">%s</a>',
1201                      Q($part['mailto']),
1202                      JS_OBJECT_NAME,
1203                      JQ($part['mailto']),
1204                      Q($part['mailto']),
1205                      Q($part['name']));
1206                     
1207      if ($addicon)
1208        $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>',
1209                        JS_OBJECT_NAME,
1210                        urlencode($part['string']),
1211                        rcube_label('addtoaddressbook'),
1212                        $CONFIG['skin_path'],
1213                        $addicon);
1214      }
1215    else
1216      {
1217      if ($part['name'])
1218        $out .= Q($part['name']);
1219      if ($part['mailto'])
1220        $out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($part['mailto']));
1221      }
1222     
1223    if ($c>$j)
1224      $out .= ','.($max ? '&nbsp;' : ' ');
1225       
1226    if ($max && $j==$max && $c>$j)
1227      {
1228      $out .= '...';
1229      break;
1230      }       
1231    }
1232   
1233  return $out;
1234  }
1235
1236
1237function rcmail_message_part_controls()
1238  {
1239  global $MESSAGE;
1240 
1241  $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
1242  if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part])
1243    return '';
1244   
1245  $part = $MESSAGE->mime_parts[$part];
1246  $table = new html_table(array('cols' => 3));
1247 
1248  if (!empty($part->filename)) {
1249    $table->add('title', Q(rcube_label('filename')));
1250    $table->add(null, Q($part->filename));
1251    $table->add(null, '[' . html::a(str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']');
1252  }
1253 
1254  if (!empty($part->size)) {
1255    $table->add('title', Q(rcube_label('filesize')));
1256    $table->add(null, Q(show_bytes($part->size)));
1257  }
1258 
1259  return $table->show($attrib);
1260  }
1261
1262
1263
1264function rcmail_message_part_frame($attrib)
1265  {
1266  global $MESSAGE;
1267 
1268  $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
1269  $ctype_primary = strtolower($part->ctype_primary);
1270
1271  $attrib['src'] = Q('./?'.str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']));
1272
1273  $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height'));
1274  $out = '<iframe '. $attrib_str . "></iframe>";
1275   
1276  return $out;
1277  }
1278
1279
1280// clear message composing settings
1281function rcmail_compose_cleanup()
1282  {
1283  if (!isset($_SESSION['compose']))
1284    return;
1285
1286  // remove attachment files from temp dir
1287  if (is_array($_SESSION['compose']['attachments']))
1288    foreach ($_SESSION['compose']['attachments'] as $attachment)
1289      @unlink($attachment['path']);
1290 
1291  unset($_SESSION['compose']);
1292  }
1293 
1294
1295/**
1296 * Send the given message compose object using the configured method
1297 */
1298function rcmail_deliver_message(&$message, $from, $mailto)
1299{
1300  global $CONFIG;
1301
1302  $msg_body = $message->get();
1303  $headers = $message->headers();
1304 
1305  // send thru SMTP server using custom SMTP library
1306  if ($CONFIG['smtp_server'])
1307    {
1308    // generate list of recipients
1309    $a_recipients = array($mailto);
1310 
1311    if (strlen($headers['Cc']))
1312      $a_recipients[] = $headers['Cc'];
1313    if (strlen($headers['Bcc']))
1314      $a_recipients[] = $headers['Bcc'];
1315 
1316    // clean Bcc from header for recipients
1317    $send_headers = $headers;
1318    unset($send_headers['Bcc']);
1319    // here too, it because txtHeaders() below use $message->_headers not only $send_headers
1320    unset($message->_headers['Bcc']);
1321
1322    // send message
1323    $smtp_response = array();
1324    $sent = smtp_mail($from, $a_recipients, ($foo = $message->txtHeaders($send_headers, true)), $msg_body, $smtp_response);
1325
1326    // log error
1327    if (!$sent)
1328      raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
1329                        'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE);
1330    }
1331 
1332  // send mail using PHP's mail() function
1333  else
1334    {
1335    // unset some headers because they will be added by the mail() function
1336    $headers_enc = $message->headers($headers);
1337    $headers_php = $message->_headers;
1338    unset($headers_php['To'], $headers_php['Subject']);
1339   
1340    // reset stored headers and overwrite
1341    $message->_headers = array();
1342    $header_str = $message->txtHeaders($headers_php);
1343 
1344    if (ini_get('safe_mode'))
1345      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
1346    else
1347      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
1348    }
1349 
1350  if ($sent)  // remove MDN headers after sending
1351    unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
1352 
1353  $message->_headers = array();
1354  $message->headers($headers);
1355 
1356  return $sent;
1357}
1358
1359
1360function rcmail_send_mdn($uid)
1361{
1362  global $CONFIG, $USER, $IMAP;
1363
1364  $message = new rcube_message($uid);
1365 
1366  if ($message->headers->mdn_to && !$message->headers->mdn_sent)
1367  {
1368    $identity = $USER->get_identity();
1369    $sender = format_email_recipient($identity['email'], $identity['name']);
1370    $recipient = array_shift($IMAP->decode_address_list($message->headers->mdn_to));
1371    $mailto = $recipient['mailto'];
1372
1373    $compose = new rcube_mail_mime(rcmail_header_delm());
1374    $compose->setParam(array(
1375      'text_encoding' => 'quoted-printable',
1376      'html_encoding' => 'quoted-printable',
1377      'head_encoding' => 'quoted-printable',
1378      'head_charset'  => RCMAIL_CHARSET,
1379      'html_charset'  => RCMAIL_CHARSET,
1380      'text_charset'  => RCMAIL_CHARSET,
1381    ));
1382   
1383    // compose headers array
1384    $headers = array(
1385      'Date' => date('r'),
1386      'From' => $sender,
1387      'To'   => $message->headers->mdn_to,
1388      'Subject' => rcube_label('receiptread') . ': ' . $message->subject,
1389      'Message-ID' => sprintf('<%s@%s>', md5(uniqid('rcmail'.rand(),true)), rcmail_mail_domain($_SESSION['imap_host'])),
1390      'X-Sender' => $identity['email'],
1391      'Content-Type' => 'multipart/report; report-type=disposition-notification',
1392    );
1393   
1394    if (!empty($CONFIG['useragent']))
1395      $headers['User-Agent'] = $CONFIG['useragent'];
1396
1397    $body = rcube_label("yourmessage") . "\r\n\r\n" .
1398      "\t" . rcube_label("to") . ': ' . rcube_imap::decode_mime_string($message->headers->to, $message->headers->charset) . "\r\n" .
1399      "\t" . rcube_label("subject") . ': ' . $message->subject . "\r\n" .
1400      "\t" . rcube_label("sent") . ': ' . format_date($message->headers->date, $CONFIG['date_long']) . "\r\n" .
1401      "\r\n" . rcube_label("receiptnote") . "\r\n";
1402   
1403    $ua = !empty($CONFIG['useragent']) ? $CONFIG['useragent'] : "RoundCube Webmail (Version ".RCMAIL_VERSION.")";
1404    $report = "Reporting-UA: $ua\r\n";
1405   
1406    if ($message->headers->to)
1407        $report .= "Original-Recipient: {$message->headers->to}\r\n";
1408   
1409    $report .= "Final-Recipient: rfc822; {$identity['email']}\r\n" .
1410               "Original-Message-ID: {$message->headers->messageID}\r\n" .
1411               "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
1412   
1413    $compose->headers($headers);
1414    $compose->setTXTBody(wordwrap($body, 75, "\r\n"));
1415    $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
1416
1417    $sent = rcmail_deliver_message($compose, $identity['email'], $mailto);
1418
1419    if ($sent)
1420    {
1421      $IMAP->set_flag($message->uid, 'MDNSENT');
1422      return true;
1423    }
1424  }
1425 
1426  return false;
1427}
1428
1429
1430// register UI objects
1431$OUTPUT->add_handlers(array(
1432  'mailboxlist' => 'rcmail_mailbox_list',
1433  'messages' => 'rcmail_message_list',
1434  'messagecountdisplay' => 'rcmail_messagecount_display',
1435  'quotadisplay' => 'rcmail_quota_display',
1436  'messageheaders' => 'rcmail_message_headers',
1437  'messagebody' => 'rcmail_message_body',
1438  'messagecontentframe' => 'rcmail_messagecontent_frame',
1439  'messagepartframe' => 'rcmail_message_part_frame',
1440  'messagepartcontrols' => 'rcmail_message_part_controls',
1441  'searchform' => array($OUTPUT, 'search_form'),
1442));
1443
1444?>
Note: See TracBrowser for help on using the repository browser.